/** * Task 3 — document-folders service: listTree + createFolder (TDD). * * Uses the makePort factory (not a "setupTestPort" helper — that name * doesn't exist in this codebase). TEST_USER_ID is resolved once via * beforeAll from any seeded user, matching the pattern in * alerts-tenant-isolation.test.ts and gdpr-export.test.ts. */ import { describe, it, expect, beforeAll, beforeEach } from 'vitest'; import { eq } from 'drizzle-orm'; import { db } from '@/lib/db'; import { documentFolders } from '@/lib/db/schema/documents'; import { user } from '@/lib/db/schema/users'; import { listTree, createFolder } from '@/lib/services/document-folders.service'; import { makePort } from '../helpers/factories'; let TEST_USER_ID = ''; beforeAll(async () => { const [u] = await db.select({ id: user.id }).from(user).limit(1); if (!u) throw new Error('No user available; run pnpm db:seed first'); TEST_USER_ID = u.id; }); describe('document-folders service · listTree', () => { let portId: string; beforeEach(async () => { const port = await makePort(); portId = port.id; await db.delete(documentFolders).where(eq(documentFolders.portId, portId)); }); it('returns an empty array when no folders exist', async () => { const tree = await listTree(portId); expect(tree).toEqual([]); }); it('returns root folders with children nested under them', async () => { const root = await createFolder(portId, TEST_USER_ID, { name: 'Deals 2026', parentId: null }); const child = await createFolder(portId, TEST_USER_ID, { name: 'Q1', parentId: root.id, }); const tree = await listTree(portId); expect(tree).toHaveLength(1); expect(tree[0]?.id).toBe(root.id); expect(tree[0]?.children).toHaveLength(1); expect(tree[0]?.children[0]?.id).toBe(child.id); }); it('only returns folders for the requested port', async () => { const otherPort = await makePort(); await createFolder(otherPort.id, TEST_USER_ID, { name: 'Other Port', parentId: null }); const tree = await listTree(portId); expect(tree).toEqual([]); }); }); describe('document-folders service · createFolder unique-sibling guard', () => { let portId: string; beforeEach(async () => { const port = await makePort(); portId = port.id; await db.delete(documentFolders).where(eq(documentFolders.portId, portId)); }); it('rejects a duplicate sibling name (case-insensitive)', async () => { await createFolder(portId, TEST_USER_ID, { name: 'Deals 2026', parentId: null }); await expect( createFolder(portId, TEST_USER_ID, { name: 'deals 2026', parentId: null }), ).rejects.toThrow(/already exists/i); }); it('allows the same name under different parents', async () => { const a = await createFolder(portId, TEST_USER_ID, { name: 'A', parentId: null }); const b = await createFolder(portId, TEST_USER_ID, { name: 'B', parentId: null }); await createFolder(portId, TEST_USER_ID, { name: 'Drafts', parentId: a.id }); await expect( createFolder(portId, TEST_USER_ID, { name: 'Drafts', parentId: b.id }), ).resolves.toBeDefined(); }); it('rejects a parentId from another port', async () => { const otherPort = await makePort(); const otherFolder = await createFolder(otherPort.id, TEST_USER_ID, { name: 'Other', parentId: null, }); await expect( createFolder(portId, TEST_USER_ID, { name: 'Should fail', parentId: otherFolder.id }), ).rejects.toThrow(/invalid parent/i); }); });