-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathroute-validation.test.ts
More file actions
144 lines (128 loc) · 5 KB
/
route-validation.test.ts
File metadata and controls
144 lines (128 loc) · 5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/**
* Route-level validation tests for memory API endpoints.
* Tests UUID validation on param/query inputs and filter behavior
* on the list endpoint. Requires DATABASE_URL in .env.test.
*/
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
// Mock embedText to avoid hitting the real embedding provider in CI where
// OPENAI_API_KEY is a placeholder. Returns a deterministic zero vector
// matching the configured embedding dimensions.
vi.mock('../services/embedding.js', async (importOriginal) => {
const actual = await importOriginal<typeof import('../services/embedding.js')>();
return {
...actual,
embedText: vi.fn(async () => {
const { config: cfg } = await import('../config.js');
return new Array(cfg.embeddingDimensions).fill(0);
}),
};
});
import { pool } from '../db/pool.js';
import { config } from '../config.js';
import { MemoryRepository } from '../db/memory-repository.js';
import { ClaimRepository } from '../db/claim-repository.js';
import { MemoryService } from '../services/memory-service.js';
import { createMemoryRouter } from '../routes/memories.js';
import express from 'express';
import { readFileSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const TEST_USER = 'route-validation-test-user';
const VALID_UUID = '00000000-0000-0000-0000-000000000001';
const INVALID_UUID = 'not-a-uuid';
let server: ReturnType<typeof app.listen>;
let baseUrl: string;
const app = express();
app.use(express.json());
beforeAll(async () => {
const raw = readFileSync(resolve(__dirname, '../db/schema.sql'), 'utf-8');
const sql = raw.replace(/\{\{EMBEDDING_DIMENSIONS\}\}/g, String(config.embeddingDimensions));
await pool.query(sql);
const repo = new MemoryRepository(pool);
const claimRepo = new ClaimRepository(pool);
const service = new MemoryService(repo, claimRepo);
app.use('/memories', createMemoryRouter(service));
await new Promise<void>((resolve) => {
server = app.listen(0, () => {
const addr = server.address();
const port = typeof addr === 'object' && addr ? addr.port : 0;
baseUrl = `http://localhost:${port}`;
resolve();
});
});
});
afterAll(async () => {
await new Promise<void>((resolve) => server.close(() => resolve()));
await pool.end();
});
describe('GET /memories/:id — UUID validation', () => {
it('returns 400 for an invalid UUID', async () => {
const res = await fetch(`${baseUrl}/memories/${INVALID_UUID}?user_id=${TEST_USER}`);
expect(res.status).toBe(400);
const body = await res.json();
expect(body.error).toMatch(/valid UUID/);
});
it('returns 404 for a valid but non-existent UUID', async () => {
const res = await fetch(`${baseUrl}/memories/${VALID_UUID}?user_id=${TEST_USER}`);
expect(res.status).toBe(404);
});
});
describe('DELETE /memories/:id — UUID validation', () => {
it('returns 400 for an invalid UUID', async () => {
const res = await fetch(`${baseUrl}/memories/${INVALID_UUID}?user_id=${TEST_USER}`, {
method: 'DELETE',
});
expect(res.status).toBe(400);
const body = await res.json();
expect(body.error).toMatch(/valid UUID/);
});
});
describe('POST /memories/ingest/quick — skip_extraction (storeVerbatim)', () => {
it('stores a single memory without extraction when skip_extraction is true', async () => {
const res = await fetch(`${baseUrl}/memories/ingest/quick`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
user_id: TEST_USER,
conversation: 'Verbatim content that should not be extracted into facts.',
source_site: 'verbatim-test',
source_url: 'https://example.com/verbatim',
skip_extraction: true,
}),
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.memoriesStored).toBe(1);
expect(body.memoryIds).toHaveLength(1);
});
});
describe('GET /memories/list — source_site filter', () => {
it('returns memories filtered by source_site', async () => {
const res = await fetch(
`${baseUrl}/memories/list?user_id=${TEST_USER}&source_site=test-site`,
);
expect(res.status).toBe(200);
const body = await res.json();
expect(body).toHaveProperty('memories');
expect(body).toHaveProperty('count');
});
});
describe('GET /memories/list — episode_id filter', () => {
it('returns 400 for an invalid episode_id', async () => {
const res = await fetch(
`${baseUrl}/memories/list?user_id=${TEST_USER}&episode_id=${INVALID_UUID}`,
);
expect(res.status).toBe(400);
const body = await res.json();
expect(body.error).toMatch(/valid UUID/);
});
it('accepts a valid episode_id UUID', async () => {
const res = await fetch(
`${baseUrl}/memories/list?user_id=${TEST_USER}&episode_id=${VALID_UUID}`,
);
expect(res.status).toBe(200);
const body = await res.json();
expect(body).toHaveProperty('memories');
});
});