From a07fb3867a66307e75a8d3ac1943c902e2b76858 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 5 Aug 2026 10:14:38 -0400 Subject: [PATCH] =?UTF-8?q?fix(net):=20thread=20hops=20into=20session=20cr?= =?UTF-8?q?eation;=20disambiguate=20card=20tests=20=E2=80=94=20#2453?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two CI failures from 381e9ced, both mine. **Go (vet, which cascaded into the integration job).** Widening auth.ClientIP to take a hop count, I updated the middleware that TOUCHES a session but missed the two places that CREATE one — handleLogin and handleRegister. So `created_ip`, the frozen origin address that the whole "address changed" comparison rests on, was the one value still being computed the old way. Both now read h.netSettings.Hops(), which is nil-safe so test handlers constructed without the service still work. Worth noting the shape of this miss: I checked call sites by searching for the middleware's own usage and stopped there, rather than for every caller of the function whose signature I changed. vet found it in seconds; a grep for `auth.ClientIP(` would have too. **Web (vitest).** Three tests waited on `findByText('198.51.100.7')`, which matches TWO elements in the fixture — the detected client address and the forwarded chain, identical strings for a single-proxy setup — and findByText throws on multiple matches. Now they wait on the unique "Your address right now" label and assert the address with getAllByText where duplication is legitimate. The duplication is correct behaviour, so the test moved rather than the component. --- internal/api/auth.go | 2 +- internal/api/auth_register.go | 2 +- web/src/lib/components/NetworkSettingsCard.test.ts | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/api/auth.go b/internal/api/auth.go index 46e18c2e..ac125463 100644 --- a/internal/api/auth.go +++ b/internal/api/auth.go @@ -100,7 +100,7 @@ func (h *handlers) handleLogin(w http.ResponseWriter, r *http.Request) { // the active-sessions surface: a session that was born somewhere the // user recognises but is being used from somewhere they don't is the // case this whole surface exists to surface. - Ip: auth.ClientIP(r), + Ip: auth.ClientIP(r, h.netSettings.Hops()), }); err != nil { h.logger.Error("api: insert session failed", "err", err) writeErr(w, apierror.InternalMsg("insert failed", err)) diff --git a/internal/api/auth_register.go b/internal/api/auth_register.go index 5bf1803e..6f098787 100644 --- a/internal/api/auth_register.go +++ b/internal/api/auth_register.go @@ -175,7 +175,7 @@ func (h *handlers) handleRegister(w http.ResponseWriter, r *http.Request) { UserID: user.ID, TokenHash: auth.HashSessionToken(sessionToken), UserAgent: r.UserAgent(), - Ip: auth.ClientIP(r), + Ip: auth.ClientIP(r, h.netSettings.Hops()), }); err != nil { h.logger.Error("register: insert session failed", "err", err) writeErr(w, apierror.Internal(err)) diff --git a/web/src/lib/components/NetworkSettingsCard.test.ts b/web/src/lib/components/NetworkSettingsCard.test.ts index b7bc4eef..11aebb36 100644 --- a/web/src/lib/components/NetworkSettingsCard.test.ts +++ b/web/src/lib/components/NetworkSettingsCard.test.ts @@ -34,7 +34,10 @@ describe('NetworkSettingsCard', () => { getNetworkSettings.mockResolvedValue(settings()); render(NetworkSettingsCard); - expect(await screen.findByText('198.51.100.7')).toBeTruthy(); + // The address legitimately appears twice — as the detected client and + // inside the forwarded chain — so wait on the unique label, not the value. + await screen.findByText('Your address right now'); + expect(screen.getAllByText('198.51.100.7').length).toBeGreaterThan(0); expect(screen.getByText('172.18.0.1:40000')).toBeTruthy(); }); @@ -83,7 +86,7 @@ describe('NetworkSettingsCard', () => { ); render(NetworkSettingsCard); - await screen.findByText('198.51.100.7'); + await screen.findByText('Your address right now'); expect(screen.queryByText(/arrived with/)).toBeNull(); }); @@ -101,6 +104,6 @@ describe('NetworkSettingsCard', () => { const retry = await screen.findByRole('button', { name: 'Try again' }); getNetworkSettings.mockResolvedValue(settings()); await fireEvent.click(retry); - await screen.findByText('198.51.100.7'); + await screen.findByText('Your address right now'); }); });