46 lines
1.7 KiB
TypeScript
46 lines
1.7 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { BadRequestException, ServiceUnavailableException } from '@nestjs/common';
|
|
import { IngestService, expectedSignature, stableJson } from '../src/ingest/ingest.service.js';
|
|
|
|
test('ingest canonical JSON is stable across object key order', () => {
|
|
assert.equal(stableJson({ z: 1, a: { y: 2, b: 3 } }), '{"a":{"b":3,"y":2},"z":1}');
|
|
});
|
|
|
|
test('ingest is disabled unless an HMAC secret is configured', () => {
|
|
const previous = process.env.CASAN_CP_INGEST_TOKEN;
|
|
delete process.env.CASAN_CP_INGEST_TOKEN;
|
|
try {
|
|
assert.throws(() => new IngestService().ingest({}, '0', 'none'), ServiceUnavailableException);
|
|
} finally {
|
|
if (previous === undefined) delete process.env.CASAN_CP_INGEST_TOKEN;
|
|
else process.env.CASAN_CP_INGEST_TOKEN = previous;
|
|
}
|
|
});
|
|
|
|
test('signed ingest rejects raw prompt/content fields before persistence', () => {
|
|
const previous = process.env.CASAN_CP_INGEST_TOKEN;
|
|
process.env.CASAN_CP_INGEST_TOKEN = 'test-only-secret';
|
|
const timestamp = String(Math.floor(Date.now() / 1000));
|
|
const body = {
|
|
schema_version: 1,
|
|
sent_at: new Date().toISOString(),
|
|
project_id: 'safe-project',
|
|
trace_id: 'safe-trace',
|
|
receipt: {},
|
|
metric: { trace_id: 'safe-trace', prompt: 'must-not-cross-boundary' },
|
|
trace: { trace_id: 'safe-trace' },
|
|
events: [],
|
|
};
|
|
try {
|
|
const signature = expectedSignature('test-only-secret', timestamp, body);
|
|
assert.throws(
|
|
() => new IngestService().ingest(body, timestamp, signature),
|
|
BadRequestException,
|
|
);
|
|
} finally {
|
|
if (previous === undefined) delete process.env.CASAN_CP_INGEST_TOKEN;
|
|
else process.env.CASAN_CP_INGEST_TOKEN = previous;
|
|
}
|
|
});
|