diff --git a/src/core/utils/cui-api.ts b/src/core/utils/cui-api.ts index 9011c568..f8f8af2c 100644 --- a/src/core/utils/cui-api.ts +++ b/src/core/utils/cui-api.ts @@ -2,6 +2,8 @@ import { HttpClient } from "../http/http-client"; import { FatalError, logger } from "./logger"; import { Context } from "../command/cli-context"; +export const CUI_MARKING_FAILED_MESSAGE = "Could not resolve CUI marking. No file was written"; + export interface CuiPdfCoverResponse { coverPage?: { pdfContent: string; encoding: string }; } @@ -39,6 +41,7 @@ export class CuiApi { return { marking: CuiMarking.CLASSIFIED, cover: data as CuiPdfCoverResponse }; } - throw new FatalError("Problem fetching cui pdf cover"); + logger.debug(`The CUI cover call answered with status ${status}`); + throw new FatalError(CUI_MARKING_FAILED_MESSAGE); } } diff --git a/src/core/utils/cui-file-service.ts b/src/core/utils/cui-file-service.ts index 5b069ccf..84ec3f04 100644 --- a/src/core/utils/cui-file-service.ts +++ b/src/core/utils/cui-file-service.ts @@ -1,10 +1,10 @@ import * as path from "node:path"; import AdmZip = require("adm-zip"); import { Context } from "../command/cli-context"; -import { CuiApi, CuiMarking, CuiPdfCoverResponse } from "./cui-api"; +import { CUI_MARKING_FAILED_MESSAGE, CuiApi, CuiMarking, CuiPdfCoverResponse } from "./cui-api"; import { fileService } from "./file-service"; import { FileConstants } from "./file.constants"; -import { FatalError } from "./logger"; +import { FatalError, logger } from "./logger"; export class CuiFileService { public static readonly COVER_SHEET_FILE_NAME = "CUI_Cover_Sheet.pdf"; @@ -96,10 +96,12 @@ export class CuiFileService { private decodeCoverPage(cover: CuiPdfCoverResponse): Buffer { const coverPage = cover.coverPage; if (!coverPage?.pdfContent) { - throw new FatalError("CUI marking applies but the response contained no cover page."); + logger.debug("CUI marking applies but the response contained no cover page"); + throw new FatalError(CUI_MARKING_FAILED_MESSAGE); } if (coverPage.encoding !== CuiFileService.BASE64_ENCODING) { - throw new FatalError(`Unsupported CUI cover page encoding: ${coverPage.encoding}`); + logger.debug(`Unsupported CUI cover page encoding: ${coverPage.encoding}`); + throw new FatalError(CUI_MARKING_FAILED_MESSAGE); } return Buffer.from(coverPage.pdfContent, CuiFileService.BASE64_ENCODING); diff --git a/tests/core/utils/cui-api.spec.ts b/tests/core/utils/cui-api.spec.ts new file mode 100644 index 00000000..320732de --- /dev/null +++ b/tests/core/utils/cui-api.spec.ts @@ -0,0 +1,50 @@ +import { CUI_MARKING_FAILED_MESSAGE, CuiApi, CuiMarking } from "../../../src/core/utils/cui-api"; +import { FatalError } from "../../../src/core/utils/logger"; +import { testContext } from "../../utls/test-context"; +import { mockAxiosGetWithStatus } from "../../utls/http-requests-mock"; + +describe("CuiApi", () => { + const COVER_URL = "https://myTeam.celonis.cloud/api/team/cui-settings/cui-pdf-cover"; + + const coverResponse = () => ({ + coverPage: { + pdfContent: Buffer.from("%PDF-1.4 cover sheet").toString("base64"), + encoding: "base64", + }, + }); + + let cuiApi: CuiApi; + + beforeEach(() => { + cuiApi = new CuiApi(testContext); + }); + + it("Should report the content as classified and pass the cover response on", async () => { + const cover = coverResponse(); + mockAxiosGetWithStatus(COVER_URL, 200, cover); + + await expect(cuiApi.getCuiMarking()).resolves.toEqual({ + marking: CuiMarking.CLASSIFIED, + cover, + }); + }); + + it("Should report that marking does not apply when the feature flag is disabled", async () => { + mockAxiosGetWithStatus(COVER_URL, 403, ""); + + await expect(cuiApi.getCuiMarking()).resolves.toEqual({ marking: CuiMarking.DISABLED }); + }); + + it("Should fail when the marking applies but the response body is empty", async () => { + mockAxiosGetWithStatus(COVER_URL, 200, ""); + + await expect(cuiApi.getCuiMarking()).rejects.toThrow(FatalError); + await expect(cuiApi.getCuiMarking()).rejects.toThrow(CUI_MARKING_FAILED_MESSAGE); + }); + + it("Should fail when the backend answers with an unexpected status", async () => { + mockAxiosGetWithStatus(COVER_URL, 204, ""); + + await expect(cuiApi.getCuiMarking()).rejects.toThrow(CUI_MARKING_FAILED_MESSAGE); + }); +}); diff --git a/tests/core/utils/cui-file-service.spec.ts b/tests/core/utils/cui-file-service.spec.ts index 826b9925..2aa99586 100644 --- a/tests/core/utils/cui-file-service.spec.ts +++ b/tests/core/utils/cui-file-service.spec.ts @@ -35,6 +35,15 @@ describe("CuiFileService", () => { expect(filename).toEqual("report.json"); expect(readFile("report.json").toString()).toEqual(PAYLOAD); }); + + it("Should keep the original filename when axios resolves the 403 instead of rejecting", async () => { + mockAxiosGetWithStatus(COVER_URL, 403, ""); + + const filename = await cuiFileService.writeToFileWithGivenName(PAYLOAD, "resolved-403.json"); + + expect(filename).toEqual("resolved-403.json"); + expect(readFile("resolved-403.json").toString()).toEqual(PAYLOAD); + }); }); describe("when the cover response cannot be used", () => { @@ -55,7 +64,8 @@ describe("CuiFileService", () => { it("Should fail when the backend answers with no content", async () => { mockAxiosGetWithStatus(COVER_URL, 204, ""); - await expect(cuiFileService.writeToFileWithGivenName(PAYLOAD, "no-content.json")).rejects.toThrow(FatalError); + await expect(cuiFileService.writeToFileWithGivenName(PAYLOAD, "no-content.json")) + .rejects.toThrow("Could not resolve CUI marking. No file was written"); expect(() => accessSync(resolve(process.cwd(), "no-content.json"))).toThrow(); }); }); @@ -81,14 +91,14 @@ describe("CuiFileService", () => { mockAxiosGetWithStatus(COVER_URL, 200, response); await expect(cuiFileService.writeToFileWithGivenName(PAYLOAD, "packages.json")) - .rejects.toThrow("Unsupported CUI cover page encoding: hex"); + .rejects.toThrow("Could not resolve CUI marking. No file was written"); }); it("Should fail when the marking applies but no cover page was returned", async () => { mockAxiosGetWithStatus(COVER_URL, 200, { teamId: "team-1" }); await expect(cuiFileService.writeToFileWithGivenName(PAYLOAD, "packages.json")) - .rejects.toThrow("CUI marking applies but the response contained no cover page."); + .rejects.toThrow("Could not resolve CUI marking. No file was written"); }); }); @@ -134,7 +144,7 @@ describe("CuiFileService", () => { mockAxiosGetWithStatus(COVER_URL, 200, { teamId: "team-1" }); await expect(cuiFileService.writeZipToFileWithGivenName(buildExportZip(), "export.zip")) - .rejects.toThrow("CUI marking applies but the response contained no cover page."); + .rejects.toThrow("Could not resolve CUI marking. No file was written"); }); }); @@ -174,7 +184,7 @@ describe("CuiFileService", () => { mockAxiosGetWithStatus(COVER_URL, 200, { teamId: "team-1" }); await expect(cuiFileService.writeDirectoryWithGivenName(writeTree, "broken-export")) - .rejects.toThrow("CUI marking applies but the response contained no cover page."); + .rejects.toThrow("Could not resolve CUI marking. No file was written"); expect(exists("broken-export")).toBe(false); expect(exists("CUI - broken-export")).toBe(false); });