mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
111 lines
3.9 KiB
TypeScript
111 lines
3.9 KiB
TypeScript
import { Readable } from "stream";
|
|
|
|
import { StampSettingsService } from "./stamp-settings.service";
|
|
|
|
/**
|
|
* The one global company stamp feeds three document paths (invoices, warehouse
|
|
* papers, contract signature blocks). All three treat the returned value as a
|
|
* `data:` URL — so the data-URL-or-null contract of getStampImageUrl is what
|
|
* these specs pin down, especially its behaviour when MinIO cannot be reached.
|
|
*/
|
|
describe("StampSettingsService.getStampImageUrl", () => {
|
|
const PNG = Buffer.from("fake-png-bytes");
|
|
const OBJECT_URL = "https://minio.local:9000/edr-freight/stamp/company.png";
|
|
|
|
const build = (
|
|
overrides: {
|
|
stampUrl?: string | null;
|
|
getFileStream?: jest.Mock;
|
|
findSingleton?: jest.Mock;
|
|
} = {},
|
|
) => {
|
|
const service = Object.create(
|
|
StampSettingsService.prototype,
|
|
) as StampSettingsService;
|
|
const warn = jest.fn();
|
|
Object.assign(service, {
|
|
logger: { warn, log: jest.fn() },
|
|
repository: {
|
|
findSingleton:
|
|
overrides.findSingleton ??
|
|
jest.fn().mockResolvedValue({
|
|
id: "s-1",
|
|
stampFileId: overrides.stampUrl ? "f-1" : null,
|
|
stampFile: overrides.stampUrl ? { url: overrides.stampUrl } : null,
|
|
updatedById: null,
|
|
updatedAt: null,
|
|
}),
|
|
create: jest.fn(),
|
|
update: jest.fn(),
|
|
},
|
|
minioService: {
|
|
getObjectNameFromUrl: jest.fn().mockReturnValue("stamp/company.png"),
|
|
getFileStream:
|
|
overrides.getFileStream ??
|
|
jest.fn().mockResolvedValue(Readable.from(PNG)),
|
|
},
|
|
filesService: { upload: jest.fn() },
|
|
});
|
|
return { service, warn };
|
|
};
|
|
|
|
it("inlines the stored stamp as a data URL", async () => {
|
|
const { service } = build({ stampUrl: OBJECT_URL });
|
|
|
|
await expect(service.getStampImageUrl()).resolves.toBe(
|
|
`data:image/png;base64,${PNG.toString("base64")}`,
|
|
);
|
|
});
|
|
|
|
it("returns null when no stamp is configured", async () => {
|
|
const { service } = build({ stampUrl: null });
|
|
|
|
await expect(service.getStampImageUrl()).resolves.toBeNull();
|
|
});
|
|
|
|
it("passes an already-inlined data URL straight through", async () => {
|
|
const dataUrl = "data:image/png;base64,QUJD";
|
|
const { service } = build({ stampUrl: dataUrl });
|
|
|
|
await expect(service.getStampImageUrl()).resolves.toBe(dataUrl);
|
|
});
|
|
|
|
/**
|
|
* Regression: inlineImageUrl falls back to the raw object URL when MinIO is
|
|
* unreachable, which is right for getView (a browser can fetch it) but wrong
|
|
* here. ContractTransitionService base64-decodes this value to snapshot the
|
|
* seal — and a URL decodes to garbage bytes WITHOUT throwing, so a transient
|
|
* MinIO failure used to seal an executed contract with a corrupt image file.
|
|
* Degrade to null instead so callers draw their text/vector seal.
|
|
*/
|
|
it("returns null rather than a raw object URL when MinIO inlining fails", async () => {
|
|
const { service, warn } = build({
|
|
stampUrl: OBJECT_URL,
|
|
getFileStream: jest.fn().mockRejectedValue(new Error("connect ECONNREFUSED")),
|
|
});
|
|
|
|
await expect(service.getStampImageUrl()).resolves.toBeNull();
|
|
expect(warn).toHaveBeenCalledWith(expect.stringMatching(/plain seal/i));
|
|
});
|
|
|
|
it("never throws when the settings lookup itself fails", async () => {
|
|
const { service, warn } = build({
|
|
findSingleton: jest.fn().mockRejectedValue(new Error("db is down")),
|
|
});
|
|
|
|
await expect(service.getStampImageUrl()).resolves.toBeNull();
|
|
expect(warn).toHaveBeenCalledWith(expect.stringContaining("db is down"));
|
|
});
|
|
|
|
it("still exposes the raw URL through getView, which a browser can load", async () => {
|
|
const { service } = build({
|
|
stampUrl: OBJECT_URL,
|
|
getFileStream: jest.fn().mockRejectedValue(new Error("connect ECONNREFUSED")),
|
|
});
|
|
|
|
await expect(service.getView()).resolves.toEqual(
|
|
expect.objectContaining({ stampImageUrl: OBJECT_URL }),
|
|
);
|
|
});
|
|
});
|