import { describe, it, expect, beforeAll } from 'vitest'; import { pool } from '../../../../lib/db/pool.js'; import { resetDb } from '../../../helpers/db.js'; import { migrateUp } from '../../../../lib/db/migrate.js'; import { readTool } from '../../../../lib/ai/agent/tools/read.js'; let spaceA, spaceB, pageInA, convoId; beforeAll(async () => { await resetDb(); await migrateUp(); ({ rows: [{ id: spaceA }] } = await pool.query(`INSERT INTO spaces(slug,name) VALUES('a','A') RETURNING id`)); ({ rows: [{ id: spaceB }] } = await pool.query(`INSERT INTO spaces(slug,name) VALUES('b','B') RETURNING id`)); ({ rows: [{ id: pageInA }] } = await pool.query( `INSERT INTO pages(space_id,slug,title,body_md) VALUES($1,'p','P','body') RETURNING id`, [spaceA])); ({ rows: [{ id: convoId }] } = await pool.query( `INSERT INTO conversations(title) VALUES('c') RETURNING id`)); }); describe('read tool space scoping', () => { it('reads an in-space page', async () => { const out = await readTool.handler({ kind: 'page', id: pageInA }, { space_id: spaceA }); expect(out.title).toBe('P'); }); it('blocks a cross-space page (scoped caller)', async () => { const out = await readTool.handler({ kind: 'page', id: pageInA }, { space_id: spaceB }); expect(out.error).toMatch(/not found/i); }); it('blocks unprovable kinds (conversation) for spaceScoped callers', async () => { const out = await readTool.handler({ kind: 'conversation', id: convoId }, { space_id: spaceA, spaceScoped: true }); expect(out.error).toMatch(/not found/i); }); it('owner (no space bound) reads anything', async () => { const out = await readTool.handler({ kind: 'page', id: pageInA }, {}); expect(out.title).toBe('P'); }); });