Files
Hammerhead/selfedge_test.go
VeillaxandClaude Sonnet 5.5 227c890409
CI / test (push) Successful in 4m52s
Release / release (push) Successful in 5m24s
Add Sandbar alert channel, tags for resources and sites, use Reef's secretbox
Sandbar channels raise and resolve alerts through its API, with the API key
sealed at rest and never returned to the browser. Resources and sites can be
tagged and filtered by tag; sites can be renamed. The secretbox package now
comes from Reef v0.1.9 instead of a private copy.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 03:32:43 -06:00

122 lines
3.6 KiB
Go

package hammerhead
import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"git.veillax.com/Veillax/reef/model"
"git.veillax.com/Veillax/reef/secretbox"
)
func edgeConfigOf(t *testing.T, s *Server, token string) model.EdgeConfig {
t.Helper()
ts := httptest.NewServer(s.Handler())
defer ts.Close()
req, _ := http.NewRequest("GET", ts.URL+"/edge/config?v=0", nil)
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
var cfg model.EdgeConfig
if resp.StatusCode != 200 {
t.Fatalf("edge config: %s", resp.Status)
}
json.NewDecoder(resp.Body).Decode(&cfg)
return cfg
}
func tokenFile(t *testing.T, dir string) string {
b, err := os.ReadFile(filepath.Join(dir, "self-edge.env"))
if err != nil {
t.Fatal(err)
}
return strings.TrimSpace(strings.SplitN(strings.SplitN(string(b), "BLACKTIP_TOKEN=", 2)[1], "\n", 2)[0])
}
var key = secretbox.GenerateKey()
func TestSelfEdgeServesTheDashboardWithoutAProxy(t *testing.T) {
dir := t.TempDir()
opts := Options{MasterKey: key, SelfDomain: "Hammerhead.Example.com", SelfTarget: "127.0.0.1:8080"}
s, err := New(dir, opts)
if err != nil {
t.Fatal(err)
}
token := tokenFile(t, dir)
cfg := edgeConfigOf(t, s, token)
if len(cfg.Mappings) != 1 || len(cfg.Local) != 1 {
t.Fatalf("mappings=%d local=%d", len(cfg.Mappings), len(cfg.Local))
}
m := cfg.Mappings[0]
if m.Proto != model.ProtoHTTPS || m.Hostname != "hammerhead.example.com" || m.Port != 443 || !m.RedirectHTTP || !m.Enabled ||
len(m.Targets) != 1 || m.Targets[0].Host != "127.0.0.1" || m.Targets[0].Port != 8080 || m.Targets[0].SiteID != cfg.Local[0] {
t.Fatalf("unexpected dashboard route: %+v", m)
}
if m.Auth.Protected() {
t.Fatal("the dashboard has its own login and must not sit behind resource auth")
}
edgeID := s.st.Settings.SelfEdgeID
if info, _ := os.Stat(filepath.Join(dir, "self-edge.env")); info.Mode().Perm()&0o077 != 0 && os.PathSeparator == '/' {
t.Fatalf("token file is readable by others: %v", info.Mode())
}
s.Close()
// a restart keeps the same edge and token instead of making a new one
s2, err := New(dir, opts)
if err != nil {
t.Fatal(err)
}
defer s2.Close()
if s2.st.Settings.SelfEdgeID != edgeID || tokenFile(t, dir) != token || len(s2.st.Edges) != 1 {
t.Fatal("restart changed the self edge")
}
if len(edgeConfigOf(t, s2, token).Mappings) != 1 {
t.Fatal("route missing after restart")
}
}
func TestNoSelfEdgeUnlessAsked(t *testing.T) {
dir := t.TempDir()
s, err := New(dir, Options{MasterKey: key})
if err != nil {
t.Fatal(err)
}
defer s.Close()
if len(s.st.Edges) != 0 {
t.Fatal("an edge appeared without HAMMERHEAD_SELF_DOMAIN")
}
if _, err := os.Stat(filepath.Join(dir, "self-edge.env")); err == nil {
t.Fatal("token file written without HAMMERHEAD_SELF_DOMAIN")
}
}
func TestSavingSettingsKeepsTheSelfEdge(t *testing.T) {
dir := t.TempDir()
opts := Options{AdminEmail: "admin@test.local", AdminPassword: "password123", MasterKey: key, SelfDomain: "hh.example.com", SelfTarget: "127.0.0.1:8080"}
s, err := New(dir, opts)
if err != nil {
t.Fatal(err)
}
token := tokenFile(t, dir)
do := apiClient(t, s)
if code, b := do("PUT", "/api/admin/settings", map[string]any{"dashboardUrl": "https://hh.example.com", "acmeEmail": "me@example.com"}); code != 200 {
t.Fatalf("settings: %d %s", code, b)
}
s.Close()
s2, err := New(dir, opts)
if err != nil {
t.Fatal(err)
}
defer s2.Close()
if len(s2.st.Edges) != 1 || tokenFile(t, dir) != token {
t.Fatalf("saving settings lost the self edge: %d edges", len(s2.st.Edges))
}
}