import { describe, it, expect, vi, beforeEach } from 'vitest'; import { bootstrapSystemProject, SYSTEM_PROJECT_NAME, SYSTEM_OWNER_ID, getSystemPromptNames } from '../src/bootstrap/system-project.js'; import type { PrismaClient } from '@prisma/client'; function mockPrisma(): PrismaClient { const prompts = new Map(); let promptIdCounter = 1; return { project: { upsert: vi.fn(async (args: { where: { name: string }; create: Record; update: Record }) => ({ id: 'sys-proj-id', name: args.where.name, ...args.create, })), }, prompt: { findFirst: vi.fn(async (args: { where: { name: string; projectId: string } }) => { return prompts.get(`${args.where.projectId}:${args.where.name}`) ?? null; }), create: vi.fn(async (args: { data: { name: string; content: string; priority: number; projectId: string } }) => { const id = `prompt-${promptIdCounter++}`; const prompt = { id, ...args.data }; prompts.set(`${args.data.projectId}:${args.data.name}`, prompt); return prompt; }), }, } as unknown as PrismaClient; } describe('bootstrapSystemProject', () => { let prisma: PrismaClient; beforeEach(() => { prisma = mockPrisma(); }); it('creates the mcpctl-system project via upsert', async () => { await bootstrapSystemProject(prisma); expect(prisma.project.upsert).toHaveBeenCalledWith( expect.objectContaining({ where: { name: SYSTEM_PROJECT_NAME }, create: expect.objectContaining({ name: SYSTEM_PROJECT_NAME, ownerId: SYSTEM_OWNER_ID, gated: false, }), update: {}, }), ); }); it('creates all system prompts', async () => { await bootstrapSystemProject(prisma); const expectedNames = getSystemPromptNames(); expect(expectedNames.length).toBeGreaterThanOrEqual(4); for (const name of expectedNames) { expect(prisma.prompt.findFirst).toHaveBeenCalledWith( expect.objectContaining({ where: { name, projectId: 'sys-proj-id' }, }), ); } expect(prisma.prompt.create).toHaveBeenCalledTimes(expectedNames.length); }); it('creates system prompts with priority 10', async () => { await bootstrapSystemProject(prisma); const createCalls = vi.mocked(prisma.prompt.create).mock.calls; for (const call of createCalls) { const data = (call[0] as { data: { priority: number } }).data; expect(data.priority).toBe(10); } }); it('does not re-create existing prompts (idempotent)', async () => { // First call creates everything await bootstrapSystemProject(prisma); const firstCallCount = vi.mocked(prisma.prompt.create).mock.calls.length; // Second call — prompts already exist in mock, should not create again await bootstrapSystemProject(prisma); // create should not have been called additional times expect(vi.mocked(prisma.prompt.create).mock.calls.length).toBe(firstCallCount); }); it('re-creates deleted prompts on subsequent startup', async () => { // First run creates everything await bootstrapSystemProject(prisma); // Simulate deletion: clear the map so findFirst returns null vi.mocked(prisma.prompt.findFirst).mockResolvedValue(null); vi.mocked(prisma.prompt.create).mockClear(); // Second run should recreate await bootstrapSystemProject(prisma); const expectedNames = getSystemPromptNames(); expect(vi.mocked(prisma.prompt.create).mock.calls.length).toBe(expectedNames.length); }); it('system project has gated=false', async () => { await bootstrapSystemProject(prisma); const upsertCall = vi.mocked(prisma.project.upsert).mock.calls[0]![0]; expect((upsertCall as { create: { gated: boolean } }).create.gated).toBe(false); }); }); describe('getSystemPromptNames', () => { it('returns all system prompt names', () => { const names = getSystemPromptNames(); expect(names).toContain('gate-instructions'); expect(names).toContain('gate-encouragement'); expect(names).toContain('gate-intercept-preamble'); expect(names).toContain('session-greeting'); }); });