package config // 本文件覆盖 LoadDurationEnv(切片 E3+E4 的 CHANNEL_EVENT_* 读取口径):三条判据 // 各自的用例 —— **未配置回落默认**、**`.env` 只写在文件里也真实生效**(这正是不能用 // os.Getenv 的原因)、**配了但读不懂 ⇒ 回落默认 + 报错**(绝不静默失效)。 // // 与 owner_identity_test.go 同一约定:读进程环境 / 写 .env 的用例**不并行** // (unsetEnvForTest 会改动进程环境,与并行读取互相干扰)。 import ( "path/filepath" "testing" "time" ) // TestLoadDurationEnv_未配置回落默认_正常:键既不在进程环境也不在 .env ⇒ 返回默认值、 // 不报错;值为空白(`KEY=`)同样按「没配」处理。 func TestLoadDurationEnv_未配置回落默认_正常(t *testing.T) { const key = "CYRENE_TEST_DURATION_UNSET" unsetEnvForTest(t, key) value, err := LoadDurationEnv("", key, 720*time.Hour) if err != nil { t.Fatalf("未配置时不该报错:%v", err) } if value != 720*time.Hour { t.Fatalf("未配置时取值 = %v, want 720h(默认)", value) } blank := writeDotEnv(t, key+"=") value, err = LoadDurationEnv(blank, key, time.Hour) if err != nil { t.Fatalf("空白取值不该报错:%v", err) } if value != time.Hour { t.Fatalf("空白取值时 = %v, want 1h(按未配置处理)", value) } } // TestLoadDurationEnv_进程环境优先_正常:优先级与其余配置一致(进程环境 > .env > 默认)。 func TestLoadDurationEnv_进程环境优先_正常(t *testing.T) { const key = "CYRENE_TEST_DURATION_PRECEDENCE" t.Setenv(key, "45m") value, err := LoadDurationEnv(writeDotEnv(t, key+"=3h"), key, 30*time.Second) if err != nil { t.Fatalf("LoadDurationEnv: %v", err) } if value != 45*time.Minute { t.Fatalf("取值 = %v, want 45m(进程环境优先于 .env)", value) } } // TestLoadDurationEnv_只写在env文件里也生效_正常:这是本函数存在的理由 —— `.env` 不写回 // 进程环境,os.Getenv 会**静默忽略**它,而本函数必须读到(否则照模板部署的新实例漏配且 // 静默失效,AGENTS §10 的 E6 条目)。显式 0 与负值也必须**原样抵达**:渠道事件 GC 正是 // 靠 ≤0 判「显式关闭」(design/26 §4.4)。 func TestLoadDurationEnv_只写在env文件里也生效_正常(t *testing.T) { const key = "CYRENE_TEST_DURATION_DOTENV" unsetEnvForTest(t, key) zero, err := LoadDurationEnv(writeDotEnv(t, "# 注释行", key+"=0"), key, 720*time.Hour) if err != nil { t.Fatalf("LoadDurationEnv(0): %v", err) } if zero != 0 { t.Fatalf("取值 = %v, want 0(.env 里的显式 0 必须原样抵达)", zero) } neg, err := LoadDurationEnv(writeDotEnv(t, key+"=-1h"), key, time.Hour) if err != nil { t.Fatalf("LoadDurationEnv(负值): %v", err) } if neg != -time.Hour { t.Fatalf("负值 = %v, want -1h(负值必须原样抵达,不得被当成默认)", neg) } } // TestLoadDurationEnv_非法取值回落默认并报错_异常:配了但读不懂 ⇒ 返回默认值 **且**返回 // 错误(调用方据此记 WARN 并继续),绝不静默失效。 func TestLoadDurationEnv_非法取值回落默认并报错_异常(t *testing.T) { const key = "CYRENE_TEST_DURATION_INVALID" unsetEnvForTest(t, key) value, err := LoadDurationEnv(writeDotEnv(t, key+"=一小时"), key, 30*time.Second) if err == nil { t.Fatal("非法 duration 必须报错(AGENTS §10:非法配置不许静默失效)") } if value != 30*time.Second { t.Fatalf("非法取值时的回落值 = %v, want 30s", value) } } // TestLoadDurationEnv_env文件不存在也不报错_边界:路径为空(不看 .env)与路径指向不存在的 // 文件都不该让启动失败 —— 三个键都是可选旋钮。⚠️ 路径非空但文件缺失时 parseDotEnv 的行为 // 由既有约定决定(缺失不算错误),本用例把它钉在「返回默认值」这一侧。 func TestLoadDurationEnv_env文件不存在也不报错_边界(t *testing.T) { const key = "CYRENE_TEST_DURATION_MISSING_DOTENV" unsetEnvForTest(t, key) value, err := LoadDurationEnv(filepath.Join(t.TempDir(), "absent.env"), key, time.Hour) if err != nil { t.Fatalf("缺失的 .env 不该让读取失败:%v", err) } if value != time.Hour { t.Fatalf("取值 = %v, want 1h(默认)", value) } }