package main import ( "io" "os" "path/filepath" "reflect" "strings" "testing" ) // dotenv_test.go pins the .env parsing rules that used to live only in comments: // paired quotes (a regression «simplifying to strings.Trim» would corrupt API keys and // surface as 401s mid-run) and «do not override an already-set environment». func TestParseDotEnvPairedQuotes(t *testing.T) { in := strings.Join([]string{ `A="v1"`, // paired double quotes — strip `B='v2'`, // paired single quotes — strip `C="v3'`, // MISMATCHED quotes — leave alone `D=v4"`, // trailing quote with no pair — keep (the main regression case) `E="`, // single quote (n<2) — keep `F=""`, // empty pair — empty string ` G = v7 `, // trim key and value `# comment`, // comment — skip ``, // empty — skip `no-equals-line`, // no «=» — skip }, "\n") got := parseDotEnv(strings.NewReader(in)) want := []dotenvPair{ {"A", "v1"}, {"B", "v2"}, {"C", `"v3'`}, {"D", `v4"`}, {"E", `"`}, {"F", ""}, {"G", "v7"}, } if !reflect.DeepEqual(got, want) { t.Fatalf("parseDotEnv:\n got %+v\n want %+v", got, want) } } func TestLoadDotEnvDoesNotOverride(t *testing.T) { path := filepath.Join(t.TempDir(), ".env") if err := os.WriteFile(path, []byte("TM_TEST_SET=from_file\nTM_TEST_UNSET=filled\n"), 0o600); err != nil { t.Fatal(err) } t.Setenv("TM_TEST_SET", "from_env") t.Setenv("TM_TEST_UNSET", "") // set to EMPTY counts as unset (frozen behaviour) loadDotEnv(path, io.Discard) if got := os.Getenv("TM_TEST_SET"); got != "from_env" { t.Fatalf("an already-set variable must win over .env, got %q", got) } if got := os.Getenv("TM_TEST_UNSET"); got != "filled" { t.Fatalf("an empty variable is treated as unset (frozen behaviour), got %q", got) } }