Files
dingtalk-workspace-cli/internal/app/coverage_edges_test.go

2286 lines
92 KiB
Go

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()
}
}