Skip to content

fix(auth): harden error handling and CI linting - #9

Merged
Jonathan0823 merged 15 commits into
masterfrom
test/auth-coverage-sonar
Aug 3, 2026
Merged

Jonathan0823 merged 15 commits into
masterfrom
test/auth-coverage-sonar

Conversation

@Jonathan0823

@Jonathan0823 Jonathan0823 commented Aug 3, 2026 •

Copy link
Copy Markdown
Owner

Related issue

  • None

Summary

  • Hardened auth HTTP error handling to consistently ignore Gin c.Error return values.
  • Aligned rate-limit/token helper types across JWT, middleware, and tests.
  • Made server startup fail fast when r.Run(...) returns an error.
  • Added a CI job for golangci-lint.

Scope

  • In scope:
    • auth and user HTTP handlers
    • OAuth callback error handling
    • rate-limit helper and token interface type cleanup
    • server startup behavior
    • CI lint workflow
  • Out of scope:
    • rate-limit policy changes
    • database/schema migrations
    • auth flow redesign

Testing

  • go test ./...
  • go test -race ./...
  • go vet ./...
  • golangci-lint run
  • git diff --check

Test details

  • Commands run:
    • go test ./...
    • go test -race ./...
    • go vet ./...
    • golangci-lint run
    • git diff --check
  • Results:
    • all passed

Reviewer focus

  • Please review:
    • internal/adapter/inbound/http/auth_handler.go
    • internal/adapter/inbound/http/oauth_handler.go
    • internal/adapter/inbound/http/user_handler.go
    • internal/platform/server.go
    • .github/workflows/ci.yml
  • Risk areas:
    • error propagation paths in auth/user handlers
    • startup failure handling
    • CI job correctness

Notes

  • Migrations/config changes: none
  • Target repo/base branch: Jonathan0823/auth-go → master
  • Follow-ups: none

Summary by CodeRabbit

  • Bug Fixes

    • Server startup failures are now reported clearly instead of being silently ignored.
    • Authentication and user-facing error handling remains consistent across supported flows.
  • Tests

    • Expanded coverage for authentication, OAuth, user operations, middleware, rate limiting, metrics, email, PostgreSQL repositories, and core services.
    • Added validation for invalid configuration and unavailable service backends.
  • Chores

    • Added automated lint checks to continuous integration.
    • Updated code quality analysis exclusions and modernized internal Go type usage.

@gitguardian

gitguardian Bot commented Aug 3, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
35590728 Triggered Username Password ce2d191 internal/adapter/outbound/postgres/user_integration_test.go View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Jonathan0823, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 20a7c6ac-8f1a-48fc-9584-34359981054b

📥 Commits

Reviewing files that changed from the base of the PR and between 2532a5e and 19725a7.

📒 Files selected for processing (7)
  • .gitguardian.yaml
  • .github/workflows/ci.yml
  • internal/adapter/inbound/http/validate.go
  • internal/adapter/outbound/jwt/jwt_test.go
  • internal/adapter/outbound/postgres/user_integration_test.go
  • internal/bootstrap/bootstrap.go
  • internal/bootstrap/ratelimit_test.go
📝 Walkthrough

Walkthrough

The pull request adds CI linting, expands HTTP, service, adapter, PostgreSQL, rate-limit, and metrics tests, standardizes selected any types, and makes Gin and server startup error handling explicit.

Changes

Authentication platform

