Files
pokemon-inventory/apps/api/tests/http/pricing-routes.test.js
T
2026-09-10 06:46:11 -04:00

220 lines
6.5 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import { buildApp } from "../../src/app/build-app.js";
test("accepts a protected pricing observation and valuation request", async (testContext) => {
let csrfChecks = 0;
const app = buildApp({
authenticateAdmin: async () => ({ sellerId: "seller-1" }),
authorizeUnsafeRequest: async () => {
csrfChecks += 1;
},
pricingService: {
async addObservation(input) {
return { id: "observation-1", ...input };
},
async calculate() {
return {
valuation: {
id: "valuation-1",
inventoryItemId: "inventory-1",
valueMinor: 2500,
currency: "USD",
strategy: "MEDIAN",
confidence: "LOW",
sourceCount: 1,
},
observations: [],
};
},
},
});
testContext.after(() => app.close());
const observationResponse = await app.inject({
method: "POST",
url: "/api/v1/admin/pricing/observations",
headers: { "x-csrf-token": "csrf" },
payload: {
productId: "d2f67d74-9647-48d5-b3a4-74d3b16ea9f8",
inventoryItemId: "21eb218b-7a95-4f80-a9e3-e306f7ca1f2e",
providerId: "manual",
priceMinor: 2500,
sourceIdentifier: "source-1",
conditionId: "ab8150c7-96c1-4b55-8d69-7d0b77d48958",
},
});
const valuationResponse = await app.inject({
method: "POST",
url: "/api/v1/admin/inventory/21eb218b-7a95-4f80-a9e3-e306f7ca1f2e/valuation",
});
assert.equal(observationResponse.statusCode, 201);
assert.equal(valuationResponse.statusCode, 200);
assert.equal(valuationResponse.json().data.valueMinor, 2500);
assert.equal(csrfChecks, 2);
});
test("accepts a CSRF-protected market-value override", async (testContext) => {
let received;
const app = buildApp({
authenticateAdmin: async () => ({ userId: "user-1", sellerId: "seller-1" }),
authorizeUnsafeRequest: async () => {},
pricingService: {
async override(input) {
received = input;
return { id: "override-1", ...input };
},
},
});
testContext.after(() => app.close());
const response = await app.inject({
method: "POST",
url: "/api/v1/admin/inventory/21eb218b-7a95-4f80-a9e3-e306f7ca1f2e/valuation/override",
headers: { "x-csrf-token": "csrf" },
payload: { valueMinor: 2600, reason: "Verified recent sale" },
});
assert.equal(response.statusCode, 201);
assert.equal(received.actorUserId, "user-1");
assert.equal(response.json().data.valueMinor, 2600);
});
test("enqueues a pricing refresh and exposes seller-scoped job status", async (testContext) => {
const app = buildApp({
authenticateAdmin: async () => ({ userId: "user-1", sellerId: "seller-1" }),
authorizeUnsafeRequest: async () => {},
pricingService: {
async enqueueRefresh(input) {
return { id: "job-1", status: "PENDING", createdAt: input.sellerId };
},
async getJob(input) {
assert.equal(input.sellerId, "seller-1");
return { id: input.jobId, status: "COMPLETED", error: null };
},
},
});
testContext.after(() => app.close());
const response = await app.inject({
method: "POST",
url: "/api/v1/admin/inventory/inventory-1/pricing/refresh",
headers: { "x-csrf-token": "csrf" },
payload: { providerId: "missing" },
});
assert.equal(response.statusCode, 202);
assert.equal(response.json().data.status, "PENDING");
const jobResponse = await app.inject({
method: "GET",
url: "/api/v1/admin/jobs/job-1",
});
assert.equal(jobResponse.statusCode, 200);
assert.equal(jobResponse.json().data.status, "COMPLETED");
});
test("returns a conflict when refresh has no active product mapping", async (testContext) => {
const app = buildApp({
authenticateAdmin: async () => ({ sellerId: "seller-1" }),
authorizeUnsafeRequest: async () => {},
pricingService: {
async enqueueRefresh() {
const error = new Error(
"No active pricing provider mapping exists for this product.",
);
error.code = "PRICING_PROVIDER_MAPPING_NOT_FOUND";
error.statusCode = 409;
throw error;
},
},
});
testContext.after(() => app.close());
const response = await app.inject({
method: "POST",
url: "/api/v1/admin/inventory/item-1/pricing/refresh",
headers: { "x-csrf-token": "csrf" },
payload: { productId: "product-1", conditionId: "condition-1" },
});
assert.equal(response.statusCode, 409);
assert.equal(
response.json().error.code,
"PRICING_PROVIDER_MAPPING_NOT_FOUND",
);
});
test("lists jobs and retries a failed job with CSRF protection", async (testContext) => {
let csrfChecks = 0;
const app = buildApp({
authenticateAdmin: async () => ({ sellerId: "seller-1" }),
authorizeUnsafeRequest: async () => {
csrfChecks += 1;
},
pricingService: {
async listJobs() {
return [
{
id: "job-1",
status: "FAILED",
providerId: "fixture",
inventoryItemId: "item-1",
},
];
},
async retryJob() {
return { id: "job-1", status: "PENDING" };
},
},
});
testContext.after(() => app.close());
const listResponse = await app.inject({
method: "GET",
url: "/api/v1/admin/jobs",
});
const retryResponse = await app.inject({
method: "POST",
url: "/api/v1/admin/jobs/job-1/retry",
headers: { "x-csrf-token": "csrf" },
});
assert.equal(listResponse.statusCode, 200);
assert.equal(retryResponse.statusCode, 202);
assert.equal(csrfChecks, 1);
});
test("lists seller-scoped latest market valuations", async (testContext) => {
const app = buildApp({
authenticateAdmin: async () => ({ sellerId: "seller-1" }),
pricingService: {
async listValuations({ sellerId }) {
assert.equal(sellerId, "seller-1");
return [
{
id: "valuation-1",
inventoryItemId: "item-1",
valueMinor: 2500,
currency: "USD",
strategy: "MEDIAN",
confidence: "MEDIUM",
sourceCount: 2,
calculatedAt: new Date().toISOString(),
},
];
},
},
});
testContext.after(() => app.close());
const response = await app.inject({
method: "GET",
url: "/api/v1/admin/valuations",
});
assert.equal(response.statusCode, 200);
const [valuation] = response.json().data;
assert.equal(valuation.inventoryItemId, "item-1");
assert.equal(valuation.valueMinor, 2500);
assert.equal(valuation.stale, false);
});