-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathcodex_plugin_dir_test.go
More file actions
194 lines (177 loc) · 8.64 KB
/
Copy pathcodex_plugin_dir_test.go
File metadata and controls
194 lines (177 loc) · 8.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// ABOUTME: `spacedock codex --plugin-dir` / `install --host codex --plugin-dir`
// ABOUTME: tests — no-flag-passthrough regression guard, edge-channel resolve, advisory.
package cli
import (
"bytes"
"context"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
// codexPluginDirHost inspects the local marketplace's plugin symlink at Install
// time — the moment the real codex host reads it — to confirm the checkout is wired
// into the marketplace the install consumes. The channel-NAME property (that an edge
// build names the marketplace `spacedock-edge` so it resolves) is proven
// behaviorally against real codex in AC-2, not by re-reading the JSON here.
type codexPluginDirHost struct {
fakeHost
installedSymlinkDest string
inspectErr error
}
func (h *codexPluginDirHost) Install(host, source, branch string) (string, error) {
if dest, err := os.Readlink(filepath.Join(source, "plugins", "spacedock")); err == nil {
h.installedSymlinkDest = dest
} else {
h.inspectErr = err
}
return h.fakeHost.Install(host, source, branch)
}
// TestRunCodexPluginDirInstallsThenLaunchesWithoutTheFlag is AC-1: `spacedock codex
// --plugin-dir <checkout>` installs the local checkout in one command (no
// operator-authored marketplace) and forwards NO --plugin-dir token into the real
// codex argv. The no-flag assertion is the direct regression guard against Spike A's
// reproduced baseline (today's argv DOES carry it and the real codex rejects it): a
// re-introduction of the passthrough bug flips this assertion.
func TestRunCodexPluginDirInstallsThenLaunchesWithoutTheFlag(t *testing.T) {
t.Setenv("CODEX_HOME", t.TempDir()) // isolate the persistent local marketplace
checkout := t.TempDir()
host := &codexPluginDirHost{fakeHost: fakeHost{manifest: compatibleManifest(t)}}
var stdout, stderr bytes.Buffer
code := runCodex(context.Background(), []string{"--plugin-dir", checkout}, t.TempDir(), host, lookFound, &stdout, &stderr)
if code != 0 {
t.Fatalf("exit = %d, want 0 (stderr=%q)", code, stderr.String())
}
if host.inspectErr != nil {
t.Fatalf("marketplace inspection at Install time failed: %v", host.inspectErr)
}
// (a) Install called exactly once, for codex, with a plugins/spacedock symlink
// resolving to the checkout. (The marketplace-name-is-the-channel property is
// AC-2's behavioral proof against real codex.)
if len(host.installCmds) != 3 || host.installCmds[0] != "codex" || host.installCmds[2] != devBranch {
t.Fatalf("install seam = %v, want exactly one {codex, <marketplace>, %q} call", host.installCmds, devBranch)
}
if host.installedSymlinkDest != checkout {
t.Fatalf("plugins/spacedock symlink = %q, want the checkout %q", host.installedSymlinkDest, checkout)
}
// (b) No --plugin-dir anywhere in the launched codex argv.
if host.launchedArg == nil {
t.Fatalf("launch seam not reached after --plugin-dir install")
}
for _, a := range host.launchedArg {
if a == "--plugin-dir" || a == checkout {
t.Fatalf("launch argv forwards a --plugin-dir token: %v", host.launchedArg)
}
}
}
// TestCodexPluginDirAdvisoryPresenceAndAbsence is AC-3: every --plugin-dir codex
// install prints the version-masquerade advisory; a plain (non---plugin-dir) launch
// prints none. The pair (not a presence-only check) means the test cannot pass by
// printing the advisory unconditionally. The present subtest also guards that the
// advisory carries its meaning-bearing clause (not necessarily its current HEAD) and
// leaks no internal branch identifier.
func TestCodexPluginDirAdvisoryPresenceAndAbsence(t *testing.T) {
const advisory = "version-masquerade advisory"
t.Run("present on --plugin-dir", func(t *testing.T) {
t.Setenv("CODEX_HOME", t.TempDir()) // isolate the persistent local marketplace
checkout := t.TempDir()
fake := &fakeHost{manifest: compatibleManifest(t)}
var stdout, stderr bytes.Buffer
code := runCodex(context.Background(), []string{"--plugin-dir", checkout}, t.TempDir(), fake, lookFound, &stdout, &stderr)
if code != 0 {
t.Fatalf("exit = %d, want 0 (stderr=%q)", code, stderr.String())
}
if !strings.Contains(stderr.String(), advisory) {
t.Fatalf("stderr missing the version-masquerade advisory: %q", stderr.String())
}
if !strings.Contains(stderr.String(), "not necessarily its current HEAD") {
t.Fatalf("advisory lost its meaning-bearing clause: %q", stderr.String())
}
if strings.Contains(stderr.String(), "next-post-release-preversion-bump") {
t.Fatalf("advisory leaks the internal branch identifier: %q", stderr.String())
}
})
t.Run("absent on plain launch", func(t *testing.T) {
fake := &fakeHost{manifest: compatibleManifest(t)}
var stdout, stderr bytes.Buffer
code := runCodex(context.Background(), nil, t.TempDir(), fake, lookFound, &stdout, &stderr)
if code != 0 {
t.Fatalf("exit = %d, want 0 (stderr=%q)", code, stderr.String())
}
if strings.Contains(stderr.String(), advisory) {
t.Fatalf("plain codex launch printed the --plugin-dir advisory: %q", stderr.String())
}
})
}
// TestInstallCodexPluginDirInstallsViaSharedHelper covers the persistent primitive:
// `spacedock install --host codex --plugin-dir <checkout>` routes through the same
// shared helper (install seam called once for codex + the advisory printed), rather
// than the pre-change rejection.
func TestInstallCodexPluginDirInstallsViaSharedHelper(t *testing.T) {
t.Setenv("CODEX_HOME", t.TempDir()) // isolate the persistent local marketplace
checkout := t.TempDir()
host := &codexPluginDirHost{fakeHost: fakeHost{manifest: compatibleManifest(t)}}
var stdout, stderr bytes.Buffer
code := runInitWithPi(context.Background(), []string{"--host", "codex", "--plugin-dir", checkout}, host, &fakePiRuntimeOps{}, nil, &stdout, &stderr)
if code != 0 {
t.Fatalf("exit = %d, want 0 (stderr=%q)", code, stderr.String())
}
if len(host.installCmds) != 3 || host.installCmds[0] != "codex" {
t.Fatalf("install seam = %v, want exactly one codex install", host.installCmds)
}
if host.installedSymlinkDest != checkout {
t.Fatalf("plugins/spacedock symlink = %q, want the checkout %q", host.installedSymlinkDest, checkout)
}
if !strings.Contains(stderr.String(), "version-masquerade advisory") {
t.Fatalf("install --host codex --plugin-dir missing the advisory: %q", stderr.String())
}
}
// TestInstallCodexLocalPluginDirResolvesOnEdgeChannel is AC-2: a --plugin-dir codex
// install names its marketplace via channelMarketplace(devBranch), so an
// edge-devBranch build's install resolves through the real ResolveManifest — where
// the OLD hardcoded `spacedock` name silently did not (Spike E baseline: empty
// resolve). Pins devBranch="next" (edge), points CODEX_HOME at a fresh temp dir,
// installs a throwaway checkout fixture through the real helper, then asserts
// ResolveManifest("codex") is non-empty. Skips when codex is absent (no auth
// required — Spike C).
func TestInstallCodexLocalPluginDirResolvesOnEdgeChannel(t *testing.T) {
if _, err := exec.LookPath("codex"); err != nil {
t.Skip("codex not on PATH; edge-channel resolve test requires the host CLI")
}
saved := devBranch
devBranch = "next" // edge channel → marketplace must be named spacedock-edge
defer func() { devBranch = saved }()
tmp := t.TempDir()
checkout := buildCodexPluginCheckout(t, filepath.Join(tmp, "checkout"), "0.0.0")
codexHomeDir := filepath.Join(tmp, "codexhome")
mustMkdir(t, codexHomeDir)
t.Setenv("CODEX_HOME", codexHomeDir)
if err := installCodexLocalPluginDir(execHost{}, checkout, io.Discard); err != nil {
t.Fatalf("installCodexLocalPluginDir(edge) failed: %v", err)
}
manifest, err := execHost{}.ResolveManifest("codex")
if err != nil {
t.Fatalf("ResolveManifest(codex) after edge install: %v", err)
}
if manifest == "" {
t.Fatalf("ResolveManifest returned empty on the edge channel — the channel-name footgun regressed to the Spike E baseline")
}
if _, err := os.Stat(manifest); err != nil {
t.Fatalf("resolved edge manifest %s does not exist: %v", manifest, err)
}
}
// buildCodexPluginCheckout writes a minimal valid codex plugin checkout under root
// (a .codex-plugin/plugin.json bracketing CONTRACT_VERSION + one skill), the shape a
// --plugin-dir target must have for codex's `plugin add` to copy it into its cache.
func buildCodexPluginCheckout(t *testing.T, root, version string) string {
t.Helper()
mustMkdir(t, filepath.Join(root, ".codex-plugin"))
mustMkdir(t, filepath.Join(root, "skills", "demo"))
mustWrite(t, filepath.Join(root, ".codex-plugin", "plugin.json"),
`{ "name": "spacedock", "version": "`+version+`", "requires-contract": ">=2,<3", "skills": "./skills/" }
`)
mustWrite(t, filepath.Join(root, "skills", "demo", "SKILL.md"), "---\nname: demo\ndescription: demo skill\n---\ndemo\n")
return root
}