135 lines
5.2 KiB
TypeScript
135 lines
5.2 KiB
TypeScript
import { beforeEach, describe, expect, it } from 'vitest';
|
|
import { isEncryptedField } from '@bytelyst/field-encrypt';
|
|
import { getCollection } from './datastore.js';
|
|
import { _resetEncryptor } from './field-encrypt.js';
|
|
import { resetMemoryDatastore } from '../test-helpers.js';
|
|
import { createNoteArtifact, getNoteArtifact } from '../modules/note-artifacts/repository.js';
|
|
import type { NoteArtifactDoc } from '../modules/note-artifacts/types.js';
|
|
import { createPromptTemplate, getPromptTemplate } from '../modules/note-prompts/repository.js';
|
|
import type { PromptTemplateDoc } from '../modules/note-prompts/types.js';
|
|
import {
|
|
createNoteAgentAction,
|
|
getNoteAgentAction,
|
|
updateNoteAgentAction,
|
|
} from '../modules/note-agent-actions/repository.js';
|
|
import type { NoteAgentActionDoc } from '../modules/note-agent-actions/types.js';
|
|
|
|
describe('field encryption coverage', () => {
|
|
beforeEach(() => {
|
|
resetMemoryDatastore();
|
|
_resetEncryptor();
|
|
});
|
|
|
|
it('encrypts sensitive artifact metadata while returning plaintext to callers', async () => {
|
|
const created = await createNoteArtifact({
|
|
id: 'artifact-1',
|
|
productId: 'notelett',
|
|
workspaceId: 'ws-1',
|
|
userId: 'user-1',
|
|
noteId: 'note-1',
|
|
artifactType: 'summary',
|
|
title: 'Private summary',
|
|
description: 'Sensitive extracted summary',
|
|
blobPath: 'notelett/user-1/private-summary.md',
|
|
contentType: 'text/markdown',
|
|
createdAt: '2026-05-05T00:00:00.000Z',
|
|
createdBy: 'user-1',
|
|
updatedAt: '2026-05-05T00:00:00.000Z',
|
|
updatedBy: 'user-1',
|
|
});
|
|
|
|
expect(created.title).toBe('Private summary');
|
|
expect(created.description).toBe('Sensitive extracted summary');
|
|
|
|
const raw = await getCollection<NoteArtifactDoc>('note_artifacts', '/workspaceId').findById('artifact-1', 'ws-1');
|
|
expect(isEncryptedField(raw?.title)).toBe(true);
|
|
expect(isEncryptedField(raw?.description)).toBe(true);
|
|
expect(isEncryptedField(raw?.blobPath)).toBe(true);
|
|
|
|
const fetched = await getNoteArtifact('artifact-1', 'ws-1');
|
|
expect(fetched).toMatchObject({
|
|
title: 'Private summary',
|
|
description: 'Sensitive extracted summary',
|
|
blobPath: 'notelett/user-1/private-summary.md',
|
|
});
|
|
});
|
|
|
|
it('encrypts custom prompt content while preserving slug lookup', async () => {
|
|
const created = await createPromptTemplate('user-1', {
|
|
slug: 'private-template',
|
|
name: 'Private Template',
|
|
description: 'Internal reasoning instructions',
|
|
systemPrompt: 'Use confidential house style.',
|
|
userPromptTemplate: 'Rewrite {{note}} for leadership.',
|
|
inputType: 'text',
|
|
outputType: 'new_note',
|
|
category: 'transform',
|
|
requiresApproval: true,
|
|
});
|
|
|
|
expect(created.systemPrompt).toBe('Use confidential house style.');
|
|
expect(created.userPromptTemplate).toBe('Rewrite {{note}} for leadership.');
|
|
|
|
const raw = await getCollection<PromptTemplateDoc>('note_prompts', '/userId').findById(created.id, 'user-1');
|
|
expect(raw?.slug).toBe('private-template');
|
|
expect(isEncryptedField(raw?.description)).toBe(true);
|
|
expect(isEncryptedField(raw?.systemPrompt)).toBe(true);
|
|
expect(isEncryptedField(raw?.userPromptTemplate)).toBe(true);
|
|
|
|
const fetched = await getPromptTemplate(created.id, 'user-1');
|
|
expect(fetched).toMatchObject({
|
|
slug: 'private-template',
|
|
description: 'Internal reasoning instructions',
|
|
systemPrompt: 'Use confidential house style.',
|
|
userPromptTemplate: 'Rewrite {{note}} for leadership.',
|
|
});
|
|
});
|
|
|
|
it('encrypts agent action details and review notes while preserving query metadata', async () => {
|
|
await createNoteAgentAction({
|
|
id: 'action-1',
|
|
productId: 'notelett',
|
|
workspaceId: 'ws-1',
|
|
userId: 'user-1',
|
|
noteId: 'note-1',
|
|
actorId: 'agent-1',
|
|
actorType: 'agent',
|
|
toolName: 'notes.notes.update',
|
|
actionType: 'update',
|
|
state: 'proposed',
|
|
reason: 'Agent proposed a sensitive rewrite',
|
|
beforeSummary: 'Original private note',
|
|
afterSummary: 'Rewritten private note',
|
|
idempotencyKey: 'idem-1',
|
|
correlationId: 'corr-1',
|
|
createdAt: '2026-05-05T00:00:00.000Z',
|
|
createdBy: 'user-1',
|
|
updatedAt: '2026-05-05T00:00:00.000Z',
|
|
updatedBy: 'user-1',
|
|
});
|
|
|
|
await updateNoteAgentAction('action-1', 'ws-1', {
|
|
state: 'approved',
|
|
reviewNote: 'Approved after private review',
|
|
updatedAt: '2026-05-05T01:00:00.000Z',
|
|
updatedBy: 'user-1',
|
|
});
|
|
|
|
const raw = await getCollection<NoteAgentActionDoc>('note_agent_actions', '/workspaceId').findById('action-1', 'ws-1');
|
|
expect(raw?.toolName).toBe('notes.notes.update');
|
|
expect(raw?.idempotencyKey).toBe('idem-1');
|
|
expect(isEncryptedField(raw?.reason)).toBe(true);
|
|
expect(isEncryptedField(raw?.beforeSummary)).toBe(true);
|
|
expect(isEncryptedField(raw?.afterSummary)).toBe(true);
|
|
expect(isEncryptedField(raw?.reviewNote)).toBe(true);
|
|
|
|
const fetched = await getNoteAgentAction('action-1', 'ws-1');
|
|
expect(fetched).toMatchObject({
|
|
reason: 'Agent proposed a sensitive rewrite',
|
|
beforeSummary: 'Original private note',
|
|
afterSummary: 'Rewritten private note',
|
|
reviewNote: 'Approved after private review',
|
|
});
|
|
});
|
|
});
|