import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest'; import type { PrismaClient } from '@prisma/client'; import { setupTestDb, cleanupTestDb, clearAllTables } from './helpers.js'; describe('ResourceRevision schema', () => { let prisma: PrismaClient; beforeAll(async () => { prisma = await setupTestDb(); }, 30_000); afterAll(async () => { await cleanupTestDb(); }); beforeEach(async () => { await clearAllTables(prisma); }); it('creates a revision with required fields and defaults', async () => { const rev = await prisma.resourceRevision.create({ data: { resourceType: 'prompt', resourceId: 'fake-prompt-id', contentHash: 'sha256:abc', body: { content: 'hello' }, }, }); expect(rev.id).toBeDefined(); expect(rev.semver).toBe('0.1.0'); expect(rev.note).toBe(''); expect(rev.authorUserId).toBeNull(); expect(rev.authorSessionId).toBeNull(); expect(rev.body).toEqual({ content: 'hello' }); expect(rev.createdAt).toBeInstanceOf(Date); }); it('survives resource deletion (soft FK)', async () => { // No actual prompt exists with this id — the soft FK design lets // revisions outlive their resources. const rev = await prisma.resourceRevision.create({ data: { resourceType: 'skill', resourceId: 'never-existed', contentHash: 'sha256:def', body: { content: 'ghost' }, }, }); expect(rev.resourceId).toBe('never-existed'); }); it('orders revisions latest-first within a resource', async () => { const resourceId = 'r1'; const a = await prisma.resourceRevision.create({ data: { resourceType: 'prompt', resourceId, semver: '0.1.0', contentHash: 'h1', body: {}, }, }); await new Promise((r) => setTimeout(r, 10)); const b = await prisma.resourceRevision.create({ data: { resourceType: 'prompt', resourceId, semver: '0.1.1', contentHash: 'h2', body: {}, }, }); await new Promise((r) => setTimeout(r, 10)); const c = await prisma.resourceRevision.create({ data: { resourceType: 'prompt', resourceId, semver: '0.2.0', contentHash: 'h3', body: {}, }, }); const rows = await prisma.resourceRevision.findMany({ where: { resourceType: 'prompt', resourceId }, orderBy: { createdAt: 'desc' }, }); expect(rows.map((r) => r.id)).toEqual([c.id, b.id, a.id]); }); it('allows multiple revisions with the same contentHash (rollback)', async () => { const resourceId = 'r2'; await prisma.resourceRevision.create({ data: { resourceType: 'prompt', resourceId, semver: '0.1.0', contentHash: 'identical', body: {}, }, }); const second = await prisma.resourceRevision.create({ data: { resourceType: 'prompt', resourceId, semver: '0.2.0', contentHash: 'identical', body: {}, }, }); expect(second.id).toBeDefined(); const rows = await prisma.resourceRevision.findMany({ where: { contentHash: 'identical' }, }); expect(rows.length).toBe(2); }); it('discriminates between prompt and skill revisions', async () => { await prisma.resourceRevision.create({ data: { resourceType: 'prompt', resourceId: 'x', contentHash: 'a', body: {} }, }); await prisma.resourceRevision.create({ data: { resourceType: 'skill', resourceId: 'x', contentHash: 'a', body: {} }, }); const prompts = await prisma.resourceRevision.findMany({ where: { resourceType: 'prompt', resourceId: 'x' }, }); const skills = await prisma.resourceRevision.findMany({ where: { resourceType: 'skill', resourceId: 'x' }, }); expect(prompts.length).toBe(1); expect(skills.length).toBe(1); }); });