Frontend in web/ (Vite via bun, pure CSS, no component libraries), built to internal/dashboard/dist and embedded into the binary; the Dockerfile builds it in a bun stage. Old static dashboard.html removed.
123 lines
3.4 KiB
Go
123 lines
3.4 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"io/fs"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.tzgyn.dev/TZGyn/tunnel/internal/dashboard"
|
|
)
|
|
|
|
func TestAdminOverview(t *testing.T) {
|
|
store, err := OpenStore("")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
api := &API{
|
|
Store: store, Hub: NewHub(store), Domain: "opentunnel.test",
|
|
AdminToken: "admin-secret", StartedAt: time.Now().Add(-90 * time.Second), Version: "test",
|
|
}
|
|
srv := httptest.NewServer(api)
|
|
defer srv.Close()
|
|
|
|
if _, _, err := store.createTunnel("opentunnel.test"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// no token configured → 503
|
|
noToken := &API{Store: store, Hub: NewHub(store), Domain: "opentunnel.test", StartedAt: time.Now()}
|
|
noTokenSrv := httptest.NewServer(noToken)
|
|
defer noTokenSrv.Close()
|
|
resp, err := http.Get(noTokenSrv.URL + "/api/admin/overview")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp.StatusCode != 503 {
|
|
t.Errorf("unconfigured admin token: got %d, want 503", resp.StatusCode)
|
|
}
|
|
resp.Body.Close()
|
|
|
|
// missing and wrong token → 401
|
|
for _, header := range []string{"", "Bearer nope"} {
|
|
req, _ := http.NewRequest("GET", srv.URL+"/api/admin/overview", nil)
|
|
if header != "" {
|
|
req.Header.Set("Authorization", header)
|
|
}
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp.StatusCode != 401 {
|
|
t.Errorf("auth %q: got %d, want 401", header, resp.StatusCode)
|
|
}
|
|
resp.Body.Close()
|
|
}
|
|
|
|
// correct token → 200 with the tunnel and server info
|
|
req, _ := http.NewRequest("GET", srv.URL+"/api/admin/overview", nil)
|
|
req.Header.Set("Authorization", "Bearer admin-secret")
|
|
resp, err = http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != 200 {
|
|
t.Fatalf("admin overview: got %d, want 200", resp.StatusCode)
|
|
}
|
|
var overview adminOverview
|
|
if err := json.NewDecoder(resp.Body).Decode(&overview); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if overview.Server.Domain != "opentunnel.test" || overview.Server.Version != "test" {
|
|
t.Errorf("server info: %+v", overview.Server)
|
|
}
|
|
if overview.Server.UptimeSeconds < 60 {
|
|
t.Errorf("uptime: %d", overview.Server.UptimeSeconds)
|
|
}
|
|
if overview.Totals.Tunnels != 1 || overview.Totals.Online != 0 {
|
|
t.Errorf("totals: %+v", overview.Totals)
|
|
}
|
|
if len(overview.Tunnels) != 1 || !strings.HasSuffix(overview.Tunnels[0].Hostname, ".opentunnel.test") {
|
|
t.Errorf("tunnels: %+v", overview.Tunnels)
|
|
}
|
|
if overview.Tunnels[0].State != "offline" {
|
|
t.Errorf("tunnel state: %q", overview.Tunnels[0].State)
|
|
}
|
|
if overview.CA.Expires == "" {
|
|
t.Error("CA expiry missing")
|
|
}
|
|
}
|
|
|
|
func TestDashboardPage(t *testing.T) {
|
|
store, err := OpenStore("")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
api := &API{Store: store, Hub: NewHub(store), Domain: "opentunnel.test", StartedAt: time.Now()}
|
|
srv := httptest.NewServer(api)
|
|
defer srv.Close()
|
|
|
|
if _, err := fs.Stat(dashboard.FS(), "index.html"); err != nil {
|
|
t.Skip("dashboard not built (run `bun install && bun run build` in web/)")
|
|
}
|
|
for _, path := range []string{"/", "/admin"} {
|
|
resp, err := http.Get(srv.URL + path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body, _ := io.ReadAll(resp.Body)
|
|
resp.Body.Close()
|
|
if resp.StatusCode != 200 || !strings.Contains(resp.Header.Get("Content-Type"), "text/html") {
|
|
t.Errorf("%s: got %d %q", path, resp.StatusCode, resp.Header.Get("Content-Type"))
|
|
}
|
|
if !strings.Contains(string(body), `id="app"`) {
|
|
t.Errorf("%s: body does not look like the built dashboard", path)
|
|
}
|
|
}
|
|
}
|