Files
certctl/internal/auth/session/coverage_fill_test.go
T
shankar0123 80cbd2db59 test(coverage): backfill 5 packages to clear v2.1.0 release-gate Phase 3 floors
Phase 3 of /Users/shankar/Desktop/cowork/v2.1.0-release-gate.md surfaced
four packages below their coverage floors. All four are regressions from
new code shipped in the audit-2026-05-10/11 fix bundles that didn't get
per-function tests:

  internal/auth/breakglass    87.5% -> 93.3% (floor: 90%)
    + List (was 0%) — 3 tests (disabled, empty+populated, repo err)
    + RemoveCredential, Unlock disabled-branch tests

  internal/auth/oidc          89.4% -> 95.4% (floor: 90%)
    + JWKSStatus (was 0%) — 2 tests (unknown provider, after AuthRequest)
    + TestDiscovery (was 0%) — 5 tests (discovery failure, happy path,
      HS256 alg-downgrade detected, missing jwks_uri, JWKS 500 fetch)

  internal/auth/session       89.9% -> 94.4% (floor: 90%)
    + SetTrustedProxies (was 0%) — round-trip + clear
    + ComputeCookieHMAC (was 0%) — determinism + key/inputs differ
    + DecryptKeyMaterial (was 0%) — round-trip + wrong-passphrase

  internal/api/handler        73.2% -> 75.5% (floor: 75%)
    + 6 auth_breakglass handler funcs (were all 0%) — 14 tests
      (disabled/404, invalid JSON, empty fields, service err, happy
      path with cookies, admin endpoints, ListCredentials no
      password_hash on the wire)
    + WithPermissionChecker setter test (was 0%, Bundle 2 MED-2)
    + NewAdminCRLCacheServiceImpl + CacheRows (were 0%) — 3 tests
    + itoaForRetryAfter + challengeURLBuilder ACME helpers (were 0%) —
      4 tests

All five coverage gates green:

  internal/service                                    72.7% (floor: 70%)
  internal/api/handler                                75.5% (floor: 75%)
  internal/api/middleware                             67.9% (floor: 30%)
  internal/auth                                       93.3% (floor: 85%)
  internal/service/auth                               91.8% (floor: 85%)
  internal/auth/oidc                                  95.4% (floor: 90%)
  internal/auth/oidc/groupclaim                      100.0% (floor: 95%)
  internal/auth/oidc/domain                           97.6% (floor: 90%)
  internal/auth/session                               94.4% (floor: 90%)
  internal/auth/session/domain                        98.3% (floor: 90%)
  internal/auth/breakglass                            93.3% (floor: 90%)
  internal/auth/breakglass/domain                    100.0% (floor: 90%)
  internal/auth/user/domain                           96.2% (floor: 90%)
  (and 6 more — all green)

Per CLAUDE.md operating rule: 'Lowering a floor REQUIRES corresponding
code-side test work — never lower the gate to make CI green.' The
floors stay at their committed values; the new tests close the gap.
2026-05-11 14:12:11 +00:00

90 lines
2.9 KiB
Go

package session
import (
"crypto/hmac"
"crypto/sha256"
"testing"
)
// Coverage fill — v2.1.0 release gate Phase 3.
//
// Three previously-uncovered surfaces:
//
// - SetTrustedProxies (cmd/server config wire)
// - ComputeCookieHMAC (pre-login cookie verifier helper)
// - DecryptKeyMaterial (pre-login HMAC-key derive)
//
// Each is a thin wrapper called by main.go or the pre-login flow that
// never exits through a unit-test fixture. The tests below run them
// directly so the coverage gate stops flagging the package.
func TestSetTrustedProxies_RoundTrip(t *testing.T) {
t.Parallel() //nolint:paralleltest // shared package-level state
// Snapshot + restore so concurrent tests don't observe the override.
prev := trustedProxyCIDRs
defer func() { trustedProxyCIDRs = prev }()
want := []string{"10.0.0.0/8", "192.0.2.1"}
SetTrustedProxies(want)
if len(trustedProxyCIDRs) != len(want) {
t.Fatalf("expected %d entries, got %d", len(want), len(trustedProxyCIDRs))
}
for i, c := range want {
if trustedProxyCIDRs[i] != c {
t.Errorf("entry %d: got %q, want %q", i, trustedProxyCIDRs[i], c)
}
}
// Empty slice clears.
SetTrustedProxies(nil)
if len(trustedProxyCIDRs) != 0 {
t.Errorf("expected nil/empty after clear; got %v", trustedProxyCIDRs)
}
}
func TestComputeCookieHMAC_Deterministic(t *testing.T) {
t.Parallel()
key := []byte("a-32-byte-key-for-hmac-test-pad!")
mac1 := ComputeCookieHMAC("ses-1", "actor-1", key)
mac2 := ComputeCookieHMAC("ses-1", "actor-1", key)
if !hmac.Equal(mac1, mac2) {
t.Errorf("HMAC must be deterministic for the same inputs")
}
// Length is sha256.Size.
if len(mac1) != sha256.Size {
t.Errorf("expected len=%d (sha256), got %d", sha256.Size, len(mac1))
}
// Differing id2 changes the HMAC.
if hmac.Equal(mac1, ComputeCookieHMAC("ses-1", "actor-2", key)) {
t.Errorf("HMAC must differ when actor changes")
}
// Differing id1 changes the HMAC.
if hmac.Equal(mac1, ComputeCookieHMAC("ses-2", "actor-1", key)) {
t.Errorf("HMAC must differ when session changes")
}
}
func TestDecryptKeyMaterial_RoundTrip(t *testing.T) {
t.Parallel()
// encryptKeyMaterial + decryptKeyMaterial are the pair; round-trip
// asserts the public DecryptKeyMaterial wrapper does not bypass
// the decryption path.
plaintext := []byte("plain-32-byte-key-for-hmac-pad!!")
const passphrase = "test-passphrase-for-key-encrypt"
ct, err := encryptKeyMaterial(plaintext, passphrase)
if err != nil {
t.Fatalf("encryptKeyMaterial: %v", err)
}
got, err := DecryptKeyMaterial(ct, passphrase)
if err != nil {
t.Fatalf("DecryptKeyMaterial: %v", err)
}
if string(got) != string(plaintext) {
t.Errorf("decrypt mismatch: got %q, want %q", got, plaintext)
}
// Wrong passphrase → error (forwarded from decryptKeyMaterial).
if _, err := DecryptKeyMaterial(ct, "wrong-passphrase"); err == nil {
t.Errorf("expected error with wrong passphrase, got nil")
}
}