UNPKG

lokalise-file-exchange

Version:
256 lines (220 loc) 6.75 kB
import type { QueuedProcess } from "@lokalise/node-api"; import { LokaliseApi, LokaliseApiOAuth } from "@lokalise/node-api"; import { logWithColor, logWithLevel } from "kliedz"; import { LokaliseFileExchange } from "../../lib/services/LokaliseFileExchange.js"; import { FakeLokaliseFileExchange } from "../fixtures/fake_classes/FakeLokaliseExchange.js"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, type Interceptable, it, MockAgent, setGlobalDispatcher, vi, } from "../setup.js"; describe("LokaliseFileExchange", () => { describe("Instance Creation", () => { it("should create an instance with valid parameters", () => { const exchange = new FakeLokaliseFileExchange( { apiKey: "abc123", }, { projectId: "123.abc" }, ); expect(exchange).toBeInstanceOf(LokaliseFileExchange); expect(exchange.getApiClient()).toBeInstanceOf(LokaliseApi); expect(exchange.getLogger()).toEqual(logWithColor); expect(exchange.getLogThreshold()).toEqual("info"); }); it("should create an OAuth2-based FakeLokaliseFileExchange with valid parameters", () => { const exchange = new FakeLokaliseFileExchange( { apiKey: "abc123", }, { projectId: "123.abc", useOAuth2: true }, ); expect(exchange).toBeInstanceOf(LokaliseFileExchange); expect(exchange.getApiClient()).toBeInstanceOf(LokaliseApiOAuth); }); it("should create a logger without colors", () => { const exchange = new FakeLokaliseFileExchange( { apiKey: "abc123", }, { projectId: "123.abc", logColor: false }, ); expect(exchange.getLogger()).toEqual(logWithLevel); }); it("should support silent mode", () => { const exchange = new FakeLokaliseFileExchange( { apiKey: "abc123", }, { projectId: "123.abc", logThreshold: "silent" }, ); expect(exchange.getLogger()).toEqual(logWithColor); expect(exchange.getApiClient().clientData.silent).toBe(true); expect(exchange.getLogThreshold()).toEqual("silent"); }); }); describe("Error Handling", () => { describe("API Key Validation", () => { it("should throw an error if the API key is not provided", () => { expect(() => { new LokaliseFileExchange({ apiKey: "" }, { projectId: "123.abc" }); }).toThrow( "Instantiation failed: A non-empty API key or JWT must be provided.", ); }); it("should throw an error if the API key is not a string", () => { expect(() => { new LokaliseFileExchange( { apiKey: 12345 as unknown as string, }, { projectId: "123.abc" }, ); }).toThrow( "Instantiation failed: A non-empty API key or JWT must be provided.", ); }); }); describe("Project ID Validation", () => { it("should throw an error if the project ID is not provided", () => { expect(() => { new LokaliseFileExchange({ apiKey: "abc123" }, { projectId: "" }); }).toThrow("Invalid or missing Project ID."); }); it("should throw an error if the project ID is not a string", () => { expect(() => { new LokaliseFileExchange( { apiKey: "abc123", }, { projectId: 67890 as unknown as string }, ); }).toThrow("Invalid or missing Project ID."); }); }); describe("Retry Parameter Validation", () => { it("should throw an error when maxRetries is negative", () => { expect(() => { new LokaliseFileExchange( { apiKey: "abc123", }, { projectId: "123.abc", retryParams: { maxRetries: -1 } }, ); }).toThrow("maxRetries must be greater than or equal to zero."); }); it("should throw an error when initialSleepTime is non-positive", () => { expect(() => { new LokaliseFileExchange( { apiKey: "abc123", }, { projectId: "123.abc", retryParams: { initialSleepTime: -1 } }, ); }).toThrow("initialSleepTime must be a positive value."); }); }); describe("withExponentialBackoff", () => { it("should reach unreachable throw without using any", async () => { const exchanger = new FakeLokaliseFileExchange( { apiKey: "abc123", }, { projectId: "123.abc" }, ); Object.defineProperty(exchanger, "retryParams", { value: { maxRetries: -1, initialSleepTime: 0, }, }); await expect( exchanger.withExponentialBackoff(() => { throw new Error("this should never happen"); }), ).rejects.toThrow("Unexpected error during operation."); }); }); describe("getUpdatedProcess", () => { let mockAgent: MockAgent; let mockPool: Interceptable; beforeAll(() => { mockAgent = new MockAgent(); setGlobalDispatcher(mockAgent); mockAgent.disableNetConnect(); }); afterAll(() => { mockAgent.close(); }); afterEach(() => { vi.restoreAllMocks(); }); beforeEach(() => { mockPool = mockAgent.get("https://api.lokalise.com"); }); it("should handle case when status is missing", async () => { const projectId = "123.abc"; const processId = "123"; const exchanger = new FakeLokaliseFileExchange( { apiKey: "abc123", }, { projectId }, ); mockPool .intercept({ path: `/api2/projects/${projectId}/processes/${processId}`, method: "GET", }) .reply(200, { project_id: projectId, process: { process_id: processId, type: "file-import", }, }); const process = await exchanger.getUpdatedProcess(processId); expect(process.process_id).toEqual(processId); expect(process.status).toEqual(undefined); }); }); describe("pollProcesses", () => { it("should warn when process cannot be fetched", async () => { const exchanger = new FakeLokaliseFileExchange( { apiKey: "abc123" }, { projectId: "123.abc" }, ); const loggerSpy = vi.spyOn(exchanger, "logMsg").mockResolvedValue(); const processId = "123"; const fakeError = new Error("cannot get process"); const process = { process_id: processId } as QueuedProcess; const fakeDatetime = "2025-05-10T12:00:00.000Z"; vi.useFakeTimers().setSystemTime(new Date(fakeDatetime)); const getProcessSpy = vi .spyOn(exchanger, "getUpdatedProcess") .mockImplementation(async () => { vi.advanceTimersByTime(1001); throw fakeError; }); const pollPromise = exchanger.pollProcesses([process], 1, 1000); await vi.advanceTimersByTimeAsync(200); await pollPromise; expect(getProcessSpy).toHaveBeenCalledWith(processId); expect(loggerSpy).toHaveBeenCalledWith( "warn", `Failed to fetch process ${processId}:`, fakeError, ); vi.useRealTimers(); }); }); }); });