Files
webui4frpc/internal/httpapi/server_test.go
jianf 5237e3b594 fix: 注册 PUT /api/manager/settings 使设置保存生效
server.go NewServeMux 按方法分发到 handleSettingsPut(原仅注册 GET,PUT 静默返回旧值)

新增 TestSettingsPutRoundTrip 回归测试;gofmt 清理历史格式问题
2026-08-17 12:00:34 +08:00

141 lines
3.9 KiB
Go

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)
}
}