import { drizzle } from 'drizzle-orm/postgres-js' import { describe, expect, it } from 'vitest' import * as schema from '../schema' import { buildAccessibleFavoriteTargetQuery, DrizzlePlaybookFavoriteStore, type PlaybookFavoriteTransaction, type PlaybookFavoriteTransactionRunner, } from './playbook-favorite-store' const workspaceId = '00000000-0000-4000-8000-000000000001' const userId = '00000000-0000-4000-8000-000000000002' const playbookId = '00000000-0000-4000-8000-000000000003' class MemoryRunner implements PlaybookFavoriteTransactionRunner { accessible = true favorite = false additions = 0 removals = 0 async run( work: (transaction: PlaybookFavoriteTransaction) => Promise, ): Promise { return work({ targetIsAccessible: async () => this.accessible, add: async () => { if (!this.favorite) this.additions += 1 this.favorite = true }, remove: async () => { if (this.favorite) this.removals += 1 this.favorite = false }, }) } } describe('favorite target query', () => { it('accepts only the requested built-in or same-workspace playbook', () => { const database = drizzle.mock({ schema }) const query = buildAccessibleFavoriteTargetQuery(database, { workspaceId, playbookId, }).toSQL() expect(query.sql).toContain('from "playbooks"') expect(query.sql).toContain('"playbooks"."source_type" = $') expect(query.sql).toContain('"playbooks"."workspace_id" = $') expect(query.params).toEqual( expect.arrayContaining([playbookId, 'built_in', workspaceId]), ) }) }) describe('DrizzlePlaybookFavoriteStore', () => { it('adds and removes idempotently', async () => { const runner = new MemoryRunner() const store = new DrizzlePlaybookFavoriteStore(runner) const input = { workspaceId, userId, playbookId } await expect( store.mutateFavorite({ ...input, mutation: 'add' }), ).resolves.toBe(true) await store.mutateFavorite({ ...input, mutation: 'add' }) await expect( store.mutateFavorite({ ...input, mutation: 'remove' }), ).resolves.toBe(true) await store.mutateFavorite({ ...input, mutation: 'remove' }) expect(runner.additions).toBe(1) expect(runner.removals).toBe(1) }) it('does not mutate a missing or inaccessible target', async () => { const runner = new MemoryRunner() runner.accessible = false const store = new DrizzlePlaybookFavoriteStore(runner) await expect( store.mutateFavorite({ workspaceId, userId, playbookId, mutation: 'add', }), ).resolves.toBe(false) expect(runner.favorite).toBe(false) }) })