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 { createPricingRefreshJobRepository } from "../../src/modules/pricing/repositories/pricing-refresh-job-repository.js"; import { createProviderMappingRepository } from "../../src/modules/pricing/repositories/provider-mapping-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 pricingRefreshJobRepository = createPricingRefreshJobRepository( database.db, ); const providerMappingRepository = createProviderMappingRepository(database.db); const saleRepository = createSaleRepository(database.db); async function createFixture(suffix = "") { const fixtureName = suffix ? `transactions-${suffix}` : "transactions"; const [user] = await database.db .insert(users) .values({ email: `${fixtureName}@example.test`, passwordHash: "not-a-real-password", }) .returning(); const [seller] = await database.db .insert(sellers) .values({ userId: user.id, displayName: `${fixtureName} Owner`, }) .returning(); const [category] = await database.db .insert(categories) .values({ name: `${fixtureName} Cards`, slug: `${fixtureName}-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: `${fixtureName} Card`, slug: `${fixtureName}-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 pricing_provider_mappings, pricing_refresh_jobs, 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); }); test("claims and completes a durable pricing refresh job", async () => { const { user, seller, product, condition, inventory } = await createFixture(); const [job] = await pricingRefreshJobRepository .create({ sellerId: seller.id, inventoryItemId: inventory[0].id, productId: product.id, conditionId: condition.id, providerId: "integration", }) .then((created) => [created]); const claimed = await pricingRefreshJobRepository.claimNext(); assert.equal(claimed.id, job.id); assert.equal(claimed.status, "PROCESSING"); const completed = await pricingRefreshJobRepository.complete( job.id, [{ priceMinor: 2500 }], { valueMinor: 2500 }, ); assert.equal(completed.status, "COMPLETED"); assert.deepEqual(completed.valuation, { valueMinor: 2500 }); assert.equal(user.email, "transactions@example.test"); }); test("purges completed pricing jobs across sellers before the cutoff", async () => { const first = await createFixture(); const second = await createFixture("second"); const jobs = await Promise.all( [first, second].map(({ seller, product, condition, inventory }) => pricingRefreshJobRepository.create({ sellerId: seller.id, inventoryItemId: inventory[0].id, productId: product.id, conditionId: condition.id, providerId: "integration", }), ), ); for (const job of jobs) { await pricingRefreshJobRepository.claimNext(); await pricingRefreshJobRepository.complete( job.id, [{ priceMinor: 2500 }], { valueMinor: 2500 }, ); } const deleted = await pricingRefreshJobRepository.purgeCompletedBefore( new Date(Date.now() + 1000), ); assert.equal(deleted, 2); assert.equal( (await pricingRefreshJobRepository.listForSeller(first.seller.id)).length, 0, ); assert.equal( (await pricingRefreshJobRepository.listForSeller(second.seller.id)).length, 0, ); }); test("persists and disables seller-owned provider mappings", async () => { const { seller, product } = await createFixture(); const created = await providerMappingRepository.createForSeller( { productId: product.id, providerId: "integration", mappingType: "PRODUCT", externalIdentifier: "catalog-1", }, seller.id, ); assert.equal(created.status, "CREATED"); assert.equal( (await providerMappingRepository.listForSeller(product.id, seller.id)) .length, 1, ); const disabled = await providerMappingRepository.disableForSeller( created.mapping.id, seller.id, ); assert.equal(disabled.status, "DISABLED"); assert.equal( (await providerMappingRepository.listForSeller(product.id, seller.id)) .length, 0, ); });