attest-endpoint.test.mjs
119 lines 4.5 KB
Raw
sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2 feat(auth): persistent login system + C7 session introspection Human minor ⚠ breaking 18 days ago
1 import { test, beforeEach, afterEach } from 'node:test';
2 import assert from 'node:assert/strict';
3
4 const TEST_SECRET = 'test-attestation-secret-that-is-at-least-32-characters-long';
5
6 /**
7 * Stub blob store backed by a Map — mirrors the subset of @netlify/blobs
8 * API that attest-store.mjs uses (get, setJSON).
9 */
10 function createStubBlobStore() {
11 const map = new Map();
12 return {
13 _map: map,
14 async get(key, opts) {
15 const raw = map.get(key);
16 if (raw === undefined) return null;
17 if (opts && opts.type === 'json') return JSON.parse(raw);
18 return raw;
19 },
20 async setJSON(key, value) {
21 map.set(key, JSON.stringify(value));
22 },
23 async delete(key) {
24 map.delete(key);
25 },
26 };
27 }
28
29 let savedSecret;
30 let savedEndpoint;
31
32 beforeEach(() => {
33 savedSecret = process.env.ATTESTATION_SECRET;
34 savedEndpoint = process.env.KNOWTATION_AIR_ENDPOINT;
35 process.env.ATTESTATION_SECRET = TEST_SECRET;
36 delete process.env.KNOWTATION_AIR_ENDPOINT;
37 globalThis.__knowtation_attest_blob = createStubBlobStore();
38 });
39
40 afterEach(() => {
41 if (savedSecret !== undefined) process.env.ATTESTATION_SECRET = savedSecret;
42 else delete process.env.ATTESTATION_SECRET;
43 if (savedEndpoint !== undefined) process.env.KNOWTATION_AIR_ENDPOINT = savedEndpoint;
44 else delete process.env.KNOWTATION_AIR_ENDPOINT;
45 delete globalThis.__knowtation_attest_blob;
46 });
47
48 test('createAttestation returns id with air- prefix and ISO timestamp', async () => {
49 const { createAttestation } = await import('../hub/gateway/attest-store.mjs');
50 const result = await createAttestation('write', 'notes/test.md');
51 assert.match(result.id, /^air-[0-9a-f-]{36}$/);
52 assert.match(result.timestamp, /^\d{4}-\d{2}-\d{2}T/);
53 });
54
55 test('verifyAttestation returns verified: true for a valid record', async () => {
56 const { createAttestation, verifyAttestation } = await import('../hub/gateway/attest-store.mjs');
57 const { id } = await createAttestation('write', 'notes/test.md');
58 const result = await verifyAttestation(id);
59 assert.equal(result.verified, true);
60 assert.equal(result.record.id, id);
61 assert.equal(result.record.action, 'write');
62 assert.equal(result.record.path, 'notes/test.md');
63 assert.equal(result.record.sig, undefined, 'sig must not be exposed');
64 });
65
66 test('verifyAttestation returns record: null for nonexistent id', async () => {
67 const { verifyAttestation } = await import('../hub/gateway/attest-store.mjs');
68 const result = await verifyAttestation('air-00000000-0000-0000-0000-000000000000');
69 assert.equal(result.verified, false);
70 assert.equal(result.record, null);
71 });
72
73 test('createAttestation throws when ATTESTATION_SECRET is missing', async () => {
74 delete process.env.ATTESTATION_SECRET;
75 const { createAttestation } = await import('../hub/gateway/attest-store.mjs');
76 await assert.rejects(
77 () => createAttestation('write', 'notes/test.md'),
78 { message: 'ATTESTATION_SECRET is not configured' }
79 );
80 });
81
82 test('createAttestation throws when ATTESTATION_SECRET is too short', async () => {
83 process.env.ATTESTATION_SECRET = 'short';
84 const { createAttestation } = await import('../hub/gateway/attest-store.mjs');
85 await assert.rejects(
86 () => createAttestation('write', 'notes/test.md'),
87 { message: 'ATTESTATION_SECRET is not configured' }
88 );
89 });
90
91 test('tampered record fails verification', async () => {
92 const { createAttestation, verifyAttestation } = await import('../hub/gateway/attest-store.mjs');
93 const { id } = await createAttestation('write', 'notes/test.md');
94
95 const store = globalThis.__knowtation_attest_blob;
96 const raw = await store.get(`attestation/${id}`, { type: 'json' });
97 raw.action = 'tampered';
98 await store.setJSON(`attestation/${id}`, raw);
99
100 const result = await verifyAttestation(id);
101 assert.equal(result.verified, false);
102 assert.ok(result.record, 'record should still be returned even if tampered');
103 });
104
105 test('content_hash is stored and returned when provided', async () => {
106 const { createAttestation, verifyAttestation } = await import('../hub/gateway/attest-store.mjs');
107 const hash = 'abc123def456';
108 const { id } = await createAttestation('write', 'notes/test.md', hash);
109 const result = await verifyAttestation(id);
110 assert.equal(result.verified, true);
111 assert.equal(result.record.content_hash, hash);
112 });
113
114 test('isAttestationConfigured reflects ATTESTATION_SECRET state', async () => {
115 const { isAttestationConfigured } = await import('../hub/gateway/attest-store.mjs');
116 assert.equal(isAttestationConfigured(), true);
117 delete process.env.ATTESTATION_SECRET;
118 assert.equal(isAttestationConfigured(), false);
119 });
File History 2 commits
sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2 feat(auth): persistent login system + C7 session introspection Human minor 18 days ago