media-write-data-integrity.test.mjs
165 lines 6.3 KB
Raw
sha256:b5f647cb9c409f563d4671fe3fc05ddea01fabfed9b41fc11cb923588e1c1baf mirror: GitHub Phase A durable MCP OAuth (#270) Human minor ⚠ breaking 10 days ago
1 /**
2 * Tier 5 — DATA INTEGRITY: propose no mutation; credential-free refs; idempotent attach.
3 */
4 import { describe, it, beforeEach, afterEach } from 'node:test';
5 import assert from 'node:assert/strict';
6 import fs from 'node:fs';
7 import path from 'node:path';
8 import { fileURLToPath } from 'node:url';
9
10 import {
11 handleMediaLinkProposeRequest,
12 handleMediaAttachProposeRequest,
13 } from '../lib/attachments/attachment-write.mjs';
14 import { loadExternalRefStore } from '../lib/attachments/attachment-external-ref-store.mjs';
15 import { readNote } from '../lib/vault.mjs';
16 import { noteStateIdFromParts } from '../lib/note-state-id.mjs';
17 import { createProposal } from '../hub/proposals-store.mjs';
18 import {
19 approveMediaProposal,
20 buildMediaWriteFixture,
21 grantActiveConsent,
22 sampleLinkProposeBody,
23 sampleAttachProposeBody,
24 } from './fixtures/media/write-helpers.mjs';
25
26 const __dirname = path.dirname(fileURLToPath(import.meta.url));
27 const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-media-write-integrity');
28
29 describe('media write — data integrity', () => {
30 beforeEach(() => {
31 fs.rmSync(tmpRoot, { recursive: true, force: true });
32 delete process.env.MEDIA_EXTERNAL_LINK_ENABLED;
33 delete process.env.MEDIA_ATTACH_ENABLED;
34 });
35 afterEach(() => {
36 delete process.env.MEDIA_EXTERNAL_LINK_ENABLED;
37 delete process.env.MEDIA_ATTACH_ENABLED;
38 });
39
40 it('propose alone does not mutate external ref store or note frontmatter', async () => {
41 process.env.MEDIA_EXTERNAL_LINK_ENABLED = '1';
42 process.env.MEDIA_ATTACH_ENABLED = '1';
43 const fx = buildMediaWriteFixture(path.join(tmpRoot, 'propose-only'));
44 const consentId = grantActiveConsent(fx.dataDir, fx.vaultId, 'gdrive');
45 const noteBefore = readNote(fx.vaultPath, fx.targetNotePath);
46 const refsBefore = JSON.stringify(loadExternalRefStore(fx.dataDir));
47
48 await handleMediaLinkProposeRequest({
49 dataDir: fx.dataDir,
50 vaultId: fx.vaultId,
51 cliScopes: ['personal', 'project', 'org'],
52 body: sampleLinkProposeBody({ consent_id: consentId }),
53 intent: 'link',
54 createProposal,
55 });
56 await handleMediaAttachProposeRequest({
57 dataDir: fx.dataDir,
58 vaultPath: fx.vaultPath,
59 vaultId: fx.vaultId,
60 cliScopes: ['personal', 'project', 'org'],
61 body: sampleAttachProposeBody(fx),
62 intent: 'attach',
63 createProposal,
64 });
65
66 assert.equal(JSON.stringify(loadExternalRefStore(fx.dataDir)), refsBefore);
67 const noteAfter = readNote(fx.vaultPath, fx.targetNotePath);
68 assert.deepEqual(noteAfter.frontmatter?.attachments ?? [], noteBefore.frontmatter?.attachments ?? []);
69 });
70
71 it('approved external ref is credential-free; idempotent re-attach adds no duplicate', async () => {
72 process.env.MEDIA_EXTERNAL_LINK_ENABLED = '1';
73 process.env.MEDIA_ATTACH_ENABLED = '1';
74 const fx = buildMediaWriteFixture(path.join(tmpRoot, 'apply'));
75 const consentId = grantActiveConsent(fx.dataDir, fx.vaultId, 'gdrive');
76
77 const link = await handleMediaLinkProposeRequest({
78 dataDir: fx.dataDir,
79 vaultId: fx.vaultId,
80 cliScopes: ['personal', 'project', 'org'],
81 body: sampleLinkProposeBody({ consent_id: consentId }),
82 intent: 'link',
83 createProposal,
84 });
85 approveMediaProposal(fx.dataDir, link.payload.proposal_id, fx.vaultPath);
86
87 const store = loadExternalRefStore(fx.dataDir);
88 const blob = JSON.stringify(store);
89 assert.ok(!blob.includes('https://'));
90 assert.ok(!blob.includes('token'));
91 assert.ok(!blob.includes('oauth'));
92
93 const attach1 = await handleMediaAttachProposeRequest({
94 dataDir: fx.dataDir,
95 vaultPath: fx.vaultPath,
96 vaultId: fx.vaultId,
97 cliScopes: ['personal', 'project', 'org'],
98 body: sampleAttachProposeBody(fx, { attachment_id: link.payload.attachment_id }),
99 intent: 'attach1',
100 createProposal,
101 });
102 approveMediaProposal(fx.dataDir, attach1.payload.proposal_id, fx.vaultPath);
103 const note1 = readNote(fx.vaultPath, fx.targetNotePath);
104 const count1 = (note1.frontmatter?.attachments ?? []).length;
105
106 const noteAfterFirst = readNote(fx.vaultPath, fx.targetNotePath);
107 const freshBase = noteStateIdFromParts(noteAfterFirst.frontmatter ?? {}, noteAfterFirst.body ?? '');
108 const attach2 = await handleMediaAttachProposeRequest({
109 dataDir: fx.dataDir,
110 vaultPath: fx.vaultPath,
111 vaultId: fx.vaultId,
112 cliScopes: ['personal', 'project', 'org'],
113 body: sampleAttachProposeBody(fx, {
114 attachment_id: link.payload.attachment_id,
115 base_state_id: freshBase,
116 }),
117 intent: 'attach2',
118 createProposal,
119 });
120 assert.equal(attach2.ok, true);
121 approveMediaProposal(fx.dataDir, attach2.payload.proposal_id, fx.vaultPath);
122 const note2 = readNote(fx.vaultPath, fx.targetNotePath);
123 assert.equal((note2.frontmatter?.attachments ?? []).length, count1);
124 });
125
126 it('revoked consent blocks new links but leaves applied refs intact', async () => {
127 process.env.MEDIA_EXTERNAL_LINK_ENABLED = '1';
128 const fx = buildMediaWriteFixture(path.join(tmpRoot, 'revoke'));
129 const consentId = grantActiveConsent(fx.dataDir, fx.vaultId, 'gdrive');
130 const link = await handleMediaLinkProposeRequest({
131 dataDir: fx.dataDir,
132 vaultId: fx.vaultId,
133 cliScopes: ['personal', 'project', 'org'],
134 body: sampleLinkProposeBody({ consent_id: consentId, opaque_ref: 'keep-ref' }),
135 intent: 'link',
136 createProposal,
137 });
138 approveMediaProposal(fx.dataDir, link.payload.proposal_id, fx.vaultPath);
139
140 const { handleMediaImportConsentRevokeRequest } = await import('../lib/attachments/attachment-write.mjs');
141 handleMediaImportConsentRevokeRequest({
142 dataDir: fx.dataDir,
143 vaultId: fx.vaultId,
144 cliScopes: ['personal', 'project', 'org'],
145 consentId,
146 });
147
148 const blocked = await handleMediaLinkProposeRequest({
149 dataDir: fx.dataDir,
150 vaultId: fx.vaultId,
151 cliScopes: ['personal', 'project', 'org'],
152 body: sampleLinkProposeBody({
153 consent_id: consentId,
154 opaque_ref: 'new-ref-after-revoke',
155 }),
156 intent: 'blocked',
157 createProposal,
158 });
159 assert.equal(blocked.ok, false);
160 assert.equal(blocked.code, 'MEDIA_IMPORT_CONSENT_REQUIRED');
161
162 const store = loadExternalRefStore(fx.dataDir);
163 assert.ok(store.vaults[fx.vaultId]?.refs?.[link.payload.attachment_id]);
164 });
165 });
File History 1 commit
sha256:b5f647cb9c409f563d4671fe3fc05ddea01fabfed9b41fc11cb923588e1c1baf mirror: GitHub Phase A durable MCP OAuth (#270) Human minor 10 days ago