metadata-facets.test.mjs
171 lines 5.4 KB
Raw
sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2 feat(auth): persistent login system + C7 session introspection Human minor ⚠ breaking 16 days ago
1 import test from 'node:test';
2 import assert from 'node:assert';
3 import { normalizeMetadataFacets } from '../lib/vault.mjs';
4
5 test('metadata facets: normalizes canonical frontmatter and inferred folder without body fields', () => {
6 const out = normalizeMetadataFacets('projects/My Project/note.md', {
7 project: 'Front Matter Project',
8 tags: 'Research, AI Safety',
9 date: '2026-05-24',
10 updated: new Date('2026-05-25T12:34:56.000Z'),
11 causal_chain_id: 'Launch Rollout',
12 entity: ['Alice B', 'Core API'],
13 episode_id: 'Episode 1',
14 body: 'body text must never be returned',
15 snippet: 'snippet must never be returned',
16 });
17
18 assert.deepStrictEqual(out, {
19 schema: 'knowtation.metadata_facets/v0',
20 path: 'projects/My Project/note.md',
21 facets: {
22 project: 'front-matter-project',
23 tags: ['research', 'ai-safety'],
24 date: '2026-05-24',
25 updated: '2026-05-25T12:34:56.000Z',
26 causal_chain_id: 'launch-rollout',
27 entity: ['alice-b', 'core-api'],
28 episode_id: 'episode-1',
29 },
30 inferred: {
31 folder: 'projects/My Project',
32 source_type: null,
33 },
34 truncated: false,
35 });
36 assert.equal('body' in out, false);
37 assert.equal('snippet' in out, false);
38 assert.equal('frontmatter' in out, false);
39 });
40
41 test('metadata facets: infers project with effective project slug semantics', () => {
42 const out = normalizeMetadataFacets('projects/Born Free/inbox/note.md', {
43 tags: ['one', 'Two Words'],
44 });
45
46 assert.equal(out.facets.project, 'born-free');
47 assert.deepStrictEqual(out.facets.tags, ['one', 'two-words']);
48 assert.equal(out.inferred.folder, 'projects/Born Free/inbox');
49 });
50
51 test('metadata facets: preserves deterministic null and empty defaults', () => {
52 const out = normalizeMetadataFacets('inbox/note.md', {});
53
54 assert.deepStrictEqual(out.facets, {
55 project: null,
56 tags: [],
57 date: null,
58 updated: null,
59 causal_chain_id: null,
60 entity: [],
61 episode_id: null,
62 });
63 assert.deepStrictEqual(out.inferred, {
64 folder: 'inbox',
65 source_type: null,
66 });
67 assert.equal(out.truncated, false);
68 });
69
70 test('metadata facets security: rejects absolute and traversal paths', () => {
71 assert.throws(
72 () => normalizeMetadataFacets('/tmp/vault/note.md', {}),
73 /Invalid path/,
74 );
75 assert.throws(
76 () => normalizeMetadataFacets('../secrets.md', {}),
77 /Invalid path/,
78 );
79 assert.throws(
80 () => normalizeMetadataFacets('inbox/../../secrets.md', {}),
81 /Invalid path/,
82 );
83 });
84
85 test('metadata facets security: excludes deferred and sensitive metadata fields', () => {
86 const out = normalizeMetadataFacets('inbox/note.md', {
87 tags: ['ok'],
88 label: 'do not include',
89 labels: ['do not include'],
90 ocr_text: 'do not include',
91 pageIndex: { text: 'do not include' },
92 media_metadata: { transcript: 'do not include' },
93 vector_score: 0.99,
94 memory_events: ['do not include'],
95 provider_key: 'secret',
96 absolute_path: '/Users/example/vault/inbox/note.md',
97 raw_upstream_payload: { secret: true },
98 summary: 'do not include',
99 });
100
101 assert.deepStrictEqual(Object.keys(out.facets), [
102 'project',
103 'tags',
104 'date',
105 'updated',
106 'causal_chain_id',
107 'entity',
108 'episode_id',
109 ]);
110 assert.deepStrictEqual(Object.keys(out.inferred), ['folder', 'source_type']);
111 assert.equal(JSON.stringify(out).includes('do not include'), false);
112 assert.equal(JSON.stringify(out).includes('secret'), false);
113 assert.equal(JSON.stringify(out).includes('/Users/example'), false);
114 });
115
116 test('metadata facets data-integrity: does not mutate input frontmatter', () => {
117 const fm = {
118 project: 'Original Project',
119 tags: ['Alpha', 'Beta'],
120 entity: ['Alice'],
121 nested: { label: 'unchanged' },
122 };
123 const before = JSON.stringify(fm);
124
125 normalizeMetadataFacets('projects/example/note.md', fm);
126
127 assert.equal(JSON.stringify(fm), before);
128 assert.deepStrictEqual(fm.tags, ['Alpha', 'Beta']);
129 assert.deepStrictEqual(fm.entity, ['Alice']);
130 });
131
132 test('metadata facets stress: caps large tag and entity arrays with bounded values', () => {
133 const tags = Array.from({ length: 150 }, (_, i) => `Tag ${i}`);
134 const entity = Array.from({ length: 150 }, (_, i) => `Entity ${i}`);
135 const out = normalizeMetadataFacets('projects/example/note.md', {
136 tags,
137 entity,
138 });
139
140 assert.equal(out.facets.tags.length, 100);
141 assert.equal(out.facets.entity.length, 100);
142 assert.equal(out.facets.tags[99], 'tag-99');
143 assert.equal(out.facets.entity[99], 'entity-99');
144 assert.equal(out.truncated, true);
145 });
146
147 test('metadata facets stress: caps oversized scalar values and remains deterministic', () => {
148 const huge = 'A'.repeat(1000);
149 const input = {
150 project: huge,
151 tags: [huge],
152 date: huge,
153 updated: huge,
154 causal_chain_id: huge,
155 entity: [huge],
156 episode_id: huge,
157 };
158
159 const first = normalizeMetadataFacets('projects/example/note.md', input);
160 const second = normalizeMetadataFacets('projects/example/note.md', input);
161
162 assert.deepStrictEqual(first, second);
163 assert.equal(first.truncated, true);
164 assert(first.facets.project.length <= 256);
165 assert(first.facets.tags.every((value) => value.length <= 256));
166 assert(first.facets.date.length <= 256);
167 assert(first.facets.updated.length <= 256);
168 assert(first.facets.causal_chain_id.length <= 256);
169 assert(first.facets.entity.every((value) => value.length <= 256));
170 assert(first.facets.episode_id.length <= 256);
171 });
File History 1 commit
sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2 feat(auth): persistent login system + C7 session introspection Human minor 16 days ago