package dingtalkdiscovery import ( "context" "os" "path/filepath" "strings" "testing" "time" ) // dws 的文件型 keychain 固定在 $HOME/.local/share/dws-cli,而 keychain 挂在 DWS_HOME 下面; // 子进程若沿用容器自身的 HOME 就会报"未登录"。这条断言把该行为钉住(实测踩过)。 func TestDWSEnvPutsTheKeychainHomeOnTheDWSHome(t *testing.T) { t.Setenv("DWS_HOME", "/data/home") t.Setenv("HOME", "/home/app") t.Setenv("DWS_CONFIG_DIR", "/data/dws") t.Setenv("DWS_DISABLE_KEYCHAIN", "1") t.Setenv("DWS_BIN", "/usr/local/bin/dws") t.Setenv("DATABASE_URL", "postgres://governance:secret@postgres/governance") t.Setenv("TEST_WEKNORA_API_KEY", "sk-should-not-leak") env := dwsEnv() joined := strings.Join(env, "\n") if !strings.Contains(joined, "HOME=/data/home") { t.Fatalf("dws child HOME should follow DWS_HOME, env=%v", env) } if strings.Contains(joined, "HOME=/home/app") { t.Fatalf("container HOME leaked into dws child: %v", env) } for _, leaked := range []string{"DATABASE_URL", "TEST_WEKNORA_API_KEY"} { if strings.Contains(joined, leaked) { t.Fatalf("%s leaked into the dws child: %v", leaked, env) } } } // 没有 DWS_HOME 时退回进程自己的 HOME。 func TestDWSEnvFallsBackToProcessHome(t *testing.T) { t.Setenv("DWS_HOME", "") t.Setenv("HOME", "/home/app") if joined := strings.Join(dwsEnv(), "\n"); !strings.Contains(joined, "HOME=/home/app") { t.Fatalf("expected fallback HOME, got %v", dwsEnv()) } } // 真跑一个假的 dws:确认参数被原样传给二进制,且非零退出码时仍把 stdout 交回上层 // (dws 把错误 JSON 写在 stdout 上,丢了它就没法分辨"未登录"与"权限不足")。 func TestExecRunnerRunsTheBinaryAndKeepsStdoutOnFailure(t *testing.T) { dir := t.TempDir() binary := filepath.Join(dir, "dws") script := "#!/bin/sh\necho \"args:$@\"\necho '{\"ok\":false,\"error\":{\"message\":\"dws: 未登录\"}}'\nexit 4\n" if err := os.WriteFile(binary, []byte(script), 0o755); err != nil { t.Fatal(err) } runner := ExecRunner{Binary: binary, Timeout: 5 * time.Second} out, err := runner.RunDWS(context.Background(), "wiki", "+space-list", "--limit", "200") if err == nil { t.Fatal("expected a non-zero exit to be reported") } if !strings.Contains(string(out), "未登录") { t.Fatalf("stdout should be preserved on failure, got %q", string(out)) } if !strings.Contains(string(out), "args:wiki +space-list --limit 200") { t.Fatalf("args not passed through: %q", string(out)) } if !strings.Contains(err.Error(), "wiki +space-list") { t.Fatalf("error should name the command: %v", err) } }