141 lines
4.0 KiB
TypeScript
141 lines
4.0 KiB
TypeScript
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "bun:test";
|
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { Database } from "bun:sqlite";
|
|
import { __dbInternals } from "@/lib/server/db";
|
|
import type {
|
|
IssuerOverlayDefinition,
|
|
IssuerOverlayDiagnostics,
|
|
IssuerOverlayStats,
|
|
} from "@/lib/server/db/schema";
|
|
import {
|
|
ensureIssuerOverlayRow,
|
|
getIssuerOverlay,
|
|
listIssuerOverlayRevisions,
|
|
publishIssuerOverlayRevision,
|
|
} from "./issuer-overlays";
|
|
|
|
let tempDir: string | null = null;
|
|
let sqliteClient: Database | null = null;
|
|
function resetDbSingletons() {
|
|
const globalState = globalThis as typeof globalThis & {
|
|
__fiscalSqliteClient?: Database;
|
|
__fiscalDrizzleDb?: unknown;
|
|
__financialIngestionSchemaStatus?: unknown;
|
|
};
|
|
|
|
globalState.__fiscalSqliteClient?.close();
|
|
globalState.__fiscalSqliteClient = undefined;
|
|
globalState.__fiscalDrizzleDb = undefined;
|
|
globalState.__financialIngestionSchemaStatus = undefined;
|
|
}
|
|
|
|
function sampleDefinition(): IssuerOverlayDefinition {
|
|
return {
|
|
version: "fiscal-v1",
|
|
ticker: "AAPL",
|
|
pack: "core",
|
|
mappings: [
|
|
{
|
|
surface_key: "revenue",
|
|
statement: "income",
|
|
allowed_source_concepts: ["aapl:ServicesRevenue", "aapl:ProductRevenue"],
|
|
allowed_authoritative_concepts: [],
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
function sampleDiagnostics(): IssuerOverlayDiagnostics {
|
|
return {
|
|
pack: "core",
|
|
sampledSnapshotIds: [11, 12],
|
|
acceptedMappings: [
|
|
{
|
|
qname: "aapl:ServicesRevenue",
|
|
surface_key: "revenue",
|
|
statement: "income",
|
|
reason: "local_name_match",
|
|
source_snapshot_ids: [11, 12],
|
|
},
|
|
],
|
|
rejectedMappings: [],
|
|
};
|
|
}
|
|
|
|
function sampleStats(): IssuerOverlayStats {
|
|
return {
|
|
pack: "core",
|
|
sampledSnapshotCount: 2,
|
|
sampledSnapshotIds: [11, 12],
|
|
acceptedMappingCount: 1,
|
|
rejectedMappingCount: 0,
|
|
publishedRevisionNumber: null,
|
|
};
|
|
}
|
|
|
|
describe("issuer overlay repo", () => {
|
|
beforeAll(async () => {
|
|
tempDir = mkdtempSync(join(tmpdir(), "fiscal-issuer-overlay-"));
|
|
const env = process.env as Record<string, string | undefined>;
|
|
env.DATABASE_URL = `file:${join(tempDir, "repo.sqlite")}`;
|
|
env.NODE_ENV = "test";
|
|
|
|
resetDbSingletons();
|
|
|
|
sqliteClient = new Database(join(tempDir, "repo.sqlite"), { create: true });
|
|
sqliteClient.exec("PRAGMA foreign_keys = ON;");
|
|
__dbInternals.ensureLocalSqliteSchema(sqliteClient);
|
|
|
|
const globalState = globalThis as typeof globalThis & {
|
|
__fiscalSqliteClient?: Database;
|
|
__fiscalDrizzleDb?: unknown;
|
|
};
|
|
globalState.__fiscalSqliteClient = sqliteClient;
|
|
globalState.__fiscalDrizzleDb = undefined;
|
|
});
|
|
|
|
afterAll(() => {
|
|
sqliteClient?.close();
|
|
resetDbSingletons();
|
|
if (tempDir) {
|
|
rmSync(tempDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
beforeEach(() => {
|
|
sqliteClient?.exec("DELETE FROM issuer_overlay;");
|
|
sqliteClient?.exec("DELETE FROM issuer_overlay_revision;");
|
|
});
|
|
|
|
it("creates an empty overlay row on ensure", async () => {
|
|
const overlay = await ensureIssuerOverlayRow("aapl");
|
|
expect(overlay?.ticker).toBe("AAPL");
|
|
expect(overlay?.status).toBe("empty");
|
|
expect(overlay?.active_revision).toBeNull();
|
|
});
|
|
|
|
it("publishes and deduplicates overlay revisions by content hash", async () => {
|
|
const first = await publishIssuerOverlayRevision({
|
|
ticker: "AAPL",
|
|
definition: sampleDefinition(),
|
|
diagnostics: sampleDiagnostics(),
|
|
stats: sampleStats(),
|
|
});
|
|
const second = await publishIssuerOverlayRevision({
|
|
ticker: "AAPL",
|
|
definition: sampleDefinition(),
|
|
diagnostics: sampleDiagnostics(),
|
|
stats: sampleStats(),
|
|
});
|
|
const overlay = await getIssuerOverlay("AAPL");
|
|
const revisions = await listIssuerOverlayRevisions("AAPL");
|
|
|
|
expect(first.published).toBe(true);
|
|
expect(second.published).toBe(false);
|
|
expect(overlay?.active_revision?.id).toBe(first.revision.id);
|
|
expect(revisions).toHaveLength(1);
|
|
});
|
|
});
|