657fca325e
Adds opt-in extension package internal/automc/ that: - Subscribes to Postgres notifications on a 'servers' table and pushes route changes into server.Routes (no file I/O, no fsnotify). - Provides a WakerFunc that POSTs to a configurable HTTP control plane (server-manager) and polls until state=running. When AUTOMC_DSN is unset, Wire() is a no-op and the binary behaves exactly like upstream itzg/mc-router. Single patch site in main.go (import + 4-line call) keeps upstream rebases trivial. See docs/AUTOMC.md for env vars and the expected DB schema/trigger.
104 lines
3.0 KiB
Go
104 lines
3.0 KiB
Go
package automc
|
|
|
|
import (
|
|
"sort"
|
|
"testing"
|
|
)
|
|
|
|
func TestDiff(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
prev, next map[string]route
|
|
wantAddHost []string
|
|
wantDel []string
|
|
}{
|
|
{
|
|
name: "empty to empty",
|
|
prev: map[string]route{},
|
|
next: map[string]route{},
|
|
wantAddHost: nil,
|
|
wantDel: nil,
|
|
},
|
|
{
|
|
name: "add one",
|
|
prev: map[string]route{},
|
|
next: map[string]route{"a.example.com": {name: "a", domain: "a.example.com", address: "10.0.0.1:25565"}},
|
|
wantAddHost: []string{"a.example.com"},
|
|
wantDel: nil,
|
|
},
|
|
{
|
|
name: "delete one",
|
|
prev: map[string]route{"a.example.com": {name: "a", domain: "a.example.com", address: "10.0.0.1:25565"}},
|
|
next: map[string]route{},
|
|
wantAddHost: nil,
|
|
wantDel: []string{"a.example.com"},
|
|
},
|
|
{
|
|
name: "address change",
|
|
prev: map[string]route{"a.example.com": {name: "a", domain: "a.example.com", address: "10.0.0.1:25565"}},
|
|
next: map[string]route{"a.example.com": {name: "a", domain: "a.example.com", address: "10.0.0.2:25565"}},
|
|
wantAddHost: []string{"a.example.com"},
|
|
wantDel: nil,
|
|
},
|
|
{
|
|
name: "name change with same address triggers re-register (waker rebind)",
|
|
prev: map[string]route{"a.example.com": {name: "a", domain: "a.example.com", address: "10.0.0.1:25565"}},
|
|
next: map[string]route{"a.example.com": {name: "b", domain: "a.example.com", address: "10.0.0.1:25565"}},
|
|
wantAddHost: []string{"a.example.com"},
|
|
wantDel: nil,
|
|
},
|
|
{
|
|
name: "no change",
|
|
prev: map[string]route{"a.example.com": {name: "a", domain: "a.example.com", address: "10.0.0.1:25565"}},
|
|
next: map[string]route{"a.example.com": {name: "a", domain: "a.example.com", address: "10.0.0.1:25565"}},
|
|
wantAddHost: nil,
|
|
wantDel: nil,
|
|
},
|
|
{
|
|
name: "mixed add + delete",
|
|
prev: map[string]route{
|
|
"a.example.com": {name: "a", domain: "a.example.com", address: "10.0.0.1:25565"},
|
|
"b.example.com": {name: "b", domain: "b.example.com", address: "10.0.0.2:25565"},
|
|
},
|
|
next: map[string]route{
|
|
"a.example.com": {name: "a", domain: "a.example.com", address: "10.0.0.1:25565"},
|
|
"c.example.com": {name: "c", domain: "c.example.com", address: "10.0.0.3:25565"},
|
|
},
|
|
wantAddHost: []string{"c.example.com"},
|
|
wantDel: []string{"b.example.com"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
add, del := diff(tc.prev, tc.next)
|
|
gotAdd := make([]string, 0, len(add))
|
|
for _, r := range add {
|
|
gotAdd = append(gotAdd, r.domain)
|
|
}
|
|
sort.Strings(gotAdd)
|
|
sort.Strings(del)
|
|
sort.Strings(tc.wantAddHost)
|
|
sort.Strings(tc.wantDel)
|
|
if !equalSlice(gotAdd, tc.wantAddHost) {
|
|
t.Errorf("add: got %v want %v", gotAdd, tc.wantAddHost)
|
|
}
|
|
if !equalSlice(del, tc.wantDel) {
|
|
t.Errorf("del: got %v want %v", del, tc.wantDel)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func equalSlice(a, b []string) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
for i := range a {
|
|
if a[i] != b[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|