From f95d88726697c70bdb6589762c26341cc74edacc Mon Sep 17 00:00:00 2001 From: Zach Buchheit Date: Fri, 21 Aug 2026 16:36:33 -0700 Subject: [PATCH] appc: reproduce route coalescing issue Today when you change from using domain discovery to a preset app for app connectors, we only coalesce the first route that matches the broader CIDR range. According to documentation at https://tailscale.com/docs/reference/best-practices/app-connectors#route-coalescing This is not intended functionality. Addresses #20956 Signed-off-by: Zach Buchheit --- appc/appconnector_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/appc/appconnector_test.go b/appc/appconnector_test.go index c58aa8041..86d977ee7 100644 --- a/appc/appconnector_test.go +++ b/appc/appconnector_test.go @@ -164,6 +164,39 @@ func TestUpdateRoutesUnadvertisesContainedRoutes(t *testing.T) { } } +func TestUpdateRoutesCoalescesAllCoveredRoutes(t *testing.T) { + ctx := t.Context() + rc := &appctest.RouteCollector{} + a := NewAppConnector(Config{ + Logf: t.Logf, + EventBus: eventbustest.NewBus(t), + RouteAdvertiser: rc, + }) + t.Cleanup(a.Close) + + discovered := prefixes("192.0.2.1/32", "192.0.2.2/32", "192.0.2.3/32") + a.domains = map[string][]netip.Addr{ + "example.com": { + netip.MustParseAddr("192.0.2.1"), + netip.MustParseAddr("192.0.2.2"), + netip.MustParseAddr("192.0.2.3"), + }, + } + rc.SetRoutes(slices.Clone(discovered)) + + want := prefixes("192.0.2.0/24") + a.updateRoutes(want) + a.Wait(ctx) + + if got := rc.Routes(); !slices.Equal(got, want) { + t.Errorf("routes = %v, want %v", got, want) + } + + if got := slices.Clone(rc.RemovedRoutes()); !slices.Equal(got, discovered) { + t.Errorf("removed routes = %v, want %v", got, discovered) + } +} + func TestDomainRoutes(t *testing.T) { bus := eventbustest.NewBus(t) for _, shouldStore := range []bool{false, true} {