Files
OmniRoute/tests/unit/db-usageanalytics-split.test.ts
Diego Rodrigues de Sa e Souza 311afb0b55 refactor(db): extract pure SQL-source builders from usageAnalytics.ts (#5722)
usageAnalytics.ts (924 lines, frozen-baselined) mixes two pure SQL-source
builders with ~20 getXxxRows() query functions. Extract the contiguous,
DB-free builder block verbatim into a leaf, leaving every query function in
the host:

  - usageAnalytics/sources.ts (208)  AnalyticsParams, BuildUnifiedSourceOptions,
    UnifiedSourceResult + buildUnifiedSource + buildPresetUnifiedSource (pure
    string builders; no DB, no imports)

Host usageAnalytics.ts: 924 -> 723. The query functions do not call the
builders (callers build the unified source then pass the string in), so the
host re-exports the 5 moved public symbols (2 fns + 3 types) and imports
AnalyticsParams as a type for its query signatures — the public API stays
IDENTICAL (39 symbols). Builder bodies moved byte-identical; the two orphaned
section-header banners that described the moved block were removed with it;
the retained query-function suffix is byte-identical to the original.

Behavior-preserving: 37 existing analytics consumer tests stay green
(usage-analytics 12, usage-endpoint-dimension 3, db-usage-analytics-3500 22);
new tests/unit/db-usageanalytics-split.test.ts (25 assertions) characterizes
buildUnifiedSource's needsAggregated branching (raw-only vs raw+daily_usage_summary)
+ guards the 39-symbol re-export barrel.

Refs #3501.
2026-06-30 18:33:00 -03:00

131 lines
5.0 KiB
TypeScript

/**
* Characterization + API-surface test: usageAnalytics.ts god-file decomposition.
*
* The two pure SQL-source-string builders (buildUnifiedSource,
* buildPresetUnifiedSource) + their types were extracted verbatim from
* src/lib/db/usageAnalytics.ts into the pure leaf
* src/lib/db/usageAnalytics/sources.ts (no DB, no imports). The ~20 query
* functions stay in the host.
*
* Verifies that:
* 1. buildUnifiedSource's needsAggregated branching is preserved (pure logic).
* 2. The host usageAnalytics.ts still exposes the FULL public API (39 names).
* 3. The sources leaf exports the builders directly.
*
* Pure value assertions — no DB handle is opened.
*/
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import {
buildUnifiedSource,
buildPresetUnifiedSource,
} from "../../src/lib/db/usageAnalytics/sources.ts";
// ── 1. buildUnifiedSource — pure branching ───────────────────────────────────
describe("usageAnalytics/sources — buildUnifiedSource", () => {
it("includes the daily_usage_summary leg for a wide window with no api-key filter", () => {
const { unifiedSource, unifiedParams } = buildUnifiedSource({
sinceIso: "2024-06-01T00:00:00.000Z",
untilIso: null,
rawCutoffDate: "2024-06-15",
apiKeyWhere: "",
apiKeyParams: {},
});
assert.ok(typeof unifiedSource === "string" && unifiedSource.length > 0);
// needsAggregated = (sinceDate < rawCutoffDate) && !apiKeyWhere => true
assert.ok(
unifiedSource.includes("daily_usage_summary"),
"aggregated leg must be present when the window predates the raw cutoff"
);
assert.equal(unifiedParams.rawCutoff, "2024-06-15");
assert.equal(unifiedParams.rawCutoffDate, "2024-06-15");
});
it("drops the aggregated leg when an api-key filter is active (raw-only)", () => {
const { unifiedSource, unifiedParams } = buildUnifiedSource({
sinceIso: "2024-06-01T00:00:00.000Z",
untilIso: null,
rawCutoffDate: "2024-06-15",
apiKeyWhere: "(api_key_id IN (@apiKey0))",
apiKeyParams: { apiKey0: "k1" },
});
// needsAggregated = ... && !apiKeyWhere => false
assert.ok(
!unifiedSource.includes("daily_usage_summary"),
"aggregated leg must be absent once an api-key filter scopes the query to raw rows"
);
// raw leg floors at @since (not @rawCutoff) and carries the api-key params
assert.equal(unifiedParams.since, "2024-06-01T00:00:00.000Z");
assert.equal(unifiedParams.apiKey0, "k1");
assert.equal(unifiedParams.rawCutoff, undefined);
});
it("buildPresetUnifiedSource returns the unifiedSource/unifiedParams shape", () => {
const result = buildPresetUnifiedSource({
sinceIso: null,
untilIso: null,
rawCutoffDate: "2024-06-15",
apiKeyWhere: "",
apiKeyParams: {},
});
assert.equal(typeof result.unifiedSource, "string");
assert.equal(typeof result.unifiedParams, "object");
assert.ok(result.unifiedParams !== null);
});
});
// ── 2. usageAnalytics.ts — full public API surface preserved ─────────────────
const host = await import("../../src/lib/db/usageAnalytics.ts");
describe("usageAnalytics.ts public API surface", () => {
// the 22 runtime functions (the 17 row/param interfaces are type-only and
// erased at runtime, so they are asserted via typecheck, not here)
const expectedFns = [
"buildUnifiedSource", // re-exported from sources
"buildPresetUnifiedSource", // re-exported from sources
"getUsageSummary",
"getDailyUsage",
"getDailyCostRows",
"getHeatmapRows",
"getModelUsageRows",
"getProviderCostRows",
"getProviderUsageRows",
"getAccountCostRows",
"getAccountUsageRows",
"getApiKeyUsageRows",
"getServiceTierUsageRows",
"getApiKeyMetadataRows",
"getWeeklyPatternRows",
"getPresetCostModelRows",
"getEndpointUsageRows",
"getAllUsageHistory",
"getAllDomainCostHistory",
"getAllDomainBudgets",
];
for (const name of expectedFns) {
it(`exposes ${name} as a function`, () => {
assert.equal(typeof host[name], "function", `${name} must be a function on the host module`);
});
}
it("loses no public runtime function in the split", () => {
const missing = expectedFns.filter((n) => typeof host[n] !== "function");
assert.deepEqual(missing, [], `missing: ${missing.join(", ")}`);
});
});
// ── 3. sources leaf exports the builders directly ────────────────────────────
describe("sources.ts exports the builders directly", () => {
it("buildUnifiedSource / buildPresetUnifiedSource are functions on the leaf", async () => {
const sources = await import("../../src/lib/db/usageAnalytics/sources.ts");
assert.equal(typeof sources.buildUnifiedSource, "function");
assert.equal(typeof sources.buildPresetUnifiedSource, "function");
});
});