package main import ( "context" "net" "net/http" "net/http/httptest" "testing" "time" ) func TestWANIPv4RejectsPrivateAddress(t *testing.T) { server := interfaceServer(t, `{"rows":[{"identifier":"wan","description":"WAN","ipv4":[{"ipaddr":"10.180.186.43"}]}]}`) defer server.Close() client := &opnsenseClient{baseURL: server.URL, key: "key", secret: "secret", http: server.Client()} if _, err := client.wanIPv4(context.Background(), "WAN"); err == nil { t.Fatal("expected private address rejection") } } func TestWANIPv4ReturnsPublicAddressByDescription(t *testing.T) { server := interfaceServer(t, `{"rows":[{"identifier":"wan","description":"WAN","ipv4":[{"ipaddr":"203.0.113.7/24"}]}]}`) defer server.Close() client := &opnsenseClient{baseURL: server.URL, http: server.Client()} ip, err := client.wanIPv4(context.Background(), "WAN") if err != nil || !ip.Equal(net.ParseIP("203.0.113.7")) { t.Fatalf("WAN address = %v, error = %v", ip, err) } } func TestWANIPv4RejectsAmbiguousSelector(t *testing.T) { server := interfaceServer(t, `{"rows":[{"identifier":"wan","description":"WAN","ipv4":[{"ipaddr":"203.0.113.7"}]},{"identifier":"WAN","description":"other","ipv4":[{"ipaddr":"203.0.113.8"}]}]}`) defer server.Close() client := &opnsenseClient{baseURL: server.URL, http: server.Client()} if _, err := client.wanIPv4(context.Background(), "WAN"); err == nil { t.Fatal("expected ambiguous selector rejection") } } func interfaceServer(t *testing.T, body string) *httptest.Server { t.Helper() return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/interfaces/overview/interfaces_info" { t.Errorf("path = %q", r.URL.Path) } _, _ = w.Write([]byte(body)) })) } type recordingReconciler struct { cancel context.CancelFunc calls int } func (r *recordingReconciler) Reconcile(_ context.Context, _ net.IP) error { r.calls++ r.cancel() return nil } func TestPollDebouncesWANChange(t *testing.T) { server := interfaceServer(t, `{"rows":[{"identifier":"wan","description":"WAN","ipv4":[{"ipaddr":"203.0.113.7"}]}]}`) defer server.Close() client := &opnsenseClient{baseURL: server.URL, http: server.Client()} ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() reconciler := &recordingReconciler{cancel: cancel} _ = poll(ctx, client, "WAN", time.Millisecond, 2, reconciler) if reconciler.calls != 1 { t.Fatalf("reconcile calls = %d", reconciler.calls) } }