import { test } from 'node:test'; import assert from 'node:assert/strict'; import { workloadProvenance, formatLineage } from './portability'; import type { SignedDecisionLogEntry, SpendDecision, Provider } from '../types'; import type { ProvenanceProvider } from './schema'; let seq = 0; function entry(model: string, fam: ProvenanceProvider, route: string, extra: Partial = {}): SignedDecisionLogEntry { const decision: SpendDecision = { decisionId: `d${seq}`, timestamp: `2026-07-10T14:${String(seq).padStart(2, '0')}:00.000Z`, action: 'allow', triggeredCap: null, triggeredScopeKey: 'tenant:acme|agent:planner', projectedCents: 10, actualCents: 10, windowSpendBefore: 0, windowSpendAfter: 0, provider: (fam === 'self_hosted' ? 'unknown' : fam) as Provider, modelRequested: model, modelResolved: model, policyId: 'p', policyVersion: 1, enforcementMode: 'enforce', reasons: [], provenance: { model_identity: { provider: fam, model_id: model, model_version: '1', model_family: fam, weights_origin_country: 'US' }, hosting: { provider_route: route, jurisdiction_country: 'US', jurisdiction_region: 'us' }, compliance: { baa_in_force: false, baa_vendor: null, hipaa_eligible: false, data_retention_days: null, data_residency_attested: false, foreign_origin_weight_flag: false, foreign_origin_consent_receipt_id: null, inference_billing: 'customer_managed' }, captured_at: '2026-07-10T14:00:00.000Z', }, ...extra, }; return { sequence: seq++, decision, previousHash: '0'.repeat(64), entryHash: 'x', signature: 'y', signerFingerprint: 'f' }; } test('lineage, swaps, and boundary crossing across a hot-swap sequence', () => { seq = 0; const entries = [ entry('claude-5', 'anthropic', 'anthropic-api'), entry('claude-5', 'anthropic', 'anthropic-api'), // repeat: no swap entry('gpt-5-mini', 'openai', 'openai-api'), // frontier→frontier swap entry('glm-5.2', 'self_hosted', 'self-hosted-gpu'), // frontier→dark swap (boundary) ]; const report = workloadProvenance(entries, { verified: true, entries: entries.length }); assert.equal(report.workloads.length, 1); const w = report.workloads[0]; assert.deepEqual(w.lineage, ['claude-5', 'gpt-5-mini', 'glm-5.2']); assert.equal(formatLineage(w), 'claude-5 → gpt-5-mini → glm-5.2'); assert.equal(w.swaps.length, 2); assert.equal(w.swaps[0].crossesBoundary, false); // frontier→frontier assert.equal(w.swaps[1].crossesBoundary, true); // frontier→dark assert.equal(w.swaps[1].fromModel, 'gpt-5-mini'); assert.equal(w.swaps[1].toModel, 'glm-5.2'); assert.equal(w.darkCents, 10); assert.equal(w.frontierCents, 30); assert.equal(report.continuity.verified, true); // one chain spans all swaps }); test('groups by keyOf; separate workloads do not merge', () => { seq = 0; const a = entry('claude-5', 'anthropic', 'api', { triggeredScopeKey: 'wf:A' }); const b = entry('glm-5.2', 'self_hosted', 'self', { triggeredScopeKey: 'wf:B' }); const report = workloadProvenance([a, b], { verified: true, entries: 2 }); assert.equal(report.workloads.length, 2); assert.ok(report.workloads.every(w => w.swaps.length === 0)); }); test('settlement/outcome entries add no model steps', () => { seq = 0; const entries = [ entry('claude-5', 'anthropic', 'api'), entry('claude-5', 'anthropic', 'api', { entryType: 'settlement', actualCents: 9 }), ]; const w = workloadProvenance(entries, { verified: true, entries: 2 }).workloads[0]; assert.equal(w.calls, 1); assert.equal(w.swaps.length, 0); });