import { INestApplication } from '@nestjs/common'; import { PrismaClient, User } from '@prisma/client'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { createTestApp } from '../testing/test-app'; import { createTestPrisma, grantOwnerAdmin, hasTestDb, uniqueSuffix } from '../testing/test-db'; import { PagesService } from './pages.service'; describe.skipIf(!hasTestDb)('PagesService.reposition (db, issue #45)', () => { let app: INestApplication; let prisma: PrismaClient; let pages: PagesService; const suffix = uniqueSuffix(); let owner: User; let pondId: string; /** Current manual order of the pond's pages, as the sidebar would render it. */ async function manualOrder(): Promise { const list = await pages.list(owner, pondId); return list.map((p) => p.title); } async function pageIdByTitle(title: string): Promise { const p = await prisma.page.findFirstOrThrow({ where: { pondId, title } }); return p.id; } beforeAll(async () => { prisma = createTestPrisma(); app = await createTestApp(); pages = app.get(PagesService); owner = await prisma.user.create({ data: { username: `pos-owner-${suffix}`, email: `pos-owner-${suffix}@example.test`, displayName: 'Position Owner', }, }); const pond = await prisma.pond.create({ data: { slug: `pos-pond-${suffix}`, name: 'Position Pond', type: 'PERSONAL', ownerId: owner.id, }, }); pondId = pond.id; await grantOwnerAdmin(prisma, pondId, owner.id); // Manual mode so `list` orders by sort_key. await prisma.pond.update({ where: { id: pondId }, data: { settings: { sidebarSort: 'manual' } }, }); // Four pages created in order A, B, C, D (each appended at the end). for (const title of ['A', 'B', 'C', 'D']) { await pages.create(owner, pondId, { title }); } }); afterAll(async () => { await prisma.page.deleteMany({ where: { pondId } }); await prisma.pond.deleteMany({ where: { id: pondId } }); await prisma.user.deleteMany({ where: { id: owner.id } }); await prisma.$disconnect(); await app.close(); }); it('starts in creation order', async () => { expect(await manualOrder()).toEqual(['A', 'B', 'C', 'D']); }); it('moves a page and the new order is server-ordered (persisted for everyone)', async () => { const d = await pageIdByTitle('D'); const a = await pageIdByTitle('A'); const b = await pageIdByTitle('B'); // Move D to sit between A and B → A, D, B, C. await pages.reposition(owner, d, { afterId: a, beforeId: b }); expect(await manualOrder()).toEqual(['A', 'D', 'B', 'C']); // A fresh read (any other user's view) sees the same order — it lives in // sort_key, not client state. const reread = (await pages.list(owner, pondId)).map((p) => p.title); expect(reread).toEqual(['A', 'D', 'B', 'C']); }); it('moves a page to the very top (afterId null)', async () => { const c = await pageIdByTitle('C'); const a = await pageIdByTitle('A'); await pages.reposition(owner, c, { afterId: null, beforeId: a }); expect(await manualOrder()).toEqual(['C', 'A', 'D', 'B']); }); it('keeps the manual order when the sort mode changes (just not applied)', async () => { await prisma.pond.update({ where: { id: pondId }, data: { settings: { sidebarSort: 'alpha' } }, }); expect(await manualOrder()).toEqual(['A', 'B', 'C', 'D']); // alpha ignores sort_key await prisma.pond.update({ where: { id: pondId }, data: { settings: { sidebarSort: 'manual' } }, }); // The manual order from before is intact — switching modes never rewrote keys. expect(await manualOrder()).toEqual(['C', 'A', 'D', 'B']); }); });