diff options
Diffstat (limited to 'test/cli/run/env.test.ts')
| -rw-r--r-- | test/cli/run/env.test.ts | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/test/cli/run/env.test.ts b/test/cli/run/env.test.ts new file mode 100644 index 000000000..328162f0b --- /dev/null +++ b/test/cli/run/env.test.ts @@ -0,0 +1,256 @@ +import { describe, expect, test } from "bun:test"; +import os from "os"; +import fs from "fs"; +import path from "path"; +import { bunEnv, bunExe } from "harness"; + +function tempDirWithFiles(basename: string, files: Record<string, string>) { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), basename + "_")); + for (const [name, contents] of Object.entries(files)) { + fs.writeFileSync(path.join(dir, name), contents); + } + return dir; +} + +function bunRun(file: string, env?: Record<string, string>) { + const result = Bun.spawnSync([bunExe(), file], { + cwd: path.dirname(file), + env: { + ...bunEnv, + NODE_ENV: undefined, + ...env, + }, + }); + if (!result.success) throw new Error(result.stderr.toString("utf8")); + return { + stdout: result.stdout.toString("utf8").trim(), + stderr: result.stderr.toString("utf8").trim(), + }; +} +function bunTest(file: string, env?: Record<string, string>) { + const result = Bun.spawnSync([bunExe(), "test", path.basename(file)], { + cwd: path.dirname(file), + env: { + ...bunEnv, + NODE_ENV: undefined, + ...env, + }, + }); + if (!result.success) throw new Error(result.stderr.toString("utf8")); + return { + stdout: result.stdout.toString("utf8").trim(), + stderr: result.stderr.toString("utf8").trim(), + }; +} + +describe(".env file is loaded", () => { + test(".env", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FOO=bar\n", + "index.ts": "console.log(process.env.FOO);", + }); + const { stdout } = bunRun(`${dir}/index.ts`); + expect(stdout).toBe("bar"); + }); + test(".env.local", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FOO=fail\nBAR=baz\n", + ".env.local": "FOO=bar\n", + "index.ts": "console.log(process.env.FOO, process.env.BAR);", + }); + const { stdout } = bunRun(`${dir}/index.ts`); + expect(stdout).toBe("bar baz"); + }); + test(".env.development (NODE_ENV=undefined)", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FOO=fail\nBAR=baz\n", + ".env.development": "FOO=bar\n", + ".env.local": "LOCAL=true\n", + "index.ts": "console.log(process.env.FOO, process.env.BAR, process.env.LOCAL);", + }); + const { stdout } = bunRun(`${dir}/index.ts`); + expect(stdout).toBe("bar baz true"); + }); + test(".env.development (NODE_ENV=development)", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FOO=fail\nBAR=baz\n", + ".env.development": "FOO=bar\n", + ".env.local": "LOCAL=true\n", + "index.ts": "console.log(process.env.FOO, process.env.BAR, process.env.LOCAL);", + }); + const { stdout } = bunRun(`${dir}/index.ts`); + expect(stdout).toBe("bar baz true"); + }); + test(".env.production", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FOO=fail\nBAR=baz\n", + ".env.production": "FOO=bar\n", + ".env.local": "LOCAL=true\n", + "index.ts": "console.log(process.env.FOO, process.env.BAR, process.env.LOCAL);", + }); + const { stdout } = bunRun(`${dir}/index.ts`, { NODE_ENV: "production" }); + expect(stdout).toBe("bar baz true"); + }); + test(".env.development and .env.test ignored when NODE_ENV=production", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FOO=bar\nBAR=baz\n", + ".env.development": "FOO=development\n", + ".env.development.local": "FOO=development.local\n", + ".env.test": "FOO=test\n", + ".env.test.local": "FOO=test.local\n", + ".env.local": "LOCAL=true\n", + "index.ts": "console.log(process.env.FOO, process.env.BAR, process.env.LOCAL);", + }); + const { stdout } = bunRun(`${dir}/index.ts`, { NODE_ENV: "production" }); + expect(stdout).toBe("bar baz true"); + }); + test(".env.production and .env.test ignored when NODE_ENV=development", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FOO=bar\nBAR=baz\n", + ".env.production": "FOO=production\n", + ".env.production.local": "FOO=production.local\n", + ".env.test": "FOO=test\n", + ".env.test.local": "FOO=test.local\n", + ".env.local": "LOCAL=true\n", + "index.ts": "console.log(process.env.FOO, process.env.BAR, process.env.LOCAL);", + }); + const { stdout } = bunRun(`${dir}/index.ts`, {}); + expect(stdout).toBe("bar baz true"); + }); + test(".env and .env.test used in testing", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "A=a\n", + ".env.test.local": "B=b\n", + ".env.test": "C=c\n", + ".env.development": "FAIL=.env.development\n", + ".env.development.local": "FAIL=.env.development.local\n", + ".env.production": "FAIL=.env.production\n", + ".env.production.local": "FAIL=.env.production.local\n", + "index.test.ts": "console.log(process.env.A,process.env.B,process.env.C,process.env.FAIL);", + }); + const { stdout } = bunTest(`${dir}/index.test.ts`, {}); + expect(stdout).toBe("a b c undefined"); + }); + test(".env.local ignored when bun test", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FAILED=false\n", + ".env.local": "FAILED=true\n", + "index.test.ts": "console.log(process.env.FAILED, process.env.NODE_ENV);", + }); + const { stdout } = bunTest(`${dir}/index.test.ts`, {}); + expect(stdout).toBe("false test"); + }); + test(".env.development and .env.production ignored when bun test", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FAILED=false\n", + ".env.development": "FAILED=development\n", + ".env.development.local": "FAILED=development.local\n", + ".env.production": "FAILED=production\n", + ".env.production.local": "FAILED=production.local\n", + "index.test.ts": "console.log(process.env.FAILED, process.env.NODE_ENV);", + }); + const { stdout } = bunTest(`${dir}/index.test.ts`); + expect(stdout).toBe("false test"); + }); +}); +describe("dotenv priority", () => { + test("process env overrides everything else", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FOO=.env\n", + ".env.development": "FOO=.env.development\n", + ".env.development.local": "FOO=.env.development.local\n", + ".env.production": "FOO=.env.production\n", + ".env.production.local": "FOO=.env.production.local\n", + ".env.test.local": "FOO=.env.test.local\n", + ".env.test": "FOO=.env.test\n", + ".env.local": "FOO=.env.local\n", + "index.ts": "console.log(process.env.FOO);", + "index.test.ts": "console.log(process.env.FOO);", + }); + const { stdout } = bunRun(`${dir}/index.ts`, { FOO: "override" }); + expect(stdout).toBe("override"); + + const { stdout: stdout2 } = bunTest(`${dir}/index.test.ts`, { FOO: "override" }); + expect(stdout2).toBe("override"); + }); + test(".env.{NODE_ENV}.local overrides .env.local", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FOO=.env\n", + ".env.development": "FOO=.env.development\n", + ".env.development.local": "FOO=.env.development.local\n", + ".env.production": "FOO=.env.production\n", + ".env.production.local": "FOO=.env.production.local\n", + ".env.test.local": "FOO=.env.test.local\n", + ".env.test": "FOO=.env.test\n", + ".env.local": "FOO=.env.local\n", + "index.ts": "console.log(process.env.FOO);", + "index.test.ts": "console.log(process.env.FOO);", + }); + const { stdout: stdout_dev } = bunRun(`${dir}/index.ts`, { NODE_ENV: "development" }); + expect(stdout_dev).toBe(".env.development.local"); + const { stdout: stdout_prod } = bunRun(`${dir}/index.ts`, { NODE_ENV: "production" }); + expect(stdout_prod).toBe(".env.production.local"); + const { stdout: stdout_test } = bunTest(`${dir}/index.test.ts`, {}); + expect(stdout_test).toBe(".env.test.local"); + }); + test(".env.local overrides .env.{NODE_ENV}", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FOO=.env\n", + ".env.development": "FOO=.env.development\n", + ".env.production": "FOO=.env.production\n", + ".env.test": "FOO=.env.test\n", + ".env.local": "FOO=.env.local\n", + "index.ts": "console.log(process.env.FOO);", + "index.test.ts": "console.log(process.env.FOO);", + }); + const { stdout: stdout_dev } = bunRun(`${dir}/index.ts`, { NODE_ENV: "development" }); + expect(stdout_dev).toBe(".env.local"); + const { stdout: stdout_prod } = bunRun(`${dir}/index.ts`, { NODE_ENV: "production" }); + expect(stdout_prod).toBe(".env.local"); + // .env.local is "not checked when `NODE_ENV` is `test`" + const { stdout: stdout_test } = bunTest(`${dir}/index.test.ts`, {}); + expect(stdout_test).toBe(".env.test"); + }); + test(".env.{NODE_ENV} overrides .env", () => { + const dir = tempDirWithFiles("dotenv", { + ".env": "FOO=.env\n", + ".env.development": "FOO=.env.development\n", + ".env.production": "FOO=.env.production\n", + ".env.test": "FOO=.env.test\n", + "index.ts": "console.log(process.env.FOO);", + "index.test.ts": "console.log(process.env.FOO);", + }); + const { stdout: stdout_dev } = bunRun(`${dir}/index.ts`, { NODE_ENV: "development" }); + expect(stdout_dev).toBe(".env.development"); + const { stdout: stdout_prod } = bunRun(`${dir}/index.ts`, { NODE_ENV: "production" }); + expect(stdout_prod).toBe(".env.production"); + const { stdout: stdout_test } = bunTest(`${dir}/index.test.ts`, {}); + expect(stdout_test).toBe(".env.test"); + }); +}); + +test.todo(".env space edgecase (issue #411)", () => { + const dir = tempDirWithFiles("dotenv-issue-411", { + ".env": "VARNAME=A B", + "index.ts": "console.log('[' + process.env.VARNAME + ']'); ", + }); + const { stdout } = bunRun(`${dir}/index.ts`); + expect(stdout).toBe("[A B]"); +}); + +test.todo(".env special characters 1 (issue #2823)", () => { + const dir = tempDirWithFiles("dotenv-issue-411", { + ".env": 'A="a$t"\n', + "index.ts": "console.log('[' + process.env.A + ']'); ", + }); + const { stdout } = bunRun(`${dir}/index.ts`); + expect(stdout).toBe("[a$t]"); +}); + +test.todo("env escaped quote (issue #2484)", () => { + const dir = tempDirWithFiles("dotenv-issue-411", { + "index.ts": "console.log(process.env.VALUE, process.env.VALUE2);", + }); + const { stdout } = bunRun(`${dir}/index.ts`, { VALUE: `\\"`, VALUE2: `\\\\"` }); + expect(stdout).toBe('\\" \\\\"'); +}); |
