|
1 | 1 | package plugin_test |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "encoding/json" |
4 | 5 | "os" |
5 | 6 | "path/filepath" |
6 | 7 | "runtime" |
@@ -77,3 +78,70 @@ func TestPluginAssetsDoNotLeakSpanishTriggers(t *testing.T) { |
77 | 78 | } |
78 | 79 | } |
79 | 80 | } |
| 81 | + |
| 82 | +// marketplaceJSON is the minimal structure of .claude-plugin/marketplace.json |
| 83 | +// needed to extract the version declared for the engram plugin entry. |
| 84 | +type marketplaceJSON struct { |
| 85 | + Plugins []struct { |
| 86 | + Name string `json:"name"` |
| 87 | + Version string `json:"version"` |
| 88 | + } `json:"plugins"` |
| 89 | +} |
| 90 | + |
| 91 | +// pluginJSON is the structure of plugin/claude-code/.claude-plugin/plugin.json. |
| 92 | +type pluginJSON struct { |
| 93 | + Version string `json:"version"` |
| 94 | +} |
| 95 | + |
| 96 | +// TestPluginVersionsMatch asserts that the version declared in |
| 97 | +// .claude-plugin/marketplace.json matches the version in |
| 98 | +// plugin/claude-code/.claude-plugin/plugin.json. |
| 99 | +// |
| 100 | +// A mismatch between these two files causes Claude Code to silently skip |
| 101 | +// installation or re-download the plugin on every run because it sees the |
| 102 | +// cached version as stale. |
| 103 | +func TestPluginVersionsMatch(t *testing.T) { |
| 104 | + root := repoRoot(t) |
| 105 | + |
| 106 | + // Read marketplace.json |
| 107 | + marketplacePath := filepath.Join(root, ".claude-plugin", "marketplace.json") |
| 108 | + marketplaceData, err := os.ReadFile(marketplacePath) |
| 109 | + if err != nil { |
| 110 | + t.Fatalf("cannot read marketplace.json: %v", err) |
| 111 | + } |
| 112 | + var marketplace marketplaceJSON |
| 113 | + if err := json.Unmarshal(marketplaceData, &marketplace); err != nil { |
| 114 | + t.Fatalf("cannot parse marketplace.json: %v", err) |
| 115 | + } |
| 116 | + |
| 117 | + // Read plugin.json |
| 118 | + pluginPath := filepath.Join(root, "plugin", "claude-code", ".claude-plugin", "plugin.json") |
| 119 | + pluginData, err := os.ReadFile(pluginPath) |
| 120 | + if err != nil { |
| 121 | + t.Fatalf("cannot read plugin.json: %v", err) |
| 122 | + } |
| 123 | + var plugin pluginJSON |
| 124 | + if err := json.Unmarshal(pluginData, &plugin); err != nil { |
| 125 | + t.Fatalf("cannot parse plugin.json: %v", err) |
| 126 | + } |
| 127 | + |
| 128 | + // Find the engram plugin entry in marketplace.json |
| 129 | + var marketplaceVersion string |
| 130 | + for _, p := range marketplace.Plugins { |
| 131 | + if p.Name == "engram" { |
| 132 | + marketplaceVersion = p.Version |
| 133 | + break |
| 134 | + } |
| 135 | + } |
| 136 | + if marketplaceVersion == "" { |
| 137 | + t.Fatal("marketplace.json contains no plugin entry named 'engram'") |
| 138 | + } |
| 139 | + |
| 140 | + if marketplaceVersion != plugin.Version { |
| 141 | + t.Errorf( |
| 142 | + "plugin version mismatch: marketplace.json declares %q but plugin/claude-code/.claude-plugin/plugin.json declares %q — keep them in sync", |
| 143 | + marketplaceVersion, |
| 144 | + plugin.Version, |
| 145 | + ) |
| 146 | + } |
| 147 | +} |
0 commit comments