diff --git a/package.json b/package.json index 26f0bba..b1cd7e1 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,6 @@ "@tsconfig/bun": "^1.0.10", "@types/bun": "^1.3.11", "bunup": "^0.16.31", - "typescript": "^5.9.3" + "typescript": "^6.0.2" } } \ No newline at end of file diff --git a/tests/snapshots/__snapshots__/cli-output.test.ts.snap b/tests/snapshots/__snapshots__/cli-output.test.ts.snap index 58014fe..ea461ae 100644 --- a/tests/snapshots/__snapshots__/cli-output.test.ts.snap +++ b/tests/snapshots/__snapshots__/cli-output.test.ts.snap @@ -14,7 +14,7 @@ exports[`CLI output snapshots template content generated template matches snapsh # op run --env-file .env.tpl -- npm start # # Pushed: 2025-01-01 00:00:00 UTC -# Generated by env2op v1.1.5 +# Generated by env2op v0.2.5 # https://github.com/tolgamorf/env2op-cli # =========================================================================== @@ -40,7 +40,7 @@ exports[`CLI output snapshots template content template with no comments matches # op run --env-file secrets.tpl -- npm start # # Pushed: 2025-01-01 00:00:00 UTC -# Generated by env2op v1.1.5 +# Generated by env2op v0.2.5 # https://github.com/tolgamorf/env2op-cli # =========================================================================== @@ -49,15 +49,13 @@ KEY=op://v1/i1/f1 `; exports[`CLI output snapshots usage instructions usage instructions match snapshot 1`] = ` -" -Usage: +"Usage: op2env .env.tpl op run --env-file .env.tpl -- npm start" `; exports[`CLI output snapshots usage instructions usage instructions with path match snapshot 1`] = ` -" -Usage: +"Usage: op2env /path/to/config.tpl op run --env-file /path/to/config.tpl -- npm start" `; diff --git a/tests/unit/env-parser.test.ts b/tests/unit/env-parser.test.ts index 80efbe3..98f64ee 100644 --- a/tests/unit/env-parser.test.ts +++ b/tests/unit/env-parser.test.ts @@ -7,8 +7,8 @@ const fixturesDir = join(import.meta.dir, "../fixtures"); describe("parseEnvFile", () => { describe("basic parsing", () => { - test("parses simple KEY=value pairs", () => { - const result = parseEnvFile(join(fixturesDir, "valid.env")); + test("parses simple KEY=value pairs", async () => { + const result = await parseEnvFile(join(fixturesDir, "valid.env")); expect(result.variables.length).toBe(7); expect(result.variables[0]).toEqual({ @@ -19,191 +19,191 @@ describe("parseEnvFile", () => { }); }); - test("returns correct variable count", () => { - const result = parseEnvFile(join(fixturesDir, "valid.env")); + test("returns correct variable count", async () => { + const result = await parseEnvFile(join(fixturesDir, "valid.env")); expect(result.variables.length).toBe(7); }); - test("captures line numbers", () => { - const result = parseEnvFile(join(fixturesDir, "valid.env")); + test("captures line numbers", async () => { + const result = await parseEnvFile(join(fixturesDir, "valid.env")); const debugVar = result.variables.find((v) => v.key === "DEBUG"); expect(debugVar?.line).toBe(11); }); }); describe("quoted values", () => { - test("handles double-quoted values", () => { - const result = parseEnvFile(join(fixturesDir, "quoted-values.env")); + test("handles double-quoted values", async () => { + const result = await parseEnvFile(join(fixturesDir, "quoted-values.env")); const doubleQuoted = result.variables.find((v) => v.key === "DOUBLE_QUOTED"); expect(doubleQuoted?.value).toBe("hello world"); }); - test("handles single-quoted values", () => { - const result = parseEnvFile(join(fixturesDir, "quoted-values.env")); + test("handles single-quoted values", async () => { + const result = await parseEnvFile(join(fixturesDir, "quoted-values.env")); const singleQuoted = result.variables.find((v) => v.key === "SINGLE_QUOTED"); expect(singleQuoted?.value).toBe("hello world"); }); - test("preserves spaces in quoted values", () => { - const result = parseEnvFile(join(fixturesDir, "quoted-values.env")); + test("preserves spaces in quoted values", async () => { + const result = await parseEnvFile(join(fixturesDir, "quoted-values.env")); const withSpaces = result.variables.find((v) => v.key === "DOUBLE_WITH_SPACES"); expect(withSpaces?.value).toBe(" spaces around "); }); - test("preserves # in quoted values (not treated as comment)", () => { - const result = parseEnvFile(join(fixturesDir, "quoted-values.env")); + test("preserves # in quoted values (not treated as comment)", async () => { + const result = await parseEnvFile(join(fixturesDir, "quoted-values.env")); const withHash = result.variables.find((v) => v.key === "DOUBLE_WITH_HASH"); expect(withHash?.value).toBe("value # not a comment"); }); - test("handles empty quoted values", () => { - const result = parseEnvFile(join(fixturesDir, "quoted-values.env")); + test("handles empty quoted values", async () => { + const result = await parseEnvFile(join(fixturesDir, "quoted-values.env")); const emptyDouble = result.variables.find((v) => v.key === "DOUBLE_EMPTY"); const emptySingle = result.variables.find((v) => v.key === "SINGLE_EMPTY"); expect(emptyDouble?.value).toBe(""); expect(emptySingle?.value).toBe(""); }); - test("handles unquoted values", () => { - const result = parseEnvFile(join(fixturesDir, "quoted-values.env")); + test("handles unquoted values", async () => { + const result = await parseEnvFile(join(fixturesDir, "quoted-values.env")); const unquoted = result.variables.find((v) => v.key === "UNQUOTED"); expect(unquoted?.value).toBe("simple_value"); }); }); describe("comments", () => { - test("preserves standalone comments in lines array", () => { - const result = parseEnvFile(join(fixturesDir, "comments.env")); + test("preserves standalone comments in lines array", async () => { + const result = await parseEnvFile(join(fixturesDir, "comments.env")); const commentLines = result.lines.filter((l) => l.type === "comment"); expect(commentLines.length).toBeGreaterThan(0); }); - test("associates comments with following variables", () => { - const result = parseEnvFile(join(fixturesDir, "comments.env")); + test("associates comments with following variables", async () => { + const result = await parseEnvFile(join(fixturesDir, "comments.env")); const key1 = result.variables.find((v) => v.key === "KEY1"); // KEY1 follows empty line, so no associated comment expect(key1?.comment).toBeUndefined(); }); - test("strips inline comments from unquoted values", () => { - const result = parseEnvFile(join(fixturesDir, "comments.env")); + test("strips inline comments from unquoted values", async () => { + const result = await parseEnvFile(join(fixturesDir, "comments.env")); const dbPort = result.variables.find((v) => v.key === "DB_PORT"); expect(dbPort?.value).toBe("5432"); }); - test("preserves original comment content including #", () => { - const result = parseEnvFile(join(fixturesDir, "comments.env")); + test("preserves original comment content including #", async () => { + const result = await parseEnvFile(join(fixturesDir, "comments.env")); const firstComment = result.lines.find((l) => l.type === "comment"); expect(firstComment?.type === "comment" && firstComment.content).toContain("#"); }); }); describe("empty lines", () => { - test("preserves empty lines in lines array", () => { - const result = parseEnvFile(join(fixturesDir, "comments.env")); + test("preserves empty lines in lines array", async () => { + const result = await parseEnvFile(join(fixturesDir, "comments.env")); const emptyLines = result.lines.filter((l) => l.type === "empty"); expect(emptyLines.length).toBeGreaterThan(0); }); }); describe("edge cases", () => { - test("handles multiple equals signs in value", () => { - const result = parseEnvFile(join(fixturesDir, "edge-cases.env")); + test("handles multiple equals signs in value", async () => { + const result = await parseEnvFile(join(fixturesDir, "edge-cases.env")); const equation = result.variables.find((v) => v.key === "EQUATION"); expect(equation?.value).toBe("a=b=c=d"); }); - test("handles URL with query parameters", () => { - const result = parseEnvFile(join(fixturesDir, "edge-cases.env")); + test("handles URL with query parameters", async () => { + const result = await parseEnvFile(join(fixturesDir, "edge-cases.env")); const url = result.variables.find((v) => v.key === "URL"); expect(url?.value).toBe("https://example.com?foo=bar&baz=qux"); }); - test("handles unicode characters in values", () => { - const result = parseEnvFile(join(fixturesDir, "edge-cases.env")); + test("handles unicode characters in values", async () => { + const result = await parseEnvFile(join(fixturesDir, "edge-cases.env")); const emoji = result.variables.find((v) => v.key === "EMOJI"); const unicode = result.variables.find((v) => v.key === "UNICODE"); expect(emoji?.value).toContain("🎉"); expect(unicode?.value).toContain("你好"); }); - test("handles long values", () => { - const result = parseEnvFile(join(fixturesDir, "edge-cases.env")); + test("handles long values", async () => { + const result = await parseEnvFile(join(fixturesDir, "edge-cases.env")); const longValue = result.variables.find((v) => v.key === "LONG_VALUE"); expect(longValue?.value.length).toBeGreaterThan(100); }); - test("handles special characters in values", () => { - const result = parseEnvFile(join(fixturesDir, "edge-cases.env")); + test("handles special characters in values", async () => { + const result = await parseEnvFile(join(fixturesDir, "edge-cases.env")); const special = result.variables.find((v) => v.key === "SPECIAL_CHARS"); expect(special?.value).toContain("@#$%"); }); - test("handles JSON in values", () => { - const result = parseEnvFile(join(fixturesDir, "edge-cases.env")); + test("handles JSON in values", async () => { + const result = await parseEnvFile(join(fixturesDir, "edge-cases.env")); const json = result.variables.find((v) => v.key === "JSON_VALUE"); expect(json?.value).toContain('"key"'); }); }); describe("variable names", () => { - test("accepts keys starting with underscore", () => { - const result = parseEnvFile(join(fixturesDir, "edge-cases.env")); + test("accepts keys starting with underscore", async () => { + const result = await parseEnvFile(join(fixturesDir, "edge-cases.env")); const underscoreKey = result.variables.find((v) => v.key === "_STARTS_WITH_UNDERSCORE"); expect(underscoreKey).toBeDefined(); }); - test("accepts keys with double underscores", () => { - const result = parseEnvFile(join(fixturesDir, "edge-cases.env")); + test("accepts keys with double underscores", async () => { + const result = await parseEnvFile(join(fixturesDir, "edge-cases.env")); const doubleUnderscore = result.variables.find((v) => v.key === "__DOUBLE_UNDERSCORE"); expect(doubleUnderscore).toBeDefined(); }); - test("accepts keys with numbers after first character", () => { - const result = parseEnvFile(join(fixturesDir, "edge-cases.env")); + test("accepts keys with numbers after first character", async () => { + const result = await parseEnvFile(join(fixturesDir, "edge-cases.env")); const key123 = result.variables.find((v) => v.key === "KEY123"); expect(key123).toBeDefined(); }); - test("rejects keys starting with numbers", () => { + test("rejects keys starting with numbers", async () => { // Need to create a test with invalid key to test this - const result = parseEnvFile(join(fixturesDir, "edge-cases.env")); + const result = await parseEnvFile(join(fixturesDir, "edge-cases.env")); const invalidKey = result.variables.find((v) => v.key.match(/^[0-9]/)); expect(invalidKey).toBeUndefined(); }); }); describe("error handling", () => { - test("throws Env2OpError for missing file", () => { - expect(() => parseEnvFile("/nonexistent/path/.env")).toThrow(Env2OpError); + test("throws Env2OpError for missing file", async () => { + expect(parseEnvFile("/nonexistent/path/.env")).rejects.toThrow(Env2OpError); }); - test("throws with correct error code for missing file", () => { + test("throws with correct error code for missing file", async () => { try { - parseEnvFile("/nonexistent/path/.env"); + await parseEnvFile("/nonexistent/path/.env"); } catch (e) { expect(e).toBeInstanceOf(Env2OpError); expect((e as Env2OpError).code).toBe("ENV_FILE_NOT_FOUND"); } }); - test("reports invalid variable names in errors array", () => { + test("reports invalid variable names in errors array", async () => { // Create inline test for invalid names // For now, we test the existing fixtures don't have errors - const result = parseEnvFile(join(fixturesDir, "valid.env")); + const result = await parseEnvFile(join(fixturesDir, "valid.env")); expect(result.errors.length).toBe(0); }); }); describe("lines array structure", () => { - test("preserves original file structure order", () => { - const result = parseEnvFile(join(fixturesDir, "comments.env")); + test("preserves original file structure order", async () => { + const result = await parseEnvFile(join(fixturesDir, "comments.env")); // First line should be a comment expect(result.lines[0]?.type).toBe("comment"); }); - test("variable lines include key and value", () => { - const result = parseEnvFile(join(fixturesDir, "valid.env")); + test("variable lines include key and value", async () => { + const result = await parseEnvFile(join(fixturesDir, "valid.env")); const varLine = result.lines.find((l) => l.type === "variable"); expect(varLine?.type === "variable" && varLine.key).toBeDefined(); expect(varLine?.type === "variable" && varLine.value).toBeDefined(); @@ -212,18 +212,18 @@ describe("parseEnvFile", () => { }); describe("validateParseResult", () => { - test("does not throw when variables exist", () => { - const result = parseEnvFile(join(fixturesDir, "valid.env")); + test("does not throw when variables exist", async () => { + const result = await parseEnvFile(join(fixturesDir, "valid.env")); expect(() => validateParseResult(result, "valid.env")).not.toThrow(); }); - test("throws Env2OpError when no variables found", () => { - const result = parseEnvFile(join(fixturesDir, "empty.env")); + test("throws Env2OpError when no variables found", async () => { + const result = await parseEnvFile(join(fixturesDir, "empty.env")); expect(() => validateParseResult(result, "empty.env")).toThrow(Env2OpError); }); - test("throws with correct error code for empty file", () => { - const result = parseEnvFile(join(fixturesDir, "empty.env")); + test("throws with correct error code for empty file", async () => { + const result = await parseEnvFile(join(fixturesDir, "empty.env")); try { validateParseResult(result, "empty.env"); } catch (e) {