返回 DeepSeek-Reasonix
compat_legacy_test.go
根目录 / internal / pluginpkg / compat_legacy_test.go
1 package pluginpkg
2
3 import (
4 "os"
5 "path/filepath"
6 "strings"
7 "testing"
8 )
9
10 // TestLegacyNativeManifestRejected pins the v2 one-shot switch: native
11 // manifests without apiVersion are refused at ParseDir. Migration still
12 // accepts them via ParseNativeForMigrate.
13 func TestLegacyNativeManifestRejected(t *testing.T) {
14 root := t.TempDir()
15 manifest := `{
16 "name": "legacy-demo",
17 "version": "0.3.1",
18 "description": "Legacy manifest without apiVersion",
19 "skills": ["skills"]
20 }`
21 if err := os.WriteFile(filepath.Join(root, NativeManifest), []byte(manifest), 0o644); err != nil {
22 t.Fatal(err)
23 }
24 _, _, err := ParseDir(root)
25 if err == nil || !strings.Contains(err.Error(), "missing apiVersion") {
26 t.Fatalf("ParseDir legacy = %v, want missing apiVersion", err)
27 }
28 pkg, _, err := ParseNativeForMigrate(root)
29 if err != nil {
30 t.Fatalf("ParseNativeForMigrate: %v", err)
31 }
32 if pkg.Manifest.Name != "legacy-demo" {
33 t.Fatalf("migrate parse name = %q", pkg.Manifest.Name)
34 }
35 }
36
37 // TestV1NativeManifestRejected ensures every native parser refuses v1.
38 func TestV1NativeManifestRejected(t *testing.T) {
39 root := t.TempDir()
40 manifest := `{
41 "apiVersion": "reasonix.io/plugin/v1",
42 "name": "old",
43 "version": "1.0.0"
44 }`
45 if err := os.WriteFile(filepath.Join(root, NativeManifest), []byte(manifest), 0o644); err != nil {
46 t.Fatal(err)
47 }
48 _, _, err := ParseDir(root)
49 if err == nil || !strings.Contains(err.Error(), "unsupported apiVersion") {
50 t.Fatalf("ParseDir v1 = %v, want unsupported apiVersion", err)
51 }
52 if _, _, err := ParseNativeForMigrate(root); err == nil || !strings.Contains(err.Error(), "unsupported apiVersion") {
53 t.Fatalf("ParseNativeForMigrate v1 = %v, want unsupported apiVersion", err)
54 }
55 }
56
57 func TestLoadInstalledAutoMigratesManagedLegacyManifest(t *testing.T) {
58 home := t.TempDir()
59 root := InstallRoot(home, "legacy-demo")
60 if err := os.MkdirAll(root, 0o755); err != nil {
61 t.Fatal(err)
62 }
63 legacy := `{"name":"legacy-demo","version":"1.0.0"}`
64 if err := os.WriteFile(filepath.Join(root, NativeManifest), []byte(legacy), 0o644); err != nil {
65 t.Fatal(err)
66 }
67 if err := Upsert(home, InstalledPlugin{Name: "legacy-demo", Root: RelativeRoot(home, root), Enabled: true}); err != nil {
68 t.Fatal(err)
69 }
70
71 installed, warnings := LoadInstalled(home)
72 if len(warnings) != 1 || !strings.Contains(warnings[0], "automatically migrated") {
73 t.Fatalf("warnings = %v", warnings)
74 }
75 if len(installed) != 1 || installed[0].Package.Manifest.APIVersion != ManifestAPIVersionV2 {
76 t.Fatalf("installed = %#v", installed)
77 }
78 backup, err := os.ReadFile(filepath.Join(root, NativeManifest+".bak"))
79 if err != nil || string(backup) != legacy {
80 t.Fatalf("backup = %q, err=%v", backup, err)
81 }
82 }
83
84 func TestLoadInstalledQuarantinesExternalLegacyManifestWithoutModifyingIt(t *testing.T) {
85 home := t.TempDir()
86 root := t.TempDir()
87 legacy := `{"name":"dev-demo","version":"1.0.0"}`
88 manifestPath := filepath.Join(root, NativeManifest)
89 if err := os.WriteFile(manifestPath, []byte(legacy), 0o644); err != nil {
90 t.Fatal(err)
91 }
92 if err := Upsert(home, InstalledPlugin{Name: "dev-demo", Root: root, Enabled: true}); err != nil {
93 t.Fatal(err)
94 }
95
96 installed, warnings := LoadInstalled(home)
97 if len(installed) != 0 {
98 t.Fatalf("external incompatible plugin loaded: %#v", installed)
99 }
100 if len(warnings) != 1 || !strings.Contains(warnings[0], PluginStatusDisabledIncompatible) || !strings.Contains(warnings[0], "plugin migrate dev-demo --to-v2") {
101 t.Fatalf("warnings = %v", warnings)
102 }
103 after, err := os.ReadFile(manifestPath)
104 if err != nil || string(after) != legacy {
105 t.Fatalf("external manifest changed: %q, err=%v", after, err)
106 }
107 state, err := LoadState(home)
108 if err != nil {
109 t.Fatal(err)
110 }
111 if len(state.Plugins) != 1 || state.Plugins[0].Status != PluginStatusDisabledIncompatible {
112 t.Fatalf("plugin state = %#v", state.Plugins)
113 }
114 }
115
115 lines GO