From 031f6f24b1b0777390108f7f2e63c2ece58e8b69 Mon Sep 17 00:00:00 2001 From: Peter Rounce Date: Wed, 17 Jun 2026 13:31:37 +0000 Subject: [PATCH] CI: cut `go test -race` time by lowering bcrypt cost in tests (0.22.3) The `build` job's Test step was ~3 min and gates `docker-card`, pacing the whole pipeline. Per-step timings showed `go test -race -count=1 ./...` is ~90% of the job, the `web` package is ~95% of that, and its slowest tests are all bcrypt-bound: every setupAdminSession hashes a password and the 2FA tests hash 10 recovery codes each at bcrypt.DefaultCost (~80ms/hash), doubled by -race. Route all hashing through a package var `bcryptCost` (production unchanged at bcrypt.DefaultCost) and lower it to bcrypt.MinCost in a web-package TestMain. CompareHashAndPassword reads the cost from the hash, so verification gets cheaper too and the hashing path stays fully exercised. Measured (1-core box): web package -race 3m51s -> 26s; full ./... -race 34s, all green. CI's ~3-min Test step should drop to well under a minute. Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 2 +- docker/card/build/build.go | 2 +- docker/card/web/main_test.go | 19 +++++++++++++++++++ docker/card/web/util.go | 8 +++++++- 4 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 docker/card/web/main_test.go diff --git a/CLAUDE.md b/CLAUDE.md index dee9bd5..addae5a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -93,7 +93,7 @@ Entry point: `main.go` → opens SQLite DB → runs CLI or starts HTTP server on - `phoenix/` — HTTP client for Phoenix Server API (invoices, payments, balance, channels). Uses basic auth from phoenix config (password cached at startup with `sync.Once`) - `crypto/` — AES-CMAC authentication and AES decryption for Bolt Card NFC protocol - `util/` — Error handling helpers (`CheckAndLog`), random hex generation, QR code encoding -- `build/` — Version string (currently "0.22.2"), date/time injected at build +- `build/` — Version string (currently "0.22.3"), date/time injected at build - `web-content/` — Static assets under `public/`, SPA build output under `admin/spa/` ### Route Groups (`web/app.go`) diff --git a/docker/card/build/build.go b/docker/card/build/build.go index 625d8b1..ece3781 100644 --- a/docker/card/build/build.go +++ b/docker/card/build/build.go @@ -1,5 +1,5 @@ package build -var Version string = "0.22.2" +var Version string = "0.22.3" var Date string var Time string diff --git a/docker/card/web/main_test.go b/docker/card/web/main_test.go new file mode 100644 index 0000000..cf9e8c0 --- /dev/null +++ b/docker/card/web/main_test.go @@ -0,0 +1,19 @@ +package web + +import ( + "os" + "testing" + + "golang.org/x/crypto/bcrypt" +) + +// TestMain lowers the bcrypt work factor for the whole web test package. +// Production keeps bcrypt.DefaultCost (see HashPassword in util.go); the +// admin-login and 2FA recovery-code tests hash dozens of values per run, and +// at DefaultCost that hashing — amplified by the race detector — dominated +// CI's `go test -race` step (~3 min). MinCost keeps the hashing path fully +// exercised while making it roughly 64x cheaper. +func TestMain(m *testing.M) { + bcryptCost = bcrypt.MinCost + os.Exit(m.Run()) +} diff --git a/docker/card/web/util.go b/docker/card/web/util.go index a97ca5e..f659dd6 100644 --- a/docker/card/web/util.go +++ b/docker/card/web/util.go @@ -25,8 +25,14 @@ func GetPwHash(db_conn *sql.DB, passwordStr string) (passwordHashStr string) { return passwordHashStr } +// bcryptCost is the work factor used for all password/recovery-code hashing. +// Production keeps bcrypt.DefaultCost; the test suite lowers it (see TestMain +// in main_test.go) because the admin-login and 2FA tests hash dozens of values +// and at DefaultCost that alone dominated CI's `go test -race` step. +var bcryptCost = bcrypt.DefaultCost + func HashPassword(passwordStr string) (string, error) { - hash, err := bcrypt.GenerateFromPassword([]byte(passwordStr), bcrypt.DefaultCost) + hash, err := bcrypt.GenerateFromPassword([]byte(passwordStr), bcryptCost) return string(hash), err }