Files
OpenFlare/internal/apps/openflare/integration/node_page_test.go
T
ArthurKoba 3c7b347d7b perf(nodes): paginate node lists and fetch details directly (#54)
* perf(nodes): paginate node lists and fetch details directly

* fix(nodes): preserve Unicode search and page transition state

* fix(nodes): validate search using the UTF-8 byte limit
2026-11-07 21:29:52 +08:00

87 lines
3.5 KiB
Go

// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package integration
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"testing"
"github.com/Rain-kl/Wavelet/internal/apps/openflare/node"
"github.com/Rain-kl/Wavelet/internal/shared/response"
"github.com/Rain-kl/Wavelet/internal/testhelper"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
)
func TestNodePageAPI(t *testing.T) {
_, seed, cleanup := setupCoreChainTest(t)
engine := testhelper.NewTestGinEngine(sessions.Sessions("node-page-test", cookie.NewStore([]byte("node-page-test-session-secret"))))
mountOpenFlareTestRoutes(engine)
t.Cleanup(cleanup)
headers := adminAuthHeaders(seed.Token)
created := performJSONRequest(t, engine, http.MethodPost, apiPath("/nodes"), node.Input{Name: "Searchable Edge"}, headers)
if created.Code != http.StatusOK {
t.Fatalf("create node status=%d body=%s", created.Code, created.Body.String())
}
var createdEnvelope struct {
Data node.View `json:"data"`
}
if err := json.Unmarshal(created.Body.Bytes(), &createdEnvelope); err != nil {
t.Fatal(err)
}
for _, path := range []string{"/nodes/page", fmt.Sprintf("/nodes/%d", createdEnvelope.Data.ID)} {
unauthorized := performJSONRequest(t, engine, http.MethodGet, apiPath(path), nil, nil)
var denied response.Any
if err := json.Unmarshal(unauthorized.Body.Bytes(), &denied); err != nil {
t.Fatal(err)
}
if unauthorized.Code != http.StatusUnauthorized || denied.Data != nil || denied.ErrorMsg == "" {
t.Errorf("GET %s withoutauth status=%d body=%s,want401 withoutdata", path, unauthorized.Code, unauthorized.Body.String())
}
}
page := performJSONRequest(t, engine, http.MethodGet, apiPath("/nodes/page?search=searchable&status=pending&node_type=edge_node"), nil, headers)
var envelope struct {
ErrorMsg string `json:"error_msg"`
Data node.ListResult `json:"data"`
}
if err := json.Unmarshal(page.Body.Bytes(), &envelope); err != nil {
t.Fatal(err)
}
if page.Code != http.StatusOK || envelope.ErrorMsg != "" || envelope.Data.Total != 1 || len(envelope.Data.Results) != 1 {
t.Fatalf("GET page status=%d body=%s,wantone", page.Code, page.Body.String())
}
detail := performJSONRequest(t, engine, http.MethodGet, apiPath(fmt.Sprintf("/nodes/%d", createdEnvelope.Data.ID)), nil, headers)
if detail.Code != http.StatusOK {
t.Errorf("GET detail status=%d body=%s,want200", detail.Code, detail.Body.String())
}
missing := performJSONRequest(t, engine, http.MethodGet, apiPath("/nodes/99999"), nil, headers)
if missing.Code != http.StatusNotFound {
t.Errorf("GET missing node status=%d,want404", missing.Code)
}
invalid := []string{"page=-1", "page=no", "page=9223372036854775807&page_size=100", "page_size=-1", "page_size=no", "status=healthy", "node_type=relay", "search=" + strings.Repeat("x", 257)}
for _, query := range invalid {
bad := performJSONRequest(t, engine, http.MethodGet, apiPath("/nodes/page?"+query), nil, headers)
var body response.Any
if err := json.Unmarshal(bad.Body.Bytes(), &body); err != nil {
t.Fatal(err)
}
if bad.Code != http.StatusBadRequest || body.Data != nil || body.ErrorMsg == "" {
t.Errorf("GET page?%s status=%d body=%s,want400 null data", query, bad.Code, bad.Body.String())
}
}
legacy := performJSONRequest(t, engine, http.MethodGet, apiPath("/nodes"), nil, headers)
var old struct {
Data []node.View `json:"data"`
}
if err := json.Unmarshal(legacy.Body.Bytes(), &old); err != nil {
t.Fatalf("legacy array JSON: %v", err)
}
if len(old.Data) != 1 {
t.Errorf("legacy len=%d,want1", len(old.Data))
}
}