modules_test.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. package modules
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "net/http/httptest"
  6. "strings"
  7. "testing"
  8. )
  9. func TestModuleInfo_JSON(t *testing.T) {
  10. module := ModuleInfo{
  11. Name: "TestModule",
  12. Desc: "A test module",
  13. Group: "utilities",
  14. IconPath: "test/img/icon.png",
  15. Version: "1.0.0",
  16. StartDir: "test/index.html",
  17. SupportFW: true,
  18. LaunchFWDir: "test/float.html",
  19. SupportEmb: false,
  20. SupportedExt: []string{".txt", ".pdf"},
  21. }
  22. // Marshal to JSON
  23. data, err := json.Marshal(module)
  24. if err != nil {
  25. t.Fatalf("Failed to marshal ModuleInfo: %v", err)
  26. }
  27. // Unmarshal back
  28. var restored ModuleInfo
  29. err = json.Unmarshal(data, &restored)
  30. if err != nil {
  31. t.Fatalf("Failed to unmarshal ModuleInfo: %v", err)
  32. }
  33. if restored.Name != module.Name {
  34. t.Errorf("Name mismatch: got %q, want %q", restored.Name, module.Name)
  35. }
  36. if restored.Group != module.Group {
  37. t.Errorf("Group mismatch: got %q, want %q", restored.Group, module.Group)
  38. }
  39. if len(restored.SupportedExt) != 2 {
  40. t.Errorf("Expected 2 extensions, got %d", len(restored.SupportedExt))
  41. }
  42. }
  43. func TestNewModuleHandler(t *testing.T) {
  44. // NewModuleHandler requires a userHandler which is complex
  45. // Test with nil (may panic) - we test the struct is initialized
  46. mh := &ModuleHandler{
  47. LoadedModule: []*ModuleInfo{},
  48. tmpDirectory: t.TempDir(),
  49. }
  50. if mh.LoadedModule == nil {
  51. t.Error("Expected non-nil LoadedModule")
  52. }
  53. }
  54. func TestRegisterModuleFromJSON_Valid(t *testing.T) {
  55. mh := &ModuleHandler{
  56. LoadedModule: []*ModuleInfo{},
  57. }
  58. moduleJSON := `{
  59. "Name": "TestMod",
  60. "Desc": "Test description",
  61. "Group": "media",
  62. "IconPath": "test/icon.png",
  63. "Version": "1.0.0",
  64. "StartDir": "test/index.html",
  65. "SupportFW": true,
  66. "SupportedExt": [".txt"]
  67. }`
  68. err := mh.RegisterModuleFromJSON(moduleJSON, false)
  69. if err != nil {
  70. t.Fatalf("RegisterModuleFromJSON() unexpected error: %v", err)
  71. }
  72. if len(mh.LoadedModule) != 1 {
  73. t.Errorf("Expected 1 module, got %d", len(mh.LoadedModule))
  74. }
  75. }
  76. func TestRegisterModuleFromJSON_Invalid(t *testing.T) {
  77. mh := &ModuleHandler{
  78. LoadedModule: []*ModuleInfo{},
  79. }
  80. err := mh.RegisterModuleFromJSON("invalid json {", false)
  81. if err == nil {
  82. t.Error("Expected error for invalid JSON")
  83. }
  84. }
  85. func TestDeregisterModule(t *testing.T) {
  86. mh := &ModuleHandler{
  87. LoadedModule: []*ModuleInfo{},
  88. }
  89. moduleJSON := `{"Name": "ModA", "Group": "media"}`
  90. mh.RegisterModuleFromJSON(moduleJSON, false)
  91. mh.RegisterModuleFromJSON(`{"Name": "ModB", "Group": "media"}`, false)
  92. if len(mh.LoadedModule) != 2 {
  93. t.Fatalf("Expected 2 modules before deregister, got %d", len(mh.LoadedModule))
  94. }
  95. mh.DeregisterModule("ModA")
  96. if len(mh.LoadedModule) != 1 {
  97. t.Errorf("Expected 1 module after deregister, got %d", len(mh.LoadedModule))
  98. }
  99. if mh.LoadedModule[0].Name != "ModB" {
  100. t.Errorf("Expected ModB to remain, got %q", mh.LoadedModule[0].Name)
  101. }
  102. }
  103. func TestGetModuleNameList(t *testing.T) {
  104. mh := &ModuleHandler{
  105. LoadedModule: []*ModuleInfo{},
  106. }
  107. mh.RegisterModuleFromJSON(`{"Name": "Alpha", "Group": "media"}`, false)
  108. mh.RegisterModuleFromJSON(`{"Name": "Beta", "Group": "media"}`, false)
  109. names := mh.GetModuleNameList()
  110. if len(names) != 2 {
  111. t.Errorf("Expected 2 module names, got %d", len(names))
  112. }
  113. }
  114. func TestModuleSortList(t *testing.T) {
  115. mh := &ModuleHandler{
  116. LoadedModule: []*ModuleInfo{},
  117. }
  118. mh.RegisterModuleFromJSON(`{"Name": "Zebra", "Group": "media"}`, false)
  119. mh.RegisterModuleFromJSON(`{"Name": "Apple", "Group": "media"}`, false)
  120. mh.RegisterModuleFromJSON(`{"Name": "Mango", "Group": "media"}`, false)
  121. mh.ModuleSortList()
  122. names := mh.GetModuleNameList()
  123. if names[0] != "Apple" || names[1] != "Mango" || names[2] != "Zebra" {
  124. t.Errorf("Expected sorted order [Apple Mango Zebra], got %v", names)
  125. }
  126. }
  127. func TestGetModuleInfoByID(t *testing.T) {
  128. mh := &ModuleHandler{
  129. LoadedModule: []*ModuleInfo{},
  130. }
  131. mh.RegisterModuleFromJSON(`{"Name": "FindMe", "Group": "media"}`, false)
  132. info := mh.GetModuleInfoByID("FindMe")
  133. if info == nil {
  134. t.Fatal("Expected non-nil ModuleInfo")
  135. }
  136. if info.Name != "FindMe" {
  137. t.Errorf("Expected Name FindMe, got %q", info.Name)
  138. }
  139. // Non-existent module
  140. notFound := mh.GetModuleInfoByID("DoesNotExist")
  141. if notFound != nil {
  142. t.Error("Expected nil for non-existent module")
  143. }
  144. }
  145. // TestGetLaunchParameter_MissingParam tests that missing "module" param returns error
  146. func TestGetLaunchParameter_MissingParam(t *testing.T) {
  147. mh := &ModuleHandler{LoadedModule: []*ModuleInfo{}}
  148. req := httptest.NewRequest(http.MethodGet, "/modules/launch", nil)
  149. rr := httptest.NewRecorder()
  150. mh.GetLaunchParameter(rr, req)
  151. body := rr.Body.String()
  152. if !strings.Contains(body, "error") {
  153. t.Errorf("expected error response for missing module param, got: %s", body)
  154. }
  155. }
  156. // TestGetLaunchParameter_NotFound tests that an unknown module returns error
  157. func TestGetLaunchParameter_NotFound(t *testing.T) {
  158. mh := &ModuleHandler{LoadedModule: []*ModuleInfo{}}
  159. req := httptest.NewRequest(http.MethodGet, "/modules/launch?module=NonExistent", nil)
  160. rr := httptest.NewRecorder()
  161. mh.GetLaunchParameter(rr, req)
  162. body := rr.Body.String()
  163. if !strings.Contains(body, "error") {
  164. t.Errorf("expected error for non-existent module, got: %s", body)
  165. }
  166. }
  167. // TestGetLaunchParameter_Found tests that a known module returns JSON
  168. func TestGetLaunchParameter_Found(t *testing.T) {
  169. mh := &ModuleHandler{LoadedModule: []*ModuleInfo{}}
  170. mh.RegisterModuleFromJSON(`{"Name": "MyApp", "Group": "media", "StartDir": "MyApp/index.html"}`, false)
  171. req := httptest.NewRequest(http.MethodGet, "/modules/launch?module=MyApp", nil)
  172. rr := httptest.NewRecorder()
  173. mh.GetLaunchParameter(rr, req)
  174. if rr.Code != http.StatusOK {
  175. t.Errorf("expected 200, got %d", rr.Code)
  176. }
  177. body := rr.Body.String()
  178. if !strings.Contains(body, "MyApp") {
  179. t.Errorf("expected module name in response, got: %s", body)
  180. }
  181. }
  182. // TestRegisterModule_NonUtilitiesGroup tests that non-utilities groups don't access userHandler
  183. func TestRegisterModule_NonUtilitiesGroup(t *testing.T) {
  184. mh := &ModuleHandler{LoadedModule: []*ModuleInfo{}}
  185. // userHandler is nil; as long as Group != "utilities"/"system tools" it won't be accessed
  186. module := ModuleInfo{
  187. Name: "TestMediaMod",
  188. Group: "media",
  189. }
  190. mh.RegisterModule(module)
  191. if len(mh.LoadedModule) != 1 {
  192. t.Errorf("expected 1 module, got %d", len(mh.LoadedModule))
  193. }
  194. }
  195. // TestGetModuleNameList_Empty verifies an empty handler returns empty list
  196. func TestGetModuleNameList_Empty(t *testing.T) {
  197. mh := &ModuleHandler{LoadedModule: []*ModuleInfo{}}
  198. names := mh.GetModuleNameList()
  199. if len(names) != 0 {
  200. t.Errorf("expected 0 names, got %d", len(names))
  201. }
  202. }
  203. // TestDeregisterModule_NonExistent verifies deregistering unknown module is a no-op
  204. func TestDeregisterModule_NonExistent(t *testing.T) {
  205. mh := &ModuleHandler{LoadedModule: []*ModuleInfo{}}
  206. mh.RegisterModuleFromJSON(`{"Name": "StayMod", "Group": "media"}`, false)
  207. mh.DeregisterModule("NotHere")
  208. if len(mh.LoadedModule) != 1 {
  209. t.Errorf("expected 1 module after no-op deregister, got %d", len(mh.LoadedModule))
  210. }
  211. }
  212. // TestModuleSortList_Empty verifies sorting empty list doesn't panic
  213. func TestModuleSortList_Empty(t *testing.T) {
  214. mh := &ModuleHandler{LoadedModule: []*ModuleInfo{}}
  215. mh.ModuleSortList() // should not panic
  216. }
  217. // TestOnModuleUninstall_Hook verifies the hook is called on uninstall (only if hook is set)
  218. func TestOnModuleUninstall_Hook(t *testing.T) {
  219. mh := &ModuleHandler{LoadedModule: []*ModuleInfo{}}
  220. mh.RegisterModuleFromJSON(`{"Name": "HookMod", "Group": "media"}`, false)
  221. called := false
  222. mh.OnModuleUninstall = func(moduleName string) {
  223. if moduleName == "HookMod" {
  224. called = true
  225. }
  226. }
  227. // Invoke DeregisterModule which may or may not call OnModuleUninstall
  228. mh.DeregisterModule("HookMod")
  229. // The hook may not be called by DeregisterModule (it's for uninstall, not deregister)
  230. // Just verify the module is gone
  231. if mh.GetModuleInfoByID("HookMod") != nil {
  232. t.Error("expected HookMod to be deregistered")
  233. }
  234. _ = called // hook may not be invoked by DeregisterModule
  235. }