UNPKG

dotenv-mono

Version:

This package permit to have a centralized dotenv on a monorepo. It also includes some extra features such as manipulation and saving of changes to the dotenv file, a default centralized file, and a file loader with ordering and priorities.

348 lines 18.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const node_cli_1 = require("./node-cli"); const index_1 = require("./index"); const mock_fs_1 = __importDefault(require("mock-fs")); describe("Run CLI", () => { const originalEnv = process.env; beforeEach(() => { process.env = Object.assign(Object.assign({}, originalEnv), { NODE_ENV: "test" }); process.argv = ["node", "script.js"]; // Reset argv to default (0, mock_fs_1.default)({ "/root": { ".env": "TEST_ROOT_ENV=1", "apps": {}, // Create the apps directory structure }, }); jest.spyOn(process, "cwd").mockReturnValue("/root/apps"); }); afterEach(() => { // Restore original process.env and argv process.env = originalEnv; process.argv = ["node", "script.js"]; jest.resetModules(); mock_fs_1.default.restore(); jest.restoreAllMocks(); }); it("should expose a function", () => { expect(node_cli_1.runNodeCli).toBeDefined(); }); it("should not throw errors", () => { expect(() => (0, node_cli_1.runNodeCli)(index_1.load)).not.toThrow(); expect(() => (0, node_cli_1.runNodeCli)(index_1.config)).not.toThrow(); }); it("should return the expected output", () => { const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv instanceof index_1.Dotenv).toBeTruthy(); expect(dotenv.env).not.toBeEmptyObject(); const output = (0, node_cli_1.runNodeCli)(index_1.config); expect(output).toHaveProperty("parsed"); expect(output.parsed).not.toBeEmptyObject(); }); it("should return the expected output using environmental options", () => { process.env.DOTENV_CONFIG_DEBUG = "true"; const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv instanceof index_1.Dotenv).toBeTruthy(); expect(dotenv.env).not.toBeEmptyObject(); }); it("should return the expected output using argv options", () => { process.argv = ["node", "script.js", "dotenv_config_debug=true"]; const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv instanceof index_1.Dotenv).toBeTruthy(); expect(dotenv.env).not.toBeEmptyObject(); }); it("should handle multiple environmental options", () => { process.env.DOTENV_CONFIG_DEBUG = "true"; process.env.DOTENV_CONFIG_OVERRIDE = "true"; process.env.DOTENV_CONFIG_EXPAND = "false"; process.env.DOTENV_CONFIG_DEPTH = "2"; const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv instanceof index_1.Dotenv).toBeTruthy(); expect(dotenv.debug).toBe(true); expect(dotenv.override).toBe(true); expect(dotenv.expand).toBe(false); expect(dotenv.depth).toBe(2); }); it("should handle multiple argv options", () => { process.argv = [ "node", "script.js", "dotenv_config_debug=true", "dotenv_config_override=false", "dotenv_config_depth=3", ]; const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv instanceof index_1.Dotenv).toBeTruthy(); expect(dotenv.debug).toBe(true); expect(dotenv.override).toBe(false); expect(dotenv.depth).toBe(3); }); it("should prioritize argv options over environmental options", () => { // Set env vars process.env.DOTENV_CONFIG_DEBUG = "false"; process.env.DOTENV_CONFIG_DEPTH = "5"; // Set argv that should override env vars process.argv = ["node", "script.js", "dotenv_config_debug=true", "dotenv_config_depth=2"]; const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv.debug).toBe(true); // argv should win expect(dotenv.depth).toBe(2); // argv should win }); it("should handle invalid argv format gracefully", () => { process.argv = [ "node", "script.js", "invalid_option=value", "dotenv_config_=empty_option", "dotenv_config_debug=true", ]; expect(() => (0, node_cli_1.runNodeCli)(index_1.load)).not.toThrow(); const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv.debug).toBe(true); // Valid option should still work }); }); describe("Parse Option", () => { it("should expose a function", () => { expect(node_cli_1.parseOption).toBeDefined(); }); it("should return expected output", () => { expect(node_cli_1.parseOption).toBeDefined(); expect((0, node_cli_1.parseOption)(undefined, node_cli_1.OptionType.number)).toBeUndefined(); expect((0, node_cli_1.parseOption)("1", node_cli_1.OptionType.number)).toEqual(1); expect((0, node_cli_1.parseOption)("true", node_cli_1.OptionType.boolean)).toBeTrue(); expect((0, node_cli_1.parseOption)("false", node_cli_1.OptionType.boolean)).toBeFalse(); expect((0, node_cli_1.parseOption)("message", node_cli_1.OptionType.string)).toEqual("message"); expect((0, node_cli_1.parseOption)('{"value": 1}', node_cli_1.OptionType.object)).toEqual({ "value": 1 }); expect((0, node_cli_1.parseOption)("[1, 2]", node_cli_1.OptionType.array)).toEqual([1, 2]); expect((0, node_cli_1.parseOption)('{"value": 1, "empty": null}', node_cli_1.OptionType.mapOfNumbers)).toEqual({ "value": 1, "empty": null, }); // Wrong JSON data jest.spyOn(console, "error").mockImplementationOnce(() => { }); expect((0, node_cli_1.parseOption)("1", node_cli_1.OptionType.object)).toBeUndefined(); jest.spyOn(console, "error").mockImplementationOnce(() => { }); expect((0, node_cli_1.parseOption)('{"value": "string"}', node_cli_1.OptionType.mapOfNumbers)).toBeUndefined(); // Malformed JSON parsing jest.spyOn(console, "error").mockImplementationOnce(() => { }); expect((0, node_cli_1.parseOption)('{"malformed": 1]]', node_cli_1.OptionType.object)).toBeUndefined(); }); it("should handle null input for parseOption", () => { expect((0, node_cli_1.parseOption)(undefined, node_cli_1.OptionType.string)).toBeUndefined(); }); it("should handle number parsing edge cases", () => { expect((0, node_cli_1.parseOption)("0", node_cli_1.OptionType.number)).toBe(0); expect((0, node_cli_1.parseOption)("-123", node_cli_1.OptionType.number)).toBe(-123); expect((0, node_cli_1.parseOption)("123.456", node_cli_1.OptionType.number)).toBe(123.456); expect((0, node_cli_1.parseOption)("Infinity", node_cli_1.OptionType.number)).toBe(Infinity); expect((0, node_cli_1.parseOption)("NaN", node_cli_1.OptionType.number)).toBeNaN(); expect((0, node_cli_1.parseOption)("abc", node_cli_1.OptionType.number)).toBeNaN(); }); it("should handle boolean parsing edge cases", () => { expect((0, node_cli_1.parseOption)("TRUE", node_cli_1.OptionType.boolean)).toBe(false); // Only "true" should be true expect((0, node_cli_1.parseOption)("false", node_cli_1.OptionType.boolean)).toBe(false); expect((0, node_cli_1.parseOption)("1", node_cli_1.OptionType.boolean)).toBe(false); expect((0, node_cli_1.parseOption)("0", node_cli_1.OptionType.boolean)).toBe(false); expect((0, node_cli_1.parseOption)("", node_cli_1.OptionType.boolean)).toBe(false); }); it("should handle array parsing", () => { expect((0, node_cli_1.parseOption)("[]", node_cli_1.OptionType.array)).toEqual([]); expect((0, node_cli_1.parseOption)('["item1", "item2"]', node_cli_1.OptionType.array)).toEqual(["item1", "item2"]); expect((0, node_cli_1.parseOption)("[1, 2, 3]", node_cli_1.OptionType.array)).toEqual([1, 2, 3]); // Invalid array JSON jest.spyOn(console, "error").mockImplementationOnce(() => { }); expect((0, node_cli_1.parseOption)("[invalid]", node_cli_1.OptionType.array)).toBeUndefined(); }); it("should handle mapOfNumbers validation", () => { expect((0, node_cli_1.parseOption)("{}", node_cli_1.OptionType.mapOfNumbers)).toEqual({}); expect((0, node_cli_1.parseOption)('{"a": 1, "b": 2}', node_cli_1.OptionType.mapOfNumbers)).toEqual({ "a": 1, "b": 2 }); expect((0, node_cli_1.parseOption)('{"a": null, "b": undefined}', node_cli_1.OptionType.mapOfNumbers)).toEqual({ "a": null, }); // Should reject non-number values jest.spyOn(console, "error").mockImplementationOnce(() => { }); expect((0, node_cli_1.parseOption)('{"a": "string"}', node_cli_1.OptionType.mapOfNumbers)).toBeUndefined(); jest.spyOn(console, "error").mockImplementationOnce(() => { }); expect((0, node_cli_1.parseOption)('{"a": true}', node_cli_1.OptionType.mapOfNumbers)).toBeUndefined(); }); it("should handle string parsing with special characters", () => { expect((0, node_cli_1.parseOption)("hello world", node_cli_1.OptionType.string)).toBe("hello world"); expect((0, node_cli_1.parseOption)("special!@#$%^&*()", node_cli_1.OptionType.string)).toBe("special!@#$%^&*()"); expect((0, node_cli_1.parseOption)("unicode: 你好", node_cli_1.OptionType.string)).toBe("unicode: 你好"); expect((0, node_cli_1.parseOption)("", node_cli_1.OptionType.string)).toBe(""); }); it("should handle null value as parameter", () => { // Test null handling which is covered in the condition: option === null expect((0, node_cli_1.parseOption)(null, node_cli_1.OptionType.string)).toBeUndefined(); expect((0, node_cli_1.parseOption)(null, node_cli_1.OptionType.number)).toBeUndefined(); expect((0, node_cli_1.parseOption)(null, node_cli_1.OptionType.boolean)).toBeUndefined(); expect((0, node_cli_1.parseOption)(null, node_cli_1.OptionType.array)).toBeUndefined(); expect((0, node_cli_1.parseOption)(null, node_cli_1.OptionType.object)).toBeUndefined(); expect((0, node_cli_1.parseOption)(null, node_cli_1.OptionType.mapOfNumbers)).toBeUndefined(); }); it("should reject non-array values for array type", () => { // Test ParseError for non-array values jest.spyOn(console, "error").mockImplementationOnce(() => { }); expect((0, node_cli_1.parseOption)('{"key": "value"}', node_cli_1.OptionType.array)).toBeUndefined(); jest.spyOn(console, "error").mockImplementationOnce(() => { }); expect((0, node_cli_1.parseOption)('"string"', node_cli_1.OptionType.array)).toBeUndefined(); jest.spyOn(console, "error").mockImplementationOnce(() => { }); expect((0, node_cli_1.parseOption)("123", node_cli_1.OptionType.array)).toBeUndefined(); }); it("should reject array values for object type", () => { // Test ParseError for array values when expecting object jest.spyOn(console, "error").mockImplementationOnce(() => { }); expect((0, node_cli_1.parseOption)("[1, 2, 3]", node_cli_1.OptionType.object)).toBeUndefined(); jest.spyOn(console, "error").mockImplementationOnce(() => { }); expect((0, node_cli_1.parseOption)('["item1", "item2"]', node_cli_1.OptionType.object)).toBeUndefined(); }); it("should handle mapOfNumbers preprocessing with undefined values", () => { // Test the undefined value preprocessing in mapOfNumbers expect((0, node_cli_1.parseOption)('{"a": 1, "b": undefined, "c": 2}', node_cli_1.OptionType.mapOfNumbers)).toEqual({ "a": 1, "c": 2, }); expect((0, node_cli_1.parseOption)('{"first": undefined, "second": 42}', node_cli_1.OptionType.mapOfNumbers)).toEqual({ "second": 42, }); expect((0, node_cli_1.parseOption)('{"onlyUndefined": undefined}', node_cli_1.OptionType.mapOfNumbers)).toEqual({}); }); // Additional CLI edge cases it("should handle CLI options with special characters and formatting", () => { // Mock console.error to suppress expected debug output const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => { }); process.argv = [ "node", "script.js", "dotenv_config_path=/path/with spaces/.env", "dotenv_config_debug=true", 'dotenv_config_priorities={"custom.env": 100}', ]; expect(() => (0, node_cli_1.runNodeCli)(index_1.load)).not.toThrow(); const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv.debug).toBe(true); consoleErrorSpy.mockRestore(); }); it("should handle mixed case and malformed CLI arguments", () => { process.argv = [ "node", "script.js", "DOTENV_CONFIG_DEBUG=true", // Wrong case "dotenv_config_=", // Empty option name "=value", // No option name "dotenv_config_depth=abc", // Invalid number "dotenv_config_override=yes", // Invalid boolean ]; expect(() => (0, node_cli_1.runNodeCli)(index_1.load)).not.toThrow(); const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); // Should only parse valid options expect(dotenv.depth).toBeNaN(); // Invalid number becomes NaN expect(dotenv.override).toBe(false); // Invalid boolean becomes false }); it("should handle environment variables with empty values", () => { process.env.DOTENV_CONFIG_PATH = ""; process.env.DOTENV_CONFIG_DEFAULTS = ""; process.env.DOTENV_CONFIG_EXTENSION = ""; expect(() => (0, node_cli_1.runNodeCli)(index_1.load)).not.toThrow(); const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv.path).toBe(""); expect(dotenv.defaults).toBe(""); expect(dotenv.extension).toBe(""); }); it("should handle CLI priority when both env and argv are set", () => { // Set environment variables process.env.DOTENV_CONFIG_DEBUG = "false"; process.env.DOTENV_CONFIG_DEPTH = "10"; process.env.DOTENV_CONFIG_OVERRIDE = "true"; // Set argv that should override env vars process.argv = [ "node", "script.js", "dotenv_config_debug=true", "dotenv_config_depth=5", // Note: not setting override, so env var should be used ]; const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv.debug).toBe(true); // argv wins expect(dotenv.depth).toBe(5); // argv wins expect(dotenv.override).toBe(true); // env var used since argv not set }); it("should handle complex JSON in CLI arguments", () => { // Mock console.error to suppress expected debug output const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => { }); process.argv = [ "node", "script.js", 'dotenv_config_priorities={"custom.env": 100, "other.env": 50}', ]; expect(() => (0, node_cli_1.runNodeCli)(index_1.load)).not.toThrow(); const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv.priorities["custom.env"]).toBe(100); expect(dotenv.priorities["other.env"]).toBe(50); consoleErrorSpy.mockRestore(); }); it("should handle malformed JSON in CLI arguments gracefully", () => { jest.spyOn(console, "error").mockImplementationOnce(() => { }); process.argv = ["node", "script.js", 'dotenv_config_priorities={"malformed": json}']; expect(() => (0, node_cli_1.runNodeCli)(index_1.load)).not.toThrow(); const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); // Should use default priorities when JSON parsing fails expect(dotenv.priorities[".env"]).toBe(1); }); it("should handle regex special characters in CLI arguments", () => { // Mock console.error to suppress expected debug output const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => { }); process.argv = [ "node", "script.js", "dotenv_config_path=/path/with[brackets]/.env", "dotenv_config_extension=server.test", ]; expect(() => (0, node_cli_1.runNodeCli)(index_1.load)).not.toThrow(); const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv.path).toBe("/path/with[brackets]/.env"); expect(dotenv.extension).toBe("server.test"); consoleErrorSpy.mockRestore(); }); it("should handle extremely long CLI arguments", () => { // Mock console.error to suppress expected debug output const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => { }); const longPath = "/very/long/path/" + "directory/".repeat(100) + ".env"; process.argv = ["node", "script.js", `dotenv_config_path=${longPath}`]; expect(() => (0, node_cli_1.runNodeCli)(index_1.load)).not.toThrow(); const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv.path).toBe(longPath); consoleErrorSpy.mockRestore(); }); it("should handle CLI arguments with unicode characters", () => { // Mock console.error to suppress expected debug output const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => { }); process.argv = [ "node", "script.js", "dotenv_config_path=/路径/файл/.env", "dotenv_config_extension=тест", ]; expect(() => (0, node_cli_1.runNodeCli)(index_1.load)).not.toThrow(); const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv.path).toBe("/路径/файл/.env"); expect(dotenv.extension).toBe("тест"); consoleErrorSpy.mockRestore(); }); it("should handle duplicate CLI arguments (last one wins)", () => { process.argv = [ "node", "script.js", "dotenv_config_debug=false", "dotenv_config_depth=1", "dotenv_config_debug=true", // This should win "dotenv_config_depth=3", // This should win ]; const dotenv = (0, node_cli_1.runNodeCli)(index_1.load); expect(dotenv.debug).toBe(true); expect(dotenv.depth).toBe(3); }); }); //# sourceMappingURL=node-cli.test.js.map