Skip to content

Commit aa37f32

Browse files
committed
test(epic-20): add scripting console route tests (F1949)
The scripting console route test was built and green-gated with its commit but not staged. Adding it so the /scripts CRUD + scope-check + gallery endpoints are covered in the repo, matching the pure scripting/analyze.test.ts.
1 parent 96da9e6 commit aa37f32

1 file changed

Lines changed: 76 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Scripting console route tests (Epic 20, F1942–F1948).
3+
*/
4+
5+
import type { FastifyInstance } from 'fastify';
6+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
7+
import { buildApp } from '../app.js';
8+
import { loadConfig } from '../config.js';
9+
10+
let app: FastifyInstance;
11+
12+
beforeAll(async () => {
13+
app = await buildApp(loadConfig({ NODE_ENV: 'test', LOG_LEVEL: 'fatal' }));
14+
});
15+
16+
afterAll(async () => {
17+
await app.close();
18+
});
19+
20+
describe('script library (F1942/F1947)', () => {
21+
it('saves a script and reports its scope check', async () => {
22+
const created = (
23+
(
24+
await app.inject({
25+
method: 'POST',
26+
url: '/api/v1/scripts',
27+
payload: {
28+
name: 'Tagger',
29+
source: 'await fables.notes.query(""); await fables.notes.create({});',
30+
scopes: ['notes:read', 'notes:write'],
31+
},
32+
})
33+
).json() as { data: { id: string } }
34+
).data;
35+
36+
const check = await app.inject({ method: 'GET', url: `/api/v1/scripts/${created.id}/check` });
37+
expect((check.json() as { data: { ok: boolean } }).data.ok).toBe(true);
38+
});
39+
40+
it('rejects an unknown scope on save', async () => {
41+
const res = await app.inject({
42+
method: 'POST',
43+
url: '/api/v1/scripts',
44+
payload: { name: 'Bad', source: 'noop()', scopes: ['totally:made-up'] },
45+
});
46+
expect(res.statusCode).toBe(422);
47+
});
48+
49+
it('rejects an invalid cron on save', async () => {
50+
const res = await app.inject({
51+
method: 'POST',
52+
url: '/api/v1/scripts',
53+
payload: { name: 'Cronny', source: 'noop()', cron: 'not a cron' },
54+
});
55+
expect(res.statusCode).toBe(422);
56+
});
57+
58+
it('checks arbitrary source without saving (F1946)', async () => {
59+
const res = await app.inject({
60+
method: 'POST',
61+
url: '/api/v1/scripts/check',
62+
payload: { source: 'await fables.notes.create({});', scopes: ['notes:read'] },
63+
});
64+
const data = (res.json() as { data: { missingScopes: string[] } }).data;
65+
expect(data.missingScopes).toEqual(['notes:write']);
66+
});
67+
68+
it('serves the example gallery and known scopes', async () => {
69+
const gallery = await app.inject({ method: 'GET', url: '/api/v1/scripts/gallery' });
70+
expect(
71+
(gallery.json() as { data: { scripts: unknown[] } }).data.scripts.length,
72+
).toBeGreaterThan(0);
73+
const scopes = await app.inject({ method: 'GET', url: '/api/v1/scripts/scopes' });
74+
expect((scopes.json() as { data: { scopes: string[] } }).data.scopes).toContain('notes:write');
75+
});
76+
});

0 commit comments

Comments
 (0)