learning_ai_notes/backend/src/modules/note-artifacts/routes.integration.test.ts
saravanakumardb1 bf2785bcf9 test(backend): add integration tests for all 7 route modules [A5]
- Add test-helpers.ts with buildTestApp() + resetMemoryDatastore()
- notes: 11 tests (CRUD, archive, filter, search, validation)
- workspaces: 7 tests (CRUD, summaries with noteCount, validation)
- note-tasks: 6 tests (CRUD, filter by workspaceId, validation)
- note-artifacts: 7 tests (CRUD, filter by noteId, validation)
- note-relationships: 4 tests (create, list, validation)
- note-agent-actions: 8 tests (CRUD, pending, batch-review, validation)
- saved-views: 8 tests (CRUD, filter by scope, delete, validation)
- Fix listPendingActions to avoid unsupported $in operator in memory provider
- Total: 75 backend tests (was 24)
2026-03-19 08:38:21 -07:00

98 lines
3.4 KiB
TypeScript

import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import type { FastifyInstance } from 'fastify';
const { extractAuthMock } = vi.hoisted(() => ({
extractAuthMock: vi.fn(async () => ({ sub: 'user_1', type: 'access' })),
}));
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' }));
import { buildTestApp, resetMemoryDatastore } from '../../test-helpers.js';
import { noteArtifactRoutes } from './routes.js';
let app: FastifyInstance;
beforeAll(async () => {
app = await buildTestApp(noteArtifactRoutes);
});
beforeEach(() => {
resetMemoryDatastore();
});
afterAll(async () => {
await app.close();
});
const validArtifact = {
id: 'artifact-1',
workspaceId: 'ws-1',
noteId: 'note-1',
artifactType: 'file',
title: 'Launch brief.pdf',
description: 'Ready for review',
blobPath: 'notelett/user-1/launch-brief.pdf',
contentType: 'application/pdf',
sizeBytes: 2048,
};
describe('note-artifacts routes — integration', () => {
it('POST /note-artifacts creates an artifact and returns 201', async () => {
const res = await app.inject({ method: 'POST', url: '/api/note-artifacts', payload: validArtifact });
expect(res.statusCode).toBe(201);
const body = res.json();
expect(body.id).toBe('artifact-1');
expect(body.title).toBe('Launch brief.pdf');
expect(body.blobPath).toBe('notelett/user-1/launch-brief.pdf');
});
it('GET /note-artifacts lists artifacts by workspaceId', async () => {
await app.inject({ method: 'POST', url: '/api/note-artifacts', payload: validArtifact });
const res = await app.inject({ method: 'GET', url: '/api/note-artifacts?workspaceId=ws-1' });
expect(res.statusCode).toBe(200);
expect(res.json().items).toHaveLength(1);
});
it('GET /note-artifacts requires workspaceId', async () => {
const res = await app.inject({ method: 'GET', url: '/api/note-artifacts' });
expect(res.statusCode).toBe(400);
});
it('GET /note-artifacts filters by noteId', async () => {
await app.inject({ method: 'POST', url: '/api/note-artifacts', payload: validArtifact });
await app.inject({
method: 'POST',
url: '/api/note-artifacts',
payload: { ...validArtifact, id: 'artifact-2', noteId: 'note-2' },
});
const res = await app.inject({ method: 'GET', url: '/api/note-artifacts?workspaceId=ws-1&noteId=note-1' });
expect(res.json().items).toHaveLength(1);
});
it('PATCH /note-artifacts/:id updates an artifact', async () => {
await app.inject({ method: 'POST', url: '/api/note-artifacts', payload: validArtifact });
const res = await app.inject({
method: 'PATCH',
url: '/api/note-artifacts/artifact-1?workspaceId=ws-1',
payload: { title: 'Updated title' },
});
expect(res.statusCode).toBe(200);
expect(res.json().title).toBe('Updated title');
});
it('PATCH /note-artifacts/:id returns 404 for missing artifact', async () => {
const res = await app.inject({
method: 'PATCH',
url: '/api/note-artifacts/missing?workspaceId=ws-1',
payload: { title: 'X' },
});
expect(res.statusCode).toBe(404);
});
it('POST /note-artifacts rejects invalid body', async () => {
const res = await app.inject({ method: 'POST', url: '/api/note-artifacts', payload: { id: 'x' } });
expect(res.statusCode).toBe(400);
});
});