// Copyright 2026 Alibaba Group // Licensed under the Apache License, Version 2.0 (the "License"); package helpers import ( "bytes" "context" "testing" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/executor" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/output" "github.com/spf13/cobra" ) func TestDevAppSharedResultMapperClassifiesServiceOutcomes(t *testing.T) { t.Run("normalized success", func(t *testing.T) { result := DevAppCommandResultFromPayload("", map[string]any{ "content": map[string]any{"success": true, "result": map[string]any{"id": "a"}}, }, false) env, err := output.EnvelopeFromResult(result) if err != nil { t.Fatal(err) } data, _ := env.Data.(map[string]any) if env.Outcome != output.OutcomeSuccess || data["id"] != "a" { t.Fatalf("success envelope=%+v data=%#v", env, env.Data) } }) t.Run("pending approval", func(t *testing.T) { result := DevAppCommandResultFromPayload("", map[string]any{ "versionStatus": "AUDIT", "versionId": "v1", "unifiedAppId": "u1", }, false) env, err := output.EnvelopeFromResult(result) if err != nil { t.Fatal(err) } if env.Outcome != output.OutcomePending || env.Meta == nil || env.Meta.Operation == nil || env.Meta.Operation.NextCommand == "" { t.Fatalf("pending envelope=%+v", env) } }) t.Run("approval selection uses tool normalization", func(t *testing.T) { result := DevAppCommandResultFromPayload(devAppVersionPublishTool, map[string]any{ "approvalMode": "SELECT_APPROVER", "unifiedAppId": "u1", "versionId": "v1", "approvalCandidates": []any{ map[string]any{"userId": "user-1", "name": "Alice"}, }, }, false) env, err := output.EnvelopeFromResult(result) if err != nil { t.Fatal(err) } if env.Outcome != output.OutcomePending || env.Meta == nil || env.Meta.Operation == nil { t.Fatalf("approval envelope=%+v", env) } if env.Meta.Operation.State != "waiting_for_approver_selection" || env.Meta.Operation.NextCommand == "" { t.Fatalf("approval operation=%+v", env.Meta.Operation) } }) t.Run("pagination", func(t *testing.T) { result := DevAppCommandResultFromPayload("", map[string]any{ "items": []any{}, "hasMore": true, "nextCursor": "next", }, false) env, err := output.EnvelopeFromResult(result) if err != nil { t.Fatal(err) } if env.Meta == nil || env.Meta.Pagination == nil || env.Meta.Pagination.EndpointExhausted || env.Meta.Pagination.NextToken != "next" { t.Fatalf("pagination envelope=%+v", env) } }) for name, payload := range map[string]map[string]any{ "missing cursor": {"items": []any{}, "hasMore": true}, "stale cursor": {"items": []any{}, "hasMore": false, "nextCursor": "stale"}, "wrong flag type": {"items": []any{}, "hasMore": "false"}, } { t.Run("pagination inconsistent "+name, func(t *testing.T) { result := DevAppCommandResultFromPayload("", payload, false) env, err := output.EnvelopeFromResult(result) if err != nil { t.Fatal(err) } if env.Outcome != output.OutcomeFailure || env.Error == nil || env.Error.Subtype != "pagination_inconsistent" { t.Fatalf("pagination envelope=%+v", env) } }) } t.Run("partial", func(t *testing.T) { started := true result := DevAppCommandResultFromPayload("", map[string]any{ "multiProfile": true, "profiles": []any{ map[string]any{"selector": "a", "ok": true, "result": map[string]any{"id": "r1"}}, map[string]any{"selector": "b", "ok": false, "error": map[string]any{ "category": "network", "reason": "timeout", "message": "bad", "stage": "response", "execution_started": started, }}, }, }, false) env, err := output.EnvelopeFromResult(result) if err != nil { t.Fatal(err) } if env.Outcome != output.OutcomePartialFailure || result.ExitCode() != 7 { t.Fatalf("partial outcome=%s rc=%d", env.Outcome, result.ExitCode()) } partial, ok := env.Data.(*output.PartialData) if !ok || len(partial.Failed) != 1 || partial.Failed[0].Error == nil || partial.Failed[0].Error.Type != "api" || partial.Failed[0].Error.Subtype != "timeout" || partial.Failed[0].Error.ExecutionStarted == nil || !*partial.Failed[0].Error.ExecutionStarted { t.Fatalf("partial error projection=%#v", env.Data) } }) t.Run("dry run is completed preview", func(t *testing.T) { result := DevAppCommandResultFromPayload("", map[string]any{"tool": "x"}, true) env, err := output.EnvelopeFromResult(result) if err != nil { t.Fatal(err) } if env.Outcome != output.OutcomeSuccess || !env.DryRun { t.Fatalf("dry-run envelope=%+v", env) } }) } func TestFrameworkDevAppMapperBoundaryMatrix(t *testing.T) { for name, payload := range map[string]any{ "scalar": "value", "bad cursor": map[string]any{"nextCursor": 1}, "cursor only": map[string]any{"nextCursor": "next"}, "empty cursor": map[string]any{"nextCursor": ""}, "exhausted": map[string]any{"hasMore": false}, } { t.Run(name, func(t *testing.T) { _, _ = devAppPaginationMeta(payload) }) } if devAppMultiProfileResult(map[string]any{}) != nil || devAppMultiProfileResult(map[string]any{"multiProfile": true}) != nil { t.Fatal("non-profile payload classified") } allSuccess := devAppMultiProfileResult(map[string]any{"multiProfile": true, "profiles": []any{map[string]any{"ok": true}}}) if allSuccess != nil { t.Fatalf("all-success multi profile=%v", allSuccess) } allFailed := devAppMultiProfileResult(map[string]any{"multiProfile": true, "profiles": []any{ map[string]any{"ok": false, "error": map[string]any{ "type": "authorization", "message": "denied", "retryable": true, "execution_started": false, "actions": []any{"retry", 1}, "details": map[string]any{"scope": "x"}, "requestId": "req", "traceId": "trace", }}, "malformed", }}) allFailedEnv, err := output.EnvelopeFromResult(allFailed) if err != nil || allFailedEnv.Outcome != output.OutcomeFailure || allFailedEnv.Error == nil || allFailedEnv.Error.Details == nil { t.Fatalf("all-failed=%+v err=%v", allFailedEnv, err) } mixed := devAppMultiProfileResult(map[string]any{"multiProfile": true, "profiles": []any{ map[string]any{"selector": "ok", "ok": true}, map[string]any{"selector": "bad", "ok": false, "error": map[string]any{"category": "forbidden", "actions": []string{"fix"}, "code": "E"}}, 123, }}) if env, err := output.EnvelopeFromResult(mixed); err != nil || env.Outcome != output.OutcomePartialFailure { t.Fatalf("mixed=%+v err=%v", env, err) } duplicate := devAppMultiProfileResult(map[string]any{"multiProfile": true, "profiles": []any{ map[string]any{"selector": "same", "ok": true}, map[string]any{"selector": "same", "ok": false}, }}) if env, err := output.EnvelopeFromResult(duplicate); err != nil || env.Outcome != output.OutcomeFailure || env.Error.Type != "internal" { t.Fatalf("duplicate=%+v err=%v", env, err) } for _, payload := range []map[string]any{ {"success": false, "message": "no", "errorCode": "E"}, {"status": "FAILED", "code": 1}, {"status": "OK"}, } { _ = devAppFailureResult(payload) } for _, raw := range []string{"validation", "forbidden", "timeout"} { _ = devAppWireErrorType(raw) } for _, content := range []map[string]any{ {}, {"mustAskUser": true}, {"status": "WAITING", "taskId": "task"}, {"status": "WAITING", "requestId": "req"}, {"status": "WAITING", "taskId": "task", "nextSteps": []map[string]any{{"command": "dws next"}}}, {"status": "WAITING", "requestId": "req", "nextSteps": []any{map[string]any{"dryRunCommand": "dws preview"}, "bad"}}, {"approvalSubmitted": true, "unifiedAppId": "app", "versionId": "v"}, } { result := devAppPendingResult(content) if result != nil { _, _ = output.EnvelopeFromResult(result) } } if got := devAppRecoveryCommand(map[string]any{"taskId": "t"}); !bytes.Contains([]byte(got), []byte("robot result")) { t.Fatalf("task recovery=%q", got) } if got := devAppRecoveryCommand(map[string]any{}); got != "" { t.Fatalf("empty recovery=%q", got) } if got := devAppEnvelopeData(executor.Result{}); got == nil { t.Fatal("unimplemented result should be preserved") } for _, result := range []executor.Result{ {Invocation: executor.Invocation{Implemented: true, Kind: "helper_invocation", DryRun: true}, Response: map[string]any{"content": map[string]any{"preview": true}}}, {Invocation: executor.Invocation{Implemented: true, Kind: "helper_invocation"}, Response: map[string]any{"content": map[string]any{"items": []any{}, "hasMore": false}}}, } { cmd := &cobra.Command{Use: "legacy-envelope"} cmd.SetContext(context.Background()) cmd.SetOut(&bytes.Buffer{}) if err := writeDevAppEnvelope(cmd, result); err != nil { t.Fatal(err) } } legacy := &cobra.Command{Use: "legacy"} legacy.SetContext(context.Background()) legacy.SetOut(&bytes.Buffer{}) if err := writeDevRolloutResult(legacy, output.Success(nil), output.NewSuccessEnvelope(nil), output.FormatJSON); err != nil { t.Fatal(err) } dual := &cobra.Command{Use: "dual"} dual.SetContext(context.Background()) dual.SetOut(&bytes.Buffer{}) output.SetCommandRollout(dual, output.RolloutDualValidate) if err := writeDevRolloutResult(dual, output.Pending(nil, nil), output.NewSuccessEnvelope(nil), output.FormatJSON); err == nil { t.Fatal("dual validate accepted malformed shadow result") } ctx, _ := output.WithResultStore(context.Background()) active := &cobra.Command{Use: "active"} active.SetContext(ctx) output.SetCommandRollout(active, output.RolloutUnifiedActive) if err := writeDevRolloutResult(active, output.Success(nil), nil, output.FormatJSON); err != nil { t.Fatal(err) } }