67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
import { test } from 'node:test';
|
|||
|
|
import { strict as assert } from 'node:assert';
|
||
|
|
import { DatabaseSync } from 'node:sqlite';
|
||
|
|
import {
|
||
|
|
curatedCusipForSymbol,
|
||
|
|
resolveCusipLocal,
|
||
|
|
seedCuratedCusips,
|
||
|
|
} from '../cusipRegistry.ts';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Unit-level tests for CUSIP cache helpers via sec fetch behavior contracts.
|
||
|
|
* Network resolve is not exercised here.
|
||
|
|
*/
|
||
|
|
test('kv_cache can store and read sec:cusip keys', () => {
|
||
|
|
const db = new DatabaseSync(':memory:');
|
||
|
|
db.exec(`
|
||
|
|
CREATE TABLE kv_cache (key TEXT PRIMARY KEY, value TEXT NOT NULL, observed_at TEXT NOT NULL);
|
||
|
|
`);
|
||
|
|
const key = 'sec:cusip:IREN';
|
||
|
|
const cusip = 'Q4982L109';
|
||
|
|
db.prepare('INSERT OR REPLACE INTO kv_cache (key,value,observed_at) VALUES (?,?,?)').run(
|
||
|
|
key,
|
||
|
|
cusip,
|
||
|
|
new Date().toISOString(),
|
||
|
|
);
|
||
|
|
const row = db.prepare('SELECT value FROM kv_cache WHERE key=?').get(key) as { value: string };
|
||
|
|
assert.equal(row.value, cusip);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('curated map resolves IREN offline', () => {
|
||
|
|
assert.equal(curatedCusipForSymbol('IREN'), 'Q4982L109');
|
||
|
|
assert.equal(curatedCusipForSymbol('AAPL'), '037833100');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('resolveCusipLocal seeds cache from curated map', () => {
|
||
|
|
const db = new DatabaseSync(':memory:');
|
||
|
|
db.exec(`
|
||
|
|
CREATE TABLE kv_cache (key TEXT PRIMARY KEY, value TEXT NOT NULL, observed_at TEXT NOT NULL);
|
||
|
|
`);
|
||
|
|
const c = resolveCusipLocal(db, 'IREN');
|
||
|
|
assert.equal(c, 'Q4982L109');
|
||
|
|
const row = db.prepare('SELECT value FROM kv_cache WHERE key=?').get('sec:cusip:IREN') as {
|
||
|
|
value: string;
|
||
|
|
};
|
||
|
|
assert.equal(row.value, 'Q4982L109');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('seedCuratedCusips is idempotent', () => {
|
||
|
|
const db = new DatabaseSync(':memory:');
|
||
|
|
db.exec(`
|
||
|
|
CREATE TABLE kv_cache (key TEXT PRIMARY KEY, value TEXT NOT NULL, observed_at TEXT NOT NULL);
|
||
|
|
`);
|
||
|
|
const n1 = seedCuratedCusips(db);
|
||
|
|
const n2 = seedCuratedCusips(db);
|
||
|
|
assert.ok(n1 > 0);
|
||
|
|
assert.equal(n2, 0);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('isPermanentDataError does not quarantine cusip failures', async () => {
|
||
|
|
const { isPermanentDataError } = await import('../../queue/sourceRatePolicy.ts');
|
||
|
|
assert.equal(
|
||
|
|
isPermanentDataError('sec-fetch:IREN: cusip not resolved - 13F holder refresh blocked'),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
assert.equal(isPermanentDataError('quote not found for symbol'), true);
|
||
|
|
});
|