Files
edgeguard-native/internal/haproxy/haproxy_test.go
Debian 8aac24b566 feat(backends): Pool-Modell — Backend = Pool, N Server pro Backend
Migration 0016: backend_servers (id, backend_id, name, address, port,
weight, backup, active) + backends.lb_algorithm. Daten-Migration kopiert
bestehende backends.address/port als ersten Server, dann DROP COLUMN.

HAProxy-Renderer: rendert pro Backend einen Block mit `balance <algo>`
+ N `server`-Zeilen (weight, backup-Flag, optional check inter 5s).
LB-Algorithmen: roundrobin / leastconn / source.

REST: /backends/:id/servers (GET/POST), /backend-servers/:id (PUT/DELETE).
Re-rendert HAProxy nach jeder Server-Mutation.

UI: address/port aus Backend-Form raus, lb_algorithm-Select rein. Server
verwaltet ein expandable Sub-Panel pro Backend-Row (Tabelle + Add/Edit/
Delete-Modal). Domain-Attachment-Multi-Select bleibt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 20:55:47 +02:00

133 lines
3.6 KiB
Go

package haproxy
import (
"bytes"
"strings"
"testing"
"git.netcell-it.de/projekte/edgeguard-native/internal/models"
)
func renderView(t *testing.T, v View) string {
t.Helper()
var buf bytes.Buffer
if err := tpl.Execute(&buf, v); err != nil {
t.Fatalf("template execute: %v", err)
}
return buf.String()
}
func mkBackend(id int64, name string, hcp *string) models.Backend {
return models.Backend{
ID: id, Name: name, Scheme: "http",
LBAlgorithm: "roundrobin", Active: true,
HealthCheckPath: hcp,
}
}
func mkServer(backendID int64, name, addr string, port int) models.BackendServer {
return models.BackendServer{
BackendID: backendID, Name: name, Address: addr, Port: port,
Weight: 100, Active: true,
}
}
func TestRender_BaselineHasFrontendsAndApiBackend(t *testing.T) {
out := renderView(t, View{})
for _, w := range []string{
"frontend public_http",
"frontend public_https",
"frontend internal_stats",
"backend api_backend",
"server api1 127.0.0.1:9443 check",
"bind :443 ssl crt /etc/edgeguard/tls/",
"path_beg /.well-known/acme-challenge/",
"http-request redirect scheme https",
} {
if !strings.Contains(out, w) {
t.Errorf("missing %q in baseline output:\n%s", w, out)
}
}
}
func TestRender_DomainRoutesEmitUseBackend(t *testing.T) {
v := View{
Backends: []BackendView{
{Backend: mkBackend(1, "app", nil), Servers: []models.BackendServer{
mkServer(1, "app", "10.0.0.10", 8080),
}},
{Backend: mkBackend(2, "api", nil), Servers: []models.BackendServer{
mkServer(2, "api", "10.0.0.20", 9000),
}},
},
Domains: []DomainView{{
Domain: models.Domain{ID: 1, Name: "example.com", Active: true},
Routes: []RouteView{
{PathPrefix: "/", BackendID: 1},
{PathPrefix: "/api", BackendID: 2},
},
}},
}
out := renderView(t, v)
for _, w := range []string{
"backend eg_backend_1",
"server app 10.0.0.10:8080",
"backend eg_backend_2",
"server api 10.0.0.20:9000",
"balance roundrobin",
"use_backend eg_backend_1 if { hdr(host) -i example.com } { path_beg / }",
"use_backend eg_backend_2 if { hdr(host) -i example.com } { path_beg /api }",
} {
if !strings.Contains(out, w) {
t.Errorf("missing %q in output:\n%s", w, out)
}
}
}
func TestRender_HealthCheckPathAddsCheckInter(t *testing.T) {
hcp := "/health"
v := View{
Backends: []BackendView{
{Backend: mkBackend(1, "app", &hcp), Servers: []models.BackendServer{
mkServer(1, "app", "10.0.0.10", 8080),
}},
},
}
out := renderView(t, v)
if !strings.Contains(out, "server app 10.0.0.10:8080 check inter 5s") {
t.Errorf("expected `check inter 5s` for backend with health_check_path:\n%s", out)
}
if !strings.Contains(out, "option httpchk") {
t.Errorf("expected `option httpchk` when health_check_path set:\n%s", out)
}
}
func TestRender_MultiServerPool(t *testing.T) {
v := View{
Backends: []BackendView{
{
Backend: models.Backend{ID: 1, Name: "vmm", Scheme: "http", LBAlgorithm: "leastconn", Active: true},
Servers: []models.BackendServer{
{BackendID: 1, Name: "vmm-1", Address: "10.0.0.11", Port: 8080, Weight: 100, Active: true},
{BackendID: 1, Name: "vmm-2", Address: "10.0.0.12", Port: 8080, Weight: 100, Active: true},
{BackendID: 1, Name: "vmm-3", Address: "10.0.0.13", Port: 8080, Weight: 50, Backup: true, Active: true},
},
},
},
}
out := renderView(t, v)
for _, w := range []string{
"backend eg_backend_1",
"balance leastconn",
"server vmm-1 10.0.0.11:8080",
"server vmm-2 10.0.0.12:8080",
"server vmm-3 10.0.0.13:8080",
"weight 50",
" backup",
} {
if !strings.Contains(out, w) {
t.Errorf("missing %q in multi-server output:\n%s", w, out)
}
}
}