Files
pokemon-inventory/apps/worker/tests/pricing-refresh-worker.test.js
fragginwagon 76e40d7289
Quality / test (push) Canceled after 0s
feat: schedule pricing job retention cleanup
Schedule configurable completed pricing-job cleanup in the worker, broaden repository cleanup across sellers, add unit and integration coverage, and update pricing operational documentation.
2026-09-09 22:32:04 -04:00

142 lines
3.9 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import {
createPricingProvider,
createPricingProviderRegistry,
} from "../../api/src/modules/pricing/providers/pricing-provider.js";
import {
getPricingJobRetentionCutoff,
processNextPersistentPricingRefreshJob,
purgeCompletedPricingRefreshJobs,
runPricingRefreshJob,
} from "../src/pricing-refresh-worker.js";
test("calculates a UTC retention cutoff and purges completed jobs before it", async () => {
const now = new Date("2026-09-09T12:00:00.000Z");
const cutoff = getPricingJobRetentionCutoff({ now, retentionDays: 30 });
assert.equal(cutoff.toISOString(), "2026-08-10T12:00:00.000Z");
let receivedCutoff;
const deleted = await purgeCompletedPricingRefreshJobs({
jobRepository: {
async purgeCompletedBefore(value) {
receivedCutoff = value;
return 3;
},
},
now,
retentionDays: 30,
});
assert.equal(deleted, 3);
assert.equal(receivedCutoff.toISOString(), cutoff.toISOString());
});
test("rejects invalid pricing job retention days", () => {
assert.throws(
() => getPricingJobRetentionCutoff({ retentionDays: 0 }),
/positive integer/,
);
});
test("completes a pricing refresh through the provider and pricing service boundaries", async () => {
const providerRegistry = createPricingProviderRegistry([
createPricingProvider({
id: "fixture-provider",
async observe() {
return [{ priceMinor: 2500, sourceIdentifier: "fixture-1" }];
},
}),
]);
const saved = [];
const result = await runPricingRefreshJob({
job: {
id: "job-1",
type: "PRICING_REFRESH",
providerId: "fixture-provider",
sellerId: "seller-1",
productId: "product-1",
inventoryItemId: "inventory-1",
conditionId: "condition-1",
},
providerRegistry,
pricingService: {
async addObservation(input) {
saved.push(input);
return input;
},
async calculate() {
return { valuation: { valueMinor: 2500 } };
},
},
});
assert.equal(result.status, "COMPLETED");
assert.equal(result.valuation.valuation.valueMinor, 2500);
assert.equal(saved[0].providerId, "fixture-provider");
});
test("fails a refresh clearly when a provider is unavailable", async () => {
const result = await runPricingRefreshJob({
job: { id: "job-2", type: "PRICING_REFRESH", providerId: "missing" },
providerRegistry: createPricingProviderRegistry(),
pricingService: {},
});
assert.equal(result.status, "FAILED");
assert.match(result.error, /not configured/);
});
test("persists completed and failed outcomes through the job repository", async () => {
const calls = [];
const jobRepository = {
async claimNext() {
return { id: "job-3", type: "PRICING_REFRESH", providerId: "fixture" };
},
async complete(id, observations, valuation) {
calls.push(["complete", id, observations, valuation]);
},
async fail(id, error) {
calls.push(["fail", id, error]);
},
};
await processNextPersistentPricingRefreshJob({
jobRepository,
async pricingRefreshJob() {
return {
status: "COMPLETED",
observations: [{ id: "observation-1" }],
valuation: { valueMinor: 2500 },
};
},
});
assert.deepEqual(calls, [
["complete", "job-3", [{ id: "observation-1" }], { valueMinor: 2500 }],
]);
});
test("persists failed pricing refresh outcomes through the repository", async () => {
const calls = [];
await processNextPersistentPricingRefreshJob({
jobRepository: {
async claimNext() {
return { id: "job-4" };
},
async complete() {
throw new Error("not expected");
},
async fail(id, error) {
calls.push([id, error]);
},
},
async pricingRefreshJob() {
return { status: "FAILED", error: "Provider unavailable." };
},
});
assert.deepEqual(calls, [["job-4", "Provider unavailable."]]);
});