From 138cb3b3e01ebaccce6316f999e939622b7936c4 Mon Sep 17 00:00:00 2001 From: mlsmaycon Date: Sat, 12 Sep 2026 12:06:18 +0000 Subject: [PATCH] [management] Cover the gateway-pin claim against a real store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Belongs with 1b6a9d8 — the file was new and untracked, so the commit that added the query it exercises went out without it. Drives HasGatewayPinnedByOtherAccount through sqlite: another account is refused the host, the pinning account may still claim it (pin first, deploy the proxy after), an unpinned host stays free, and a labeled pin claims the cluster address rather than the labeled endpoint beneath it. --- .../sql_store_agentnetwork_gatewaypin_test.go | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 management/server/store/sql_store_agentnetwork_gatewaypin_test.go diff --git a/management/server/store/sql_store_agentnetwork_gatewaypin_test.go b/management/server/store/sql_store_agentnetwork_gatewaypin_test.go new file mode 100644 index 000000000..aa28adef0 --- /dev/null +++ b/management/server/store/sql_store_agentnetwork_gatewaypin_test.go @@ -0,0 +1,73 @@ +package store + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + agentNetworkTypes "github.com/netbirdio/netbird/management/internals/modules/agentnetwork/types" +) + +// TestHasGatewayPinnedByOtherAccount_RealStore drives the query a proxy +// registration asks before claiming a cluster address, against a real sqlite +// store. +// +// A gateway pin is a claim on the host: it is immutable, it is served by +// whichever proxy declares that address, and an account-scoped proxy only ever +// receives its own account's mappings — so a proxy from a different account +// taking the address strands the pin. The account's own pin is the opposite +// case and must stay claimable, because pinning first and deploying the proxy +// after is the documented order. +func TestHasGatewayPinnedByOtherAccount_RealStore(t *testing.T) { + ctx := context.Background() + s, cleanup, err := NewTestStoreFromSQL(ctx, "", t.TempDir()) + require.NoError(t, err, "real sqlite test store must come up") + defer cleanup() + + const ( + pinnedHost = "gw.account1.example.com" + freeHost = "nobody.example.com" + ) + + settings := agentNetworkTypes.DefaultSettings("account1") + settings.Domain = pinnedHost + settings.ProxyAddress = pinnedHost + require.NoError(t, s.CreateAgentNetworkSettings(ctx, settings), "seeding the pin must succeed") + + t.Run("another account is refused the host", func(t *testing.T) { + pinned, err := s.HasGatewayPinnedByOtherAccount(ctx, pinnedHost, "account2") + require.NoError(t, err) + assert.True(t, pinned, "a host another account pinned its gateway to is claimed") + }) + + t.Run("the pinning account may still claim it", func(t *testing.T) { + pinned, err := s.HasGatewayPinnedByOtherAccount(ctx, pinnedHost, "account1") + require.NoError(t, err) + assert.False(t, pinned, "an account must be able to deploy the proxy for its own pin") + }) + + t.Run("an unpinned host is free", func(t *testing.T) { + pinned, err := s.HasGatewayPinnedByOtherAccount(ctx, freeHost, "account2") + require.NoError(t, err) + assert.False(t, pinned, "a host no gateway is pinned to stays claimable") + }) + + t.Run("a labeled pin claims the cluster, not just the endpoint", func(t *testing.T) { + // A labeled bootstrap hangs