返回 DeepSeek-Reasonix
shortcut_policy_test.go
根目录 / internal / appidentity / shortcut_policy_test.go
1 package appidentity
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7 )
8
9 func TestShortcutRepairPolicy(t *testing.T) {
10 root := t.TempDir()
11 launcher := filepath.Join(root, "reasonix-launcher.exe")
12 if err := os.WriteFile(launcher, []byte("launcher"), 0o600); err != nil {
13 t.Fatal(err)
14 }
15 electron := filepath.Join(root, "versions", "v1.38.6", "app", "Reasonix.exe")
16 goDesktop := filepath.Join(root, "versions", "v1.20.0", "reasonix-desktop.exe")
17 flat := filepath.Join(root, "reasonix-desktop.exe")
18 customIcon := filepath.Join(root, "custom.ico")
19 for _, tt := range []struct {
20 name, target, icon, id string
21 want shortcutRepair
22 }{
23 {"new pin", launcher, "", "", shortcutRepair{identity: AppUserModelID}},
24 {"legacy pin", launcher, "", "Reasonix", shortcutRepair{identity: AppUserModelID}},
25 {"healthy", launcher, launcher, AppUserModelID, shortcutRepair{}},
26 {"versioned electron", electron, electron, "Reasonix", shortcutRepair{target: launcher, icon: launcher, identity: AppUserModelID}},
27 {"versioned Go", goDesktop, goDesktop, "", shortcutRepair{target: launcher, icon: launcher, identity: AppUserModelID}},
28 {"flat electron", filepath.Join(root, "app", "Reasonix.exe"), "", "Reasonix", shortcutRepair{target: launcher, identity: AppUserModelID}},
29 {"custom icon", electron, customIcon, "", shortcutRepair{target: launcher, identity: AppUserModelID}},
30 {"missing flat", flat, flat, "", shortcutRepair{target: launcher, icon: launcher, identity: AppUserModelID}},
31 {"current ID stale target", electron, launcher, AppUserModelID, shortcutRepair{target: launcher}},
32 {"stable target stale icon", launcher, electron, AppUserModelID, shortcutRepair{icon: launcher}},
33 {"studio ID", electron, electron, "io.reasonix.studio", shortcutRepair{}},
34 {"Tauri ID", electron, electron, "dev.reasonix.desktop", shortcutRepair{}},
35 {"unknown ID", electron, electron, "Custom.App", shortcutRepair{}},
36 {"other install", filepath.Join(t.TempDir(), "Reasonix.exe"), electron, "Reasonix", shortcutRepair{}},
37 {"Studio executable", filepath.Join(root, "Reasonix Studio.exe"), electron, "Reasonix", shortcutRepair{}},
38 {"unrelated executable", filepath.Join(root, "other.exe"), electron, "", shortcutRepair{}},
39 } {
40 t.Run(tt.name, func(t *testing.T) {
41 if got := planShortcutRepair(tt.target, tt.icon, tt.id, root, false); got != tt.want {
42 t.Fatalf("repair = %+v, want %+v", got, tt.want)
43 }
44 })
45 }
46 if err := os.WriteFile(flat, []byte("flat desktop"), 0o600); err != nil {
47 t.Fatal(err)
48 }
49 if got := planShortcutRepair(flat, flat, AppUserModelID, root, false); got != (shortcutRepair{}) {
50 t.Fatalf("live flat install must keep its entry: %+v", got)
51 }
52 if got := planShortcutRepair(flat, flat, AppUserModelID, root, true); got != (shortcutRepair{target: launcher, icon: launcher}) {
53 t.Fatalf("activated versioned install must replace stale flat entry: %+v", got)
54 }
55 if err := os.Remove(launcher); err != nil {
56 t.Fatal(err)
57 }
58 if got := planShortcutRepair(electron, electron, "Reasonix", root, false); got != (shortcutRepair{identity: AppUserModelID}) {
59 t.Fatalf("missing launcher must not create a dangling entry: %+v", got)
60 }
61 }
62
63 func TestShortcutOwnershipResolvesLinksBeforeCheckingLayout(t *testing.T) {
64 root := t.TempDir()
65 outside := t.TempDir()
66 if err := os.MkdirAll(filepath.Join(root, "versions"), 0o700); err != nil {
67 t.Fatal(err)
68 }
69 if err := os.Symlink(outside, filepath.Join(root, "versions", "v1.0.0")); err != nil {
70 t.Skipf("symbolic links unavailable: %v", err)
71 }
72 for _, suffix := range []string{"reasonix-desktop.exe", filepath.Join("app", "Reasonix.exe")} {
73 if ownedShortcutTarget(filepath.Join(root, "versions", "v1.0.0", suffix), root) {
74 t.Fatalf("external target %s treated as owned", suffix)
75 }
76 }
77 alias := filepath.Join(t.TempDir(), "current")
78 if err := os.Symlink(root, alias); err != nil {
79 t.Fatal(err)
80 }
81 if !ownedShortcutTarget(filepath.Join(alias, "versions", "v2.0.0", "app", "Reasonix.exe"), root) {
82 t.Fatal("alias of the owned root with a pruned version must remain repairable")
83 }
84 if ownedShortcutTarget(filepath.Join(root, "Reasonix.exe"), filepath.Join(root, "missing-root")) {
85 t.Fatal("missing installation root must not establish ownership")
86 }
87 }
88
89 func TestCanonicalShortcutMigratesLegacyButPreservesCustomProperties(t *testing.T) {
90 root := t.TempDir()
91 canonical := filepath.Join(root, "Reasonix.exe")
92 legacy := filepath.Join(root, "reasonix-launcher.exe")
93 for _, path := range []string{canonical, legacy} {
94 if err := os.WriteFile(path, []byte("launcher"), 0o600); err != nil {
95 t.Fatal(err)
96 }
97 }
98 for _, tc := range []struct {
99 name, target, icon, id string
100 want shortcutRepair
101 }{
102 {"legacy stable", legacy, legacy, AppUserModelID, shortcutRepair{target: canonical, icon: canonical}},
103 {"custom icon", legacy, filepath.Join(root, "custom.ico"), AppUserModelID, shortcutRepair{target: canonical}},
104 {"canonical", canonical, canonical, AppUserModelID, shortcutRepair{}},
105 {"foreign identity", legacy, legacy, "io.reasonix.studio", shortcutRepair{}},
106 } {
107 t.Run(tc.name, func(t *testing.T) {
108 if got := planShortcutRepair(tc.target, tc.icon, tc.id, root, true); got != tc.want {
109 t.Fatalf("got %+v, want %+v", got, tc.want)
110 }
111 })
112 }
113 oldTarget := filepath.Join(root, "versions", "v1.38.9", "app", "Reasonix.exe")
114 for _, tc := range []struct {
115 dir string
116 want bool
117 }{
118 {"", true}, {filepath.Dir(oldTarget), true}, {root, false}, {t.TempDir(), false}, {filepath.Join(root, "workspace"), false},
119 } {
120 if got := repairShortcutWorkingDirectory(tc.dir, oldTarget, root); got != tc.want {
121 t.Fatalf("directory %q: got %v, want %v", tc.dir, got, tc.want)
122 }
123 }
124 if err := os.Remove(canonical); err != nil {
125 t.Fatal(err)
126 }
127 if got := planShortcutRepair(legacy, legacy, AppUserModelID, root, true); got != (shortcutRepair{}) {
128 t.Fatalf("missing canonical entry broke legacy shortcut: %+v", got)
129 }
130 }
131
131 lines GO