Files
EvoBGP/internal/httpapi/speakers_test.go
T
Denozordec 927e27640a
quality / commitlint (push) Skipped
quality / changes (push) Successful in 8s
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 26s
quality / web (push) Successful in 1m27s
quality / go (push) Successful in 1m18s
quality / bird2 (push) Successful in 16s
CD / quality (push) Successful in 3m43s
CD / publish (push) Successful in 3m11s
feat(docs): update speaker installation instructions and logging details
- Enhanced the speaker installation documentation to clarify the use of TCP port 179 and the logging commands for monitoring BIRD and evobgp-agent.
- Updated the speaker form dialog to include additional information about MikroTik connections and logging commands.
- Modified the BIRD configuration to include logging to stderr for better visibility during operations.
- Adjusted the Docker Compose configuration to ensure proper network settings and sysctl configurations for BGP functionality.
2026-08-21 16:11:28 +07:00

199 lines
6.4 KiB
Go

package httpapi
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestPostSpeaker_defaultsFromEndpointIP(t *testing.T) {
srv, err := New(Options{InsecureDev: true, SeedDemo: true, BundleSeedHex: testBundleSeed})
if err != nil {
t.Fatal(err)
}
defer srv.Close()
tenant, _, _, _, _ := srv.Store().DemoIDs()
mustSetTestAPIKeys(t, srv, "edkey|"+tenant+"|editor")
body := `{"endpoint":"https://203.0.113.55:8443","role":"replica"}`
req := httptest.NewRequest(http.MethodPost, "/v1/speakers", strings.NewReader(body))
req.Header.Set("Authorization", "Bearer edkey")
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
srv.Handler().ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Fatalf("status %d body %s", rec.Code, rec.Body.String())
}
var out map[string]any
if err := json.Unmarshal(rec.Body.Bytes(), &out); err != nil {
t.Fatal(err)
}
if out["agent_secret"] == nil || out["agent_secret"] == "" {
t.Fatal("expected agent_secret on create")
}
if out["node_token"] == nil || out["node_token"] == "" {
t.Fatal("expected node_token on replica create")
}
if out["node_ipv4"] != "203.0.113.55" {
t.Fatalf("node_ipv4: %#v", out["node_ipv4"])
}
if out["bird_bgp_source_ipv4"] != "203.0.113.55" {
t.Fatalf("bird_bgp_source_ipv4: %#v", out["bird_bgp_source_ipv4"])
}
install, _ := out["install"].(map[string]any)
if install == nil {
t.Fatal("expected install on replica create")
}
cmd, _ := install["docker_commands"].(string)
if !strings.Contains(cmd, "traefik") || !strings.Contains(cmd, "dnschallenge") {
t.Fatalf("docker_commands missing traefik dns challenge: %s", cmd[:min(200, len(cmd))])
}
}
func TestDeleteSpeaker(t *testing.T) {
srv, err := New(Options{InsecureDev: true, SeedDemo: true, BundleSeedHex: testBundleSeed})
if err != nil {
t.Fatal(err)
}
defer srv.Close()
tenant, _, _, _, demoSpk := srv.Store().DemoIDs()
mustSetTestAPIKeys(t, srv, "edkey|"+tenant+"|editor")
req := httptest.NewRequest(http.MethodDelete, "/v1/speakers/"+demoSpk, nil)
req.Header.Set("Authorization", "Bearer edkey")
rec := httptest.NewRecorder()
srv.Handler().ServeHTTP(rec, req)
if rec.Code != http.StatusNoContent {
t.Fatalf("status %d body %s", rec.Code, rec.Body.String())
}
if _, err := srv.Store().GetSpeaker(tenant, demoSpk); err == nil {
t.Fatal("speaker should be deleted")
}
}
func TestGetBundleSigningPublicKey(t *testing.T) {
srv, err := New(Options{InsecureDev: true, SeedDemo: true, BundleSeedHex: testBundleSeed})
if err != nil {
t.Fatal(err)
}
defer srv.Close()
tenant, _, _, _, _ := srv.Store().DemoIDs()
mustSetTestAPIKeys(t, srv, "vwkey|"+tenant+"|viewer")
req := httptest.NewRequest(http.MethodGet, "/v1/bundle/signing-public-key", nil)
req.Header.Set("Authorization", "Bearer vwkey")
rec := httptest.NewRecorder()
srv.Handler().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status %d body %s", rec.Code, rec.Body.String())
}
var out map[string]any
_ = json.Unmarshal(rec.Body.Bytes(), &out)
if out["public_key_base64"] == nil || out["public_key_base64"] == "" {
t.Fatalf("missing public_key_base64: %#v", out)
}
}
func TestPostSpeaker_installCommandsAndMetaObject(t *testing.T) {
srv, err := New(Options{InsecureDev: true, SeedDemo: true, BundleSeedHex: testBundleSeed})
if err != nil {
t.Fatal(err)
}
defer srv.Close()
tenant, _, _, _, _ := srv.Store().DemoIDs()
mustSetTestAPIKeys(t, srv, "edkey|"+tenant+"|editor")
body := `{
"endpoint":"https://bgp-dc2.example.com",
"role":"replica",
"meta_json":{"agent_domain":"bgp-dc2.example.com","node_ipv4":"203.0.113.10"},
"letsencrypt_email":"ops@example.com",
"cf_dns_api_token":"cf-token-xyz",
"panel_ip_whitelist":"203.0.113.1/32",
"control_plane_url":"https://cp.example.com"
}`
req := httptest.NewRequest(http.MethodPost, "/v1/speakers", strings.NewReader(body))
req.Header.Set("Authorization", "Bearer edkey")
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
h := srv.Handler()
h.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Fatalf("status %d body %s", rec.Code, rec.Body.String())
}
var out map[string]any
if err := json.Unmarshal(rec.Body.Bytes(), &out); err != nil {
t.Fatal(err)
}
id, _ := out["id"].(string)
if id == "" {
t.Fatal("missing id")
}
secret, _ := out["agent_secret"].(string)
token, _ := out["node_token"].(string)
pub, _ := out["bundle_pubkey_base64"].(string)
if secret == "" || token == "" || pub == "" {
t.Fatalf("missing one-shot secrets: %#v", out)
}
install, _ := out["install"].(map[string]any)
cmd, _ := install["docker_commands"].(string)
for _, want := range []string{
"traefik",
"dnschallenge=true",
"dnschallenge.provider=cloudflare",
"CF_DNS_API_TOKEN",
"cf-token-xyz",
"Host(`bgp-dc2.example.com`)",
secret,
token,
"https://cp.example.com",
`"179:179/tcp"`,
} {
if !strings.Contains(cmd, want) {
t.Errorf("docker_commands missing %q", want)
}
}
get := httptest.NewRequest(http.MethodGet, "/v1/speakers/"+id, nil)
get.Header.Set("Authorization", "Bearer edkey")
grec := httptest.NewRecorder()
h.ServeHTTP(grec, get)
if grec.Code != http.StatusOK {
t.Fatalf("GET status %d body %s", grec.Code, grec.Body.String())
}
got := grec.Body.String()
if strings.Contains(got, secret) || strings.Contains(got, token) || strings.Contains(got, "docker_commands") {
t.Fatalf("GET must not leak install secrets: %s", got)
}
}
func TestPostSpeaker_masterSkipsInstall(t *testing.T) {
srv, err := New(Options{InsecureDev: true, SeedDemo: true, BundleSeedHex: testBundleSeed})
if err != nil {
t.Fatal(err)
}
defer srv.Close()
tenant, _, _, _, _ := srv.Store().DemoIDs()
mustSetTestAPIKeys(t, srv, "edkey|"+tenant+"|editor")
body := `{"endpoint":"https://127.0.0.1:8080","role":"master"}`
req := httptest.NewRequest(http.MethodPost, "/v1/speakers", strings.NewReader(body))
req.Header.Set("Authorization", "Bearer edkey")
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
srv.Handler().ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Fatalf("status %d body %s", rec.Code, rec.Body.String())
}
var out map[string]any
_ = json.Unmarshal(rec.Body.Bytes(), &out)
if out["node_token"] != nil {
t.Fatalf("master must not mint node_token: %#v", out["node_token"])
}
if out["install"] != nil {
t.Fatalf("master must not include install: %#v", out["install"])
}
}