diff --git a/src/lib/processors/validate-processor.test.ts b/src/lib/processors/validate-processor.test.ts index f54cdfc6..b7550da3 100644 --- a/src/lib/processors/validate-processor.test.ts +++ b/src/lib/processors/validate-processor.test.ts @@ -7,7 +7,34 @@ import { AdmissionRequest, Binding, Filters } from "../types"; import { Event, Operation } from "../enums"; import { PeprValidateRequest } from "../validate-request"; import { clone } from "ramda"; -import * as sut from "./validate-processor"; +import { processRequest, validateProcessor } from "./validate-processor"; +import { ModuleConfig } from "../types"; +import { Capability } from "../core/capability"; +import { MeasureWebhookTimeout } from "../telemetry/webhookTimeouts"; +import * as utils from "../utils"; +import { shouldSkipRequest } from "../filter/filter"; + +jest.mock("../telemetry/logger", () => ({ + info: jest.fn(), + debug: jest.fn(), + error: jest.fn(), +})); + +jest.mock("../telemetry/metrics", () => ({ + metricsCollector: { + addCounter: jest.fn(), + incCounter: jest.fn(), + }, + MeasureWebhookTimeout: jest.fn(), +})); + +jest.mock("../telemetry/timeUtils", () => ({ + getNow: jest.fn(() => 1000), +})); + +jest.mock("../filter/filter", () => ({ + shouldSkipRequest: jest.fn(), +})); const testFilters: Filters = { annotations: {}, @@ -71,7 +98,7 @@ describe("processRequest", () => { const callback = jest.fn().mockImplementation(() => cbResult) as Binding["validateCallback"]; binding = { ...clone(testBinding), validateCallback: callback }; - const result = await sut.processRequest(binding, actionMetadata, peprValidateRequest); + const result = await processRequest(binding, actionMetadata, peprValidateRequest); expect(result).toEqual({ uid: peprValidateRequest.Request.uid, @@ -89,7 +116,7 @@ describe("processRequest", () => { }) as Binding["validateCallback"]; binding = { ...clone(testBinding), validateCallback: callback }; - const result = await sut.processRequest(binding, actionMetadata, peprValidateRequest); + const result = await processRequest(binding, actionMetadata, peprValidateRequest); expect(result).toEqual({ uid: peprValidateRequest.Request.uid, @@ -101,3 +128,134 @@ describe("processRequest", () => { }); }); }); + +describe("validateProcessor", () => { + let config: ModuleConfig; + beforeEach(() => { + jest.clearAllMocks(); + config = { + webhookTimeout: 11, + uuid: "some-uuid", + alwaysIgnore: {}, + }; + }); + it("should measure if a timeout occurred based on webhookTimeout", async () => { + const capability = new Capability({ + name: "test", + description: "test", + }); + + const req = testAdmissionRequest; + const reqMetadata = {}; + + const spyStart = jest.spyOn(MeasureWebhookTimeout.prototype, "start"); + + await validateProcessor(config, [capability], req, reqMetadata); + + expect(spyStart).toHaveBeenCalledWith(config.webhookTimeout); + spyStart.mockRestore(); + }); + + it("should call convertFromBase64Map if the kind is a Secret", async () => { + const capability = new Capability({ + name: "test", + description: "test", + }); + const testGroupVersionKind: GroupVersionKind = { + kind: "Secret", + version: "v1", + group: "", + }; + const req: AdmissionRequest = { ...testAdmissionRequest, kind: testGroupVersionKind }; + const reqMetadata = {}; + + const spyConvert = jest.spyOn(utils, "convertFromBase64Map"); + + await validateProcessor(config, [capability], req, reqMetadata); + + expect(spyConvert).toHaveBeenCalled(); + spyConvert.mockRestore(); + }); + + it("should stop the timer after processing", async () => { + const capability = new Capability({ + name: "test", + description: "test", + }); + + const req = testAdmissionRequest; + const reqMetadata = {}; + + const spyStop = jest.spyOn(MeasureWebhookTimeout.prototype, "stop"); + + await validateProcessor(config, [capability], req, reqMetadata); + + expect(spyStop).toHaveBeenCalled(); + spyStop.mockRestore(); + }); + + it("should skip bindings that do not have validateCallback", async () => { + config = { + webhookTimeout: 10, + uuid: "some-uuid", + alwaysIgnore: {}, + }; + + const capability = new Capability({ + name: "test", + description: "test", + bindings: [ + { + isValidate: true, + validateCallback: undefined, + }, + ], + } as unknown as Capability); + + const req = testAdmissionRequest; + const reqMetadata = {}; + + // This rule is skipped because we cannot mock this function globally as it is tested above + // eslint-disable-next-line @typescript-eslint/no-require-imports + const spyProcessRequest = jest.spyOn(require("./validate-processor"), "processRequest"); + + await validateProcessor(config, [capability], req, reqMetadata); + + expect(spyProcessRequest).not.toHaveBeenCalled(); + + spyProcessRequest.mockRestore(); + }); + + it("should skip bindings if shouldSkipRequest returns a reason", async () => { + config = { + webhookTimeout: 10, + uuid: "some-uuid", + alwaysIgnore: {}, + }; + + const capability = new Capability({ + name: "test", + description: "test", + bindings: [ + { + isValidate: true, + validateCallback: jest.fn(), + }, + ], + } as unknown as Capability); + + const req = testAdmissionRequest; + const reqMetadata = {}; + + // This rule is skipped because we cannot mock this function globally as it is tested above + // eslint-disable-next-line @typescript-eslint/no-require-imports + const spyProcessRequest = jest.spyOn(require("./validate-processor"), "processRequest"); + (shouldSkipRequest as jest.Mock).mockReturnValue("Skip reason"); + + await validateProcessor(config, [capability], req, reqMetadata); + + expect(spyProcessRequest).not.toHaveBeenCalled(); + + spyProcessRequest.mockRestore(); + }); +});