import { jest, describe, it, expect, beforeEach } from "@jest/globals"; // Mock @actions/core before importing the module under test const getInputMock = jest.fn<(name: string, options?: { required?: boolean }) => string>(); const getMultilineInputMock = jest.fn<(name: string, options?: { required?: boolean }) => string[]>(); jest.unstable_mockModule("@actions/core", () => ({ getInput: getInputMock, getMultilineInput: getMultilineInputMock, debug: jest.fn(), info: jest.fn(), warning: jest.fn(), })); // Mock node:fs const existsSyncMock = jest.fn<(path: string) => boolean>(); jest.unstable_mockModule("node:fs", () => ({ existsSync: existsSyncMock, default: { existsSync: existsSyncMock }, })); // Dynamic imports after mock setup const { InputService, InputNames } = await import("./input.service.js"); const { LogLevel } = await import("./logger.service.js"); describe("InputService", () => { let service: InstanceType; beforeEach(() => { jest.clearAllMocks(); getMultilineInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.ComposeFile: return ["file1"]; default: return []; } }); getInputMock.mockReturnValue(""); service = new InputService(); }); describe("getInputs", () => { describe("docker-flags", () => { it("should return given docker-flags input", () => { getInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.DockerFlags: return "docker-flag1 docker-flag2"; default: return ""; } }); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.dockerFlags).toEqual(["docker-flag1", "docker-flag2"]); }); it("should return empty array when no docker-flags input", () => { getInputMock.mockReturnValue(""); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.dockerFlags).toEqual([]); }); }); describe("composeFiles", () => { it("should return given composeFiles input", () => { getMultilineInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.ComposeFile: return ["file1", "file2"]; default: return []; } }); getInputMock.mockReturnValue(""); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.composeFiles).toEqual(["file1", "file2"]); }); it("should ignore empty compose file entries", () => { getMultilineInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.ComposeFile: return [" ", "file1"]; default: return []; } }); getInputMock.mockReturnValue(""); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.composeFiles).toEqual(["file1"]); }); it("should accept compose file when it exists at the original path", () => { getMultilineInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.ComposeFile: return ["./compose.yml"]; default: return []; } }); getInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.Cwd: return "/current/working/directory"; default: return ""; } }); existsSyncMock.mockImplementation((file) => file === "./compose.yml"); const inputs = service.getInputs(); expect(inputs.composeFiles).toEqual(["./compose.yml"]); }); it("should throws an error when a compose file does not exist", () => { getMultilineInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.ComposeFile: return ["file1", "file2"]; default: return []; } }); getInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.Cwd: return "/current/working/directory"; default: return ""; } }); existsSyncMock.mockImplementation((file) => file === "/current/working/directory/file1"); expect(() => service.getInputs()).toThrow( 'Compose file not found in "/current/working/directory/file2", "file2"' ); }); it("should throws an error when no composeFiles input", () => { getMultilineInputMock.mockReturnValue([]); getInputMock.mockReturnValue(""); expect(() => service.getInputs()).toThrow("No compose files found"); }); }); describe("services", () => { it("should return given services input", () => { getMultilineInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.Services: return ["service1", "service2"]; case InputNames.ComposeFile: return ["file1"]; default: return []; } }); getInputMock.mockReturnValue(""); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.services).toEqual(["service1", "service2"]); }); }); describe("compose-flags", () => { it("should return given compose-flags input", () => { getInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.ComposeFlags: return "compose-flag1 compose-flag2"; default: return ""; } }); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.composeFlags).toEqual(["compose-flag1", "compose-flag2"]); }); it("should return empty array when no compose-flags input", () => { getInputMock.mockReturnValue(""); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.composeFlags).toEqual([]); }); }); describe("up-flags", () => { it("should return given up-flags input", () => { getInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.UpFlags: return "up-flag1 up-flag2"; default: return ""; } }); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.upFlags).toEqual(["up-flag1", "up-flag2"]); }); it("should return empty array when no up-flags input", () => { getInputMock.mockReturnValue(""); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.upFlags).toEqual([]); }); }); describe("down-flags", () => { it("should return given down-flags input", () => { getInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.DownFlags: return "down-flag1 down-flag2"; default: return ""; } }); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.downFlags).toEqual(["down-flag1", "down-flag2"]); }); it("should return empty array when no down-flags input", () => { getInputMock.mockReturnValue(""); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.downFlags).toEqual([]); }); }); describe("cwd", () => { it("should return given cwd input", () => { getInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.Cwd: return "cwd"; default: return ""; } }); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.cwd).toEqual("cwd"); }); }); describe("compose-version", () => { it("should return given compose-version input", () => { getInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.ComposeVersion: return "compose-version"; default: return ""; } }); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.composeVersion).toEqual("compose-version"); }); }); describe("services-log-level", () => { it("should return given services-log-level input", () => { getInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.ServiceLogLevel: return "info"; default: return ""; } }); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.serviceLogLevel).toEqual(LogLevel.Info); }); it("should return default services-log-level input", () => { getInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.ServiceLogLevel: return ""; default: return ""; } }); existsSyncMock.mockReturnValue(true); const inputs = service.getInputs(); expect(inputs.serviceLogLevel).toEqual(LogLevel.Debug); }); it("should throw an error when services-log-level input is invalid", () => { getInputMock.mockImplementation((inputName) => { switch (inputName) { case InputNames.ServiceLogLevel: return "invalid-log-level"; default: return ""; } }); existsSyncMock.mockReturnValue(true); expect(() => service.getInputs()).toThrow( 'Invalid service log level "invalid-log-level". Valid values are: debug, info' ); }); }); }); });