| 1 | import { describe, expect, it } from 'vitest';
|
| 2 | import {
|
| 3 | HOSTED_MIGRATIONS,
|
| 4 | buildSourceRecordEnvelope,
|
| 5 | shouldJournalSourceRecord,
|
| 6 | } from '@openclasp/persistence';
|
| 7 | import { SourceRecordEnvelopeSchema, canonicalHash } from '@openclasp/protocol';
|
| 8 |
|
| 9 | describe('production source record journal', () => {
|
| 10 | it('wraps a hosted record with explicit lineage and stable content integrity', () => {
|
| 11 | const payload = {
|
| 12 | interactionId: '11111111-1111-4111-8111-111111111111',
|
| 13 | agentId: 'agent:provider',
|
| 14 | outcome: 'success',
|
| 15 | completedAt: '2026-08-31T10:15:00.000Z',
|
| 16 | contributionMode: 'local_only',
|
| 17 | };
|
| 18 | const source = buildSourceRecordEnvelope({
|
| 19 | operatorId: 'operator:buyer',
|
| 20 | kind: 'completion_report',
|
| 21 | recordId: 'report:1',
|
| 22 | payload,
|
| 23 | metadata: {
|
| 24 | eventId: '22222222-2222-4222-8222-222222222222',
|
| 25 | ingestedAt: '2026-08-31T10:16:00.000Z',
|
| 26 | },
|
| 27 | });
|
| 28 |
|
| 29 | expect(SourceRecordEnvelopeSchema.parse(source)).toEqual(source);
|
| 30 | expect(source.payloadDigest).toBe(canonicalHash(payload));
|
| 31 | expect(source.reportedAt).toBe(payload.completedAt);
|
| 32 | expect(source.ingestedAt).toBe('2026-08-31T10:16:00.000Z');
|
| 33 | expect(source.entityRefs).toEqual({
|
| 34 | agentId: 'agent:provider',
|
| 35 | interactionId: payload.interactionId,
|
| 36 | });
|
| 37 | expect(source.learningScope).toBe('local_only');
|
| 38 | expect(source.retentionClass).toBe('audit');
|
| 39 | expect(source.provenance).toBe('observed');
|
| 40 | expect(source.visibility).toBe('shared_participants');
|
| 41 | });
|
| 42 |
|
| 43 | it('uses canonical payload digests and permits explicit collection metadata', () => {
|
| 44 | const base = {
|
| 45 | operatorId: 'operator:one',
|
| 46 | kind: 'agent_profile',
|
| 47 | recordId: 'agent:one',
|
| 48 | metadata: {
|
| 49 | eventId: '33333333-3333-4333-8333-333333333333',
|
| 50 | reportedAt: '2026-08-31T00:00:00.000Z',
|
| 51 | ingestedAt: '2026-08-31T00:00:01.000Z',
|
| 52 | provenance: 'operator_attested' as const,
|
| 53 | visibility: 'shared_participants' as const,
|
| 54 | entityRefs: { deploymentId: 'deployment:one' },
|
| 55 | },
|
| 56 | };
|
| 57 | const left = buildSourceRecordEnvelope({ ...base, payload: { b: 2, a: 1 } });
|
| 58 | const right = buildSourceRecordEnvelope({ ...base, payload: { a: 1, b: 2 } });
|
| 59 |
|
| 60 | expect(left.payloadDigest).toBe(right.payloadDigest);
|
| 61 | expect(left.retentionClass).toBe('account');
|
| 62 | expect(left.provenance).toBe('operator_attested');
|
| 63 | expect(left.visibility).toBe('shared_participants');
|
| 64 | expect(left.entityRefs.deploymentId).toBe('deployment:one');
|
| 65 | });
|
| 66 |
|
| 67 | it('rejects values that cannot be retained as JSON', () => {
|
| 68 | expect(() =>
|
| 69 | buildSourceRecordEnvelope({
|
| 70 | operatorId: 'operator:one',
|
| 71 | kind: 'event',
|
| 72 | recordId: 'event:one',
|
| 73 | payload: { invalid: undefined },
|
| 74 | }),
|
| 75 | ).toThrow();
|
| 76 | });
|
| 77 |
|
| 78 | it('keeps hosted migrations numbered, ordered, and uniquely named', () => {
|
| 79 | expect(HOSTED_MIGRATIONS.map((migration) => migration.version)).toEqual([1, 2]);
|
| 80 | expect(new Set(HOSTED_MIGRATIONS.map((migration) => migration.name)).size).toBe(
|
| 81 | HOSTED_MIGRATIONS.length,
|
| 82 | );
|
| 83 | });
|
| 84 |
|
| 85 | it('journals intelligence inputs but not high-volume operational projections', () => {
|
| 86 | expect(shouldJournalSourceRecord('completion_report')).toBe(true);
|
| 87 | expect(shouldJournalSourceRecord('interaction_feedback')).toBe(true);
|
| 88 | expect(shouldJournalSourceRecord('profile_delta')).toBe(true);
|
| 89 | expect(shouldJournalSourceRecord('presence')).toBe(false);
|
| 90 | expect(shouldJournalSourceRecord('profile')).toBe(false);
|
| 91 | expect(shouldJournalSourceRecord('feedback_request')).toBe(false);
|
| 92 | });
|
| 93 | });
|