diff --git a/docs/cui-marking.md b/docs/cui-marking.md index 5c71903b..88996bae 100644 --- a/docs/cui-marking.md +++ b/docs/cui-marking.md @@ -11,21 +11,19 @@ Example: `list packages --json` would otherwise write `packages.json`. | Cover response | Meaning | Outcome | |---|---|---| | **403** | Feature flag disabled | `packages.json` | -| **204** | Team has CUI disabled | `packages.json` | -| **200**, no categories | Marking applies, unclassified | `Unclassified - packages.json` | -| **200**, with categories | Marking applies, classified | `CUI - packages.zip` containing `packages.json` and `CUI_Cover_Sheet.pdf` | -| Unexpected response | Fail closed | Nothing written; the command errors | +| **200** | Classified | `CUI - packages.zip` containing `packages.json` and `CUI_Cover_Sheet.pdf` | +| Any other response, including **204** | Fail closed | Nothing written; the command errors | -**403** and **204** both leave the artifact unmarked. They are not the same as **200 with no categories**, which still renames it to `Unclassified - …`. +The status code alone decides the outcome. Marked content is always classified: there is no unclassified artifact. Any other answer, whether a **204**, an unexpected status, a transport failure, or a **200** without a usable cover page, aborts the command and leaves no output behind. ## Scope: how the write is triggered -| Trigger | Commands | Example (unclassified) | Example (classified) | -|---|---|---|---| -| `--json` listings and reports | `list spaces`, `list packages`, `list assets` / `assignments` / `data-pools`, `config *`, `t2tc package list` / `diff`, `deployment *`, `asset-registry *` | `list packages --json` → `Unclassified - packages.json` | `list packages --json` → `CUI - packages.zip` containing `packages.json` and `CUI_Cover_Sheet.pdf` | -| `-o, --outputToJsonFile` reports | `analyze` / `import action-flows`, `export data-pool`, `import data-pools`, `t2tc package import` report | `export data-pool -o` → `Unclassified - .json` | `export data-pool -o` → `CUI - .zip` containing the JSON and `CUI_Cover_Sheet.pdf` | -| Artifact is already an archive | `config package export --zip`, `config branch export --zip`, `t2tc package export`, `export action-flows`, `pull package` | `config package export --zip` → `Unclassified - my-package.zip` | `config package export --zip` → `CUI - my-package.zip` with `CUI_Cover_Sheet.pdf` inside the archive | -| Single non-archive export | `pull asset` / `skill` / `data-pool` / `view-bookmarks` / `bookmarks`, `export bookmarks` | `pull asset` → `Unclassified - asset_.yml` | `pull asset` → `CUI - asset_.zip` containing the YAML and `CUI_Cover_Sheet.pdf` | -| Output is a directory | `config package export`, `config branch export`, `t2tc package export --unzip` | `config package export` → `Unclassified - my-package/` | `config package export` → `CUI - my-package/` with `CUI_Cover_Sheet.pdf` inside | -| `--gitBranch` variants | `config package export`, `config branch export`, `t2tc package export` | Out of scope | Out of scope | -| No output flag | Console-only listings, profile / git-profile / log files | Out of scope | Out of scope | +| Trigger | Commands | Example when classified | +|---|---|---| +| `--json` listings and reports | `list spaces`, `list packages`, `list assets` / `assignments` / `data-pools`, `config *`, `t2tc package list` / `diff`, `deployment *`, `asset-registry *` | `list packages --json` → `CUI - packages.zip` containing `packages.json` and `CUI_Cover_Sheet.pdf` | +| `-o, --outputToJsonFile` reports | `analyze` / `import action-flows`, `export data-pool`, `import data-pools`, `t2tc package import` report | `export data-pool -o` → `CUI - .zip` containing the JSON and `CUI_Cover_Sheet.pdf` | +| Artifact is already an archive | `config package export --zip`, `config branch export --zip`, `t2tc package export`, `export action-flows`, `pull package` | `config package export --zip` → `CUI - my-package.zip` with `CUI_Cover_Sheet.pdf` inside the archive | +| Single non-archive export | `pull asset` / `skill` / `data-pool` / `view-bookmarks` / `bookmarks`, `export bookmarks` | `pull asset` → `CUI - asset_.zip` containing the YAML and `CUI_Cover_Sheet.pdf` | +| Output is a directory | `config package export`, `config branch export`, `t2tc package export --unzip` | `config package export` → `CUI - my-package/` with `CUI_Cover_Sheet.pdf` inside | +| `--gitBranch` variants | `config package export`, `config branch export`, `t2tc package export` | Out of scope | +| No output flag | Console-only listings, profile / git-profile / log files | Out of scope | diff --git a/src/core/utils/cui-api.ts b/src/core/utils/cui-api.ts index e17254e1..9011c568 100644 --- a/src/core/utils/cui-api.ts +++ b/src/core/utils/cui-api.ts @@ -3,15 +3,22 @@ import { FatalError, logger } from "./logger"; import { Context } from "../command/cli-context"; export interface CuiPdfCoverResponse { - resolvedCuiMarking?: { categories?: unknown[] }; coverPage?: { pdfContent: string; encoding: string }; } +export enum CuiMarking { + DISABLED = "DISABLED", + CLASSIFIED = "CLASSIFIED", +} + +export type CuiMarkingDecision = + | { marking: CuiMarking.DISABLED } + | { marking: CuiMarking.CLASSIFIED; cover: CuiPdfCoverResponse }; + export class CuiApi { private static readonly CUI_PDF_COVER_SHEET_URL = "/api/team/cui-settings/cui-pdf-cover"; private static readonly STATUS_OK = 200; - private static readonly STATUS_NO_CONTENT = 204; private static readonly STATUS_FORBIDDEN = 403; private readonly httpClient: () => HttpClient; @@ -20,21 +27,16 @@ export class CuiApi { this.httpClient = () => context.httpClient; } - public async getCuiPdfCover(): Promise { + public async getCuiMarking(): Promise { const { status, data } = await this.httpClient().getStatusAndData(CuiApi.CUI_PDF_COVER_SHEET_URL); if (status === CuiApi.STATUS_FORBIDDEN) { logger.debug("CUI marking does not apply, the feature flag is disabled"); - return null; - } - - if (status === CuiApi.STATUS_NO_CONTENT) { - logger.debug("CUI marking does not apply, the team has CUI disabled"); - return null; + return { marking: CuiMarking.DISABLED }; } if (status === CuiApi.STATUS_OK && data) { - return data as CuiPdfCoverResponse; + return { marking: CuiMarking.CLASSIFIED, cover: data as CuiPdfCoverResponse }; } throw new FatalError("Problem fetching cui pdf cover"); diff --git a/src/core/utils/cui-file-service.ts b/src/core/utils/cui-file-service.ts index 7a9b01f1..5b069ccf 100644 --- a/src/core/utils/cui-file-service.ts +++ b/src/core/utils/cui-file-service.ts @@ -1,7 +1,7 @@ import * as path from "node:path"; import AdmZip = require("adm-zip"); import { Context } from "../command/cli-context"; -import { CuiApi, CuiPdfCoverResponse } from "./cui-api"; +import { CuiApi, CuiMarking, CuiPdfCoverResponse } from "./cui-api"; import { fileService } from "./file-service"; import { FileConstants } from "./file.constants"; import { FatalError } from "./logger"; @@ -9,7 +9,6 @@ import { FatalError } from "./logger"; export class CuiFileService { public static readonly COVER_SHEET_FILE_NAME = "CUI_Cover_Sheet.pdf"; public static readonly CLASSIFIED_PREFIX = "CUI - "; - public static readonly UNCLASSIFIED_PREFIX = "Unclassified - "; private static readonly BASE64_ENCODING = "base64"; @@ -60,21 +59,14 @@ export class CuiFileService { filename: string, onClassified: (cover: CuiPdfCoverResponse) => string ): Promise { - const cover = await this.cuiApi.getCuiPdfCover(); + const decision = await this.cuiApi.getCuiMarking(); - if (!cover) { + if (decision.marking === CuiMarking.DISABLED) { write(filename); return filename; } - if (!this.isClassified(cover)) { - const unclassifiedName = this.prefixFileName(filename, CuiFileService.UNCLASSIFIED_PREFIX); - write(unclassifiedName); - - return unclassifiedName; - } - - return onClassified(cover); + return onClassified(decision.cover); } private writeClassifiedArchive(filename: string, data: string, cover: CuiPdfCoverResponse): string { @@ -113,10 +105,6 @@ export class CuiFileService { return Buffer.from(coverPage.pdfContent, CuiFileService.BASE64_ENCODING); } - private isClassified(cover: CuiPdfCoverResponse): boolean { - return (cover.resolvedCuiMarking?.categories?.length ?? 0) > 0; - } - private buildClassifiedArchiveName(filename: string): string { const baseName = path.basename(filename); const nameWithoutExtension = baseName.slice(0, baseName.length - path.extname(baseName).length); diff --git a/tests/commands/cui-marking-directory-exports.spec.ts b/tests/commands/cui-marking-directory-exports.spec.ts index abedef92..6f0822d4 100644 --- a/tests/commands/cui-marking-directory-exports.spec.ts +++ b/tests/commands/cui-marking-directory-exports.spec.ts @@ -23,7 +23,6 @@ const BRANCH = "feature-a"; function markAsClassified(): void { mockAxiosGetWithStatus(COVER_URL, 200, { - resolvedCuiMarking: { categories: [{ code: "PRVCY", name: "Privacy" }] }, coverPage: { pdfContent: PDF_BYTES.toString("base64"), encoding: "base64" }, }); } diff --git a/tests/commands/cui-marking-json-commands.spec.ts b/tests/commands/cui-marking-json-commands.spec.ts index b4f28d6e..cd186f25 100644 --- a/tests/commands/cui-marking-json-commands.spec.ts +++ b/tests/commands/cui-marking-json-commands.spec.ts @@ -1,11 +1,12 @@ import { resolve } from "node:path"; import { readFileSync } from "node:fs"; import AdmZip = require("adm-zip"); -import { mockAxiosGet, mockAxiosGetWithStatus, mockAxiosPost } from "../utls/http-requests-mock"; +import { mockAxiosGet, mockAxiosGetError, mockAxiosGetWithStatus, mockAxiosPost } from "../utls/http-requests-mock"; import { testContext } from "../utls/test-context"; import { loggingTestTransport } from "../jest.setup"; import { FileService } from "../../src/core/utils/file-service"; import { CuiFileService } from "../../src/core/utils/cui-file-service"; +import { FatalError } from "../../src/core/utils/logger"; import { ConfigUtils } from "../utls/config-utils"; import { zipToTempFolder } from "../utls/fs-utils"; import { DeploymentService } from "../../src/commands/deployment/deployment.service"; @@ -24,7 +25,6 @@ const PDF_BYTES = Buffer.from("%PDF-1.4 cover sheet"); function markAsClassified(): void { mockAxiosGetWithStatus(COVER_URL, 200, { - resolvedCuiMarking: { categories: [{ code: "PRVCY", name: "Privacy" }] }, coverPage: { pdfContent: PDF_BYTES.toString("base64"), encoding: "base64" }, }); } @@ -64,6 +64,14 @@ describe("CUI marking of --json commands", () => { expect(markedPayload()).toEqual(targets); }); + it("Should fail the command without writing anything when the cover call fails", async () => { + mockAxiosGetError(COVER_URL, 500, { message: "boom" }); + mockAxiosGet("https://myTeam.celonis.cloud/pacman/api/deployments/targets?deployableType=app-package&packageKey=package-key", []); + + await expect(new DeploymentService(testContext).getTargets(true, "app-package", "package-key")).rejects.toThrow(FatalError); + expect(loggingTestTransport.logMessages.some(entry => entry.message.includes(FileService.fileDownloadedMessage))).toBe(false); + }); + it("Should mark configuration node listings", async () => { const nodes = [{ id: "node-id-1", key: "node-key-1", name: "Node 1" }]; mockAxiosGet("https://myTeam.celonis.cloud/pacman/api/core/packages/package-key/nodes?version=1.0.0&withConfiguration=false&limit=10", nodes); diff --git a/tests/commands/cui-marking-output-to-json-file.spec.ts b/tests/commands/cui-marking-output-to-json-file.spec.ts index 0940a6fc..5a4ff707 100644 --- a/tests/commands/cui-marking-output-to-json-file.spec.ts +++ b/tests/commands/cui-marking-output-to-json-file.spec.ts @@ -21,7 +21,6 @@ const POOL_ID = "pool-1"; function markAsClassified(): void { mockAxiosGetWithStatus(COVER_URL, 200, { - resolvedCuiMarking: { categories: [{ code: "PRVCY", name: "Privacy" }] }, coverPage: { pdfContent: PDF_BYTES.toString("base64"), encoding: "base64" }, }); } diff --git a/tests/commands/cui-marking-single-file-exports.spec.ts b/tests/commands/cui-marking-single-file-exports.spec.ts index 736138cf..e574f7dd 100644 --- a/tests/commands/cui-marking-single-file-exports.spec.ts +++ b/tests/commands/cui-marking-single-file-exports.spec.ts @@ -26,7 +26,6 @@ const PACKAGE_KEY = "my-package"; function markAsClassified(): void { mockAxiosGetWithStatus(COVER_URL, 200, { - resolvedCuiMarking: { categories: [{ code: "PRVCY", name: "Privacy" }] }, coverPage: { pdfContent: PDF_BYTES.toString("base64"), encoding: "base64" }, }); } diff --git a/tests/commands/cui-marking-zip-commands.spec.ts b/tests/commands/cui-marking-zip-commands.spec.ts index bf66ad3f..840c5682 100644 --- a/tests/commands/cui-marking-zip-commands.spec.ts +++ b/tests/commands/cui-marking-zip-commands.spec.ts @@ -27,7 +27,6 @@ const T2TC_DOWNLOAD_MESSAGE = "File downloaded successfully. New filename: "; function markAsClassified(): void { mockAxiosGetWithStatus(COVER_URL, 200, { - resolvedCuiMarking: { categories: [{ code: "PRVCY", name: "Privacy" }] }, coverPage: { pdfContent: PDF_BYTES.toString("base64"), encoding: "base64" }, }); } diff --git a/tests/commands/studio/list-cui-marking.spec.ts b/tests/commands/studio/list-cui-marking.spec.ts index f07dc53c..2da0c536 100644 --- a/tests/commands/studio/list-cui-marking.spec.ts +++ b/tests/commands/studio/list-cui-marking.spec.ts @@ -1,7 +1,7 @@ import { resolve } from "node:path"; import { readFileSync } from "node:fs"; import AdmZip = require("adm-zip"); -import { mockAxiosGet, mockAxiosGetWithStatus, mockedAxiosInstance } from "../../utls/http-requests-mock"; +import { mockAxiosGet, mockAxiosGetError, mockAxiosGetWithStatus, mockedAxiosInstance } from "../../utls/http-requests-mock"; import { SpaceCommandService } from "../../../src/commands/studio/command-service/space-command.service"; import { PackageCommandService } from "../../../src/commands/studio/command-service/package-command.service"; import { testContext } from "../../utls/test-context"; @@ -20,7 +20,6 @@ const PDF_BYTES = Buffer.from("%PDF-1.4 cover sheet"); function classifiedCover(): object { return { - resolvedCuiMarking: { categories: [{ code: "PRVCY", name: "Privacy" }] }, coverPage: { pdfContent: PDF_BYTES.toString("base64"), encoding: "base64" }, }; } @@ -68,13 +67,12 @@ describe("CUI marking of Studio listings", () => { expect(payloadFromArchive(filename)).toEqual(SPACES); }); - it("Should keep the original filename when no marking applies", async () => { - mockAxiosGetWithStatus(COVER_URL, 204, ""); + it("Should keep the original filename when the feature flag is disabled", async () => { + mockAxiosGetError(COVER_URL, 403, { errorCode: "feature-disabled" }); await listSpaces(); const filename = loggedFileName(); - expect(filename.startsWith(CuiFileService.UNCLASSIFIED_PREFIX)).toBe(false); expect(filename.startsWith(CuiFileService.CLASSIFIED_PREFIX)).toBe(false); expect(readWrittenJson(filename)).toEqual(SPACES); }); @@ -105,13 +103,12 @@ describe("CUI marking of Studio listings", () => { expect(payloadFromArchive(filename)).toEqual(LISTED_PACKAGES); }); - it("Should keep the original filename when no marking applies", async () => { - mockAxiosGetWithStatus(COVER_URL, 204, ""); + it("Should keep the original filename when the feature flag is disabled", async () => { + mockAxiosGetError(COVER_URL, 403, { errorCode: "feature-disabled" }); await listPackages(); const filename = loggedFileName(); - expect(filename.startsWith(CuiFileService.UNCLASSIFIED_PREFIX)).toBe(false); expect(filename.startsWith(CuiFileService.CLASSIFIED_PREFIX)).toBe(false); expect(readWrittenJson(filename)).toEqual(LISTED_PACKAGES); }); diff --git a/tests/core/utils/cui-file-service.spec.ts b/tests/core/utils/cui-file-service.spec.ts index f05250c4..826b9925 100644 --- a/tests/core/utils/cui-file-service.spec.ts +++ b/tests/core/utils/cui-file-service.spec.ts @@ -13,8 +13,7 @@ describe("CuiFileService", () => { let cuiFileService: CuiFileService; - const coverResponse = (categories: Array<{ code: string; name: string }>) => ({ - resolvedCuiMarking: { categories }, + const coverResponse = () => ({ coverPage: { pdfContent: PDF_BYTES.toString("base64"), encoding: "base64", @@ -36,16 +35,9 @@ describe("CuiFileService", () => { expect(filename).toEqual("report.json"); expect(readFile("report.json").toString()).toEqual(PAYLOAD); }); + }); - it("Should keep the original filename when the team has CUI disabled", async () => { - mockAxiosGetWithStatus(COVER_URL, 204, ""); - - const filename = await cuiFileService.writeToFileWithGivenName(PAYLOAD, "no-marking.json"); - - expect(filename).toEqual("no-marking.json"); - expect(readFile("no-marking.json").toString()).toEqual(PAYLOAD); - }); - + describe("when the cover response cannot be used", () => { it("Should fail on an unexpected backend error", async () => { mockAxiosGetError(COVER_URL, 500, { message: "boom" }); @@ -59,23 +51,18 @@ describe("CuiFileService", () => { await expect(cuiFileService.writeToFileWithGivenName(PAYLOAD, "empty-cover.json")).rejects.toThrow(FatalError); expect(() => accessSync(resolve(process.cwd(), "empty-cover.json"))).toThrow(); }); - }); - describe("when the content is unclassified", () => { - it("Should only prefix the filename and write no cover sheet", async () => { - mockAxiosGetWithStatus(COVER_URL, 200, coverResponse([])); - - const filename = await cuiFileService.writeToFileWithGivenName(PAYLOAD, "packages.json"); + it("Should fail when the backend answers with no content", async () => { + mockAxiosGetWithStatus(COVER_URL, 204, ""); - expect(filename).toEqual("Unclassified - packages.json"); - expect(readFile(filename).toString()).toEqual(PAYLOAD); - expect(() => accessSync(resolve(process.cwd(), CuiFileService.COVER_SHEET_FILE_NAME))).toThrow(); + await expect(cuiFileService.writeToFileWithGivenName(PAYLOAD, "no-content.json")).rejects.toThrow(FatalError); + expect(() => accessSync(resolve(process.cwd(), "no-content.json"))).toThrow(); }); }); describe("when the content is classified", () => { it("Should wrap the payload and the decoded cover sheet into a CUI archive", async () => { - mockAxiosGetWithStatus(COVER_URL, 200, coverResponse([{ code: "PRVCY", name: "Privacy" }])); + mockAxiosGetWithStatus(COVER_URL, 200, coverResponse()); const filename = await cuiFileService.writeToFileWithGivenName(PAYLOAD, "packages.json"); @@ -89,7 +76,7 @@ describe("CuiFileService", () => { }); it("Should fail when the cover page uses an unsupported encoding", async () => { - const response = coverResponse([{ code: "PRVCY", name: "Privacy" }]); + const response = coverResponse(); response.coverPage.encoding = "hex"; mockAxiosGetWithStatus(COVER_URL, 200, response); @@ -98,9 +85,7 @@ describe("CuiFileService", () => { }); it("Should fail when the marking applies but no cover page was returned", async () => { - mockAxiosGetWithStatus(COVER_URL, 200, { - resolvedCuiMarking: { categories: [{ code: "PRVCY", name: "Privacy" }] }, - }); + 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."); @@ -119,7 +104,7 @@ describe("CuiFileService", () => { new AdmZip(readFile(filename)).getEntries().map(entry => entry.entryName).sort(); it("Should keep the archive untouched when no marking applies", async () => { - mockAxiosGetWithStatus(COVER_URL, 204, ""); + mockAxiosGetError(COVER_URL, 403, { errorCode: "feature-disabled" }); const exportZip = buildExportZip(); const filename = await cuiFileService.writeZipToFileWithGivenName(exportZip, "export.zip"); @@ -128,18 +113,8 @@ describe("CuiFileService", () => { expect(readFile(filename).equals(exportZip)).toBe(true); }); - it("Should only prefix the archive when the content is unclassified", async () => { - mockAxiosGetWithStatus(COVER_URL, 200, coverResponse([])); - const exportZip = buildExportZip(); - - const filename = await cuiFileService.writeZipToFileWithGivenName(exportZip, "export.zip"); - - expect(filename).toEqual("Unclassified - export.zip"); - expect(readFile(filename).equals(exportZip)).toBe(true); - }); - it("Should add the cover sheet into the given archive instead of nesting it", async () => { - mockAxiosGetWithStatus(COVER_URL, 200, coverResponse([{ code: "PRVCY", name: "Privacy" }])); + mockAxiosGetWithStatus(COVER_URL, 200, coverResponse()); const filename = await cuiFileService.writeZipToFileWithGivenName(buildExportZip(), "export.zip"); @@ -156,9 +131,7 @@ describe("CuiFileService", () => { }); it("Should fail when the marking applies but no cover page was returned", async () => { - mockAxiosGetWithStatus(COVER_URL, 200, { - resolvedCuiMarking: { categories: [{ code: "PRVCY", name: "Privacy" }] }, - }); + 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."); @@ -175,7 +148,7 @@ describe("CuiFileService", () => { const exists = (...segments: string[]): boolean => existsSync(resolve(process.cwd(), ...segments)); it("Should keep the original directory name when no marking applies", async () => { - mockAxiosGetWithStatus(COVER_URL, 204, ""); + mockAxiosGetError(COVER_URL, 403, { errorCode: "feature-disabled" }); const directoryName = await cuiFileService.writeDirectoryWithGivenName(writeTree, "unmarked-export"); @@ -184,19 +157,8 @@ describe("CuiFileService", () => { expect(exists(directoryName, CuiFileService.COVER_SHEET_FILE_NAME)).toBe(false); }); - it("Should only prefix the directory when the content is unclassified", async () => { - mockAxiosGetWithStatus(COVER_URL, 200, coverResponse([])); - - const directoryName = await cuiFileService.writeDirectoryWithGivenName(writeTree, "plain-export"); - - expect(directoryName).toEqual("Unclassified - plain-export"); - expect(exists(directoryName, "nodes", "node-1.json")).toBe(true); - expect(exists(directoryName, CuiFileService.COVER_SHEET_FILE_NAME)).toBe(false); - expect(exists("plain-export")).toBe(false); - }); - it("Should prefix the directory and write the cover sheet into it", async () => { - mockAxiosGetWithStatus(COVER_URL, 200, coverResponse([{ code: "PRVCY", name: "Privacy" }])); + mockAxiosGetWithStatus(COVER_URL, 200, coverResponse()); const directoryName = await cuiFileService.writeDirectoryWithGivenName(writeTree, "classified-export"); @@ -209,9 +171,7 @@ describe("CuiFileService", () => { }); it("Should fail without writing anything when no cover page was returned", async () => { - mockAxiosGetWithStatus(COVER_URL, 200, { - resolvedCuiMarking: { categories: [{ code: "PRVCY", name: "Privacy" }] }, - }); + 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."); diff --git a/tests/utls/http-requests-mock.ts b/tests/utls/http-requests-mock.ts index 3878734e..50734e5b 100644 --- a/tests/utls/http-requests-mock.ts +++ b/tests/utls/http-requests-mock.ts @@ -45,9 +45,9 @@ const mockAxios = () : void => { } } // CUI marking is probed on every user-facing write. Unless a test opts in, - // answer 204 so the CLI keeps the original filename. + // answer 403 so the CLI keeps the original filename. if (requestUrl.endsWith(CUI_PDF_COVER_PATH)) { - return Promise.resolve({ status: 204, data: "" }); + return Promise.resolve({ status: 403, data: "" }); } fail("API call not mocked.") });