package web import ( "encoding/json" "path/filepath" "testing" "github.com/yuanyuanxiang/SimpleRemoter/server/go/connection" "github.com/yuanyuanxiang/SimpleRemoter/server/go/devicehistory" "github.com/yuanyuanxiang/SimpleRemoter/server/go/hub" "github.com/yuanyuanxiang/SimpleRemoter/server/go/logger" "github.com/yuanyuanxiang/SimpleRemoter/server/go/protocol" "github.com/yuanyuanxiang/SimpleRemoter/server/go/wsauth" ) // deleteTestHub builds the minimal wsHub wiring handleDeleteDevice needs. func deleteTestHub(t *testing.T) (*wsHub, *hub.Hub, *devicehistory.Store) { t.Helper() h := hub.New() hist, err := devicehistory.Open(filepath.Join(t.TempDir(), "devices.json"), 100, nil) if err != nil { t.Fatalf("Open history: %v", err) } t.Cleanup(func() { _ = hist.Close() }) h.Subscribe(hist) auth := wsauth.New() wh := &wsHub{ auth: auth, devices: h, history: hist, log: logger.New(logger.Config{}), clients: make(map[*wsClient]struct{}), } return wh, h, hist } func deleteTestClient(wh *wsHub, role string) (*wsClient, string) { tok, err := wh.auth.IssueToken("tester", role) if err != nil { panic(err) } c := &wsClient{send: make(chan wsMsg, 8), closed: make(chan struct{})} wh.register(c) return c, tok } func deleteReply(t *testing.T, c *wsClient) map[string]any { t.Helper() for { select { case m := <-c.send: var out map[string]any if err := json.Unmarshal(m.data, &out); err != nil { t.Fatalf("decode reply: %v", err) } if out["cmd"] == "delete_device_result" { return out } default: t.Fatal("no delete_device_result reply") return nil } } } func TestDeleteDeviceOfflineRemovesHistoryRecord(t *testing.T) { wh, h, hist := deleteTestHub(t) h.Register(&hub.Device{ID: "gone", Name: "Gone"}, &connection.Context{RemoteAddr: "10.0.0.1:1000"}) h.Unregister("gone") c, tok := deleteTestClient(wh, "admin") raw, _ := json.Marshal(map[string]any{"token": tok, "id": "gone"}) wh.handleDeleteDevice(c, raw) rep := deleteReply(t, c) if rep["ok"] != true || rep["mode"] != "removed" { t.Fatalf("reply = %+v, want ok/removed", rep) } if hist.Remove("gone") { t.Fatal("history record still present after delete") } } func TestDeleteDeviceOnlineSendsUninstall(t *testing.T) { wh, h, hist := deleteTestHub(t) var sent []byte h.SetSender(func(_ *connection.Context, data []byte) error { sent = append([]byte(nil), data...) return nil }) h.Register(&hub.Device{ID: "live", Name: "Live"}, &connection.Context{RemoteAddr: "10.0.0.2:1000"}) c, tok := deleteTestClient(wh, "admin") raw, _ := json.Marshal(map[string]any{"token": tok, "id": "live"}) wh.handleDeleteDevice(c, raw) rep := deleteReply(t, c) if rep["ok"] != true || rep["mode"] != "uninstall" { t.Fatalf("reply = %+v, want ok/uninstall", rep) } if len(sent) != 1 || sent[0] != protocol.CommandUninstall { t.Fatalf("sent = %v, want single %d byte", sent, protocol.CommandUninstall) } // The history record is dropped along with the uninstall, so the device // does not reappear as a dead offline entry after it disconnects. if hist.Remove("live") { t.Fatal("online device's history record survived the uninstall") } } func TestDeleteDeviceRequiresAdmin(t *testing.T) { wh, h, hist := deleteTestHub(t) h.Register(&hub.Device{ID: "gone", Name: "Gone"}, &connection.Context{RemoteAddr: "10.0.0.3:1000"}) h.Unregister("gone") c, tok := deleteTestClient(wh, "viewer") raw, _ := json.Marshal(map[string]any{"token": tok, "id": "gone"}) wh.handleDeleteDevice(c, raw) rep := deleteReply(t, c) if rep["ok"] != false { t.Fatalf("reply = %+v, want ok:false", rep) } if !hist.Remove("gone") { t.Fatal("viewer delete removed the history record") } }