Test the checkout code; publish images only for releases (#5)
- src/checkout/ (PayPal order request, pricing, VAT) and src/logging/format.ts hold the checkout's pure code, moved out of PayPalApi, the product service and config without a behaviour change. - npm test runs node:test on the TypeScript sources (type stripping), with no dependencies: 21 tests, 13 pin today's output, 8 describe the correct behaviour for the known defects of libretech/mp#67 and are marked todo. - CI: the tests run in the image's base (node:22.14.0-alpine) for every pull request, push to main and release tag. The image is built for pull requests and built and published only for v* tags; main no longer publishes :main and :sha-* images. Refs libretech/mp#71 Co-authored-by: Michael Czechowski <mail@librete.ch>
This commit was merged in pull request #5.
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { formatLogLine } from "../../src/logging/format.ts";
|
||||
|
||||
const TIMESTAMP = "2026-10-08 21:29:40.426";
|
||||
const CREDENTIALS = "RFVNTVlfQ0xJRU5UX0lEOkRVTU1ZX0NMSUVOVF9TRUNSRVQ="; // base64 of the dummy "DUMMY_CLIENT_ID:DUMMY_CLIENT_SECRET"
|
||||
|
||||
// Characterization: what the console line looks like today.
|
||||
|
||||
test("renders timestamp, level and message, with a trailing space when there are no other fields", () => {
|
||||
assert.equal(
|
||||
formatLogLine({ timestamp: TIMESTAMP, level: "info", message: "app:i:paypal-api: hello" }),
|
||||
"2026-10-08 21:29:40.426Z info: app:i:paypal-api: hello "
|
||||
);
|
||||
});
|
||||
|
||||
test("appends the other fields as JSON indented by two spaces", () => {
|
||||
assert.equal(
|
||||
formatLogLine({
|
||||
timestamp: TIMESTAMP,
|
||||
level: "debug",
|
||||
message: "app:d:order-service: Calculating totals",
|
||||
productsTotal: 17.9,
|
||||
deliveryPrice: 4.95
|
||||
}),
|
||||
'2026-10-08 21:29:40.426Z debug: app:d:order-service: Calculating totals {\n "productsTotal": 17.9,\n "deliveryPrice": 4.95\n}'
|
||||
);
|
||||
});
|
||||
|
||||
test("ignores the symbol keys winston adds", () => {
|
||||
const info = { timestamp: TIMESTAMP, level: "info", message: "hello", [Symbol.for("level")]: "info", [Symbol.for("splat")]: [] };
|
||||
|
||||
assert.equal(formatLogLine(info), "2026-10-08 21:29:40.426Z info: hello ");
|
||||
});
|
||||
|
||||
// Known defects: these assert the correct behaviour and fail today, which the todo marks as expected.
|
||||
|
||||
test(
|
||||
"does not throw on a circular reference",
|
||||
{ todo: "JSON.stringify throws on a circular reference — https://git.librete.ch/libretech/mp/issues/67" },
|
||||
() => {
|
||||
const request: Record<string, unknown> = { method: "POST", path: "/v1/oauth2/token" };
|
||||
request.self = request;
|
||||
|
||||
assert.doesNotThrow(() => formatLogLine({ timestamp: TIMESTAMP, level: "error", message: "app:e:paypal-api", request }));
|
||||
}
|
||||
);
|
||||
|
||||
test(
|
||||
"does not write the Authorization header of an axios-like error into the line",
|
||||
{ todo: "the PayPal client credentials are logged — https://git.librete.ch/libretech/mp/issues/67" },
|
||||
() => {
|
||||
const error = Object.assign(new Error("getaddrinfo EAI_AGAIN api-m.paypal.com"), {
|
||||
name: "AxiosError",
|
||||
code: "EAI_AGAIN",
|
||||
config: { method: "post", url: "https://api-m.paypal.com/v1/oauth2/token", headers: { Authorization: `Basic ${CREDENTIALS}` } }
|
||||
});
|
||||
// What strapi.log.error(error) hands this formatter: Strapi's logErrors format spreads the error and appends its stack.
|
||||
const line = formatLogLine({ ...error, message: `${error.message}\n${error.stack}`, level: "error", timestamp: TIMESTAMP });
|
||||
|
||||
assert.ok(!line.includes(CREDENTIALS), line);
|
||||
}
|
||||
);
|
||||
|
||||
test(
|
||||
"renders an Error with its message instead of {}",
|
||||
{ todo: "an Error is rendered as {} — https://git.librete.ch/libretech/mp/issues/67" },
|
||||
() => {
|
||||
const error = new Error("getaddrinfo EAI_AGAIN api-m.paypal.com");
|
||||
// strapi.log.error("…", { error }), as in the order controller's capturePayment
|
||||
const line = formatLogLine({
|
||||
timestamp: TIMESTAMP,
|
||||
level: "error",
|
||||
message: "app:e:order-controller: Error capturing payment for order x",
|
||||
error
|
||||
});
|
||||
|
||||
assert.ok(line.includes("getaddrinfo EAI_AGAIN api-m.paypal.com"), line);
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user