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 = { 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); } );