package agentapi import ( "context" "net/http" "net/http/httptest" "strings" "testing" ) func netStub(t *testing.T) (*httptest.Server, string, *struct { addBody AddNetStorageRequest removed string }) { captured := &struct { addBody AddNetStorageRequest removed string }{} mux := http.NewServeMux() mux.HandleFunc("POST /netstorage/add", func(w http.ResponseWriter, r *http.Request) { _ = decodeJSON(r, &captured.addBody) _, _ = w.Write([]byte(`{"ok":true,"data":{"name":"media","protocol":"nfs","where":"/mnt/felhom-drives/media","guest_path":"/mnt/felhom-drives/media","host_uid":101000,"host_gid":101000}}`)) }) mux.HandleFunc("GET /netstorage", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(`{"ok":true,"data":{"vmid":8200,"network_mounts":[ {"name":"media","protocol":"nfs","server":"10.0.0.5","export":"/srv/media","where":"/mnt/felhom-drives/media","configured":true,"mounted":true,"reachable":true,"health":"ok"}, {"name":"photos","protocol":"smb","server":"10.0.0.5","export":"photos","where":"/mnt/felhom-drives/photos","configured":true,"mounted":false,"reachable":true,"health":"idle"}, {"name":"vids","protocol":"nfs","server":"10.0.0.6","export":"/srv/vids","where":"/mnt/felhom-drives/vids","configured":true,"mounted":true,"reachable":false,"health":"unreachable"} ]}}`)) }) mux.HandleFunc("POST /netstorage/remove", func(w http.ResponseWriter, r *http.Request) { var body struct { Name string `json:"name"` } _ = decodeJSON(r, &body) captured.removed = body.Name _, _ = w.Write([]byte(`{"ok":true,"data":{"name":"` + body.Name + `","removed":true}}`)) }) s := httptest.NewTLSServer(mux) return s, strings.TrimPrefix(s.URL, "https://"), captured } func TestNetStorage_Add_ForwardsCredsAndMapping(t *testing.T) { s, ep, cap := netStub(t) defer s.Close() c := clientFor(t, s, ep) res, err := c.AddNetStorage(context.Background(), AddNetStorageRequest{ Name: "media", Protocol: "smb", Server: "10.0.0.5", Export: "media", MappedUID: 1000, MappedGID: 1000, Username: "u", Password: "p", }) if err != nil { t.Fatal(err) } if res.GuestPath != "/mnt/felhom-drives/media" || res.HostUID != 101000 { t.Fatalf("add result mismatch: %+v", res) } // The SMB creds + mapping must be forwarded to the agent verbatim (the agent writes the 0600 file). if cap.addBody.Username != "u" || cap.addBody.Password != "p" { t.Fatalf("creds not forwarded to the agent: %+v", cap.addBody) } if cap.addBody.MappedUID != 1000 || cap.addBody.Protocol != "smb" { t.Fatalf("mapping/protocol not forwarded: %+v", cap.addBody) } } func TestNetStorage_List_HealthStates(t *testing.T) { s, ep, _ := netStub(t) defer s.Close() c := clientFor(t, s, ep) mounts, err := c.ListNetStorage(context.Background()) if err != nil { t.Fatal(err) } if len(mounts) != 3 { t.Fatalf("want 3 mounts, got %d", len(mounts)) } byName := map[string]NetworkMountStatus{} for _, m := range mounts { byName[m.Name] = m } // ok + idle are NOT degraded; only unreachable is. if byName["media"].Unreachable() || byName["photos"].Unreachable() { t.Fatalf("ok/idle must not be unreachable: %+v", byName) } if byName["photos"].Health != "idle" { t.Fatalf("photos should be idle (benign), got %q", byName["photos"].Health) } if !byName["vids"].Unreachable() { t.Fatalf("vids should be unreachable (degraded), got %q", byName["vids"].Health) } } func TestNetStorage_Remove(t *testing.T) { s, ep, cap := netStub(t) defer s.Close() c := clientFor(t, s, ep) if err := c.RemoveNetStorage(context.Background(), "media"); err != nil { t.Fatal(err) } if cap.removed != "media" { t.Fatalf("remove did not forward the name, got %q", cap.removed) } }