import assert from 'node:assert/strict'; import test, { after, beforeEach } from 'node:test'; import { sql } from 'drizzle-orm'; import { createDatabase } from '../../src/infrastructure/database/client.js'; import { categories, conditionProfiles, conditions, inventoryItems, products, securityAuditEvents, sellers, users, } from '../../src/infrastructure/database/schema.js'; import { createAcquisitionRepository } from '../../src/modules/acquisitions/repositories/acquisition-repository.js'; import { createPricingRepository } from '../../src/modules/pricing/repositories/pricing-repository.js'; import { createSaleRepository } from '../../src/modules/sales/repositories/sale-repository.js'; const connectionString = process.env.TEST_DATABASE_URL; if (!connectionString) { throw new Error('TEST_DATABASE_URL is required to run integration tests.'); } if (!new URL(connectionString).pathname.endsWith('_test')) { throw new Error('TEST_DATABASE_URL must reference a database whose name ends in "_test".'); } const database = createDatabase(connectionString); const acquisitionRepository = createAcquisitionRepository(database.db); const pricingRepository = createPricingRepository(database.db); const saleRepository = createSaleRepository(database.db); async function createFixture() { const [user] = await database.db.insert(users).values({ email: 'transactions@example.test', passwordHash: 'not-a-real-password', }).returning(); const [seller] = await database.db.insert(sellers).values({ userId: user.id, displayName: 'Transactions Owner', }).returning(); const [category] = await database.db.insert(categories).values({ name: 'Transactions Cards', slug: 'transactions-cards', }).returning(); const [profile] = await database.db.insert(conditionProfiles).values({ categoryId: category.id, name: 'Card', }).returning(); const [condition] = await database.db.insert(conditions).values({ conditionProfileId: profile.id, name: 'Near Mint', code: 'NM', qualityRank: 1, }).returning(); const [product] = await database.db.insert(products).values({ sellerId: seller.id, categoryId: category.id, name: 'Integration Card', slug: 'integration-card', }).returning(); const inventory = await database.db.insert(inventoryItems).values([ { productId: product.id, conditionId: condition.id }, { productId: product.id, conditionId: condition.id }, ]).returning(); return { user, seller, product, condition, inventory }; } beforeEach(async () => { await database.db.execute(sql`TRUNCATE market_value_overrides, market_valuations, pricing_observations, sale_items, sales, acquisitions, security_audit_events, inventory_items, products, conditions, condition_profiles, categories, sellers, users CASCADE`); }); after(async () => { await database.close(); }); test('persists acquisitions, valuations, multi-item sales, voids, and audit events transactionally', async () => { const { user, seller, product, condition, inventory } = await createFixture(); const [firstItem, secondItem] = inventory; const acquisition = await acquisitionRepository.createForSeller({ sellerId: seller.id, acquisition: { inventoryItemId: firstItem.id, method: 'PURCHASE', costMinor: 1000, currency: 'USD', shippingMinor: 100, taxMinor: 50, otherFeesMinor: 0, estimatedTradeValueMinor: 0, }, }); assert.equal(acquisition.status, 'CREATED'); for (const [item, price] of [[firstItem, 2500], [secondItem, 3000]]) { const observation = await pricingRepository.addObservationForSeller({ sellerId: seller.id, observation: { productId: product.id, inventoryItemId: item.id, providerId: 'integration', priceMinor: price, currency: 'USD', sourceIdentifier: `source-${item.id}`, conditionId: condition.id, }, }); assert.equal(observation.status, 'CREATED'); } const valuation = await pricingRepository.calculateForSeller({ sellerId: seller.id, inventoryItemId: firstItem.id, }); assert.equal(valuation.status, 'CALCULATED'); assert.equal(valuation.valuation.valueMinor, 2500); assert.equal(valuation.valuation.sourceCount, 1); const sale = await saleRepository.createForInventoryItems({ sale: { sellerId: seller.id, saleDate: '2026-09-09', soldPriceMinor: 5500, currency: 'USD', platformFeesMinor: 200, paymentProcessingFeesMinor: 0, shippingCostMinor: 0, taxesMinor: 0, otherFeesMinor: 0, }, items: [ { inventoryItemId: firstItem.id, soldPriceMinor: 2500, costBasisMinor: 1150 }, { inventoryItemId: secondItem.id, soldPriceMinor: 3000, costBasisMinor: 900 }, ], }); assert.equal(sale.status, 'CREATED'); assert.equal(sale.saleItems.length, 2); assert.deepEqual(sale.inventoryItems.map(({ status }) => status), ['SOLD', 'SOLD']); const voided = await saleRepository.voidForSeller({ saleId: sale.sale.id, sellerId: seller.id, actorUserId: user.id, reason: 'Integration correction', }); assert.equal(voided.status, 'VOIDED'); assert.equal(voided.sale.status, 'VOIDED'); assert.equal(voided.saleItems.length, 2); const [auditEvent] = await database.db.select().from(securityAuditEvents).where(sql`event = 'SALE_VOIDED'`); assert.equal(auditEvent.userId, user.id); });