package app import ( "archive/tar" "archive/zip" "bytes" "compress/gzip" "context" "encoding/json" "errors" "fmt" "io" "log/slog" "net/http" "net/http/httptest" "os" "path/filepath" "runtime" "strings" "testing" "time" authpkg "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/auth" apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors" dwsevent "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event" eventbus "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/bus" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/busctl" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/consume" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/personal" eventtransport "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/transport" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/executor" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/keychain" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/pat" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/plugin" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/safety" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/skillstate" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/transport" upgradepkg "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/upgrade" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/config" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/mcptypes" tea "github.com/charmbracelet/bubbletea" "github.com/spf13/cobra" ) type coverageRunner struct { result executor.Result err error last executor.Invocation } func (r *coverageRunner) Run(_ context.Context, inv executor.Invocation) (executor.Result, error) { r.last = inv return r.result, r.err } type coverageScanner struct{ report safety.Report } func (s coverageScanner) ScanPayload(any) safety.Report { return s.report } type coverageRawStderr string func (e coverageRawStderr) Error() string { return string(e) } func (e coverageRawStderr) RawStderr() string { return string(e) } func TestCrossPlatformCoverageToolCallerAdapterCoverage(t *testing.T) { runner := &coverageRunner{} adapter := newToolCallerAdapter(runner, nil).(*toolCallerAdapter) if adapter.Format() != "json" || adapter.DryRun() || adapter.Fields() != "" || adapter.JQ() != "" { t.Fatal("nil flag adapter defaults changed") } adapter.flags = &GlobalFlags{Format: "raw", DryRun: true, Fields: "a", JQ: ".a"} if adapter.Format() != "raw" || !adapter.DryRun() || adapter.Fields() != "a" || adapter.JQ() != ".a" { t.Fatal("adapter flags were not forwarded") } adapter.flags.DryRun = false if _, err := (*toolCallerAdapter)(nil).CallToolWithToken(context.Background(), "token", "doc", "get", nil); err == nil { t.Fatal("nil adapter token override succeeded") } authpkg.SetRuntimeProfile("saved-profile") adapter.flags.Token = "saved-token" runner.result = executor.Result{Response: map[string]any{"content": []any{map[string]any{"type": "text", "text": "ok"}}}} if _, err := adapter.CallToolWithToken(context.Background(), "temporary-token", "doc", "get", nil); err != nil { t.Fatal(err) } if adapter.flags.Token != "saved-token" || authpkg.RuntimeProfile() != "saved-profile" { t.Fatal("token override state was not restored") } authpkg.SetRuntimeProfile("") runner.err = errors.New("runner failure") if _, err := adapter.CallTool(context.Background(), "doc", "get", map[string]any{"x": 1}); !errors.Is(err, runner.err) { t.Fatalf("adapter runner error = %v", err) } runner.err = nil for name, response := range map[string]map[string]any{ "nil": nil, "whole": {"value": 1}, "list": {"content": []any{map[string]any{"type": "text", "text": "hello"}, "skip"}}, "map": {"content": map[string]any{"value": 2}}, "primitive": {"content": 3}, } { t.Run(name, func(t *testing.T) { runner.result = executor.Result{Invocation: runner.last, Response: response} got, err := adapter.CallTool(context.Background(), "doc", "get", nil) if err != nil || got == nil { t.Fatalf("adapter result = %#v, %v", got, err) } }) } if strVal(map[string]any{"x": 1}, "x") != "" || strVal(map[string]any{"x": "v"}, "x") != "v" { t.Fatal("strVal mismatch") } } func TestCrossPlatformCoverageRunnerPureCoverage(t *testing.T) { for _, tc := range []struct { content map[string]any want string }{ {nil, ""}, {map[string]any{"success": true}, ""}, {map[string]any{"success": "false"}, ""}, {map[string]any{"success": false, "errorMsg": " message "}, "message"}, {map[string]any{"success": false, "errorCode": " CODE "}, "business error: code CODE"}, {map[string]any{"success": false}, "business error: success=false"}, {map[string]any{"data": map[string]any{"success": false, "errorMsg": "nested"}}, "nested"}, {map[string]any{"result": []any{"skip", map[string]any{"success": false, "errorMsg": "array"}}}, "array"}, } { if got := detectBusinessError(tc.content); got != tc.want { t.Errorf("detectBusinessError(%#v) = %q, want %q", tc.content, got, tc.want) } } deep := map[string]any{"success": false} for range 10 { deep = map[string]any{"data": deep} } _ = detectBusinessError(deep) for _, tc := range []struct { result transport.ToolCallResult want string }{ {transport.ToolCallResult{Blocks: []transport.ContentBlock{{Text: " block "}}}, "block"}, {transport.ToolCallResult{Content: map[string]any{"message": " message "}}, "message"}, {transport.ToolCallResult{Content: map[string]any{"error": " error "}}, "error"}, {transport.ToolCallResult{}, "MCP tool returned an error response"}, } { if got := extractMCPErrorMessage(tc.result); got != tc.want { t.Errorf("extractMCPErrorMessage = %q, want %q", got, tc.want) } } if runtimeFlagEnabled("", false) || runtimeFlagEnabled("off", true) || !runtimeFlagEnabled("yes", false) { t.Fatal("runtime flag parsing mismatch") } if isAuthError(nil) || !isAuthError(apperrors.NewAuth("auth")) || isAuthError(errors.New("plain")) { t.Fatal("auth error classification mismatch") } t.Setenv("DINGTALK_MY_PRODUCT_MCP_URL", " https://override.test ") if got, ok := productEndpointOverride("my-product"); !ok || got != "https://override.test" { t.Fatalf("endpoint override = %q %v", got, ok) } if got, ok := productEndpointOverride("missing"); ok || got != "" { t.Fatalf("missing endpoint override = %q %v", got, ok) } r := &runtimeRunner{} if report, err := r.scanContent(nil); err != nil || report.Scanned { t.Fatalf("nil scanner = %#v %v", report, err) } r.scanner = coverageScanner{report: safety.Report{Scanned: true}} if report, err := r.scanContent(map[string]any{}); err != nil || !report.Scanned { t.Fatalf("scanner = %#v %v", report, err) } r.scanner = coverageScanner{report: safety.Report{Scanned: true, Findings: []safety.Finding{{Pattern: "bad"}}}} r.enforceContentScan = true if _, err := r.scanContent(map[string]any{}); err == nil { t.Fatal("enforced finding was accepted") } t.Setenv(runtimeContentScanEnv, "false") if newRuntimeContentScanner() != nil { t.Fatal("disabled scanner was created") } t.Setenv(runtimeContentScanEnv, "true") if newRuntimeContentScanner() == nil { t.Fatal("enabled scanner missing") } logger := slog.New(slog.NewTextHandler(io.Discard, nil)) inv := executor.Invocation{CanonicalProduct: "doc", Tool: "get"} logBusinessError(nil, "reason", inv, nil, apperrors.ServerDiagnostics{}) logBusinessError(logger, "reason", inv, map[string]any{"error": "e", "errorMsg": "em", "message": "m"}, apperrors.ServerDiagnostics{TraceID: "t", ServerErrorCode: "c", TechnicalDetail: "d"}) if len(generateExecutionID()) != 16 { t.Fatal("execution ID length changed") } ResetRuntimeTokenCache() } func TestCrossPlatformCoverageDocDownloadPureCoverage(t *testing.T) { for _, inv := range []executor.Invocation{ {}, {CanonicalProduct: "DOC", Tool: docDownloadFileTool}, {CanonicalProduct: "doc", Tool: "other"}, } { _ = isDocDownloadInvocation(inv) } for _, params := range []map[string]any{ nil, {"nodeId": 1}, {"nodeId": " ", "node": " node "}, {"dentryUuid": "uuid"}, } { _ = docDownloadNodeID(params) } _ = unsupportedAXLSDownloadError() for _, content := range []map[string]any{ {"result": map[string]any{"extension": " axls "}}, {"data": map[string]any{"extension": "doc"}}, {"extension": "sheet"}, {"result": "bad"}, } { _ = documentInfoExtension(content) } if stringAtPath(map[string]any{"x": map[string]any{"y": 1}}, "x", "y") != "" || stringAtPath("bad", "x") != "" { t.Fatal("stringAtPath accepted a non-string path") } } func TestCrossPlatformCoverageSmallAppRegistryAndRootCoverage(t *testing.T) { RegisterPluginAuth("coverage-registry", &PluginAuth{Token: "token"}) t.Cleanup(func() { pluginAuthMu.Lock() delete(pluginAuthRegistry, "coverage-registry") pluginAuthMu.Unlock() }) if got, ok := LookupPluginAuth("coverage-registry"); !ok || got.Token != "token" { t.Fatalf("plugin auth = %#v %v", got, ok) } if _, ok := LookupPluginAuth("missing"); ok { t.Fatal("missing plugin auth found") } configureOAuthProviderCompatibility(authpkg.NewOAuthProvider(t.TempDir(), nil), t.TempDir()) configureLegacyAuthManagerCompatibility(authpkg.NewManager(t.TempDir(), nil)) if IsAuthRetrying(nil) || !IsAuthRetrying(context.WithValue(context.Background(), authRetryingKey, true)) { t.Fatal("auth retry context mismatch") } if IsAuthRetrying(context.WithValue(context.Background(), authRetryingKey, "bad")) { t.Fatal("invalid auth retry value accepted") } oldVersion, oldBuild, oldCommit := version, buildTime, gitCommit t.Cleanup(func() { version, buildTime, gitCommit = oldVersion, oldBuild, oldCommit }) version, buildTime, gitCommit = "dev", "unknown", "unknown" SetVersion("", "", "") if Version() != "dev" { t.Fatal("plain version mismatch") } SetVersion("1.2.3", "today", "abc") if RawVersion() != "1.2.3" || BuildTime() != "today" || GitCommit() != "abc" || !strings.Contains(Version(), "abc") { t.Fatal("version metadata mismatch") } for _, err := range []error{nil, errors.New("plain"), errors.New(`required flag(s) "email", "name" not set`), errors.New("required flag(s) not set")} { _ = rewordRequiredFlagError(err) _ = isUnknownCommandError(err) } if !isUnknownCommandError(errors.New("unknown command x")) { t.Fatal("unknown command not recognized") } if resolveVerbosity(nil) != apperrors.VerbosityNormal { t.Fatal("nil verbosity mismatch") } cmd := &cobra.Command{Use: "cmd"} cmd.Flags().Bool("debug", false, "") cmd.Flags().Bool("verbose", false, "") _ = cmd.Flags().Set("verbose", "true") if resolveVerbosity(cmd) != apperrors.VerbosityVerbose { t.Fatal("verbose level mismatch") } _ = cmd.Flags().Set("debug", "true") if resolveVerbosity(cmd) != apperrors.VerbosityDebug { t.Fatal("debug level mismatch") } if wantsJSONErrors(nil) || commandRequestsJSONErrors(nil) { t.Fatal("nil command requested JSON") } jsonCmd := &cobra.Command{Use: "root"} jsonCmd.PersistentFlags().String("format", "table", "") child := &cobra.Command{Use: "child"} jsonCmd.AddCommand(child) _ = jsonCmd.PersistentFlags().Set("format", "json") if !wantsJSONErrors(child) || !commandRequestsJSONErrors(jsonCmd) { t.Fatal("JSON format was not recognized") } jsonBool := &cobra.Command{Use: "root"} jsonBool.Flags().Bool("json", false, "") _ = jsonBool.Flags().Set("json", "true") if !commandRequestsJSONErrors(jsonBool) { t.Fatal("JSON boolean was not recognized") } var human, raw bytes.Buffer if err := printExecutionError(nil, &human, &human, errors.New("plain")); err != nil { t.Fatal(err) } rawErr := coverageRawStderr("raw line") if err := printExecutionError(nil, &raw, &raw, rawErr); err != nil || !strings.Contains(raw.String(), "raw line") { t.Fatalf("raw stderr = %q %v", raw.String(), err) } if err := printExecutionError(jsonBool, io.Discard, io.Discard, errors.New("json")); err != nil { t.Fatal(err) } _ = MCPIdentityHeaders() if noCredentialsError() == nil { t.Fatal("standalone credentials error missing") } oldEdition := edition.Get() defer edition.Override(oldEdition) edition.Override(&edition.Hooks{IsEmbedded: true}) if !strings.Contains(noCredentialsError().Error(), "认证") { t.Fatal("embedded credentials error mismatch") } } func TestCrossPlatformCoverageDirectRuntimeCoverage(t *testing.T) { oldEdition := edition.Get() t.Cleanup(func() { edition.Override(oldEdition); SetDynamicServers(nil) }) for _, tc := range []struct { raw string region authpkg.LoginRegion want string }{ {raw: "%", want: "%"}, {raw: "https://dingtalk.io/path", want: "https://dingtalk.com/path"}, {raw: "https://mcp.dingtalk.com:8443/path", region: authpkg.LoginRegionInternational, want: "https://mcp.dingtalk.io:8443/path"}, } { if got := mcpBaseURLForLoginRegion(tc.raw, tc.region); got != tc.want { t.Fatalf("mcpBaseURLForLoginRegion(%q, %q) = %q, want %q", tc.raw, tc.region, got, tc.want) } } if hasDirectRuntimeEndpointOverride("") { t.Fatal("blank product unexpectedly has an endpoint override") } t.Setenv("DINGTALK_COVERAGE_PRODUCT_MCP_URL", "https://override.test") if !hasDirectRuntimeEndpointOverride("coverage-product") { t.Fatal("configured product endpoint override was not detected") } if got := activeDingTalkGatewayEndpointWithBase("https://mcp-gw.dingtalk.com/server/contact", "%"); got != "https://mcp-gw.dingtalk.com/server/contact" { t.Fatalf("invalid gateway base rewrote endpoint to %q", got) } server := mcptypes.ServerDescriptor{ Endpoint: "https://one.test", CLI: mcptypes.CLIOverlay{ ID: "one", Command: "cmd", Aliases: []string{"alias", " "}, Tools: []mcptypes.CLITool{{Name: "tool"}, {Name: " "}}, ToolOverrides: map[string]mcptypes.CLIToolOverride{ "override": {}, "skip": {ServerOverride: "other"}, " ": {}, }, }, } SetDynamicServers([]mcptypes.ServerDescriptor{{CLI: mcptypes.CLIOverlay{Skip: true}}, server, {CLI: mcptypes.CLIOverlay{ID: "empty"}}}) if got, ok := directRuntimeEndpoint("unknown", "tool"); !ok || got != "https://one.test" { t.Fatalf("tool endpoint = %q %v", got, ok) } if got, ok := directRuntimeEndpoint("unknown", "override"); !ok || got != "https://one.test" { t.Fatalf("tool override endpoint = %q %v", got, ok) } if _, ok := directRuntimeEndpoint("unknown", "skip"); ok { t.Fatal("server-override tool endpoint resolved") } for _, id := range []string{"one", "cmd", "alias"} { if got, ok := directRuntimeEndpoint(id, ""); !ok || got != "https://one.test" { t.Errorf("direct endpoint %q = %q %v", id, got, ok) } } if normalizeDirectRuntimeProductID("alias") != "one" || normalizeDirectRuntimeProductID("tb") != "teambition" || normalizeDirectRuntimeProductID("plain") != "plain" { t.Fatal("direct runtime alias mismatch") } if ids := DirectRuntimeProductIDs(); !ids["one"] || !ids[defaultPATProductID] || !ids[devappProductID] { t.Fatalf("direct runtime IDs = %#v", ids) } if !shouldUseDirectRuntime(executor.Invocation{Kind: "helper_invocation"}) || !shouldUseDirectRuntime(executor.Invocation{Kind: "compat_invocation"}) || shouldUseDirectRuntime(executor.Invocation{}) { t.Fatal("direct runtime kind mismatch") } edition.Override(&edition.Hooks{ StaticServers: func() []edition.ServerInfo { return []edition.ServerInfo{{ID: "", Endpoint: ""}, {ID: "static", Endpoint: " https://static.test "}} }, SupplementServers: func() []edition.ServerInfo { return []edition.ServerInfo{{ID: "supplement", Endpoint: "https://supp.test", Prefixes: []string{"prefix"}}} }, }) for id, want := range map[string]string{"static": "https://static.test", "supplement": "https://supp.test", "prefix": "https://supp.test"} { if got, ok := editionServerEndpoint(id); !ok || got != want { t.Errorf("edition endpoint %q = %q %v", id, got, ok) } } if _, ok := editionServerEndpoint(""); ok { t.Fatal("blank edition server resolved") } if _, ok := endpointFromEditionServers("x", nil); ok { t.Fatal("nil edition server function resolved") } SetDynamicServers(nil) AppendDynamicServer(mcptypes.ServerDescriptor{CLI: mcptypes.CLIOverlay{Skip: true}}) AppendDynamicServer(server) AppendDynamicServer(mcptypes.ServerDescriptor{Endpoint: "https://two.test", CLI: mcptypes.CLIOverlay{ID: "two", Command: "cmd", Tools: []mcptypes.CLITool{{Name: "tool2"}}}}) if got, ok := directRuntimeEndpoint("cmd", ""); !ok || got != "https://one.test" { t.Fatalf("append preserved command endpoint = %q %v", got, ok) } if got, ok := directRuntimeEndpoint("unknown", "tool2"); !ok || got != "https://two.test" { t.Fatalf("tool fallback endpoint = %q %v", got, ok) } if _, ok := directRuntimeEndpoint("missing", "missing"); ok { t.Fatal("missing direct endpoint resolved") } _ = devappMCPEndpoint() _ = defaultPATServerDescriptor() _ = defaultPATMCPEndpoint() } func TestCrossPlatformCoverageEventCommandPureCoverage(t *testing.T) { oldEdition := edition.Get() t.Cleanup(func() { edition.Override(oldEdition) }) edition.Override(&edition.Hooks{}) if editionNameOrDefault() != "open" { t.Fatal("default edition name changed") } edition.Override(&edition.Hooks{Name: "enterprise"}) if editionNameOrDefault() != "enterprise" { t.Fatal("edition name not used") } for _, tc := range []struct { value string want string err bool }{{"", "user", false}, {" USER ", "user", false}, {"app", "", true}, {"bot", "", true}, {"bad", "", true}} { got, err := normalizeEventAs(tc.value) if got != tc.want || (err != nil) != tc.err { t.Errorf("normalizeEventAs(%q) = %q, %v", tc.value, got, err) } } if firstArg(nil) != "" || firstArg([]string{"one"}) != "one" { t.Fatal("firstArg mismatch") } if got := eventTypesWithDefault([]string{"one"}); len(got) != 1 || got[0] != "one" { t.Fatalf("explicit event types = %#v", got) } _ = eventTypesWithDefault(nil) if sourceKindLabel("") != string(dwsevent.SourceKindAppStream) || sourceKindLabel(dwsevent.SourceKindPersonalStream) != string(dwsevent.SourceKindPersonalStream) { t.Fatal("source kind labels mismatch") } if got := eventWorkDir("/tmp/config", "open", "", "hash"); !strings.Contains(got, "app_stream") { t.Fatalf("default event workdir = %q", got) } if got := eventWorkDir("/tmp/config", "open", dwsevent.SourceKindPersonalStream, "hash"); !strings.Contains(got, "personal_stream") { t.Fatalf("personal event workdir = %q", got) } _ = defaultIPCEndpoint(t.TempDir(), "open", dwsevent.SourceKindAppStream, "hash") for _, opts := range []eventStreamTicketOptions{ {}, {Mode: " NORMAL ", SourceID: " source ", TicketURL: " https://ticket.test "}, {Mode: "websocket"}, } { _ = opts.enabled() _ = opts.normalizedMode() _ = opts.usesPortalNormalMode() _ = opts.spawnArgs() } if got := eventStreamTicketURL(" explicit "); got != "explicit" { t.Fatalf("explicit ticket URL = %q", got) } if got := eventStreamTicketURL(""); !strings.HasSuffix(got, "/stream/connections/ticket") { t.Fatalf("default ticket URL = %q", got) } t.Setenv("DWS_STREAM_SOURCE_ID", " env-source ") if defaultEventStreamSourceID() != "env-source" || eventStreamSourceID("") != "env-source" || eventStreamSourceID("direct") != "direct" { t.Fatal("event source ID precedence mismatch") } t.Setenv("DWS_STREAM_SOURCE_ID", "") _ = defaultEventStreamSourceID() if clientID, secret, err := resolveEventCredentials(t.TempDir(), eventStreamTicketOptions{Mode: "normal", SourceID: "source"}); err != nil || secret != "" || !strings.HasPrefix(clientID, "portal-ticket-normal:") { t.Fatalf("portal event credentials = %q %q %v", clientID, secret, err) } if got := eventStreamBusID(eventStreamTicketOptions{SourceID: "source"}); got != "portal-ticket-normal:source" { t.Fatalf("event bus ID = %q", got) } } func TestCrossPlatformCoverageEventListAndStatusCoverage(t *testing.T) { dir := t.TempDir() t.Setenv("DWS_CONFIG_DIR", dir) cmd := &cobra.Command{Use: "event"} entries, err := collectEntries(cmd, "client", false, false) if err != nil || len(entries) != 1 || entries[0].Entry.State != busctl.BusStateNotRunning || entries[0].Entry.Meta == nil { t.Fatalf("single event entry = %#v %v", entries, err) } if all, err := collectEntries(cmd, "", true, false); err != nil || len(all) != 0 { t.Fatalf("all event entries = %#v %v", all, err) } if all, err := collectEntries(cmd, "", false, true); err != nil || len(all) != 0 { t.Fatalf("all-edition event entries = %#v %v", all, err) } if got := queryAll([]busctl.BusEntry{{State: busctl.BusStateNotRunning}}); len(got) != 1 { t.Fatalf("queryAll = %#v", got) } now := time.Now().Add(-time.Minute) live := &eventtransport.StatusResp{ Bus: eventtransport.StatusBus{UptimeSecs: 60}, SourceState: eventtransport.StatusSource{State: "connected", Source: "hook", ReconnectCount: 2}, Consumers: []eventtransport.StatusConsumer{ {PID: 2, Received: 3, Dropped: 1}, {PID: 3, EventTypes: []string{"event.one"}, SubscribeID: "sub", Received: 4}, }, PerEventTypeCounters: map[string]eventtransport.Counters{"z": {Received: 1}, "a": {Dropped: 2}}, } statuses := []busctl.EntryStatus{ {Entry: busctl.BusEntry{ClientIDHash: "hash", Edition: "open", State: busctl.BusStateNotRunning, WorkDir: dir}}, {Entry: busctl.BusEntry{ClientIDHash: "hash", Edition: "open", State: busctl.BusStateOrphan, HolderPID: 1, WorkDir: dir, Meta: &eventbus.Meta{ClientID: "client", SourceID: "source", StartedAt: now}}}, {Entry: busctl.BusEntry{ClientIDHash: "hash", Edition: "open", State: busctl.BusStateRunning, HolderPID: 2, WorkDir: dir, Meta: &eventbus.Meta{ClientID: "client", StartedAt: now}}}, {Entry: busctl.BusEntry{ClientIDHash: "hash", Edition: "open", State: busctl.BusStateRunning, HolderPID: 3, WorkDir: dir}, Live: live}, } var output bytes.Buffer if err := renderStatus(&output, statuses, "text"); err != nil || !strings.Contains(output.String(), "orphan") || !strings.Contains(output.String(), "Consumers") { t.Fatalf("status text = %q %v", output.String(), err) } output.Reset() if err := renderStatus(&output, statuses, "json"); err != nil || !strings.Contains(output.String(), "not_running") { t.Fatalf("status JSON = %q %v", output.String(), err) } list := make([]listEntry, 0, len(statuses)) for _, status := range statuses { list = append(list, buildListEntry(status)) } list = append(list, listEntry{ClientIDHash: "unknown", BusState: busctl.BusStateRunning, Consumers: live.Consumers}) output.Reset() if err := renderList(&output, list, "table"); err != nil || !strings.Contains(output.String(), "catch-all") || !strings.Contains(output.String(), "unknown") { t.Fatalf("list table = %q %v", output.String(), err) } output.Reset() if err := renderList(&output, list, "json"); err != nil || !strings.Contains(output.String(), "client_id_hash") { t.Fatalf("list JSON = %q %v", output.String(), err) } } func TestCrossPlatformCoverageEventCommandValidationCoverage(t *testing.T) { execute := func(t *testing.T, cmd *cobra.Command, args ...string) error { t.Helper() cmd.SilenceErrors = true cmd.SilenceUsage = true cmd.SetOut(io.Discard) cmd.SetErr(io.Discard) cmd.SetArgs(args) return cmd.Execute() } if err := execute(t, newEventCommand()); err != nil { t.Fatal(err) } for _, args := range [][]string{{"--as", "bad"}, {"--as", "app", "--debug-raw-events"}, {"--as", "app", "--subscribe-id", "x"}, {"--as", "app", "key"}} { if err := execute(t, newEventConsumeCommand(), args...); err == nil { t.Errorf("consume args %#v unexpectedly succeeded", args) } } for _, maker := range []func() *cobra.Command{newEventListCommand, newEventStatusCommand, newEventStopCommand} { if err := execute(t, maker(), "--as", "bad"); err == nil { t.Fatal("invalid event identity succeeded") } } flagCmd := &cobra.Command{Use: "flags"} flagCmd.Flags().String("one", "", "") flagCmd.Flags().String("two", "", "") hideEventInternalFlags(flagCmd, "one", "missing") if err := rejectChangedFlags(flagCmd, "user", "one", "missing"); err != nil { t.Fatalf("unchanged flags rejected: %v", err) } _ = flagCmd.Flags().Set("one", "value") if err := rejectChangedFlags(flagCmd, "user", "one", "missing"); err == nil { t.Fatal("changed app flag accepted") } if err := rejectPersonalEventUnsupportedFlags(flagCmd, "one", "missing"); err == nil { t.Fatal("changed personal flag accepted") } } func TestCrossPlatformCoverageVersionCacheCompletionCoverage(t *testing.T) { oldVersion, oldBuild, oldCommit := version, buildTime, gitCommit t.Cleanup(func() { version, buildTime, gitCommit = oldVersion, oldBuild, oldCommit }) version, buildTime, gitCommit = "1.0", "unknown", "unknown" for _, args := range [][]string{nil, {"--format", "json"}} { cmd := newVersionCommand() cmd.Flags().String("format", "table", "") var output bytes.Buffer cmd.SetOut(&output) cmd.SetArgs(args) if err := cmd.Execute(); err != nil || output.Len() == 0 { t.Fatalf("version %#v = %q %v", args, output.String(), err) } } version, buildTime, gitCommit = "1.0", "today", "abc" cmd := newVersionCommand() cmd.SetOut(io.Discard) if err := cmd.Execute(); err != nil { t.Fatal(err) } root := &cobra.Command{Use: "dws"} root.PersistentFlags().String("format", "json", "") for _, format := range []string{"json", "pretty", "table"} { _ = root.PersistentFlags().Set("format", format) child := &cobra.Command{Use: "child"} root.AddCommand(child) child.SetOut(io.Discard) if err := printCacheCompatNotice(child, "dws cache status"); err != nil { t.Fatalf("cache %s: %v", format, err) } root.RemoveCommand(child) } cache := newCacheCommand() cache.SetOut(io.Discard) cache.SetArgs([]string{"status"}) if err := cache.Execute(); err != nil { t.Fatal(err) } completionRoot := &cobra.Command{Use: "dws"} completionRoot.AddCommand(&cobra.Command{Use: "child"}) for _, shell := range []string{"bash", "zsh", "fish"} { completion := newCompletionCommand(completionRoot) completion.SetOut(io.Discard) completion.SetArgs([]string{shell}) if err := completion.Execute(); err != nil { t.Fatalf("completion %s: %v", shell, err) } } } func TestCrossPlatformCoverageRuntimeRunnerRoutingCoverage(t *testing.T) { inv := executor.Invocation{CanonicalProduct: "product", Tool: "tool", Params: map[string]any{"x": 1}} fallback := &coverageRunner{result: executor.Result{Response: map[string]any{"fallback": true}}} r := &runtimeRunner{fallback: fallback} if got, err := r.runSingle(context.Background(), inv, false); err != nil || got.Response["fallback"] != true { t.Fatalf("fallback runner = %#v %v", got, err) } r.transport = transport.NewClient(nil) r.globalFlags = &GlobalFlags{Mock: true} if got, err := r.runSingle(context.Background(), inv, false); err != nil || got.Response["content"] == nil { t.Fatalf("mock route = %#v %v", got, err) } t.Setenv("DINGTALK_PRODUCT_MCP_URL", "https://override.test") if got, err := r.runSingle(context.Background(), inv, false); err != nil || got.Response["endpoint"] != "https://override.test" { t.Fatalf("mock override route = %#v %v", got, err) } t.Setenv("DINGTALK_PRODUCT_MCP_URL", "") // Dry-run is an execution barrier enforced by Run before endpoint // resolution, so a dry-run invocation returns a local preview and never // reaches handleCatalogMiss or the fallback runner. r.globalFlags = &GlobalFlags{DryRun: true} if got, err := r.Run(context.Background(), inv); err != nil || got.Response["dry_run"] != true || got.Response["fallback"] != nil { t.Fatalf("dry endpoint miss = %#v %v", got, err) } r.globalFlags = &GlobalFlags{} if _, err := r.runSingle(context.Background(), inv, false); err == nil || !strings.Contains(err.Error(), "no dynamic endpoint registered for product or tool") { t.Fatalf("endpoint miss = %v", err) } devInv := inv devInv.CanonicalProduct = devappProductID if _, err := r.handleCatalogMiss(context.Background(), devInv, "missing"); err == nil { t.Fatal("devapp catalog miss succeeded") } SetDynamicServers([]mcptypes.ServerDescriptor{{Endpoint: "https://direct.test", CLI: mcptypes.CLIOverlay{ID: "product", Tools: []mcptypes.CLITool{{Name: "tool"}}}}}) t.Cleanup(func() { SetDynamicServers(nil) }) directInv := inv directInv.Kind = "helper_invocation" directInv.DryRun = true if got, err := r.runSingle(context.Background(), directInv, false); err != nil || got.Response["dry_run"] != true || got.Response["endpoint"] != "https://direct.test" { t.Fatalf("direct runtime route = %#v %v", got, err) } SetDynamicServers(nil) // With no dynamic endpoint registered, a dry-run helper invocation is still // stopped by the Run barrier before endpoint resolution. r.globalFlags = &GlobalFlags{DryRun: true} if got, err := r.Run(context.Background(), directInv); err != nil || got.Response["dry_run"] != true || got.Response["fallback"] != nil { t.Fatalf("direct runtime dry miss = %#v %v", got, err) } for _, result := range []executor.Result{{}, {Response: map[string]any{"content": "value"}}, {Response: map[string]any{"value": 1}}} { _ = multiProfileResultPayload(result) } for _, err := range []error{errors.New("plain"), apperrors.NewAPI("api", apperrors.WithReason("reason"), apperrors.WithOperation("op"))} { payload := multiProfileErrorPayload(err) if payload["message"] == "" { t.Fatalf("multi-profile error payload = %#v", payload) } } } func TestCrossPlatformCoverageExecuteInvocationCoverage(t *testing.T) { oldEdition := edition.Get() edition.Override(&edition.Hooks{TokenProvider: func(context.Context, func() (string, error)) (string, error) { return "", nil }}) ResetRuntimeTokenCache() t.Cleanup(func() { edition.Override(oldEdition) ResetRuntimeTokenCache() }) pluginAuthMu.Lock() oldRegistry := pluginAuthRegistry pluginAuthRegistry = make(map[string]*PluginAuth) pluginAuthMu.Unlock() t.Cleanup(func() { pluginAuthMu.Lock() pluginAuthRegistry = oldRegistry pluginAuthMu.Unlock() }) var rpcResult any = map[string]any{ "content": []map[string]any{{"type": "text", "text": "ok"}}, "structuredContent": map[string]any{"value": 1}, } server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var request struct { ID int `json:"id"` } _ = json.NewDecoder(r.Body).Decode(&request) _ = json.NewEncoder(w).Encode(map[string]any{"jsonrpc": "2.0", "id": request.ID, "result": rpcResult}) })) defer server.Close() r := &runtimeRunner{ transport: transport.NewClient(server.Client()), globalFlags: &GlobalFlags{}, fallback: &coverageRunner{}, } inv := executor.Invocation{CanonicalProduct: "product", Tool: "tool", Params: map[string]any{"x": 1}} inv.DryRun = true if got, err := r.executeInvocation(context.Background(), server.URL, inv); err != nil || got.Response["dry_run"] != true { t.Fatalf("HTTP dry-run = %#v %v", got, err) } inv.Params = map[string]any{"bad": func() {}} if _, err := r.executeInvocation(context.Background(), server.URL, inv); err != nil { t.Fatalf("unmarshalable dry-run = %v", err) } inv.Params = map[string]any{"x": 1} inv.DryRun = false r.globalFlags.Mock = true if got, err := r.executeInvocation(context.Background(), server.URL, inv); err != nil || got.Response["content"] == nil { t.Fatalf("HTTP mock = %#v %v", got, err) } r.globalFlags.Mock = false if _, err := r.executeInvocation(context.Background(), server.URL, inv); err == nil || !isAuthError(err) { t.Fatalf("unauthenticated execution = %v", err) } pluginAuthMu.Lock() pluginAuthRegistry = map[string]*PluginAuth{ "product": {Token: "plugin-token", ExtraHeaders: map[string]string{"X-Test": "yes"}, TrustedDomains: []string{strings.TrimPrefix(server.URL, "http://")}}, } pluginAuthMu.Unlock() r.scanner = coverageScanner{report: safety.Report{Scanned: true}} r.includeScanReport = true if got, err := r.executeInvocation(context.Background(), server.URL, inv); err != nil || got.Response["safety"] == nil || !got.Invocation.Implemented { t.Fatalf("successful execution = %#v %v", got, err) } rpcResult = map[string]any{"structuredContent": map[string]any{"success": false, "errorMsg": "business failed"}} if _, err := r.executeInvocation(context.Background(), server.URL, inv); err == nil || !strings.Contains(err.Error(), "business failed") { t.Fatalf("business error = %v", err) } rpcResult = map[string]any{"isError": true, "content": []map[string]any{{"type": "text", "text": "tool failed"}}, "structuredContent": map[string]any{"traceId": "trace"}} if _, err := r.executeInvocation(context.Background(), server.URL, inv); err == nil || !strings.Contains(err.Error(), "tool failed") { t.Fatalf("MCP tool error = %v", err) } hookErr := errors.New("edition classification") edition.Override(&edition.Hooks{ClassifyToolResult: func(map[string]any) error { return hookErr }}) rpcResult = map[string]any{"structuredContent": map[string]any{"value": 1}} if _, err := r.executeInvocation(context.Background(), server.URL, inv); !errors.Is(err, hookErr) { t.Fatalf("edition classification = %v", err) } edition.Override(&edition.Hooks{}) r.globalFlags.Timeout = 1 server.Close() if _, err := r.executeInvocation(context.Background(), server.URL, inv); err == nil { t.Fatal("network failure succeeded") } stdioInv := inv stdioInv.DryRun = true if got, err := r.executeInvocation(context.Background(), "stdio://plugin/server", stdioInv); err != nil || got.Response["transport"] != "stdio" { t.Fatalf("stdio dry-run = %#v %v", got, err) } stdioInv.DryRun = false if _, err := r.executeStdioInvocationAtEndpoint(context.Background(), "", stdioInv); err == nil { t.Fatal("missing stdio client succeeded") } } func TestCrossPlatformCoverageAuthCommandPureCoverage(t *testing.T) { oldEdition := edition.Get() oldPrompt := authLoginManualCredentialsPrompt oldInteractive := authLoginInteractiveTerminal t.Cleanup(func() { edition.Override(oldEdition) authLoginManualCredentialsPrompt = oldPrompt authLoginInteractiveTerminal = oldInteractive }) clearCompatCache() _ = authLoginHuhTheme() for _, value := range []time.Time{time.Time{}, time.Now().Add(-time.Hour), time.Now().Add(2 * time.Hour), time.Now().Add(48 * time.Hour)} { _ = timeOrEmpty(value) _ = authLoginFormatExpiry(value) } for _, data := range []*authpkg.TokenData{ nil, {}, {ExpiresAt: time.Now().Add(time.Hour)}, {ExpiresAt: time.Now().Add(time.Hour), RefreshToken: "r", RefreshExpAt: time.Now().Add(48 * time.Hour)}, } { _ = authLoginDisplayExpiry(data) _ = authStatusAuthenticated(data) _ = authStatusUpdatedAt(data) } for _, plan := range []*pat.LoginRecommendPlan{nil, {}, {AllGranted: true}, {Scopes: []string{"one"}}} { _ = authLoginRecommendPlanSkipsInteractiveAuthorization(plan) } for _, product := range []pat.LoginRecommendProduct{ {ProductCode: "doc"}, {ProductCode: "doc", ProductName: "doc"}, {ProductCode: "doc", ProductName: "Document", Summary: strings.Repeat("说明", 30)}, } { _ = loginRecommendProductLabel(product) } for _, limit := range []int{-1, 2, 20} { _ = clipRunes("abcdef", limit) } _ = authLoginStatusLine("ok") _ = authLoginInfoLine("key", "value") _ = authLoginMutedStyle() root := &cobra.Command{Use: "root"} root.PersistentFlags().String("format", "table", "") child := &cobra.Command{Use: "child"} root.AddCommand(child) if authLoginAllowsInteractiveDefault(nil, "table") || !authLoginAllowsInteractiveDefault(child, "table") { t.Fatal("interactive auth default mismatch") } _ = root.PersistentFlags().Set("format", "json") if authLoginAllowsInteractiveDefault(child, "json") { t.Fatal("explicit JSON unexpectedly allowed interactive auth") } for _, interactive := range []bool{false, true} { for _, recommend := range []bool{false, true} { _ = authLoginShouldUsePostLoginTUIModeForTerminal(child, "table", recommend, interactive) _ = authLoginShouldShowPostLoginTUIForTerminal(child, "table", recommend, interactive) _ = authLoginShouldUseHumanAuthorizationModeForTerminal(child, "table", recommend, interactive) } } for _, err := range []error{nil, errors.New("plain"), keychain.ErrDEKMissing, keychain.NewUnavailableError("read", errors.New("locked"))} { _ = authStatusDiagnosticFromError(err) } var output bytes.Buffer valid := &authpkg.TokenData{ AccessToken: "a", RefreshToken: "r", ExpiresAt: time.Now().Add(time.Hour), RefreshExpAt: time.Now().Add(2 * time.Hour), CorpID: "corp", CorpName: "Corp", UserID: "user", UserName: "Name", } for _, tc := range []struct { authenticated bool refreshed bool data *authpkg.TokenData diagnostic *authStatusDiagnostic }{{false, false, nil, nil}, {false, false, nil, &authStatusDiagnostic{Reason: "reason", Message: "message", Hint: "hint"}}, {true, true, valid, nil}} { output.Reset() if err := writeAuthStatusJSON(&output, tc.authenticated, tc.refreshed, tc.data, tc.diagnostic); err != nil { t.Fatal(err) } } for _, tc := range []struct { data *authpkg.TokenData forced bool }{{nil, true}, {valid, true}, {valid, false}} { output.Reset() if err := writeAuthLoginJSON(&output, tc.data, tc.forced); err != nil { t.Fatal(err) } } for _, data := range [][]byte{ []byte("{"), []byte(`{"result":[]}`), []byte(`{"result":[{"orgEmployeeModel":{}}]}`), []byte(`{"result":[{"orgEmployeeModel":{"corpId":"corp","orgName":"Corp","userid":"user","name":"Name"}}]}`), } { _, _ = contactProfileIdentityFromJSON(data) } for _, result := range []*edition.ToolResult{ nil, {}, {Content: []edition.ContentBlock{{Text: " "}, {Text: `{"result":[{"orgEmployeeModel":{"corpId":"corp"}}]}`}}}, } { _, _ = contactProfileIdentityFromToolResult(result) } if firstNonEmptyString(" ", " value ") != "value" || firstNonEmptyString(" ") != "" { t.Fatal("first non-empty string mismatch") } cmd := &cobra.Command{Use: "auth"} cmd.SetErr(io.Discard) authLoginManualCredentialsPrompt = func() (string, string, error) { return "client", "secret", nil } for _, action := range []authLoginGuideAction{authLoginGuideDirectCLI, authLoginGuideConfigureAgentApp, authLoginGuideManualCredentials, "unknown"} { err := applyAuthLoginGuideAction(cmd, t.TempDir(), action) if action == "unknown" && err == nil { t.Fatal("unknown auth guide action succeeded") } } authLoginManualCredentialsPrompt = func() (string, string, error) { return "", "", errors.New("prompt") } if err := applyAuthLoginGuideAction(cmd, t.TempDir(), authLoginGuideManualCredentials); err == nil { t.Fatal("manual credential prompt error succeeded") } } func TestCrossPlatformCoverageAuthLoginTokenCommandCoverage(t *testing.T) { t.Setenv(keychain.DisableKeychainEnv, "1") t.Setenv(keychain.StorageDirEnv, t.TempDir()) oldInteractive := authLoginInteractiveTerminal authLoginInteractiveTerminal = func() bool { return false } t.Cleanup(func() { authLoginInteractiveTerminal = oldInteractive; authpkg.SetRuntimeProfile("") }) for _, tc := range []struct { format string international bool }{ {format: "table"}, {format: "json", international: true}, } { t.Run(tc.format, func(t *testing.T) { configDir := t.TempDir() t.Setenv("DWS_CONFIG_DIR", configDir) root := &cobra.Command{Use: "dws"} root.PersistentFlags().String("format", "table", "") root.PersistentFlags().Bool("yes", false, "") root.PersistentFlags().String("profile", "", "") cmd := newAuthLoginCommand(nil) root.AddCommand(cmd) var output bytes.Buffer root.SetOut(&output) root.SetErr(io.Discard) args := []string{"login", "--token", "manual-token", "--yes"} if tc.international { args = append(args, "--intl") } if tc.format == "json" { args = append(args, "--format", "json") } root.SetArgs(args) if err := root.Execute(); err != nil || output.Len() == 0 { t.Fatalf("token login = %q %v", output.String(), err) } data, err := authpkg.LoadTokenData(configDir) if err != nil { t.Fatalf("LoadTokenData error = %v", err) } wantRegion := "" if tc.international { wantRegion = string(authpkg.LoginRegionInternational) } if data.LoginRegion != wantRegion { t.Fatalf("LoginRegion = %q, want %q", data.LoginRegion, wantRegion) } if tc.international { snapshot, err := resolveAccessTokenSnapshotFromDir(context.Background(), configDir, "") if err != nil { t.Fatalf("resolveAccessTokenSnapshotFromDir error = %v", err) } gotEndpoint := activeDingTalkGatewayEndpointForLoginRegion( "https://mcp-gw.dingtalk.com/server/contact", snapshot.LoginRegion, ) if wantEndpoint := "https://mcp-gw.dingtalk.io/server/contact"; gotEndpoint != wantEndpoint { t.Fatalf("international manual-token endpoint = %q, want %q", gotEndpoint, wantEndpoint) } } }) } t.Run("international then domestic login restores domestic MCP URL", func(t *testing.T) { configDir := t.TempDir() t.Setenv("DWS_CONFIG_DIR", configDir) runLogin := func(international bool) { t.Helper() root := &cobra.Command{Use: "dws"} root.PersistentFlags().String("format", "table", "") root.PersistentFlags().Bool("yes", false, "") root.PersistentFlags().String("profile", "", "") root.AddCommand(newAuthLoginCommand(nil)) args := []string{"login", "--token", "manual-token", "--yes"} if international { args = append(args, "--intl") } root.SetArgs(args) root.SetOut(io.Discard) root.SetErr(io.Discard) if err := root.Execute(); err != nil { t.Fatalf("international=%v login error = %v", international, err) } } runLogin(true) if data, err := os.ReadFile(filepath.Join(configDir, "mcp_url")); err != nil || string(data) != authpkg.InternationalMCPBaseURL { t.Fatalf("international mcp_url = %q, %v", string(data), err) } runLogin(false) if data, err := os.ReadFile(filepath.Join(configDir, "mcp_url")); err != nil || string(data) != authpkg.DefaultMCPBaseURL { t.Fatalf("domestic mcp_url = %q, %v", string(data), err) } if _, err := os.Stat(filepath.Join(configDir, config.ManagedMCPURLRegionFileName)); !os.IsNotExist(err) { t.Fatalf("managed MCP region marker remains: %v", err) } const customURL = "https://private-mcp.example.com" if err := os.WriteFile(filepath.Join(configDir, "mcp_url"), []byte(customURL), config.FilePerm); err != nil { t.Fatal(err) } runLogin(true) if data, err := os.ReadFile(filepath.Join(configDir, "mcp_url")); err != nil || string(data) != customURL { t.Fatalf("explicit mcp_url after international login = %q, %v; want preserved custom URL", string(data), err) } if _, err := os.Stat(filepath.Join(configDir, config.ManagedMCPURLRegionFileName)); !os.IsNotExist(err) { t.Fatalf("explicit mcp_url acquired a managed marker: %v", err) } }) for _, hidden := range []bool{false, true} { old := edition.Get() edition.Override(&edition.Hooks{HideAuthLogin: hidden}) cmd := buildAuthCommand(nil) found, _, err := cmd.Find([]string{"login"}) hasLogin := err == nil && found != nil && found.Name() == "login" if hasLogin == hidden { t.Fatalf("HideAuthLogin=%v command tree mismatch", hidden) } edition.Override(old) } } func TestCrossPlatformCoveragePluginCommandLifecycleCoverage(t *testing.T) { home := t.TempDir() work := t.TempDir() t.Setenv("HOME", home) oldWD, err := os.Getwd() if err != nil { t.Fatal(err) } if err := os.Chdir(work); err != nil { t.Fatal(err) } t.Cleanup(func() { _ = os.Chdir(oldWD) }) run := func(t *testing.T, cmd *cobra.Command, args ...string) (string, error) { t.Helper() var output bytes.Buffer cmd.SetOut(&output) cmd.SetErr(io.Discard) cmd.SilenceErrors = true cmd.SilenceUsage = true cmd.SetArgs(args) err := cmd.Execute() return output.String(), err } if _, err := run(t, newPluginCreateCommand(), "Bad_Name"); err == nil { t.Fatal("invalid plugin scaffold succeeded") } if output, err := run(t, newPluginCreateCommand(), "demo-plugin", "--description", "Demo"); err != nil || !strings.Contains(output, "Created plugin") { t.Fatalf("plugin create = %q %v", output, err) } if _, err := run(t, newPluginCreateCommand(), "demo-plugin"); err == nil { t.Fatal("duplicate plugin scaffold succeeded") } if _, err := run(t, newPluginValidateCommand(), "missing"); err == nil { t.Fatal("missing plugin validation succeeded") } if output, err := run(t, newPluginValidateCommand(), "demo-plugin"); err != nil || !strings.Contains(output, "Valid") { t.Fatalf("plugin validate = %q %v", output, err) } createdManifest, err := plugin.ParseManifest(filepath.Join("demo-plugin", "plugin.json")) if err != nil { t.Fatal(err) } createdManifest.Build = nil manifestData, err := json.Marshal(createdManifest) if err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join("demo-plugin", "plugin.json"), manifestData, 0o600); err != nil { t.Fatal(err) } if _, err := run(t, newPluginInstallCommand()); err == nil { t.Fatal("source-less plugin install succeeded") } if _, err := run(t, newPluginInstallCommand(), "--dir", "missing"); err == nil { t.Fatal("missing plugin install succeeded") } if output, err := run(t, newPluginInstallCommand(), "--dir", "demo-plugin"); err != nil || !strings.Contains(output, "Installed") { t.Fatalf("plugin install = %q %v", output, err) } if output, err := run(t, newPluginListCommand()); err != nil || !strings.Contains(output, "demo-plugin") { t.Fatalf("plugin list = %q %v", output, err) } if output, err := run(t, newPluginListCommand(), "--json"); err != nil || !strings.Contains(output, "demo-plugin") { t.Fatalf("plugin list JSON = %q %v", output, err) } if output, err := run(t, newPluginInfoCommand(), "demo-plugin"); err != nil || !strings.Contains(output, "Description") { t.Fatalf("plugin info = %q %v", output, err) } if _, err := run(t, newPluginInfoCommand(), "missing"); err == nil { t.Fatal("missing plugin info succeeded") } if _, err := run(t, newPluginEnableCommand(), "missing"); err == nil { t.Fatal("missing plugin enable succeeded") } if output, err := run(t, newPluginDisableCommand(), "demo-plugin"); err != nil || !strings.Contains(output, "disabled") { t.Fatalf("plugin disable = %q %v", output, err) } if output, err := run(t, newPluginEnableCommand(), "demo-plugin"); err != nil || !strings.Contains(output, "enabled") { t.Fatalf("plugin enable = %q %v", output, err) } if _, err := run(t, newPluginConfigSetCommand(), "missing", "KEY", "value"); err == nil { t.Fatal("missing plugin config set succeeded") } if output, err := run(t, newPluginConfigSetCommand(), "demo-plugin", "API_KEY", "abcdefghijk"); err != nil || !strings.Contains(output, "saved") { t.Fatalf("plugin config set = %q %v", output, err) } if output, err := run(t, newPluginConfigGetCommand(), "demo-plugin", "API_KEY"); err != nil || !strings.Contains(output, "abcdefghijk") { t.Fatalf("plugin config get = %q %v", output, err) } if _, err := run(t, newPluginConfigGetCommand(), "demo-plugin", "missing"); err == nil { t.Fatal("missing plugin config get succeeded") } for _, args := range [][]string{{"demo-plugin"}, {"demo-plugin", "--json"}, {"missing"}} { if _, err := run(t, newPluginConfigListCommand(), args...); err != nil { t.Fatalf("plugin config list %#v: %v", args, err) } } if _, err := run(t, newPluginConfigUnsetCommand(), "demo-plugin", "missing"); err == nil { t.Fatal("missing plugin config unset succeeded") } if output, err := run(t, newPluginConfigUnsetCommand(), "demo-plugin", "API_KEY"); err != nil || !strings.Contains(output, "removed") { t.Fatalf("plugin config unset = %q %v", output, err) } if _, err := run(t, newPluginDevCommand(), "missing"); err == nil { t.Fatal("missing dev plugin registration succeeded") } if output, err := run(t, newPluginDevCommand(), "demo-plugin"); err != nil || !strings.Contains(output, "registered") { t.Fatalf("dev plugin register = %q %v", output, err) } if output, err := run(t, newPluginDevCommand(), "demo-plugin", "--off"); err != nil || !strings.Contains(output, "unregistered") { t.Fatalf("dev plugin unregister = %q %v", output, err) } if _, err := run(t, newPluginDevCommand(), "missing", "--off"); err == nil { t.Fatal("missing dev plugin unregister succeeded") } if _, err := run(t, newPluginBuildCommand(), "missing"); err == nil { t.Fatal("missing plugin build succeeded") } if _, err := run(t, newPluginBuildCommand(), "demo-plugin"); err == nil { t.Fatal("scaffold placeholder build unexpectedly succeeded") } buildDir := filepath.Join(work, "build-plugin") if err := os.MkdirAll(buildDir, 0o755); err != nil { t.Fatal(err) } manifest := `{"name":"build-plugin","version":"0.1.0","type":"user","build":{"command":"mkdir -p bin && touch bin/server","output":"bin/server"}}` if err := os.WriteFile(filepath.Join(buildDir, "plugin.json"), []byte(manifest), 0o600); err != nil { t.Fatal(err) } if output, err := run(t, newPluginBuildCommand(), buildDir); err != nil || !strings.Contains(output, "succeeded") { t.Fatalf("plugin build = %q %v", output, err) } loader := plugin.NewLoader(RawVersion()) if got := loadDeclaredUserConfig(loader, "missing"); got != nil { t.Fatalf("missing declared config = %#v", got) } for _, value := range []string{"", "short", "abcdefghijk"} { _ = maskSensitiveValue(value) } if statusStr(true) != "enabled" || statusStr(false) != "disabled" { t.Fatal("plugin status labels mismatch") } _ = newPluginCommand() _ = newPluginConfigCommand() if output, err := run(t, newPluginRemoveCommand(), "demo-plugin", "--keep-data"); err != nil || !strings.Contains(output, "removed") { t.Fatalf("plugin remove = %q %v", output, err) } if _, err := run(t, newPluginRemoveCommand(), "missing"); err == nil { t.Fatal("missing plugin remove succeeded") } if output, err := run(t, newPluginListCommand()); err != nil || !strings.Contains(output, "No plugins") { t.Fatalf("empty plugin list = %q %v", output, err) } } func TestCrossPlatformCoverageUpgradeCommandHTTPAndDryRunCoverage(t *testing.T) { oldEdition := edition.Get() oldVersion := version t.Cleanup(func() { edition.Override(oldEdition); version = oldVersion }) t.Setenv("HOME", t.TempDir()) assetName := "dws-" + runtime.GOOS + "-" + runtime.GOARCH + ".tar.gz" if runtime.GOOS == "windows" { assetName = "dws-" + runtime.GOOS + "-" + runtime.GOARCH + ".zip" } releases := []upgradepkg.GitHubRelease{ {TagName: "v9.9.9-beta.1", Prerelease: true, PublishedAt: "2026-01-02T03:04:05Z", Body: "* abcdef1 - beta change", Assets: []upgradepkg.GitHubAsset{{Name: assetName}}}, {TagName: "v9.9.9", PublishedAt: "2026-01-01T03:04:05Z", Body: "* abcdef1 - stable change", HTMLURL: "https://release.test", Assets: []upgradepkg.GitHubAsset{{Name: assetName}, {Name: "dws-skills.zip"}}}, } server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.URL.Path, "/releases/tags/") { if len(releases) == 0 { http.NotFound(w, r) return } _ = json.NewEncoder(w).Encode(releases[len(releases)-1]) return } _ = json.NewEncoder(w).Encode(releases) })) defer server.Close() t.Setenv("DWS_UPGRADE_URL", server.URL) t.Setenv("DWS_UPGRADE_REPOSITORY", "owner/repo") cmd := &cobra.Command{Use: "upgrade"} var output bytes.Buffer cmd.SetOut(&output) for _, format := range []string{"json", "table"} { for _, track := range []upgradepkg.ReleaseTrack{upgradepkg.ReleaseTrackRelease, upgradepkg.ReleaseTrackBeta} { output.Reset() version = "1.0.0" if err := runUpgradeCheck(cmd, format, track); err != nil { t.Fatalf("upgrade check %s/%s: %v", format, track, err) } if err := runUpgradeList(cmd, format, 1, track); err != nil { t.Fatalf("upgrade list %s/%s: %v", format, track, err) } if err := runUpgradeList(cmd, format, 0, track); err != nil { t.Fatalf("upgrade list all %s/%s: %v", format, track, err) } } } version = "9.9.9" if err := runUpgradeCheck(cmd, "table", upgradepkg.ReleaseTrackRelease); err != nil { t.Fatal(err) } releases = nil if err := runUpgradeList(cmd, "table", 10, upgradepkg.ReleaseTrackRelease); err != nil { t.Fatal(err) } releases = []upgradepkg.GitHubRelease{{TagName: "v9.9.9", Assets: []upgradepkg.GitHubAsset{{Name: assetName}, {Name: "dws-skills.zip"}}}} version = "1.0.0" if err := runUpgrade(context.Background(), upgradeOptions{dryRun: true, track: upgradepkg.ReleaseTrackRelease}); err != nil { t.Fatalf("upgrade dry-run: %v", err) } if err := runUpgrade(context.Background(), upgradeOptions{dryRun: true, targetVersion: "v9.9.9", skipSkills: true}); err != nil { t.Fatalf("target upgrade dry-run: %v", err) } releases[0].Assets = nil if err := runUpgrade(context.Background(), upgradeOptions{dryRun: true, force: true}); err == nil { t.Fatal("upgrade dry-run without binary succeeded") } server.Close() if err := runUpgradeCheck(cmd, "json", upgradepkg.ReleaseTrackRelease); err == nil { t.Fatal("upgrade network failure succeeded") } if err := runUpgradeList(cmd, "json", 1, upgradepkg.ReleaseTrackRelease); err == nil { t.Fatal("upgrade list network failure succeeded") } if err := runUpgrade(context.Background(), upgradeOptions{dryRun: true}); err == nil { t.Fatal("upgrade network failure succeeded") } if err := runUpgradeRollback(true); err == nil { t.Fatal("rollback without backup succeeded") } edition.Override(&edition.Hooks{IsEmbedded: true, Name: "host"}) upgradeCmd := newUpgradeCommand() upgradeCmd.SetArgs(nil) if err := upgradeCmd.Execute(); err == nil || !strings.Contains(err.Error(), "嵌入") { t.Fatalf("embedded upgrade = %v", err) } edition.Override(&edition.Hooks{}) upgradeCmd = newUpgradeCommand() upgradeCmd.SetArgs([]string{"--beta", "--version", "v1.0.0"}) if err := upgradeCmd.Execute(); err == nil { t.Fatal("conflicting upgrade track succeeded") } } func TestCrossPlatformCoverageUpgradeRemainingPureCoverage(t *testing.T) { originalCommandOutput := upgradeCommandOutput t.Cleanup(func() { upgradeCommandOutput = originalCommandOutput }) for _, beta := range []bool{false, true} { track := upgradeTrack(beta) _ = upgradeTrackSuffix(track) _ = upgradeTrackVersionName(track) _ = upgradeHintForTrack(track) } var output bytes.Buffer writeDryRunPlan(&output, "1.0.0", "binary.tar.gz", false) writeDryRunPlan(&output, "1.0.0", "binary.tar.gz", true) if output.Len() == 0 { t.Fatal("empty upgrade dry-run plan") } root := &cobra.Command{Use: "root"} root.PersistentFlags().String("format", "table", "") child := &cobra.Command{Use: "child"} root.AddCommand(child) if resolveUpgradeFormat(child) != "table" { t.Fatal("default upgrade format mismatch") } _ = root.PersistentFlags().Set("format", " JSON ") if resolveUpgradeFormat(child) != "json" { t.Fatal("JSON upgrade format mismatch") } if err := writeJSON(io.Discard, map[string]any{"ok": true}); err != nil { t.Fatal(err) } dir := t.TempDir() archive := filepath.Join(dir, "archive.tar.gz") file, err := os.Create(archive) if err != nil { t.Fatal(err) } gz := gzip.NewWriter(file) tw := tar.NewWriter(gz) body := []byte("data") _ = tw.WriteHeader(&tar.Header{Name: "nested/file", Mode: 0o600, Size: int64(len(body))}) _, _ = tw.Write(body) _ = tw.Close() _ = gz.Close() _ = file.Close() dest := filepath.Join(dir, "dest") if runtime.GOOS == "windows" { upgradeCommandOutput = func(_ string, args ...string) ([]byte, error) { for _, arg := range args { if strings.Contains(arg, "missing") { return nil, os.ErrNotExist } } return nil, nil } } if err := extractTarGz(archive, dest); err != nil { t.Fatalf("extract tar: %v", err) } if err := extractTarGz(filepath.Join(dir, "missing"), filepath.Join(dir, "bad")); err == nil { t.Fatal("missing tar extraction succeeded") } _ = shortenHome(filepath.Join(os.Getenv("HOME"), "path")) } func TestCrossPlatformCoveragePersonalEventPureCoverage(t *testing.T) { cmd := &cobra.Command{Use: "personal"} var out bytes.Buffer cmd.SetOut(&out) for _, opts := range []personalListOptions{ {Format: "json", IncludePending: true}, {Format: "table", Category: "im", EnabledOnly: true}, } { out.Reset() if err := runPersonalEventList(cmd, opts); err != nil || out.Len() == 0 { t.Fatalf("personal list %#v = %q, %v", opts, out.String(), err) } } def, ok := personal.Lookup(personal.EventMention) if !ok { t.Fatal("mention definition missing") } if err := renderPersonalSchema(io.Discard, def, "", false); err != nil { t.Fatal(err) } if err := renderPersonalSchema(io.Discard, def, "yaml", true); err == nil { t.Fatal("unsupported schema format succeeded") } for _, key := range []string{"", "unknown", personal.EventMention, personal.EventFromUser} { if err := ensurePublicPersonalEvent(key); err != nil { t.Fatalf("public event %q: %v", key, err) } } var cfg consume.Config applyPersonalConsumeFilters(nil, personalConsumeOptions{}, "", "") applyPersonalConsumeFilters(&cfg, personalConsumeOptions{ Common: personalCommonOptions([]string{"explicit"}, "filter"), }, " sub ", personal.EventMention) if len(cfg.EventTypes) != 1 || cfg.SubscribeID != "sub" || cfg.Filter != "filter" { t.Fatalf("personal filters = %#v", cfg) } applyPersonalConsumeFilters(&cfg, personalConsumeOptions{DebugRawEvents: true}, "sub", personal.EventMention) if cfg.EventTypes != nil || cfg.SubscribeID != "" || cfg.Filter != "" { t.Fatalf("debug filters = %#v", cfg) } for _, tc := range []struct { explicit []string key string wantNil bool }{ {nil, "", true}, {nil, personal.EventMention, false}, {[]string{"x"}, personal.EventMention, false}, } { got := personalEventTypes(tc.key, tc.explicit) if (got == nil) != tc.wantNil { t.Errorf("personalEventTypes(%q, %#v) = %#v", tc.key, tc.explicit, got) } } identity := personal.Identity{CorpID: "corp", UserID: "user", ClientID: "client", SourceID: "source"} if len(redactedPersonalIdentity(identity, "hash")) != 5 || displayIdentityPart(" ") != "unknown" || displayIdentityPart(" value ") != "value" { t.Fatal("personal identity display mismatch") } if firstNonEmptyPersonalString(" ", " x ") != "x" || firstNonEmptyPersonalString(" ") != "" { t.Fatal("first nonempty mismatch") } if personalTokenSubject("access", "") != "" || !strings.HasPrefix(personalTokenSubject(" access ", " token "), "access:") { t.Fatal("personal token subject mismatch") } if displayPersonalStatusValue(" ") != "-" || displayPersonalStatusValue(" x ") != "x" { t.Fatal("personal status display mismatch") } args := personalBusSpawnArgs(identity, "custom", "https://ticket") if len(args) != 10 || len(personalBusSpawnArgs(identity, "", "")) != 6 { t.Fatalf("personal bus args = %#v", args) } if personalEventControlBaseURL(" https://control/ ", "") != "https://control" || personalEventStreamTicketURL(" https://ticket/ ", "") != "https://ticket" { t.Fatal("personal explicit URL mismatch") } if personalEventStreamSourceID(" explicit ") != "explicit" { t.Fatal("personal explicit source mismatch") } emptyDir := t.TempDir() if configuredMCPBaseURL(emptyDir) != "" { t.Fatal("missing MCP URL unexpectedly resolved") } if err := os.WriteFile(filepath.Join(emptyDir, "mcp_url"), []byte(" https://mcp.test/ \n"), 0o600); err != nil { t.Fatal(err) } if configuredMCPBaseURL(emptyDir) != "https://mcp.test/" || personalEventMCPBaseURL(emptyDir) != "https://mcp.test" { t.Fatal("configured MCP URL mismatch") } var rendered bytes.Buffer printPersonalStopResult(&rendered, []string{"sub"}, true, "stopped") printPersonalStopResult(&rendered, []string{"a", "b"}, false, "running") qs := busctl.EntryStatus{Entry: busctl.BusEntry{WorkDir: "work", State: busctl.BusStateRunning, HolderPID: 42}, Live: &eventtransport.StatusResp{}} renderPersonalStatusText(&rendered, identity, "hash", []personal.Subscription{{SubscribeID: "sub", EventKey: personal.EventMention, Status: "active"}}, qs) if !strings.Contains(rendered.String(), "SUBSCRIBE_ID") || !strings.Contains(rendered.String(), "cancelled") { t.Fatalf("personal rendering = %q", rendered.String()) } renderPersonalConsumers(io.Discard, busctl.EntryStatus{Entry: busctl.BusEntry{State: busctl.BusStateRunning}, Live: &eventtransport.StatusResp{Consumers: []eventtransport.StatusConsumer{{PID: 7}}}}) if got, err := personalStopTargets(t.TempDir(), " sub ", false); err != nil || len(got) != 1 || got[0] != "sub" { t.Fatalf("explicit stop targets = %#v, %v", got, err) } if _, err := personalStopTargets(t.TempDir(), "sub", true); err == nil { t.Fatal("conflicting stop target succeeded") } if _, err := personalStopTargets(t.TempDir(), "", false); err == nil { t.Fatal("missing stop target succeeded") } workDir := t.TempDir() for _, id := range []string{"b", "a"} { if err := personal.UpsertRunState(workDir, personal.RunState{SubscribeID: id}); err != nil { t.Fatal(err) } } if got, err := personalStopTargets(workDir, "", true); err != nil || strings.Join(got, ",") != "a,b" { t.Fatalf("all stop targets = %#v, %v", got, err) } if err := interruptPersonalConsumers("", []string{"sub"}); err != nil { t.Fatal(err) } if err := interruptPersonalConsumers("bad-endpoint", nil); err != nil { t.Fatal(err) } if err := interruptPersonalConsumers("bad-endpoint", []string{"sub"}); err != nil { t.Fatal(err) } } func personalCommonOptions(eventTypes []string, filter string) commonConsumeOptions { return commonConsumeOptions{EventTypes: eventTypes, Filter: filter} } func TestCrossPlatformCoveragePersonalSubscriptionAndSourceCoverage(t *testing.T) { identity := personal.Identity{AccessToken: "token", CorpID: "corp", UserID: "user", ClientID: "client", SourceID: "source"} var createCount, cancelCount int server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/event/sublist": _ = json.NewEncoder(w).Encode(map[string]any{"items": []map[string]any{{"subId": "existing", "eventKey": personal.EventMention, "ruleType": "at", "status": "active"}}, "total": 1}) case "/subscription/user": createCount++ _ = json.NewEncoder(w).Encode(map[string]any{"success": true, "result": []string{"created"}}) case "/subscription/cancel": cancelCount++ w.WriteHeader(http.StatusNoContent) default: http.NotFound(w, r) } })) defer server.Close() client := personal.NewClient(server.URL, identity) for _, tc := range []struct { name string opts personalConsumeOptions ok bool }{ {"lookup", personalConsumeOptions{SubscribeID: "existing"}, true}, {"create", personalConsumeOptions{EventKey: personal.EventMention, Name: "name", FilterJSON: `{"field":"content","op":"eq","value":"x"}`, QueryCSV: "a,b", TTL: time.Minute}, true}, {"missing", personalConsumeOptions{}, false}, {"sender", personalConsumeOptions{EventKey: personal.EventFromUser, UserID: "u"}, true}, {"bad-rule", personalConsumeOptions{EventKey: personal.EventMention, Rule: "other"}, false}, {"bad-filter", personalConsumeOptions{EventKey: personal.EventMention, FilterJSON: "{"}, false}, } { t.Run(tc.name, func(t *testing.T) { sub, key, rule, err := ensurePersonalSubscription(context.Background(), client, identity, tc.opts) if tc.ok && (err != nil || sub == nil || key == "" || rule == "") { t.Fatalf("subscription = %#v %q %q, %v", sub, key, rule, err) } if !tc.ok && err == nil { t.Fatal("invalid subscription succeeded") } }) } if createCount != 2 { t.Fatalf("create count = %d", createCount) } dir := t.TempDir() if _, err := newPersonalStreamSource(context.Background(), personalStreamSourceOptions{ConfigDir: dir, Identity: identity, TicketMode: "bad"}); err == nil { t.Fatal("invalid ticket mode succeeded") } if src, err := newPersonalStreamSource(context.Background(), personalStreamSourceOptions{ConfigDir: dir, Identity: identity}); err != nil || src == nil { t.Fatalf("normal source = %#v, %v", src, err) } t.Setenv(authpkg.EnvClientID, "resolved-client") t.Setenv(authpkg.EnvClientSecret, "secret") if src, err := newPersonalStreamSource(context.Background(), personalStreamSourceOptions{ConfigDir: dir, Identity: personal.Identity{AccessToken: "token", SourceID: "source"}, TicketMode: "custom", ClientIDOverride: "override"}); err != nil || src == nil { t.Fatalf("custom source = %#v, %v", src, err) } if cancelCount != 0 { t.Fatalf("unexpected cancel count = %d", cancelCount) } } func TestCrossPlatformCoveragePersonalEventCommandRuntimeCoverage(t *testing.T) { authpkg.SetRuntimeProfile("") t.Cleanup(func() { authpkg.SetRuntimeProfile("") }) configDir := setupPersonalIdentityToken(t, &authpkg.TokenData{ AccessToken: "access", RefreshToken: "refresh", ExpiresAt: time.Now().Add(time.Hour), CorpID: "corp", UserID: "user", ClientID: "client", }) t.Setenv("DWS_CONFIG_DIR", configDir) var subscribeCount, cancelCount int server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/event/sublist": _ = json.NewEncoder(w).Encode(map[string]any{"items": []map[string]any{{"subId": "sub", "eventKey": personal.EventMention, "ruleType": "at", "status": "active", "sourceId": "open"}}, "total": 1}) case "/subscription/user": subscribeCount++ _ = json.NewEncoder(w).Encode(map[string]any{"success": true, "result": []string{"created"}}) case "/subscription/cancel": cancelCount++ w.WriteHeader(http.StatusNoContent) default: http.NotFound(w, r) } })) defer server.Close() newCmd := func() (*cobra.Command, *bytes.Buffer, *bytes.Buffer) { cmd := &cobra.Command{Use: "event"} cmd.SetContext(context.Background()) out, errOut := &bytes.Buffer{}, &bytes.Buffer{} cmd.SetOut(out) cmd.SetErr(errOut) cmd.Flags().String("format", "table", "") cmd.Flags().String("output", "", "") return cmd, out, errOut } for _, format := range []string{"json", "table"} { cmd, out, _ := newCmd() if err := runPersonalEventStatus(cmd, personalStatusOptions{Format: format, ControlBaseURL: server.URL}); err != nil || out.Len() == 0 { t.Fatalf("personal status %s = %q, %v", format, out.String(), err) } } cmd, _, _ := newCmd() if err := runPersonalEventConsume(cmd, personalConsumeOptions{Common: commonConsumeOptions{DryRun: true}, EventKey: personal.EventMention, ControlBaseURL: server.URL}); err != nil { t.Fatalf("personal dry-run consume: %v", err) } cmd, _, _ = newCmd() if err := runPersonalEventConsume(cmd, personalConsumeOptions{Common: commonConsumeOptions{Foreground: true}, EventKey: personal.EventMention, ControlBaseURL: server.URL, StreamTicketMode: "invalid"}); err == nil { t.Fatal("invalid foreground consume succeeded") } if subscribeCount != 0 || cancelCount != 0 { t.Fatalf("invalid local configuration reached subscription control: subscribe=%d cancel=%d", subscribeCount, cancelCount) } if err := runPersonalEventStop(cmd, personalStopOptions{SubscribeID: "sub", All: true, ControlBaseURL: server.URL}); err == nil { t.Fatal("conflicting stop succeeded") } if err := runPersonalEventStop(cmd, personalStopOptions{ControlBaseURL: server.URL}); err == nil { t.Fatal("missing stop target succeeded") } if err := runPersonalEventStop(cmd, personalStopOptions{SubscribeID: "sub", ControlBaseURL: server.URL}); err != nil { t.Fatalf("single stop: %v", err) } } func TestCrossPlatformCoverageDoctorCommandCoverage(t *testing.T) { configDir := setupPersonalIdentityToken(t, &authpkg.TokenData{ AccessToken: "access", ExpiresAt: time.Now().Add(time.Hour), ClientID: "client", }) t.Setenv("DWS_CONFIG_DIR", configDir) home := t.TempDir() oldVersion := version oldDiagnose := doctorKeychainDiagnose oldTimingHome := timingUserHomeDir timingUserHomeDir = func() (string, error) { return home, nil } t.Cleanup(func() { version = oldVersion doctorKeychainDiagnose = oldDiagnose timingUserHomeDir = oldTimingHome }) version = "1.0.0" doctorKeychainDiagnose = func() keychain.Diagnostic { return keychain.Diagnostic{OK: true, Message: "available"} } server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.URL.Path, "/releases/latest") { _ = json.NewEncoder(w).Encode(map[string]any{"tag_name": "v1.0.0"}) return } _, _ = io.WriteString(w, "ok") })) defer server.Close() if err := os.WriteFile(filepath.Join(configDir, "mcp_url"), []byte(server.URL), 0o600); err != nil { t.Fatal(err) } t.Setenv("DWS_UPGRADE_URL", server.URL) t.Setenv("DWS_UPGRADE_REPOSITORY", "owner/repo") t.Setenv(PerfReportEnv, "auto") tc := NewTimingCollector() tc.Record("phase", 2*time.Millisecond) tc.WriteReportIfEnabled("1.0.0", "doctor-test") for _, args := range [][]string{{"--json", "--perf", "--timeout", "1"}, {"--timeout", "1"}} { cmd := newDoctorCommand() cmd.SilenceUsage = true cmd.SilenceErrors = true var out bytes.Buffer cmd.SetOut(&out) cmd.SetArgs(args) if err := cmd.Execute(); err != nil || out.Len() == 0 { t.Fatalf("doctor %#v = %q, %v", args, out.String(), err) } } for _, jsonOut := range []bool{false, true} { if got := doctorCheckPerf(io.Discard, jsonOut); got.Status != statusPass { t.Fatalf("perf check = %#v", got) } } if p, w, f := countResults([]checkResult{{Status: statusPass}, {Status: statusWarn}, {Status: statusFail}, {Status: "other"}}); p != 1 || w != 1 || f != 1 { t.Fatalf("doctor counts = %d/%d/%d", p, w, f) } for _, status := range []checkStatus{statusPass, statusWarn, statusFail, "other"} { _ = statusIcon(status) printCheckResult(io.Discard, checkResult{Status: status, Message: "message", Hint: "hint"}) } printPerfReportSummary(io.Discard, &PerfReport{Command: "cmd", Timestamp: time.Now(), Phases: []PerfPhase{{Name: "slow", DurationMs: 3}}, Slowest: "slow", TotalMs: 4, OverheadMs: 1}) doctorKeychainDiagnose = func() keychain.Diagnostic { return keychain.Diagnostic{OK: false, Message: "bad", Hint: "fix", Reason: "reason"} } if got := doctorCheckKeychain(io.Discard, false); got.Status != statusFail { t.Fatal("keychain failure not reported") } doctorKeychainDiagnose = func() keychain.Diagnostic { return keychain.Diagnostic{OK: false, Message: "bad", Reason: "reason", Detail: map[string]string{"backend": "test"}} } _ = doctorCheckKeychain(io.Discard, true) badConfig := t.TempDir() t.Setenv("DWS_CONFIG_DIR", badConfig) if err := os.WriteFile(filepath.Join(badConfig, "mcp_url"), []byte("://bad"), 0o600); err != nil { t.Fatal(err) } if got := doctorCheckNetwork(context.Background(), io.Discard, false, time.Second); got.Status != statusFail { t.Fatalf("bad network check = %#v", got) } t.Setenv("DWS_UPGRADE_URL", "http://127.0.0.1:1") if got := doctorCheckVersion(io.Discard, false, time.Millisecond); got.Status != statusFail { t.Fatalf("bad version check = %#v", got) } missingHome := t.TempDir() timingUserHomeDir = func() (string, error) { return missingHome, nil } if got := doctorCheckPerf(io.Discard, false); got.Status != statusWarn { t.Fatalf("missing perf check = %#v", got) } } func coverageSkillZip(t *testing.T) []byte { t.Helper() var buf bytes.Buffer zw := zip.NewWriter(&buf) dir, err := zw.Create("demo/") if err != nil { t.Fatal(err) } _, _ = dir.Write(nil) f, err := zw.Create("demo/SKILL.md") if err != nil { t.Fatal(err) } _, _ = io.WriteString(f, "# demo") if err := zw.Close(); err != nil { t.Fatal(err) } return buf.Bytes() } func TestCrossPlatformCoverageSkillCommandHTTPCoverage(t *testing.T) { configDir := setupPersonalIdentityToken(t, &authpkg.TokenData{AccessToken: "access", ExpiresAt: time.Now().Add(time.Hour), ClientID: "client"}) t.Setenv("DWS_CONFIG_DIR", configDir) home := t.TempDir() oldSkillHome := skillUserHomeDir skillUserHomeDir = func() (string, error) { return home, nil } t.Cleanup(func() { skillUserHomeDir = oldSkillHome }) zipData := coverageSkillZip(t) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/cli/find-skills": switch r.URL.Query().Get("keyword") { case "empty": _ = json.NewEncoder(w).Encode(findSkillsResponse{Success: true}) case "business": _ = json.NewEncoder(w).Encode(findSkillsResponse{ErrorCode: "E"}) case "malformed": _, _ = io.WriteString(w, "{") default: _ = json.NewEncoder(w).Encode(findSkillsResponse{Success: true, Result: []CliSkillDTO{{SkillID: "id", Name: "name", Desc: "desc"}}}) } case "/cli/install", "/package.zip": w.Header().Set("Content-Disposition", `attachment; filename="demo.zip"`) _, _ = w.Write(zipData) case "/info": _ = json.NewEncoder(w).Encode(downloadSkillResponse{Success: true, Result: &downloadSkillResult{DownloadURL: serverURL(r) + "/package.zip", FileName: "demo.zip"}}) case "/unauthorized": w.WriteHeader(http.StatusUnauthorized) case "/bad-request": w.WriteHeader(http.StatusBadRequest) case "/not-found": w.WriteHeader(http.StatusNotFound) case "/failure": w.WriteHeader(http.StatusInternalServerError) default: http.NotFound(w, r) } })) defer server.Close() t.Setenv("DWS_SKILL_API_HOST", server.URL+"/") oldEndpoint := skillDownloadEndpoint skillDownloadEndpoint = server.URL + "/info" t.Cleanup(func() { skillDownloadEndpoint = oldEndpoint }) run := func(cmd *cobra.Command, args ...string) (string, error) { var out bytes.Buffer cmd.SilenceUsage = true cmd.SilenceErrors = true cmd.SetOut(&out) cmd.SetArgs(args) err := cmd.Execute() return out.String(), err } for _, args := range [][]string{{"--query", "demo", "--source", "DingtalkMarket"}, {"--query", "empty"}} { if output, err := run(newSkillSearchCommand(), args...); err != nil || output == "" { t.Fatalf("skill search %#v = %q, %v", args, output, err) } } for _, query := range []string{"business", "malformed"} { if _, err := run(newSkillSearchCommand(), "--query", query); err == nil { t.Fatalf("skill search %s succeeded", query) } } if output, err := run(newSkillGetCommand(), "--skill-id", "id"); err != nil || !strings.Contains(output, "dws-skill-") { t.Fatalf("skill get = %q, %v", output, err) } if output, err := run(newSkillInstallCommand(), "id", "agents"); err != nil || !strings.Contains(output, "技能安装成功") { t.Fatalf("skill install = %q, %v", output, err) } if _, err := os.Stat(filepath.Join(home, ".agents", "skills", "demo", "SKILL.md")); err != nil { t.Fatal(err) } for _, cmd := range []*cobra.Command{buildSkillCommand(), newSkillFindHintCommand(), newSkillAddHintCommand()} { _, _ = run(cmd) } if _, err := run(newSkillInstallCommand(), "", "agents"); err == nil { t.Fatal("empty skill ID succeeded") } if _, err := run(newSkillInstallCommand(), "id", "missing"); err == nil { t.Fatal("bad target succeeded") } } func serverURL(r *http.Request) string { return "http://" + r.Host } func TestCrossPlatformCoverageSkillHelpersAndErrorsCoverage(t *testing.T) { oldEmbedded := edition.Get() t.Cleanup(func() { edition.Override(oldEmbedded) }) for _, embedded := range []bool{false, true} { edition.Override(&edition.Hooks{IsEmbedded: embedded}) _ = skillAuthError() } t.Setenv("DWS_SKILL_API_HOST", " https://host.test/ ") if skillAPIHost() != "https://host.test" || supportedTargets() == "" || longestAgentTargetName() == 0 || formatAgentSkillPathsForHelp() == "" { t.Fatal("skill helper mismatch") } if _, err := resolveSkillTargetPath(""); err == nil { t.Fatal("empty target succeeded") } if _, err := resolveSkillTargetPath("missing"); err == nil { t.Fatal("unknown target succeeded") } if got, err := resolveSkillTargetPath("."); err != nil || got == "" { t.Fatalf("dot target = %q, %v", got, err) } if filenameFromDisposition(`attachment; filename="named.zip"`) != "named.zip" || filenameFromDisposition("bad;") != "skill.zip" || filenameFromDisposition("") != "skill.zip" { t.Fatal("content disposition parsing mismatch") } for _, status := range []int{http.StatusUnauthorized, http.StatusBadRequest, http.StatusNotFound, http.StatusInternalServerError} { _ = parseLegacySkillAPIError(&http.Response{StatusCode: status}) } cleanupTempFile("") missing := filepath.Join(t.TempDir(), "missing.zip") cleanupTempFile(missing) if err := extractSkillZip(missing, t.TempDir()); err == nil { t.Fatal("missing skill zip succeeded") } zipPath := filepath.Join(t.TempDir(), "bad.zip") var buf bytes.Buffer zw := zip.NewWriter(&buf) f, _ := zw.Create("../escape") _, _ = io.WriteString(f, "bad") _ = zw.Close() if err := os.WriteFile(zipPath, buf.Bytes(), 0o600); err != nil { t.Fatal(err) } if err := extractSkillZip(zipPath, t.TempDir()); err == nil { t.Fatal("zip slip succeeded") } server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/ok": _, _ = io.WriteString(w, "data") case "/unauthorized": w.WriteHeader(http.StatusUnauthorized) case "/bad": w.WriteHeader(http.StatusBadRequest) case "/missing": w.WriteHeader(http.StatusNotFound) default: w.WriteHeader(http.StatusInternalServerError) } })) defer server.Close() if p, err := downloadSkillFile(context.Background(), server.URL+"/ok", ""); err != nil { t.Fatal(err) } else { cleanupTempFile(p) } if _, err := downloadSkillFile(context.Background(), server.URL+"/bad", "x"); err == nil { t.Fatal("bad download succeeded") } for _, path := range []string{"unauthorized", "bad", "missing", "failure"} { if _, err := downloadSkillToTmpDir(context.Background(), server.URL+"/"+path, "token"); err == nil { t.Fatalf("legacy download %s succeeded", path) } } if _, err := downloadSkillToTmpDir(context.Background(), "://bad", "token"); err == nil { t.Fatal("invalid legacy URL succeeded") } if _, err := downloadSkillFile(context.Background(), "://bad", "x"); err == nil { t.Fatal("invalid download URL succeeded") } } func TestCrossPlatformCoverageProfileCommandAndModelCoverage(t *testing.T) { configDir := t.TempDir() t.Setenv("DWS_CONFIG_DIR", configDir) now := time.Now().UTC() cfg := &authpkg.ProfilesConfig{ Version: 1, PrimaryProfile: "corp-a", CurrentProfile: "corp-a", PreviousProfile: "corp-b", Profiles: []authpkg.Profile{ {Name: "alpha", CorpID: "corp-a", CorpName: "Alpha 组织", UserID: "u-a", UserName: "Alice", LastLoginAt: now.Add(-time.Hour).Format(time.RFC3339)}, {Name: "beta", CorpID: "corp-b", UserID: "u-b", UpdatedAt: now.Format(time.RFC3339), Status: authpkg.ProfileStatusExpired}, {Name: "gamma", CorpID: "corp-c", LastUsedAt: "bad-time"}, }, } if err := authpkg.SaveProfiles(configDir, cfg); err != nil { t.Fatal(err) } for _, profile := range cfg.Profiles { data := &authpkg.TokenData{ AccessToken: "token-" + profile.CorpID, CorpID: profile.CorpID, UserID: profile.UserID, } var err error if profile.UserID == "" { err = authpkg.SaveTokenDataKeychainForCorpID(profile.CorpID, data) } else { err = authpkg.SaveTokenDataKeychainForIdentity(profile.CorpID, profile.UserID, data) } if err != nil { t.Fatal(err) } } t.Cleanup(func() { for _, profile := range cfg.Profiles { if profile.UserID == "" { _ = authpkg.DeleteTokenDataKeychainForCorpID(profile.CorpID) } else { _ = authpkg.DeleteTokenDataKeychainForIdentity(profile.CorpID, profile.UserID) } } }) oldInteractive := profileSwitchInteractiveTerminal oldSelector := profileSwitchSelector t.Cleanup(func() { profileSwitchInteractiveTerminal = oldInteractive profileSwitchSelector = oldSelector }) profileSwitchInteractiveTerminal = func() bool { return false } run := func(cmd *cobra.Command, args ...string) (string, error) { root := &cobra.Command{Use: "root"} root.PersistentFlags().String("format", "table", "") root.AddCommand(cmd) var out bytes.Buffer root.SetOut(&out) root.SetErr(io.Discard) root.SilenceUsage = true root.SilenceErrors = true root.SetArgs(append([]string{cmd.Name()}, args...)) err := root.Execute() return out.String(), err } if output, err := run(newProfileListCommand()); err != nil || !strings.Contains(output, "corp-a") { t.Fatalf("profile list = %q, %v", output, err) } rootJSON := &cobra.Command{Use: "root"} rootJSON.PersistentFlags().String("format", "json", "") listJSON := newProfileListCommand() rootJSON.AddCommand(listJSON) var out bytes.Buffer rootJSON.SetOut(&out) rootJSON.SetArgs([]string{"list"}) if err := rootJSON.Execute(); err != nil || !strings.Contains(out.String(), `"profiles"`) { t.Fatalf("profile JSON = %q, %v", out.String(), err) } if output, err := run(newProfileSwitchCommand(), "corp-b"); err != nil || !strings.Contains(output, "corp-b") { t.Fatalf("profile switch = %q, %v", output, err) } if output, err := run(newProfileUseCommand(), "-"); err != nil || !strings.Contains(output, "corp-a") { t.Fatalf("profile previous = %q, %v", output, err) } if _, err := run(newProfileSwitchCommand()); err == nil { t.Fatal("noninteractive empty profile switch succeeded") } profileSwitchSelector = func(*cobra.Command, string) (string, error) { return "corp-c", nil } if output, err := run(newProfileSwitchCommand()); err != nil || !strings.Contains(output, "corp-c") { t.Fatalf("profile injected selection = %q, %v", output, err) } if _, err := run(newProfileSwitchCommand(), "missing"); err == nil { t.Fatal("missing profile switch succeeded") } _, _ = run(newProfileCommand()) selectorCmd := newProfileSwitchCommand() if got, err := profileSwitchSelectorFromCommand(selectorCmd, nil); err != nil || got != "" { t.Fatalf("empty selector = %q, %v", got, err) } _ = selectorCmd.Flags().Set("corpId", "corp-a") if got, err := profileSwitchSelectorFromCommand(selectorCmd, []string{"corp-a"}); err != nil || got != "corp-a" { t.Fatalf("matching selectors = %q, %v", got, err) } _ = selectorCmd.Flags().Set("name", "beta") if _, err := profileSwitchSelectorFromCommand(selectorCmd, []string{"corp-a"}); err == nil { t.Fatal("conflicting profile selectors succeeded") } if _, changed := changedStringFlag(nil, "x"); changed { t.Fatal("nil command flag changed") } model := newProfileSwitchTUIModel(cfg, "corp-a") _ = model.Init() if model.View() == "" || model.tableView() == "" || model.selectedCorpID() == "" { t.Fatal("profile model rendering empty") } for _, key := range []tea.KeyType{tea.KeyDown, tea.KeyUp, tea.KeyEnter, tea.KeyEsc} { updated, _ := model.Update(tea.KeyMsg{Type: key}) model = updated.(profileSwitchTUIModel) } updated, _ := model.Update(struct{}{}) model = updated.(profileSwitchTUIModel) for range 10 { model.profiles = append(model.profiles, authpkg.Profile{CorpID: fmt.Sprintf("corp-%d", len(model.profiles))}) } model.selected = 99 model.offset = -1 model.ensureSelectedVisible() model.selected = -1 model.ensureSelectedVisible() emptyModel := newProfileSwitchTUIModel(nil, "") emptyModel.ensureSelectedVisible() if emptyModel.selectedCorpID() != "" { t.Fatal("empty profile model selected a profile") } _ = profileSwitchSortedProfiles(cfg.Profiles) for _, p := range cfg.Profiles { _ = profileSwitchOptionLabel(p, cfg) _, _ = profileSwitchProfileCells(p, cfg) } _ = profileSwitchProfileIndex(cfg.Profiles, "missing", cfg) _ = profileSwitchBorder("a", "b", "c") _ = profileSwitchTableLine("org", "status") _ = profileSwitchStyledTableLine("org", "status", profileSwitchNormalRowStyle()) _ = profileSwitchTableSeparator() _ = profileSwitchTableCell("organization", 4) _ = padProfileDisplayCell("long", 1) _ = profileSwitchCellWidth(3) _ = profileSwitchSelectedRowStyle() _ = profileSwitchHeaderStyle() _ = profileSwitchBorderStyle() _ = profileSwitchTitleStyle() _ = profileSwitchMutedStyle() writeProfileListTable(io.Discard, "", nil) writeProfileListTable(io.Discard, "", cfg) if err := writeProfileListJSON(io.Discard, "", cfg); err != nil || writeProfileUseJSON(io.Discard, nil, nil) != nil || writeProfileUseJSON(io.Discard, &cfg.Profiles[0], cfg) != nil { t.Fatal("profile JSON write failed") } _ = profileUseMessage(nil) for _, p := range []authpkg.Profile{{CorpID: "corp"}, {Name: "name", CorpID: "corp"}, {CorpName: "org", CorpID: "corp"}} { _ = profileUseMessage(&p) _ = profileOrgName(p) } _ = profileViews("", nil) _ = profileViews("", cfg) for _, limit := range []int{0, 2, 5, 40} { _ = clipProfileCell("abcdefgh", limit) _ = clipProfileDisplayCell("中文abcdefgh", limit) } } func TestCrossPlatformCoverageSkillSetupRuntimeCoverage(t *testing.T) { home := t.TempDir() oldSetupHome := skillSetupUserHomeDir skillSetupUserHomeDir = func() (string, error) { return home, nil } t.Cleanup(func() { skillSetupUserHomeDir = oldSetupHome }) mono := filepath.Join(t.TempDir(), "mono") if err := os.MkdirAll(filepath.Join(mono, "references"), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(mono, "SKILL.md"), []byte("# mono"), 0o600); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(mono, "references", "ref.md"), []byte("ref"), 0o600); err != nil { t.Fatal(err) } _ = os.Symlink(filepath.Join(mono, "SKILL.md"), filepath.Join(mono, "linked.md")) multi := filepath.Join(t.TempDir(), "multi") for _, name := range []string{"dingtalk-shared", "dingtalk-a", "dingtalk-b"} { dir := filepath.Join(multi, name) if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "SKILL.md"), []byte("# "+name), 0o600); err != nil { t.Fatal(err) } } run := func(args ...string) (string, string, error) { cmd := newSkillSetupCommand() cmd.Flags().Bool("dry-run", false, "") var out, errOut bytes.Buffer cmd.SetOut(&out) cmd.SetErr(&errOut) cmd.SetArgs(args) err := cmd.Execute() return out.String(), errOut.String(), err } if output, _, err := run("--mode", "mono", "--source", mono, "--target", "agents", "--yes"); err != nil || !strings.Contains(output, "installed=1") { t.Fatalf("mono setup = %q, %v", output, err) } if _, err := os.Stat(filepath.Join(home, ".agents", "skills", "dws", "SKILL.md")); err != nil { t.Fatal(err) } if output, _, err := run("--mode", "multi", "--source", multi, "--target", "agents", "--yes", "--skill", "a"); err != nil || !strings.Contains(output, "installed=2") { t.Fatalf("multi setup = %q, %v", output, err) } if _, err := os.Stat(filepath.Join(home, ".agents", "skills", "dingtalk-shared", "SKILL.md")); err != nil { t.Fatal(err) } state, readable, err := skillstate.Read(home) if err != nil || !readable || len(state.OfficialSkills) != 3 || len(state.UpdatedSkills) != 2 { t.Fatalf("setup state = %#v, readable=%v, err=%v", state, readable, err) } if output, _, err := run("--mode", "multi", "--source", multi, "--target", "agents", "--yes", "--dry-run", "--exclude", "b"); err != nil || !strings.Contains(output, "DRY-RUN") { t.Fatalf("multi dry run = %q, %v", output, err) } for _, args := range [][]string{ {"--mode", "bad", "--source", mono, "--yes"}, {"--mode", "mono", "--source", mono, "--yes", "--skill", "a"}, {"--mode", "mono", "--source", filepath.Join(home, "missing"), "--yes"}, {"--mode", "mono", "--source", mono, "--target", "missing", "--yes"}, {"--mode", "multi", "--source", multi, "--yes", "--skill", "missing"}, {"--mode", "multi", "--source", multi, "--yes", "--skill", "a", "--exclude", "b"}, } { if _, _, err := run(args...); err == nil { t.Fatalf("invalid setup %#v succeeded", args) } } if _, _, err := run("--mode", "mono", "--source", mono, "--target", "agents", "--yes", "--dry-run"); err != nil { t.Fatalf("mono setup: %v", err) } if output, _, err := run("--source", multi, "--target", "agents", "--yes", "--dry-run"); err != nil || !strings.Contains(output, "mode=multi") { t.Fatalf("default mode should be multi: %q, %v", output, err) } } func TestCrossPlatformCoverageSkillSetupPureCoverage(t *testing.T) { all := []string{"dingtalk-a", "dingtalk-b", "dingtalk-shared"} for _, tc := range []struct { include []string exclude []string ok bool }{ {nil, nil, true}, {[]string{"a", "DINGTALK-A", ""}, nil, true}, {nil, []string{"b"}, true}, {[]string{"missing"}, nil, false}, {nil, []string{"missing"}, false}, {[]string{"a"}, []string{"b"}, false}, {nil, all, false}, } { if _, err := filterMultiSkillNames(all, tc.include, tc.exclude); (err == nil) != tc.ok { t.Errorf("filter %#v/%#v = %v", tc.include, tc.exclude, err) } } for _, selected := range [][]string{nil, {"dingtalk-shared"}, {"dingtalk-a"}} { _ = ensureMandatorySharedSkill(selected, all) } _ = ensureMandatorySharedSkill([]string{"dingtalk-a"}, []string{"dingtalk-a"}) for _, name := range []string{"", " A ", "DINGTALK-B"} { _ = normalizeMultiSkillName(name) } if _, err := listMultiSkillNames(filepath.Join(t.TempDir(), "missing")); err == nil { t.Fatal("missing multi source succeeded") } if mode, err := resolveSkillSetupMode("", true, io.Discard); err != nil || mode != skillSetupModeMulti { t.Fatalf("default setup mode = %q, %v", mode, err) } if _, err := resolveSkillSetupMode("bad", true, io.Discard); err == nil { t.Fatal("bad setup mode succeeded") } for _, mode := range []string{skillSetupModeMono, skillSetupModeMulti} { if got, err := resolveSkillSetupMode(mode, false, io.Discard); err != nil || got != mode { t.Fatalf("setup mode %s = %q, %v", mode, got, err) } } root := t.TempDir() mono := filepath.Join(root, "skills", "mono") multi := filepath.Join(root, "skills", "multi", "dingtalk-a") if err := os.MkdirAll(multi, 0o755); err != nil { t.Fatal(err) } if err := os.MkdirAll(mono, 0o755); err != nil { t.Fatal(err) } _ = os.WriteFile(filepath.Join(mono, "SKILL.md"), []byte("x"), 0o600) _ = os.WriteFile(filepath.Join(multi, "SKILL.md"), []byte("x"), 0o600) for _, tc := range []struct{ explicit, mode string }{{root, skillSetupModeMono}, {root, skillSetupModeMulti}, {mono, skillSetupModeMono}} { if got, err := resolveSkillSetupSource(tc.explicit, tc.mode); err != nil || got == "" { t.Fatalf("skill source %#v = %q, %v", tc, got, err) } } if _, err := resolveSkillSetupSource(filepath.Join(root, "missing"), skillSetupModeMono); err == nil { t.Fatal("bad explicit source succeeded") } _ = skillSourceCandidates(root, skillSetupModeMono) for _, tc := range []struct{ path, mode string }{{"", skillSetupModeMono}, {mono, skillSetupModeMono}, {filepath.Dir(multi), skillSetupModeMulti}, {root, "bad"}} { _ = isSkillSourceRoot(tc.path, tc.mode) } setTestHome(t, t.TempDir()) for _, tc := range []struct{ target, mode string }{{"agents", skillSetupModeMono}, {"agents", skillSetupModeMulti}, {"all", skillSetupModeMono}, {"missing", skillSetupModeMono}} { _, _ = resolveSkillSetupTargets(tc.target, tc.mode) } _ = agentHomeForMode("base", skillSetupModeMono) _ = agentHomeForMode("base", skillSetupModeMulti) _ = detectExistingAgentHomes(t.TempDir(), skillSetupModeMono) for _, mode := range []string{skillSetupModeMono, skillSetupModeMulti, "bad"} { _, _ = confirmSkillSetup(io.Discard, mode, root, []string{root}, all, false) _, _ = mutualExclusionVictims(root, mode) } if isCharDevice(nil) || isInteractiveTerminal() { t.Fatal("test process unexpectedly interactive") } monoDest := filepath.Join(t.TempDir(), "agent", "dws") _ = os.MkdirAll(filepath.Join(filepath.Dir(monoDest), "dingtalk-old"), 0o755) _, _ = mutualExclusionVictims(monoDest, skillSetupModeMono) multiDest := filepath.Join(t.TempDir(), "agent") _ = os.MkdirAll(filepath.Join(multiDest, "dws"), 0o755) _, _ = mutualExclusionVictims(multiDest, skillSetupModeMulti) cleanupMutualExclusion(monoDest, skillSetupModeMono, io.Discard, io.Discard) cleanupMutualExclusion(multiDest, skillSetupModeMulti, io.Discard, io.Discard) badParent := filepath.Join(t.TempDir(), "file") _ = os.WriteFile(badParent, []byte("x"), 0o600) _, _, _ = installSkillToHomes(root, []string{filepath.Join(badParent, "dest")}, io.Discard, io.Discard) _, _, _ = installMultiSkillToHomes(root, []string{"missing"}, []string{filepath.Join(badParent, "dest")}, io.Discard, io.Discard, true) if err := copyDir(filepath.Join(root, "missing"), t.TempDir()); err == nil { t.Fatal("copy missing directory succeeded") } if err := copyFileContent(filepath.Join(root, "missing"), filepath.Join(t.TempDir(), "out"), 0o600); err == nil { t.Fatal("copy missing file succeeded") } for _, mode := range []string{skillSetupModeMono, skillSetupModeMulti} { src, cleanup, err := materializeEmbeddedSkillSource(mode) if err != nil || src == "" { t.Fatalf("embedded %s = %q, %v", mode, src, err) } cleanup() } if _, _, err := materializeEmbeddedSkillSource("missing"); err == nil { t.Fatal("missing embedded setup source succeeded") } if src, cleanup, err := resolveSkillSetupSourceOrEmbedded(root, skillSetupModeMono); err != nil || src == "" { t.Fatalf("explicit embedded wrapper = %q, %v", src, err) } else { cleanup() } }