From 55795ddfc4d6d8fdc7e659d6079ff4b9435f5676 Mon Sep 17 00:00:00 2001 From: sneak Date: Sat, 3 Oct 2026 00:37:04 +0000 Subject: [PATCH] Hash passwords with 1 MiB in tests so make test fits in 4 GiB (closes #261) On a build machine with 4 GiB, docker build . failed: the auth test binary was killed for lack of memory. upaasd hashes passwords with argon2id at 64 MiB per hash; the auth and handlers tests run many such hashes at once, and the race detector multiplies what each one takes, so make test peaked at 2.7 GiB. The memory per hash is now the auth service's ArgonMemory field. New sets it to the same 64 MiB, which upaasd keeps; the test helpers lower it to 1 MiB. A new test checks that New sets 64 MiB and hashes and verifies a password with it. GOMAXPROCS=4 make test now peaks at about 1.4 GiB. Model: opus-5-5 --- TODO.md | 6 ++++++ internal/handlers/handlers_test.go | 3 +++ internal/service/auth/auth.go | 20 ++++++++++++++------ internal/service/auth/auth_test.go | 19 +++++++++++++++++++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/TODO.md b/TODO.md index 196aba3..cefc74b 100644 --- a/TODO.md +++ b/TODO.md @@ -20,6 +20,12 @@ regress. # Completed Steps +- 2026-10-03: `make test`, and so `docker build .`, fits a machine with 4 GiB of + memory: tests hash passwords with 1 MiB instead of upaasd's 64 MiB, so + `GOMAXPROCS=4 make test` peaks at about 1.4 GiB instead of 2.7 GiB. upaasd + still hashes with 64 MiB, and one test hashes and verifies a password at that + cost (#261). + - 2026-10-02: In a window too narrow for the top bar, such as 390 px, the New App and Logout buttons move to a second row instead of running into "by @sneak"; the bar keeps a gap between its two sides at every width (#272). diff --git a/internal/handlers/handlers_test.go b/internal/handlers/handlers_test.go index 211d3c6..d784811 100644 --- a/internal/handlers/handlers_test.go +++ b/internal/handlers/handlers_test.go @@ -109,6 +109,9 @@ func createAppServices( }) require.NoError(t, authErr) + // 1 MiB per password hash instead of 64 MiB; see auth.Service.ArgonMemory. + authSvc.ArgonMemory = 1024 + appSvc, appErr := app.New(fx.Lifecycle(nil), app.ServiceParams{ Logger: logInstance, Database: dbInstance, diff --git a/internal/service/auth/auth.go b/internal/service/auth/auth.go index 0277a47..6dd8db8 100644 --- a/internal/service/auth/auth.go +++ b/internal/service/auth/auth.go @@ -59,6 +59,13 @@ type ServiceParams struct { // Service provides authentication functionality. type Service struct { + // ArgonMemory is the memory each argon2id hash takes, in KiB. New sets + // argonMemory, 64 MiB, and upaasd never changes it. Tests lower it, since + // many 64 MiB hashes at once under the race detector need more memory + // than a 4 GiB build machine has. A hash verifies only with the value it + // was made with. + ArgonMemory uint32 + log *slog.Logger db *database.Database store *sessions.CookieStore @@ -77,10 +84,11 @@ func New(_ fx.Lifecycle, params ServiceParams) (*Service, error) { } return &Service{ - log: params.Logger.Get(), - db: params.Database, - store: store, - params: ¶ms, + ArgonMemory: argonMemory, + log: params.Logger.Get(), + db: params.Database, + store: store, + params: ¶ms, }, nil } @@ -97,7 +105,7 @@ func (svc *Service) HashPassword(password string) (string, error) { []byte(password), salt, argonTime, - argonMemory, + svc.ArgonMemory, argonThreads, argonKeyLen, ) @@ -132,7 +140,7 @@ func (svc *Service) VerifyPassword(hashedPassword, password string) bool { []byte(password), salt, argonTime, - argonMemory, + svc.ArgonMemory, argonThreads, argonKeyLen, ) diff --git a/internal/service/auth/auth_test.go b/internal/service/auth/auth_test.go index 3e0c8c2..e284db7 100644 --- a/internal/service/auth/auth_test.go +++ b/internal/service/auth/auth_test.go @@ -65,6 +65,10 @@ func setupTestService(t *testing.T) (*auth.Service, func()) { }) require.NoError(t, err) + // 1 MiB per hash instead of 64 MiB; see Service.ArgonMemory. The tests + // that use setupAuthService keep 64 MiB. + svc.ArgonMemory = 1024 + // t.TempDir() automatically cleans up after test cleanup := func() {} @@ -237,6 +241,21 @@ func TestVerifyPassword(testingT *testing.T) { }) } +// TestHashPasswordWithUpaasdMemory hashes and verifies a password with the +// memory New sets, which upaasd uses. setupTestService lowers it. +func TestHashPasswordWithUpaasdMemory(t *testing.T) { + t.Parallel() + + svc := setupAuthService(t, false) + require.Equal(t, uint32(64*1024), svc.ArgonMemory) + + hash, err := svc.HashPassword("correctpassword") + require.NoError(t, err) + + assert.True(t, svc.VerifyPassword(hash, "correctpassword")) + assert.False(t, svc.VerifyPassword(hash, "wrongpassword")) +} + func TestIsSetupRequired(testingT *testing.T) { testingT.Parallel()