dde53319e5
- New custom server.js (replaces react-router-serve): ISO timestamps on all console.* output and on access logs, and skip successful /healthz polls so real traffic stays visible. - New ProcessedWebhook table + dedupe helper keyed on X-Shopify-Webhook-Id; stops Shopify retries from triggering a second invoice email when the original delivery exceeded the 5s ack timeout. - orders/create + orders/fulfilled now respond 200 immediately and run the PDF/email work in the background so we stay under that timeout. - pickLanguage(): non-German locales (it, fr, es, ...) now default to English instead of falling back to German. Empty/unknown still maps to 'de' so the per-shop defaultLanguage chain keeps working. - Tests for pickLanguage and dedupe via node --test + tsx.
80 lines
3.0 KiB
TypeScript
80 lines
3.0 KiB
TypeScript
import { strict as assert } from "node:assert";
|
|
import { describe, it } from "node:test";
|
|
|
|
import { pickLanguage } from "../app/services/invoice/i18n";
|
|
import { isDuplicateWebhook, type DedupeDeps } from "../app/services/webhooks/dedupe.server";
|
|
|
|
describe("pickLanguage", () => {
|
|
it("returns 'de' only for explicit German locales", () => {
|
|
assert.equal(pickLanguage("de"), "de");
|
|
assert.equal(pickLanguage("de-AT"), "de");
|
|
assert.equal(pickLanguage("de-DE"), "de");
|
|
assert.equal(pickLanguage("de_CH"), "de");
|
|
assert.equal(pickLanguage("DE-AT"), "de"); // case-insensitive
|
|
});
|
|
|
|
it("returns 'en' for non-German locales (regression: it/fr/es no longer fall back to de)", () => {
|
|
assert.equal(pickLanguage("en"), "en");
|
|
assert.equal(pickLanguage("en-US"), "en");
|
|
assert.equal(pickLanguage("it"), "en");
|
|
assert.equal(pickLanguage("it-IT"), "en");
|
|
assert.equal(pickLanguage("fr"), "en");
|
|
assert.equal(pickLanguage("fr-FR"), "en");
|
|
assert.equal(pickLanguage("es"), "en");
|
|
assert.equal(pickLanguage("hu-HU"), "en");
|
|
});
|
|
|
|
it("falls back to 'de' for empty/unknown input so the per-shop default chain still works", () => {
|
|
assert.equal(pickLanguage(undefined), "de");
|
|
assert.equal(pickLanguage(null), "de");
|
|
assert.equal(pickLanguage(""), "de");
|
|
});
|
|
});
|
|
|
|
function makeRequest(headers: Record<string, string> = {}): Request {
|
|
return new Request("https://example.com/webhooks/test", {
|
|
method: "POST",
|
|
headers,
|
|
});
|
|
}
|
|
|
|
function makeDeps(behaviour: "ok" | "p2002" | "boom"): DedupeDeps {
|
|
return {
|
|
db: {
|
|
processedWebhook: {
|
|
create: async () => {
|
|
if (behaviour === "ok") return {};
|
|
if (behaviour === "p2002") {
|
|
const err = new Error("Unique constraint failed") as Error & { code?: string };
|
|
err.code = "P2002";
|
|
throw err;
|
|
}
|
|
throw new Error("DB unavailable");
|
|
},
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
describe("isDuplicateWebhook", () => {
|
|
it("returns false on first delivery (insert succeeds)", async () => {
|
|
const req = makeRequest({ "x-shopify-webhook-id": "abc-123" });
|
|
assert.equal(await isDuplicateWebhook(req, "shop.myshopify.com", "ORDERS_CREATE", makeDeps("ok")), false);
|
|
});
|
|
|
|
it("returns true on a retried delivery (P2002)", async () => {
|
|
const req = makeRequest({ "x-shopify-webhook-id": "abc-123" });
|
|
assert.equal(await isDuplicateWebhook(req, "shop.myshopify.com", "ORDERS_CREATE", makeDeps("p2002")), true);
|
|
});
|
|
|
|
it("returns false (and proceeds) when the dedupe table itself errors \u2014 fail-open, never drop a webhook silently", async () => {
|
|
const req = makeRequest({ "x-shopify-webhook-id": "abc-123" });
|
|
assert.equal(await isDuplicateWebhook(req, "shop.myshopify.com", "ORDERS_CREATE", makeDeps("boom")), false);
|
|
});
|
|
|
|
it("returns false when the X-Shopify-Webhook-Id header is missing (test harness / non-Shopify caller)", async () => {
|
|
const req = makeRequest();
|
|
assert.equal(await isDuplicateWebhook(req, "shop.myshopify.com", "ORDERS_CREATE", makeDeps("ok")), false);
|
|
});
|
|
});
|