test(windows): make app coverage portable

This commit is contained in:
修雨
2026-07-15 16:45:57 +08:00
parent 296e9a73f0
commit 5e41b8a6e8
8 changed files with 60 additions and 11 deletions
+7
View File
@@ -37,6 +37,10 @@ import (
)
func TestAuthExportImportBase64RoundTrip(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("portable auth export is intentionally unsupported on Windows")
}
t.Setenv(keychain.DisableKeychainEnv, "1")
sourceKeychain := filepath.Join(t.TempDir(), "source-keychain")
sourceConfig := filepath.Join(t.TempDir(), ".dws")
@@ -123,6 +127,7 @@ func TestAuthExportRejectsWindowsDPAPIBackend(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("Windows DPAPI contract requires a native Windows runner")
}
t.Cleanup(CloseFileLogger)
exportCmd := NewRootCommand()
exportCmd.SetOut(&bytes.Buffer{})
@@ -147,6 +152,7 @@ func TestAuthExportRejectsWindowsDPAPIBackend(t *testing.T) {
func TestAuthImportRequiresForceWhenPopulated(t *testing.T) {
t.Setenv(keychain.DisableKeychainEnv, "1")
root := t.TempDir()
t.Cleanup(CloseFileLogger)
configDir := filepath.Join(root, ".dws")
t.Setenv(keychain.StorageDirEnv, filepath.Join(root, "keychain"))
t.Setenv("DWS_CONFIG_DIR", configDir)
@@ -1120,6 +1126,7 @@ func setupAuthLogoutProfiles(t *testing.T, tokens ...*authpkg.TokenData) string
ResetRuntimeTokenCache()
clearCompatCache()
t.Cleanup(func() {
CloseFileLogger()
authpkg.SetRuntimeProfile("")
ResetRuntimeTokenCache()
clearCompatCache()
+1 -1
View File
@@ -7,7 +7,7 @@ import (
func TestDefaultConfigDirUsesHomeDirectoryInOSSMode(t *testing.T) {
homeDir := filepath.Join(t.TempDir(), "home")
t.Setenv("HOME", homeDir)
setTestHome(t, homeDir)
t.Setenv("DWS_CONFIG_DIR", "")
got := defaultConfigDir()
@@ -67,7 +67,7 @@ func TestManualAgentExamplesDryRun(t *testing.T) {
t.Fatalf("create isolated test directory %s: %v", dir, err)
}
}
t.Setenv("HOME", homeDir)
setTestHome(t, homeDir)
t.Setenv("DWS_CONFIG_DIR", configDir)
t.Setenv("HTTP_PROXY", "http://127.0.0.1:1")
t.Setenv("HTTPS_PROXY", "http://127.0.0.1:1")
@@ -470,7 +470,7 @@ func TestManualAgentExamplePromptObservedRejectsInteractiveConfirmation(t *testi
}
func TestAitableAdvpermDisableDryRunSkipsConfirmationAndToolCall(t *testing.T) {
t.Setenv("HOME", t.TempDir())
setTestHome(t, t.TempDir())
t.Setenv("DWS_CONFIG_DIR", t.TempDir())
args := []string{
@@ -518,7 +518,7 @@ func TestManualAgentExampleChatGroupMuteMemberUsesCommandDryRunPreview(t *testin
if err := os.MkdirAll(configDir, 0o700); err != nil {
t.Fatalf("create isolated config directory: %v", err)
}
t.Setenv("HOME", sandboxRoot)
setTestHome(t, sandboxRoot)
t.Setenv("DWS_CONFIG_DIR", configDir)
capture, err := executeManualAgentExampleCapture(t, []string{
+2
View File
@@ -369,6 +369,7 @@ func TestSkillInstallCommandValidation(t *testing.T) {
func TestSkillInstallInvalidTarget(t *testing.T) {
// Setup: Create config directory with valid token
tempDir := t.TempDir()
t.Cleanup(CloseFileLogger)
configDir := filepath.Join(tempDir, "config")
t.Setenv("DWS_CONFIG_DIR", configDir)
@@ -402,6 +403,7 @@ func TestSkillInstallInvalidTarget(t *testing.T) {
func TestSkillInstallRequiresAuth(t *testing.T) {
// Setup: Create config directory without token
tempDir := t.TempDir()
t.Cleanup(CloseFileLogger)
configDir := filepath.Join(tempDir, "config")
t.Setenv("DWS_CONFIG_DIR", configDir)
+4 -4
View File
@@ -88,7 +88,7 @@ func TestResolveSkillSetupSourceErrorWhenMissing(t *testing.T) {
// Isolate HOME so the ~/.dws/skills/<mode>/ fallback (added by the release
// pipeline cache work) does not pick up real cached content on the
// developer machine.
t.Setenv("HOME", t.TempDir())
setTestHome(t, t.TempDir())
_, err := resolveSkillSetupSource(tmp, skillSetupModeMono)
if err == nil {
t.Fatalf("expected error when source missing")
@@ -106,7 +106,7 @@ func TestResolveSkillSetupTargetsSingleAgent(t *testing.T) {
if len(got) != 1 {
t.Fatalf("expected 1 dest, got %d", len(got))
}
if !strings.Contains(got[0], ".claude/skills/dws") {
if !strings.Contains(filepath.Clean(got[0]), filepath.Join(".claude", "skills", "dws")) {
t.Fatalf("expected .claude/skills/dws path, got %s", got[0])
}
}
@@ -125,10 +125,10 @@ func TestResolveSkillSetupTargetsMultiOmitsDwsTail(t *testing.T) {
if len(got) != 1 {
t.Fatalf("expected 1 dest, got %d", len(got))
}
if strings.HasSuffix(got[0], "/dws") {
if filepath.Base(got[0]) == "dws" {
t.Fatalf("multi target must not end with /dws, got %s", got[0])
}
if !strings.HasSuffix(got[0], ".claude/skills") {
if !strings.HasSuffix(filepath.Clean(got[0]), filepath.Join(".claude", "skills")) {
t.Fatalf("expected suffix .claude/skills, got %s", got[0])
}
}
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package app
import (
"runtime"
"testing"
)
func setTestHome(t *testing.T, home string) {
t.Helper()
t.Setenv("HOME", home)
if runtime.GOOS == "windows" {
// os.UserHomeDir uses USERPROFILE on Windows rather than HOME.
t.Setenv("USERPROFILE", home)
}
}
+12
View File
@@ -15,6 +15,7 @@ package app
import (
"os"
"path/filepath"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/keychain"
@@ -49,8 +50,19 @@ func TestMain(m *testing.M) {
_ = os.RemoveAll(tmpDir)
panic("set " + keychain.StorageDirEnv + ": " + err.Error())
}
if err := os.Setenv("DWS_CONFIG_DIR", filepath.Join(tmpDir, "config")); err != nil {
_ = os.RemoveAll(tmpDir)
panic("set DWS_CONFIG_DIR: " + err.Error())
}
// Keep the process-wide audit sink outside per-test TempDir trees. Cobra
// skips PersistentPostRunE on expected command errors, and Windows cannot
// remove a TempDir while the audit lock is still open.
setupAuditSink()
openBrowserFunc = func(string) error { return nil }
code := m.Run()
StopAllStdioClients()
CloseAuditSink()
CloseFileLogger()
_ = os.RemoveAll(tmpDir)
os.Exit(code)
}
+3 -3
View File
@@ -283,7 +283,7 @@ func TestWriteReportIfEnabled_Auto(t *testing.T) {
expected := filepath.Join(tmpHome, ".dws", "perf", "latest.json")
// Temporarily override HOME for defaultPerfReportPath
t.Setenv("HOME", tmpHome)
setTestHome(t, tmpHome)
t.Setenv(PerfReportEnv, "auto")
tc := NewTimingCollector()
@@ -312,7 +312,7 @@ func TestWriteReportIfEnabled_NilCollector(t *testing.T) {
func TestLoadLatestReport(t *testing.T) {
tmpHome := t.TempDir()
t.Setenv("HOME", tmpHome)
setTestHome(t, tmpHome)
perfDir := filepath.Join(tmpHome, ".dws", "perf")
if err := os.MkdirAll(perfDir, 0o700); err != nil {
@@ -348,7 +348,7 @@ func TestLoadLatestReport(t *testing.T) {
func TestLoadLatestReport_NotFound(t *testing.T) {
tmpHome := t.TempDir()
t.Setenv("HOME", tmpHome)
setTestHome(t, tmpHome)
_, err := LoadLatestReport()
if err == nil {