feat: schedule pricing job retention cleanup
Quality / test (push) Canceled after 0s

Schedule configurable completed pricing-job cleanup in the worker, broaden repository cleanup across sellers, add unit and integration coverage, and update pricing operational documentation.
This commit is contained in:
2026-09-09 22:32:04 -04:00
parent 660690bf34
commit 76e40d7289
8 changed files with 126 additions and 14 deletions
@@ -164,12 +164,11 @@ export function createPricingRefreshJobRepository(db) {
});
},
async purgeCompletedBefore(sellerId, cutoff) {
async purgeCompletedBefore(cutoff) {
const deleted = await db
.delete(pricingRefreshJobs)
.where(
and(
eq(pricingRefreshJobs.sellerId, sellerId),
eq(pricingRefreshJobs.status, "COMPLETED"),
lte(pricingRefreshJobs.updatedAt, cutoff),
),
@@ -41,11 +41,12 @@ const pricingRefreshJobRepository = createPricingRefreshJobRepository(
const providerMappingRepository = createProviderMappingRepository(database.db);
const saleRepository = createSaleRepository(database.db);
async function createFixture() {
async function createFixture(suffix = "") {
const fixtureName = suffix ? `transactions-${suffix}` : "transactions";
const [user] = await database.db
.insert(users)
.values({
email: "transactions@example.test",
email: `${fixtureName}@example.test`,
passwordHash: "not-a-real-password",
})
.returning();
@@ -53,14 +54,14 @@ async function createFixture() {
.insert(sellers)
.values({
userId: user.id,
displayName: "Transactions Owner",
displayName: `${fixtureName} Owner`,
})
.returning();
const [category] = await database.db
.insert(categories)
.values({
name: "Transactions Cards",
slug: "transactions-cards",
name: `${fixtureName} Cards`,
slug: `${fixtureName}-cards`,
})
.returning();
const [profile] = await database.db
@@ -84,8 +85,8 @@ async function createFixture() {
.values({
sellerId: seller.id,
categoryId: category.id,
name: "Integration Card",
slug: "integration-card",
name: `${fixtureName} Card`,
slug: `${fixtureName}-card`,
})
.returning();
const inventory = await database.db
@@ -229,6 +230,44 @@ test("claims and completes a durable pricing refresh job", async () => {
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(