import { beforeEach, describe, expect, it, vi } from 'vitest' const prismaMock = vi.hoisted(() => ({ $queryRaw: vi.fn(), $executeRaw: vi.fn(), $transaction: vi.fn(), locationImage: { createMany: vi.fn() }, globalLocationImage: { createMany: vi.fn() }, })) vi.mock('@/lib/prisma', () => ({ prisma: prismaMock, })) describe('location-backed assets service', () => { beforeEach(() => { vi.clearAllMocks() prismaMock.$queryRaw .mockResolvedValueOnce([ { id: 'location-1', novelPromotionProjectId: 'novel-project-1', name: 'Bronze Dagger', summary: 'Old bronze dagger', selectedImageId: null, sourceGlobalLocationId: null, assetKind: 'prop', }, ]) .mockResolvedValueOnce([]) }) it('queries project location-backed assets with real schema column names', async () => { const mod = await import('@/lib/assets/services/location-backed-assets') await mod.listProjectLocationBackedAssets('novel-project-1', 'prop') const assetQuery = prismaMock.$queryRaw.mock.calls[0]?.[0] as { strings?: ReadonlyArray; sql?: string } const imageQuery = prismaMock.$queryRaw.mock.calls[1]?.[0] as { strings?: ReadonlyArray; sql?: string } const assetSql = assetQuery.strings?.join(' ') ?? assetQuery.sql ?? '' const imageSql = imageQuery.strings?.join(' ') ?? imageQuery.sql ?? '' expect(assetSql).toContain('FROM novel_promotion_locations') expect(assetSql).toContain('novelPromotionProjectId') expect(assetSql).not.toContain('projectId') expect(imageSql).toContain('FROM location_images') expect(imageSql).toContain('NULL AS previousImageMediaId') }) it('seeds an initial project image slot when creating a prop asset', async () => { const mod = await import('@/lib/assets/services/location-backed-assets') const result = await mod.createProjectLocationBackedAsset({ novelPromotionProjectId: 'novel-project-1', name: 'Bronze Dagger', summary: 'Old bronze dagger', initialDescription: 'A bronze dagger with a carved handle and weathered blade', kind: 'prop', }) expect(prismaMock.locationImage.createMany).toHaveBeenCalledWith({ data: [ { locationId: result.id, imageIndex: 0, description: 'A bronze dagger with a carved handle and weathered blade', availableSlots: '[]', }, ], }) }) it('seeds multiple project image slots when explicit descriptions are provided', async () => { const mod = await import('@/lib/assets/services/location-backed-assets') await mod.seedProjectLocationBackedImageSlots({ locationId: 'location-1', descriptions: ['Night street', 'Rainy alley'], fallbackDescription: 'Night street', }) expect(prismaMock.locationImage.createMany).toHaveBeenCalledWith({ data: [ { locationId: 'location-1', imageIndex: 0, description: 'Night street', availableSlots: '[]', }, { locationId: 'location-1', imageIndex: 1, description: 'Rainy alley', availableSlots: '[]', }, ], }) }) })