Layer / File(s) Summary
HTTP error handling and runtime cleanup
internal/adapter/inbound/http/..., internal/platform/server.go
Handlers and middleware explicitly discard c.Error return values. User claims use map[string]any. Server startup handles r.Run errors.
HTTP handler and middleware coverage
internal/adapter/inbound/http/*_test.go, internal/adapter/inbound/http/middleware/*_test.go
Tests cover authentication, OAuth, user operations, cookies, validation, middleware authentication, metrics, and DTO mappings.
Service and adapter contracts
internal/core/service/service_test.go, internal/adapter/outbound/..., internal/core/port/token.go
Tests cover service flows, repositories, email, OAuth hooks, password hashing, and rate-limit adapters. Selected signatures and assertions use any.
Platform validation and CI
internal/platform/*_test.go, .github/workflows/ci.yml, sonar-project.properties
Tests cover rate-limit configuration, backend selection, limiter behavior, and metric normalization. CI adds golangci-lint. Sonar exclusions expand.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: hardened authentication error handling and added CI linting.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/auth-coverage-sonar

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
internal/adapter/outbound/oauth/oauth_test.go (1)

45-47: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the returned error identity.

The check accepts any non-nil error. The test passes even if withProviderErr fails for an unrelated reason instead of propagating the injected error. Compare against the injected sentinel error.

♻️ Proposed change
-	if err := withProviderErr("google", func() error { return errors.New("provider failure") }); err == nil {
-		t.Fatal("withProviderErr succeeded unexpectedly")
+	wantErr := errors.New("provider failure")
+	if err := withProviderErr("google", func() error { return wantErr }); !errors.Is(err, wantErr) {
+		t.Fatalf("withProviderErr error = %v, want %v", err, wantErr)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/adapter/outbound/oauth/oauth_test.go` around lines 45 - 47, Update
the withProviderErr test to define and retain the injected sentinel error, then
assert that the returned error matches that exact error identity rather than
only checking for non-nil. Keep the existing unexpected-success failure
behavior.
internal/bootstrap/ratelimit_test.go (2)

31-34: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Compare the sentinel error with errors.Is.

Line 32 uses != on the error value. That comparison breaks if newRateLimitStore ever wraps port.ErrRateLimitBackendUnavailable with fmt.Errorf("%w", ...). Use errors.Is so the assertion survives wrapping.

♻️ Proposed change
 	store, client, err := newRateLimitStore(platform.RateLimitConfig{Backend: "unknown"}, nil)
-	if store != nil || client != nil || err != port.ErrRateLimitBackendUnavailable {
+	if store != nil || client != nil || !errors.Is(err, port.ErrRateLimitBackendUnavailable) {
 		t.Fatalf("unknown backend = %v, %v, %v", store, client, err)
 	}

Add "errors" to the import block at lines 3-9.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/bootstrap/ratelimit_test.go` around lines 31 - 34, Update the
unknown-backend assertion for newRateLimitStore to use errors.Is when checking
port.ErrRateLimitBackendUnavailable, and add the errors import required by that
assertion. Keep the existing nil checks and failure message unchanged.

16-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Discard the Close error returns explicitly.

port.RateLimitStore.Close and redis.Client.Close both return error, and the CI job runs golangci-lint without an explicit linter config. Match the _ = pattern used for intentional discards at these calls.

♻️ Proposed change
-	memory.Close()
+	_ = memory.Close()
-	postgres.Close()
+	_ = postgres.Close()
-	redis.Close()
-	redisClient.Close()
+	_ = redis.Close()
+	_ = redisClient.Close()

Also applies to: 22-22, 28-29

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/bootstrap/ratelimit_test.go` at line 16, Explicitly discard the
errors returned by the Close calls in the rate-limit tests by assigning each
intentional result to the blank identifier, including the calls on memory and
redis clients. Apply the same pattern to all referenced cleanup calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Line 48: Update the actions/checkout@v4 step in the lint job to set
persist-credentials to false, while preserving the existing read-only job
permissions and subsequent third-party action flow.

In `@internal/adapter/outbound/email/email_test.go`:
- Around line 7-13: Update TestSenderRequiresCredentials to assert that Send
returns the exact expected “email credentials not set” error, rather than only
checking for a non-nil error. Preserve the existing empty EMAIL and PASSWORD
setup and failure behavior.

In `@internal/adapter/outbound/postgres/user_integration_test.go`:
- Line 63: Update the test fixtures around Users().UpdatePassword to avoid
credential-shaped literals and rename any related fixture variables as needed,
while preserving the test’s behavior; use clearly non-secret placeholder values
such as generic test data rather than hash- or password-like strings.

In `@internal/platform/metrics_test.go`:
- Around line 41-50: The test TestMetricsNormalizesUnknownValues currently
passes values that are already valid fallback labels and does not verify
normalization. Replace those inputs with unsupported values such as bad_event,
then inspect the gathered metrics and assert their labels use only the allowed
fallback values, including unknown and provider none, while retaining the
valid-event coverage.

---

Nitpick comments:
In `@internal/adapter/outbound/oauth/oauth_test.go`:
- Around line 45-47: Update the withProviderErr test to define and retain the
injected sentinel error, then assert that the returned error matches that exact
error identity rather than only checking for non-nil. Keep the existing
unexpected-success failure behavior.

In `@internal/bootstrap/ratelimit_test.go`:
- Around line 31-34: Update the unknown-backend assertion for newRateLimitStore
to use errors.Is when checking port.ErrRateLimitBackendUnavailable, and add the
errors import required by that assertion. Keep the existing nil checks and
failure message unchanged.
- Line 16: Explicitly discard the errors returned by the Close calls in the
rate-limit tests by assigning each intentional result to the blank identifier,
including the calls on memory and redis clients. Apply the same pattern to all
referenced cleanup calls.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6d6840f2-0771-40ce-ace5-d49d02153d6c

📥 Commits

Reviewing files that changed from the base of the PR and between bcfadb5 and 2532a5e.

📒 Files selected for processing (27)
  • .github/workflows/ci.yml
  • internal/adapter/inbound/http/auth_handler.go
  • internal/adapter/inbound/http/dto/user_test.go
  • internal/adapter/inbound/http/handler_test.go
  • internal/adapter/inbound/http/middleware/auth_test.go
  • internal/adapter/inbound/http/middleware/logging_test.go
  • internal/adapter/inbound/http/middleware/metrics_test.go
  • internal/adapter/inbound/http/middleware/middleware.go
  • internal/adapter/inbound/http/oauth_handler.go
  • internal/adapter/inbound/http/user_handler.go
  • internal/adapter/inbound/http/user_helper.go
  • internal/adapter/outbound/email/email_test.go
  • internal/adapter/outbound/jwt/jwt.go
  • internal/adapter/outbound/oauth/oauth_test.go
  • internal/adapter/outbound/password/password_test.go
  • internal/adapter/outbound/postgres/db.go
  • internal/adapter/outbound/postgres/helpers.go
  • internal/adapter/outbound/postgres/user_integration_test.go
  • internal/adapter/outbound/ratelimit/redis.go
  • internal/bootstrap/ratelimit_test.go
  • internal/core/port/token.go
  • internal/core/service/service_test.go
  • internal/platform/metrics_test.go
  • internal/platform/rate_limit_config_test.go
  • internal/platform/rate_limiter_test.go
  • internal/platform/server.go
  • sonar-project.properties
💤 Files with no reviewable changes (1)
  • internal/adapter/outbound/postgres/helpers.go

Comment thread .github/workflows/ci.yml
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files around workflows =="
git ls-files .github/workflows || true

echo "== ci.yml relevant content =="
if [ -f .github/workflows/ci.yml ]; then
  nl -ba .github/workflows/ci.yml | sed -n '1,140p'
else
  echo "missing .github/workflows/ci.yml"
fi

echo "== checkout uses with persist-credentials context =="
rg -n "actions/checkout|persist-credentials|permissions:" .github/workflows || true

Repository: Jonathan0823/auth-go

Length of output: 287


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== available tools =="
command -v cat || true
command -v sed || true
command -v grep || true

echo "== ci.yml relevant content =="
if [ -f .github/workflows/ci.yml ]; then
  cat -n .github/workflows/ci.yml | sed -n '1,140p'
else
  echo "missing .github/workflows/ci.yml"
fi

echo "== checkout and permissions matching =="
grep -nE "actions/checkout|persist-credentials|permissions:" .github/workflows/ci.yml || true

Repository: Jonathan0823/auth-go

Length of output: 4791


🌐 Web query:

actions/checkout v4 persist-credentials default true README

💡 Result:

The persist-credentials input for the actions/checkout GitHub Action has a default value of true [1][2][3]. This setting controls whether the action configures the provided GitHub token (or SSH key) within the local git configuration [1][4]. When enabled (the default), it allows your workflow steps to execute authenticated git commands (such as git fetch or git push) automatically without requiring additional manual configuration [5][4]. While persist-credentials: true remains the default, security implementations have evolved. In version 6.0.0 and later, the action was updated to store these credentials in a separate file under $RUNNER_TEMP rather than writing them directly into the .git/config file [4][3][6]. This change was designed to improve security by isolating the credentials [4][3]. If you wish to opt out of this behavior, you can explicitly set persist-credentials: false in your workflow step [5][4]. During post-job cleanup, the action automatically removes the persisted credentials [5][4].

Citations:


Do not persist the checkout credential.

actions/checkout persists GITHUB_TOKEN by default, and this lint job hands control to a third-party action afterward. The workflow already limits job permissions to contents: read, so add persist-credentials: false.

🧰 Tools
🪛 zizmor (1.28.0)

[warning] 48-48: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 48, Update the actions/checkout@v4 step in
the lint job to set persist-credentials to false, while preserving the existing
read-only job permissions and subsequent third-party action flow.

Source: Linters/SAST tools

Comment on lines +7 to +13
func TestSenderRequiresCredentials(t *testing.T) {
t.Setenv("EMAIL", "")
t.Setenv("PASSWORD", "")
if err := NewSender().Send("user@example.com", "subject", "body"); err == nil {
t.Fatal("Send succeeded without credentials")
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate the email sender implementation and inspect credential handling in Send.
fd -t f -e go . internal/adapter/outbound/email
ast-grep outline internal/adapter/outbound/email --items all
rg -n -C 10 'func .*Send\(' internal/adapter/outbound/email

Repository: Jonathan0823/auth-go

Length of output: 1837


Assert the credential guard error explicitly.

Send returns fmt.Errorf("email credentials not set") before constructing the message. Update the test to expect that error instead of any non-nil error so a future refactor cannot remove the guard accidentally.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/adapter/outbound/email/email_test.go` around lines 7 - 13, Update
TestSenderRequiresCredentials to assert that Send returns the exact expected
“email credentials not set” error, rather than only checking for a non-nil
error. Preserve the existing empty EMAIL and PASSWORD setup and failure
behavior.

Comment thread internal/adapter/outbound/postgres/user_integration_test.go Outdated
Comment on lines +41 to +50
func TestMetricsNormalizesUnknownValues(t *testing.T) {
metrics := NewMetrics(nil)
metrics.RecordSecurityEvent("unknown", "unknown", "unknown", "unknown")
metrics.RecordRateLimitEvent("/unknown", "unknown", "unknown", "unknown", "unknown")
metrics.RecordSecurityEvent(EventAuthLogin, OutcomeSuccess, ReasonNone, "github")
metrics.RecordRateLimitEvent("/api/auth/login", "login_ip", "memory", OutcomeDetected, ReasonRateLimited)
if _, err := metrics.Registry.Gather(); err != nil {
t.Fatal(err)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline internal/platform/metrics.go --items all --type function
rg -n -A20 -B2 'func normalize(Event|Outcome|Reason|Provider|Route|Policy|Backend)' internal/platform/metrics.go

Repository: Jonathan0823/auth-go

Length of output: 3335


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## metrics.go relevant sections"
sed -n '1,230p' internal/platform/metrics.go | cat -n

echo "## metrics_test.go relevant sections"
sed -n '1,140p' internal/platform/metrics_test.go | cat -n

echo "## call sites for normalize functions and label use"
rg -n -C3 'normalize(Event|Outcome|Reason|Provider|Route|Policy|Backend)|RegisterSecurityEvent|RecordRateLimitEvent|RecordSecurityEvent|OutcomesLabels|ReasonsLabels|ProvidersLabels|RoutesLabels|PoliciesLabels|BackendsLabels|EventsLabels' internal/platform

Repository: Jonathan0823/auth-go

Length of output: 18967


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Prometheus normal label value handling candidates"
rg -n -C2 'NormalizeLabel|normalize.*label|labelValues|WithLabel|LabelValues' vendor/github.com/prometheus/client_golang prometheus 2>/dev/null || true

echo "## Read-only behavioral probe for normalized constant inputs"
cat > /tmp/check_normalize.go <<'EOF'
package main

import (
	"fmt"
)

func normalizeEvent(event string) string {
	switch event {
	case "register", "auth_login":
		return event
	default:
		return "unknown"
	}
}

func normalizeOutcome(outcome string) string {
	switch outcome {
	case "success", "failure", "detected":
		return outcome
	default:
		return "unknown"
	}
}

func normalizeProvider(provider string) string {
	switch provider {
	case "github", "google":
		return provider
	default:
		return "none"
	}
}

func main() {
	records := [][4]string{
		{"unknown", "unknown", "unknown", "unknown"},
		{"unknown", "unknown", "unknown", "valid_provider"},
		{"/unknown", "unknown", "none", "unknown", "unknown"},
	}
	for _, r := range records {
		fmt.Printf("%v\n", recordLabels(r))
	}
}

func recordLabels(r [5]string) [4]string {
	return [4]string{
		normalizeEvent(r[0]),
		normalizeOutcome(r[1]),
		normalizeProvider(r[3]),
		"",
	}
}
EOF
go run /tmp/check_normalize.go

Repository: Jonathan0823/auth-go

Length of output: 480


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## normalize input constants from metrics.go"
sed -n '1,170p' internal/platform/metrics.go | cat -n

echo
echo "## Test normalization: raw unsupported versus literal known constants"
python3 - <<'PY'
def normalize_event(v):
    return v if v in {
        "register","auth_login","logout","refresh","refresh_replay","password_reset_request","password_reset","email_verification","oauth_auth","rate_limited","rate_limiter_unavailable"
    } else "unknown"

def normalize_outcome(v):
    return v if v in {"success","failure","detected"} else "unknown"

def normalize_reason(v):
    return v if v.startswith("reason") else "unknown"

def normalize_policy(v):
    return v if v.startswith("login_ip") or v in {"register_ip","register_email"} else "unknown"

def normalize_backend(v):
    return v if v in {"memory","redis","postgres"} else "unknown"

def normalize_route(v):
    return "/api/auth/login" if v == "/known" else "/unknown" if v == "/other" else v

record_supported = {
    "event": "auth_login",
    "outcome": "success",
    "reason": "reason_none",
    "provider": "github",
}

record_literal_unknown = {
    "event": "unknown",
    "outcome": "unknown",
    "reason": "unknown",
    "provider": "unknown",
}

record_unsupported = {
    "event": "bad_event",
    "outcome": "bad_outcome",
    "reason": "bad_reason",
    "provider": `bad_provider`,
}

for name, record in {
    "known_supported": record_supported,
    "literal_unknown": record_literal_unknown,
    "unsupported": record_unsupported,
}.items():
    print(name, {
        "event": normalize_event(record["event"]),
        "outcome": normalize_outcome(record["outcome"]),
        "reason": normalize_reason(record["reason"]),
        "provider": normalize_provider(record["provider"]),
    })
PY

echo
echo "## Read-only behavioral probe for normalized constant inputs"
cat > /tmp/check_normalize.go <<'EOF'
package main

import (
	"fmt"
)

func normalizeEvent(v string) string {
	if v == "auth_login" {
		return v
	}
	if v == "unknown" {
		return v
	}
	return "unknown"
}

func normalizeOutcome(outcome string) string {
	if outcome == "success" || outcome == "failure" || outcome == "detected" {
		return outcome
	}
	return "unknown"
}

func normalizeProvider(provider string) string {
	if provider == "github" || provider == "google" {
		return provider
	}
	return "none"
}

func main() {
	for _, r := range [][4]string{
		{"auth_login", "success", "bad_reason", "github"},
		{"unknown", "unknown", "unknown", "unknown"},
		{"bad_event", "bad_outcome", "bad_reason", "bad_provider"},
	} {
		fmt.Printf("%v -> event=%v outcome=%v reason=%v provider=%v\n",
			r,
			normalizeEvent(r[0]),
			normalizeOutcome(r[1]),
			normalizeReason(r[2]),
			normalizeProvider(r[3]),
		)
	}
}

func normalizeReason(reason string) string {
	if reason == "reason_none" {
		return reason
	}
	return "unknown"
}
EOF
go run /tmp/check_normalize.go

Repository: Jonathan0823/auth-go

Length of output: 6473


Assert the normalized label values for unsupported inputs.

TestMetricsNormalizesUnknownValues passes literal "unknown" inputs, which match normalizeEvent, normalizeOutcome, and normalizeReason before Gather. Use unsupported values such as bad_event and assert that gathered labels only contain the allowed fallback labels, like "unknown" or provider "none", to keep the bounded-label-cardinality test coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/platform/metrics_test.go` around lines 41 - 50, The test
TestMetricsNormalizesUnknownValues currently passes values that are already
valid fallback labels and does not verify normalization. Replace those inputs
with unsupported values such as bad_event, then inspect the gathered metrics and
assert their labels use only the allowed fallback values, including unknown and
provider none, while retaining the valid-event coverage.

@Jonathan0823
Jonathan0823 merged commit f545a4d into master Aug 3, 2026
11 checks passed
@Jonathan0823
Jonathan0823 deleted the test/auth-coverage-sonar branch August 3, 2026 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant