返回 DeepSeek-Reasonix
process_test.go
根目录 / internal / extension / sidecar / process_test.go
1 package sidecar
2
3 import (
4 "context"
5 "errors"
6 "runtime"
7 "strings"
8 "testing"
9 "time"
10
11 "reasonix/internal/pluginpkg"
12 )
13
14 func TestResolveRuntimeCommandContract(t *testing.T) {
15 root := t.TempDir()
16 shellPath := "/bin/sh"
17 if runtime.GOOS == "windows" {
18 shellPath = `C:\Windows\System32\cmd.exe`
19 }
20 cases := []struct {
21 name string
22 command string
23 wantErr string
24 }{
25 {name: "empty", command: " ", wantErr: "empty"},
26 {name: "relative bare name", command: "node", wantErr: "not an absolute path"},
27 {name: "relative path", command: "bin/sidecar", wantErr: "not an absolute path"},
28 {name: "shell indirection", command: shellPath, wantErr: "exec form"},
29 }
30 for _, tc := range cases {
31 t.Run(tc.name, func(t *testing.T) {
32 _, err := resolveRuntimeCommand(&pluginpkg.RuntimeSpec{Command: tc.command}, root)
33 if err == nil || !strings.Contains(err.Error(), tc.wantErr) {
34 t.Fatalf("resolveRuntimeCommand(%q) error = %v, want one containing %q", tc.command, err, tc.wantErr)
35 }
36 })
37 }
38 }
39
40 func TestResolveRuntimeCommandExpandsPluginRoot(t *testing.T) {
41 root := t.TempDir()
42 got, err := resolveRuntimeCommand(&pluginpkg.RuntimeSpec{Command: "${REASONIX_PLUGIN_ROOT}/bin/sidecar"}, root)
43 if err != nil {
44 t.Fatalf("resolveRuntimeCommand: %v", err)
45 }
46 if !strings.HasPrefix(got, root) || !strings.HasSuffix(got, "sidecar") {
47 t.Fatalf("expanded command = %q, want inside %q", got, root)
48 }
49 }
50
51 // TestStartupFailureRedactsAndBoundsStderr floods stderr and fails the
52 // handshake: the surfaced diagnostics must carry a bounded, redacted tail.
53 func TestStartupFailureRedactsAndBoundsStderr(t *testing.T) {
54 pkg, installed := fakeSidecarPackage(t, "fakeplugin", func(rt *pluginpkg.RuntimeSpec) {
55 rt.Env[fakeEnvMode] = "stderr_flood"
56 rt.Env[fakeEnvInitResult] = `{"protocolVersion":"2","name":"fake","version":"1","stateSchemaVersion":0}`
57 })
58 _, err := StartClient(context.Background(), ClientOptions{Package: pkg, Installed: installed, Session: testSessionContext()})
59 if err == nil {
60 t.Fatal("StartClient succeeded with protocol major 2")
61 }
62 var failure *startupFailure
63 if !errors.As(err, &failure) {
64 t.Fatalf("error %T is not a startupFailure", err)
65 }
66 if failure.Stage != "handshake" {
67 t.Fatalf("stage = %q, want handshake", failure.Stage)
68 }
69 if len(failure.Stderr) > stderrTailBytes {
70 t.Fatalf("stderr tail is %d bytes, want <= %d", len(failure.Stderr), stderrTailBytes)
71 }
72 if strings.Contains(failure.Stderr, "sk-abcdef1234567890SECRETKEY") {
73 t.Fatalf("stderr tail leaks the credential: %q", failure.Stderr)
74 }
75 if !strings.Contains(failure.Stderr, "***") {
76 t.Fatalf("stderr tail shows no redaction mask: %q", failure.Stderr)
77 }
78 }
79
80 func TestStartupFailureRedactsCauseWithoutLosingIdentity(t *testing.T) {
81 const secret = "sk-abcdef1234567890SECRETKEY"
82 cause := errors.New("initialize rejected api_key=" + secret)
83 err := newStartupFailure("handshake", time.Now(), "", cause)
84 if strings.Contains(err.Error(), secret) {
85 t.Fatalf("startup failure leaked its cause: %q", err)
86 }
87 if !strings.Contains(err.Error(), "****") {
88 t.Fatalf("startup failure contains no redaction marker: %q", err)
89 }
90 if !errors.Is(err, cause) {
91 t.Fatal("startup failure no longer unwraps to its original cause")
92 }
93 }
94
95 // TestRuntimeEnvFullTrustContract pins the documented contract: the sidecar
96 // inherits the unfiltered environment, manifest env layers over it, and the
97 // plugin identity variables are always set.
98 func TestRuntimeEnvFullTrustContract(t *testing.T) {
99 t.Setenv("REASONIX_TEST_INHERITED_MARKER", "present")
100 root := t.TempDir()
101 rt := &pluginpkg.RuntimeSpec{Command: "/bin/sidecar", Env: map[string]string{"MANIFEST_KEY": "manifest-value"}}
102 pkg := pluginpkg.Package{Root: root, Manifest: pluginpkg.Manifest{Name: "p", Version: "2.0.0", Runtime: rt}}
103 installed := pluginpkg.InstalledPlugin{Name: "p", Version: "1.0.0"}
104
105 env := runtimeEnv(rt, pkg, installed)
106 values := map[string]string{}
107 for _, entry := range env {
108 key, value, _ := strings.Cut(entry, "=")
109 values[key] = value
110 }
111 if values["REASONIX_TEST_INHERITED_MARKER"] != "present" {
112 t.Fatal("inherited environment was filtered")
113 }
114 if values["MANIFEST_KEY"] != "manifest-value" {
115 t.Fatal("manifest env missing")
116 }
117 if values[envPluginRoot] != root || values[envPluginName] != "p" || values[envPluginVersion] != "1.0.0" {
118 t.Fatalf("plugin identity env = %q %q %q", values[envPluginRoot], values[envPluginName], values[envPluginVersion])
119 }
120 }
121
121 lines GO