feat: reconcile application vertical slices
Backfill the completed acquisitions, sales, dashboard, pricing, search, public detail, worker, migration, contracts, validation, tests, and implementation-status changes into one verified application slice.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test, { after, beforeEach } from 'node:test';
|
||||
import assert from "node:assert/strict";
|
||||
import test, { after, beforeEach } from "node:test";
|
||||
|
||||
import { sql } from 'drizzle-orm';
|
||||
import { sql } from "drizzle-orm";
|
||||
|
||||
import { createDatabase } from '../../src/infrastructure/database/client.js';
|
||||
import { createDatabase } from "../../src/infrastructure/database/client.js";
|
||||
import {
|
||||
categories,
|
||||
conditionProfiles,
|
||||
@@ -13,76 +13,103 @@ import {
|
||||
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';
|
||||
} 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.');
|
||||
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".');
|
||||
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 pricingRefreshJobRepository = createPricingRefreshJobRepository(
|
||||
database.db,
|
||||
);
|
||||
const providerMappingRepository = createProviderMappingRepository(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();
|
||||
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 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`);
|
||||
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 () => {
|
||||
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;
|
||||
|
||||
@@ -90,47 +117,50 @@ test('persists acquisitions, valuations, multi-item sales, voids, and audit even
|
||||
sellerId: seller.id,
|
||||
acquisition: {
|
||||
inventoryItemId: firstItem.id,
|
||||
method: 'PURCHASE',
|
||||
method: "PURCHASE",
|
||||
costMinor: 1000,
|
||||
currency: 'USD',
|
||||
currency: "USD",
|
||||
shippingMinor: 100,
|
||||
taxMinor: 50,
|
||||
otherFeesMinor: 0,
|
||||
estimatedTradeValueMinor: 0,
|
||||
},
|
||||
});
|
||||
assert.equal(acquisition.status, 'CREATED');
|
||||
assert.equal(acquisition.status, "CREATED");
|
||||
|
||||
for (const [item, price] of [[firstItem, 2500], [secondItem, 3000]]) {
|
||||
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',
|
||||
providerId: "integration",
|
||||
priceMinor: price,
|
||||
currency: 'USD',
|
||||
currency: "USD",
|
||||
sourceIdentifier: `source-${item.id}`,
|
||||
conditionId: condition.id,
|
||||
},
|
||||
});
|
||||
assert.equal(observation.status, 'CREATED');
|
||||
assert.equal(observation.status, "CREATED");
|
||||
}
|
||||
|
||||
const valuation = await pricingRepository.calculateForSeller({
|
||||
sellerId: seller.id,
|
||||
inventoryItemId: firstItem.id,
|
||||
});
|
||||
assert.equal(valuation.status, 'CALCULATED');
|
||||
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',
|
||||
saleDate: "2026-09-09",
|
||||
soldPriceMinor: 5500,
|
||||
currency: 'USD',
|
||||
currency: "USD",
|
||||
platformFeesMinor: 200,
|
||||
paymentProcessingFeesMinor: 0,
|
||||
shippingCostMinor: 0,
|
||||
@@ -138,58 +168,92 @@ test('persists acquisitions, valuations, multi-item sales, voids, and audit even
|
||||
otherFeesMinor: 0,
|
||||
},
|
||||
items: [
|
||||
{ inventoryItemId: firstItem.id, soldPriceMinor: 2500, costBasisMinor: 1150 },
|
||||
{ inventoryItemId: secondItem.id, soldPriceMinor: 3000, costBasisMinor: 900 },
|
||||
{
|
||||
inventoryItemId: firstItem.id,
|
||||
soldPriceMinor: 2500,
|
||||
costBasisMinor: 1150,
|
||||
},
|
||||
{
|
||||
inventoryItemId: secondItem.id,
|
||||
soldPriceMinor: 3000,
|
||||
costBasisMinor: 900,
|
||||
},
|
||||
],
|
||||
});
|
||||
assert.equal(sale.status, 'CREATED');
|
||||
assert.equal(sale.status, "CREATED");
|
||||
assert.equal(sale.saleItems.length, 2);
|
||||
assert.deepEqual(sale.inventoryItems.map(({ status }) => status), ['SOLD', 'SOLD']);
|
||||
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',
|
||||
reason: "Integration correction",
|
||||
});
|
||||
assert.equal(voided.status, 'VOIDED');
|
||||
assert.equal(voided.sale.status, 'VOIDED');
|
||||
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'`);
|
||||
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 () => {
|
||||
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 [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.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');
|
||||
assert.equal(user.email, "transactions@example.test");
|
||||
});
|
||||
|
||||
test('persists and disables seller-owned provider mappings', async () => {
|
||||
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);
|
||||
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,
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user