import assert from 'node:assert/strict'; import test from 'node:test'; import { buildApp } from '../../src/app/build-app.js'; import { createListingService } from '../../src/modules/listings/services/listing-service.js'; function createTestApp({ authenticateAdmin, authorizeUnsafeRequest, listing } = {}) { const app = buildApp({ authenticateAdmin, authorizeUnsafeRequest, listingService: createListingService({ listingRepository: { async create(listing) { return { id: 'listing-1', ...listing, createdAt: new Date('2026-09-07T00:00:00.000Z'), updatedAt: new Date('2026-09-07T00:00:00.000Z'), }; }, async findByIdForSeller() { return listing ?? { id: 'listing-1', inventoryItemId: 'inventory-1', productName: 'Charizard ex', categoryId: 'category-1', conditionId: 'condition-1', askingPriceMinor: 2499, status: 'DRAFT', }; }, async listBySellerId() { return [{ id: 'listing-1', inventoryItemId: 'inventory-1', productName: 'Charizard ex', categoryId: 'category-1', conditionId: 'condition-1', askingPriceMinor: 2499, status: 'DRAFT', }]; }, async updateStatus(id, status) { return { ...(listing ?? { id, inventoryItemId: 'inventory-1', productName: 'Charizard ex', categoryId: 'category-1', conditionId: 'condition-1', askingPriceMinor: 2499, }), id, status, }; }, }, inventoryImageRepository: { async countByInventoryItemId() { return 2; }, }, }), inventoryImageService: { async create(input) { return { id: 'image-1', ...input, processingStatus: 'PENDING', createdAt: new Date('2026-09-08T00:00:00.000Z'), updatedAt: new Date('2026-09-08T00:00:00.000Z'), }; }, }, }); return app; } test('creates a draft listing for the authenticated seller', async (testContext) => { const app = createTestApp({ authenticateAdmin: async () => ({ sellerId: 'seller-1' }), }); testContext.after(() => app.close()); const response = await app.inject({ method: 'POST', url: '/api/v1/admin/listings', payload: { inventoryItemId: 'd2f67d74-9647-48d5-b3a4-74d3b16ea9f8', productName: 'Charizard ex', categoryId: 'ab8150c7-96c1-4b55-8d69-7d0b77d48958', conditionId: '21eb218b-7a95-4f80-a9e3-e306f7ca1f2e', askingPriceMinor: 2499, notes: ' Near mint ', }, }); assert.equal(response.statusCode, 201); assert.equal(response.json().data.status, 'DRAFT'); assert.equal(response.json().data.productName, 'Charizard ex'); }); test('rejects a listing write without CSRF validation', async (testContext) => { const app = createTestApp({ authenticateAdmin: async () => ({ sellerId: 'seller-1' }), authorizeUnsafeRequest: async () => { const error = new Error('CSRF validation failed.'); error.code = 'FORBIDDEN'; error.statusCode = 403; throw error; }, }); testContext.after(() => app.close()); const response = await app.inject({ method: 'POST', url: '/api/v1/admin/listings', headers: { cookie: 'pokemon_inventory_session=raw-session-token' }, payload: { inventoryItemId: 'd2f67d74-9647-48d5-b3a4-74d3b16ea9f8', productName: 'Charizard ex', categoryId: 'ab8150c7-96c1-4b55-8d69-7d0b77d48958', conditionId: '21eb218b-7a95-4f80-a9e3-e306f7ca1f2e', askingPriceMinor: 2499, }, }); assert.equal(response.statusCode, 403); assert.equal(response.json().error.code, 'FORBIDDEN'); }); test('publishes a listing through the authenticated, CSRF-protected lifecycle route', async (testContext) => { let csrfChecks = 0; const app = createTestApp({ authenticateAdmin: async () => ({ sellerId: 'seller-1' }), authorizeUnsafeRequest: async () => { csrfChecks += 1; }, }); testContext.after(() => app.close()); const response = await app.inject({ method: 'POST', url: '/api/v1/admin/listings/listing-1/publish', headers: { 'x-csrf-token': 'csrf-token' }, }); assert.equal(response.statusCode, 200); assert.equal(response.json().data.status, 'AVAILABLE'); assert.equal(csrfChecks, 1); }); test('reads seller-owned listings without requiring CSRF validation', async (testContext) => { const app = createTestApp({ authenticateAdmin: async () => ({ sellerId: 'seller-1' }), authorizeUnsafeRequest: async () => { throw new Error('CSRF should not be checked for reads.'); }, }); testContext.after(() => app.close()); const collectionResponse = await app.inject({ method: 'GET', url: '/api/v1/admin/listings', }); const detailResponse = await app.inject({ method: 'GET', url: '/api/v1/admin/listings/listing-1', }); assert.equal(collectionResponse.statusCode, 200); assert.equal(collectionResponse.json().data[0].id, 'listing-1'); assert.equal(detailResponse.statusCode, 200); assert.equal(detailResponse.json().data.status, 'DRAFT'); }); test('adds inventory image metadata for the authenticated seller', async (testContext) => { const app = createTestApp({ authenticateAdmin: async () => ({ sellerId: 'seller-1' }), }); testContext.after(() => app.close()); const response = await app.inject({ method: 'POST', url: '/api/v1/admin/inventory/inventory-1/images', payload: { storageKey: 'inventory/inventory-1/front.jpg', originalFilename: 'front.jpg', mimeType: 'image/jpeg', fileSize: 12000, width: 1200, height: 900, altText: 'Front of Charizard ex card', isPrimary: true, }, }); assert.equal(response.statusCode, 201); assert.equal(response.json().data.inventoryItemId, 'inventory-1'); assert.equal(response.json().data.processingStatus, 'PENDING'); });