package config import ( "os" "path/filepath" "strings" "testing" ) // 本文件锁定 OWNER_IDENTITY_INJECTION 的读取与判定(design/11 §0 收窄后的 // 开关):默认档是 owner_only,取值优先级与其余配置一致(进程环境 > .env > // 默认),非法取值**必须报错**,由调用方明确回退并记日志。 // // 读环境/写 .env 的用例按本包既有约定**不并行**(unsetEnvForTest 会改动 // 进程环境,与并行读取互相干扰)。 // writeDotEnv 把给定行写进一个临时 .env 并返回路径。 func writeDotEnv(t *testing.T, lines ...string) string { t.Helper() path := filepath.Join(t.TempDir(), ".env") if err := os.WriteFile(path, []byte(strings.Join(lines, "\n")+"\n"), 0o600); err != nil { t.Fatalf("write .env: %v", err) } return path } // TestLoadOwnerIdentityInjection_DefaultsToOwnerOnly 钉住默认档:没配(进程 // 环境与 .env 都没有)时是 owner_only,也就是最安全的一侧。 func TestLoadOwnerIdentityInjection_DefaultsToOwnerOnly(t *testing.T) { unsetEnvForTest(t, OwnerIdentityInjectionEnv) got, err := LoadOwnerIdentityInjection(filepath.Join(t.TempDir(), "absent.env")) if err != nil { t.Fatalf("LoadOwnerIdentityInjection: %v", err) } if got != OwnerIdentityOwnerOnly { t.Fatalf("默认档 = %q, want %q", got, OwnerIdentityOwnerOnly) } mode, perr := ParseOwnerIdentityInjection(got) if perr != nil || mode != OwnerIdentityOwnerOnly { t.Fatalf("Parse(%q) = (%q, %v), want (%q, nil)", got, mode, perr, OwnerIdentityOwnerOnly) } } // TestLoadOwnerIdentityInjection_DotEnvApplies 证明只写在 .env 里的声明真实 // 生效 —— 这正是不能直接用 os.Getenv 的原因(.env 不写回进程环境,直接读 // 会静默忽略用户写在 .env 里的开关)。 func TestLoadOwnerIdentityInjection_DotEnvApplies(t *testing.T) { unsetEnvForTest(t, OwnerIdentityInjectionEnv) path := writeDotEnv(t, "# 注释行", OwnerIdentityInjectionEnv+"=always") got, err := LoadOwnerIdentityInjection(path) if err != nil { t.Fatalf("LoadOwnerIdentityInjection: %v", err) } if got != OwnerIdentityAlways { t.Fatalf(".env 取值 = %q, want %q", got, OwnerIdentityAlways) } } // TestLoadOwnerIdentityInjection_ProcessEnvWins 证明优先级:进程环境压过 .env // (与 internal/config 其余配置同一口径)。 func TestLoadOwnerIdentityInjection_ProcessEnvWins(t *testing.T) { unsetEnvForTest(t, OwnerIdentityInjectionEnv) t.Setenv(OwnerIdentityInjectionEnv, OwnerIdentityOff) path := writeDotEnv(t, OwnerIdentityInjectionEnv+"=always") got, err := LoadOwnerIdentityInjection(path) if err != nil { t.Fatalf("LoadOwnerIdentityInjection: %v", err) } if got != OwnerIdentityOff { t.Fatalf("进程环境取值 = %q, want %q(进程环境优先于 .env)", got, OwnerIdentityOff) } } // TestLoadOwnerIdentityInjection_ReadErrorIsExplicit 钉住「读不动就显式报错」: // 一个不可读的 .env(这里是目录路径)返回错误,绝不悄悄变成默认值 —— // 调用方据此记日志并明确回退。 func TestLoadOwnerIdentityInjection_ReadErrorIsExplicit(t *testing.T) { unsetEnvForTest(t, OwnerIdentityInjectionEnv) if _, err := LoadOwnerIdentityInjection(t.TempDir()); err == nil { t.Fatal("把目录当 .env 读本应报错,却返回 nil") } } // TestParseOwnerIdentityInjection_ThreeModesAndIllegal 钉住三档取值与非法值: // 大小写/空白容忍,空串按默认,其余取值返回**带原始取值的错误**。 func TestParseOwnerIdentityInjection_ThreeModesAndIllegal(t *testing.T) { t.Parallel() for _, tc := range []struct { raw string want string }{ {raw: "", want: OwnerIdentityOwnerOnly}, {raw: OwnerIdentityOwnerOnly, want: OwnerIdentityOwnerOnly}, {raw: " OWNER_ONLY ", want: OwnerIdentityOwnerOnly}, {raw: "Always", want: OwnerIdentityAlways}, {raw: "off", want: OwnerIdentityOff}, {raw: " OFF", want: OwnerIdentityOff}, } { got, err := ParseOwnerIdentityInjection(tc.raw) if err != nil { t.Fatalf("Parse(%q) 报错: %v", tc.raw, err) } if got != tc.want { t.Fatalf("Parse(%q) = %q, want %q", tc.raw, got, tc.want) } } for _, raw := range []string{"everyone", "true", "1", "owner-only", "允许"} { mode, err := ParseOwnerIdentityInjection(raw) if err == nil { t.Fatalf("Parse(%q) 本应报错(非法取值不得静默生效),却返回 %q", raw, mode) } if !strings.Contains(err.Error(), raw) { t.Fatalf("错误信息里没有原始取值 %q: %v", raw, err) } if mode != OwnerIdentityOwnerOnly { t.Fatalf("Parse(%q) 的回退档 = %q, want %q(回退方向必须是最安全一侧)", raw, mode, OwnerIdentityOwnerOnly) } } }