返回 DeepSeek-Reasonix
plugin_test.go
根目录 / internal / cli / plugin_test.go
1 package cli
2
3 import (
4 "encoding/json"
5 "path/filepath"
6 "strings"
7 "testing"
8
9 "reasonix/internal/pluginpkg"
10 )
11
12 func TestPluginInstallReturnsFailureExitForFailedJSON(t *testing.T) {
13 home := t.TempDir()
14 t.Setenv("REASONIX_HOME", home)
15
16 source := filepath.Join(t.TempDir(), "superpowers")
17 writePluginTestFile(t, filepath.Join(source, pluginpkg.CodexManifest), `{
18 "name": "superpowers",
19 "version": "6.1.1",
20 "description": "Planning workflows",
21 "skills": "skills"
22 }`)
23 writePluginTestFile(t, filepath.Join(source, "skills", "using-superpowers", "SKILL.md"), "---\ndescription: Use Superpowers\n---\nUse Superpowers.")
24
25 firstOut := captureStdout(t, func() {
26 if rc := pluginCommand([]string{"install", source, "--yes"}); rc != 0 {
27 t.Fatalf("first plugin install rc = %d, want 0", rc)
28 }
29 })
30 var first struct {
31 OK bool `json:"ok"`
32 }
33 if err := json.Unmarshal([]byte(firstOut), &first); err != nil {
34 t.Fatalf("first output is not JSON: %v\n%s", err, firstOut)
35 }
36 if !first.OK {
37 t.Fatalf("first output ok = false:\n%s", firstOut)
38 }
39
40 secondOut := captureStdout(t, func() {
41 if rc := pluginCommand([]string{"install", source, "--yes"}); rc != 1 {
42 t.Fatalf("duplicate plugin install rc = %d, want 1", rc)
43 }
44 })
45 var second struct {
46 OK bool `json:"ok"`
47 Status string `json:"status"`
48 }
49 if err := json.Unmarshal([]byte(secondOut), &second); err != nil {
50 t.Fatalf("second output is not JSON: %v\n%s", err, secondOut)
51 }
52 if second.OK || second.Status != "failed" {
53 t.Fatalf("duplicate output ok/status = %v/%q, want false/failed\n%s", second.OK, second.Status, secondOut)
54 }
55 }
56
57 func TestInstallSourceOutputPreservesPlanAndRedactsSecrets(t *testing.T) {
58 out, err := redactInstallSourceJSON(`{"ok":false,"status":"failed","planId":"approved-plan","actions":[{"name":"demo","risk":"high","error":"Authorization: Bearer secret-credential","env":{"API_KEY":"private-value"}}],"next":["preview again"]}`)
59 if err != nil {
60 t.Fatal(err)
61 }
62 if strings.Contains(out, "secret-credential") || strings.Contains(out, "private-value") {
63 t.Fatal("secret leaked")
64 }
65 var result struct {
66 PlanID string `json:"planId"`
67 Actions []json.RawMessage `json:"actions"`
68 Next []string `json:"next"`
69 }
70 if err := json.Unmarshal([]byte(out), &result); err != nil {
71 t.Fatal(err)
72 }
73 if result.PlanID != "approved-plan" || len(result.Actions) != 1 || len(result.Next) != 1 {
74 t.Fatalf("lost plan metadata: %s", out)
75 }
76 }
77
77 lines GO