Repository navigation
Expand file tree
/
Copy pathplugin_test.go
More file actions
112 lines (103 loc) · 3.84 KB
/
Copy pathplugin_test.go
File metadata and controls
112 lines (103 loc) · 3.84 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
package pgedgecli
import (
"encoding/json"
"os"
"path"
"path/filepath"
"testing"
)
const pluginDir = "plugins/pgedge"
type pluginManifest struct {
Name string `json:"name"`
Description string `json:"description"`
Version string `json:"version"`
Icon string `json:"icon"`
Logo string `json:"logo"`
Skills string `json:"skills"`
Interface struct {
Logo string `json:"logo"`
} `json:"interface"`
}
func readJSON(t *testing.T, file string, v any) {
t.Helper()
b, err := os.ReadFile(file)
if err != nil {
t.Fatal(err)
}
if err := json.Unmarshal(b, v); err != nil {
t.Fatalf("%s: %v", file, err)
}
}
func mustExist(t *testing.T, what, p string) {
t.Helper()
if _, err := os.Stat(p); err != nil {
t.Errorf("%s %q: %v", what, p, err)
}
}
// TestPluginManifestsAgree holds the Claude, Cursor and Codex manifests to
// one plugin, pinned to one release: the marketplace installs the skills from
// the tag of that release, so they describe the binary users have. The
// release workflow refuses a tag that doesn't match the version.
func TestPluginManifestsAgree(t *testing.T) {
var claude, cursor, codex pluginManifest
readJSON(t, filepath.Join(pluginDir, ".claude-plugin", "plugin.json"), &claude)
readJSON(t, ".cursor-plugin/plugin.json", &cursor)
readJSON(t, filepath.Join(pluginDir, ".codex-plugin", "plugin.json"), &codex)
type marketplace struct {
Plugins []struct {
Name string `json:"name"`
Source struct {
Source string `json:"source"`
URL string `json:"url"`
Path string `json:"path"`
Ref string `json:"ref"`
} `json:"source"`
} `json:"plugins"`
}
var claudeMarket, codexMarket marketplace
readJSON(t, ".claude-plugin/marketplace.json", &claudeMarket)
readJSON(t, ".agents/plugins/marketplace.json", &codexMarket)
if claude.Name != "pgedge" || cursor.Name != "pgedge" || codex.Name != "pgedge" {
t.Errorf("plugin names are %q (Claude), %q (Cursor) and %q (Codex), want pgedge; "+
"a published name must never change", claude.Name, cursor.Name, codex.Name)
}
if claude.Description == "" || claude.Description != cursor.Description ||
claude.Description != codex.Description {
t.Errorf("descriptions differ or are empty:\nClaude: %q\nCursor: %q\nCodex: %q",
claude.Description, cursor.Description, codex.Description)
}
if claude.Version == "" || claude.Version != cursor.Version || claude.Version != codex.Version {
t.Errorf("versions are %q (Claude), %q (Cursor) and %q (Codex), want one release version",
claude.Version, cursor.Version, codex.Version)
}
for file, m := range map[string]marketplace{
".claude-plugin/marketplace.json": claudeMarket,
".agents/plugins/marketplace.json": codexMarket,
} {
if len(m.Plugins) != 1 {
t.Errorf("%s has %d plugins, want 1", file, len(m.Plugins))
continue
}
entry := m.Plugins[0]
if entry.Name != claude.Name || entry.Source.Source != "git-subdir" ||
entry.Source.URL != "https://github.com/pgEdge/pgedge-cli.git" ||
path.Clean(entry.Source.Path) != pluginDir || entry.Source.Ref != "v"+claude.Version {
t.Errorf("%s entry = %+v, want %q from git-subdir "+
"https://github.com/pgEdge/pgedge-cli.git path %s ref v%s",
file, entry, claude.Name, pluginDir, claude.Version)
}
}
// Claude resolves the icon from the plugin folder, Cursor resolves its
// paths from the repository root.
mustExist(t, "Claude icon", filepath.Join(pluginDir, claude.Icon))
mustExist(t, "Cursor logo", cursor.Logo)
mustExist(t, "Codex logo", filepath.Join(pluginDir, codex.Interface.Logo))
if path.Clean(codex.Skills) != "skills" {
t.Errorf("Codex skills = %q, want ./skills/", codex.Skills)
}
if path.Clean(cursor.Skills) != pluginDir+"/skills" {
t.Errorf("Cursor skills = %q, want ./%s/skills", cursor.Skills, pluginDir)
}
mustExist(t, "README the Anthropic directory requires",
filepath.Join(pluginDir, "README.md"))
}