package httpapi import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "path/filepath" "testing" "webui4frpc/internal/process" "webui4frpc/internal/store" ) func newTestHandler(t *testing.T) (*Handler, *httptest.Server) { t.Helper() dir := t.TempDir() st, err := store.New(filepath.Join(dir, "test.db")) if err != nil { t.Fatal(err) } pm := process.NewManager(process.Options{ ConfigsDir: filepath.Join(dir, "configs"), LogsDir: filepath.Join(dir, "logs"), BinaryPath: func() string { return "" }, Render: func(string) ([]byte, error) { return []byte(`{}`), nil }, AutoRestart: func(string) bool { return false }, RestartInterval: func() int { return 5 }, }) h := &Handler{Store: st, Process: pm, WorkDir: dir, User: "admin", Password: "pw"} mux, err := NewServeMux(h) if err != nil { t.Fatal(err) } ts := httptest.NewServer(mux) t.Cleanup(ts.Close) t.Cleanup(func() { _ = st.Close() }) return h, ts } func TestAuthRequired(t *testing.T) { _, ts := newTestHandler(t) resp, err := http.Get(ts.URL + "/api/manager/status") if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusUnauthorized { t.Fatalf("status = %d, want 401", resp.StatusCode) } } func TestCanvasRoundTrip(t *testing.T) { _, ts := newTestHandler(t) client := ts.Client() body := `{ "locals": [{"name":"web","ip":"127.0.0.1","port":8080,"protocol":"tcp"}], "remotes": [{"name":"srv-a","ip":"1.2.3.4","port":7000,"enabled":true}], "links": [{"local":"web","remote":"srv-a","remotePort":8080}] }` req, _ := http.NewRequest(http.MethodPut, ts.URL+"/api/manager/canvas", bytes.NewBufferString(body)) req.SetBasicAuth("admin", "pw") resp, err := client.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("save canvas status = %d", resp.StatusCode) } var got canvasData if err := json.NewDecoder(resp.Body).Decode(&got); err != nil { t.Fatal(err) } if len(got.Locals) != 1 || got.Locals[0].Name != "web" { t.Fatalf("locals = %+v", got.Locals) } if len(got.Links) != 1 || got.Links[0].RemotePort != 8080 { t.Fatalf("links = %+v", got.Links) } // GET again to confirm persistence via store. req2, _ := http.NewRequest(http.MethodGet, ts.URL+"/api/manager/canvas", nil) req2.SetBasicAuth("admin", "pw") resp2, err2 := client.Do(req2) if err2 != nil { t.Fatal(err2) } defer resp2.Body.Close() var got2 canvasData _ = json.NewDecoder(resp2.Body).Decode(&got2) if len(got2.Remotes) != 1 || got2.Remotes[0].Name != "srv-a" { t.Fatalf("remotes after reload = %+v", got2.Remotes) } } func TestSettingsPutRoundTrip(t *testing.T) { _, ts := newTestHandler(t) client := ts.Client() // PUT updated settings body := `{"autoStartProfiles":false,"restartOnExit":false,"restartIntervalSeconds":12}` req, _ := http.NewRequest(http.MethodPut, ts.URL+"/api/manager/settings", bytes.NewBufferString(body)) req.SetBasicAuth("admin", "pw") resp, err := client.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("PUT settings status = %d, want 200", resp.StatusCode) } // Confirm response body reflects saved values var saved store.Settings if err := json.NewDecoder(resp.Body).Decode(&saved); err != nil { t.Fatal(err) } if saved.AutoStartProfiles || saved.RestartOnExit || saved.RestartIntervalSeconds != 12 { t.Fatalf("saved settings = %+v", saved) } // GET again to confirm persistence req2, _ := http.NewRequest(http.MethodGet, ts.URL+"/api/manager/settings", nil) req2.SetBasicAuth("admin", "pw") resp2, err2 := client.Do(req2) if err2 != nil { t.Fatal(err2) } defer resp2.Body.Close() var got store.Settings _ = json.NewDecoder(resp2.Body).Decode(&got) if got.AutoStartProfiles || got.RestartOnExit || got.RestartIntervalSeconds != 12 { t.Fatalf("settings after reload = %+v", got) } }