Files
Charon/docs/plans/phase2_user_mgmt_discovery.md
GitHub Actions eee9f429d9 docs: Add QA Definition of Done Verification Report and update Vulnerability Assessment Phase 2
- Created a comprehensive QA Definition of Done (DoD) Verification Report detailing the status of E2E tests, coverage, type safety, pre-commit hooks, linting, and security scans.
- Documented findings on React rendering issues, test execution times, and recommendations for CI scheduling.
- Updated the Vulnerability Assessment Phase 2 report with detailed CVE findings, risk assessments, and remediation plans for identified vulnerabilities in dependencies.
2026-02-10 07:24:14 +00:00

6.6 KiB

Phase 2.2 - User Management Discovery & Root Cause Analysis

Status: Discovery Complete - Root Cause Identified Date Started: 2026-02-09 Objective: Identify root causes of 6 failing user management tests

Root Cause: Synchronous Email Blocking in InviteUser

CRITICAL FINDING

Code Location: /projects/Charon/backend/internal/api/handlers/user_handler.go (lines 400-470) Problem Method: InviteUser handler Issue: Email sending blocks HTTP response - entire request hangs until SMTP completes or times out

Why Tests Timeout (Test #248)

Request flow in InviteUser:

1. Check admin role                          ✅ <1ms
2. Parse request JSON                        ✅ <1ms
3. Check email exists                        ✅ Database query
4. Generate invite token                     ✅ <1ms
5. Create user in database (transaction)     ✅ Database write
6. ❌ BLOCKS: Call h.MailService.SendInvite() - SYNCHRONOUS SMTP
   └─ Connect to SMTP server
   └─ Authenticate
   └─ Send email
   └─ Wait for confirmation (NO TIMEOUT!)
7. Return JSON response (if email succeeds)

The Problem: Lines 462-469:

// Try to send invite email
emailSent := false
if h.MailService.IsConfigured() {
    baseURL, ok := utils.GetConfiguredPublicURL(h.DB)
    if ok {
        appName := getAppName(h.DB)
        if err := h.MailService.SendInvite(user.Email, inviteToken, appName, baseURL); err == nil {
            emailSent = true
        }
    }
}

This code blocks the HTTP request until SendInvite() returns.

MailService Architecture

File: /projects/Charon/backend/internal/services/mail_service.go Method: SendEmail() at line 255

The SendEmail() method:

  • Makes direct SMTP connections via smtp.SendMail() (line 315)
  • OR custom TLS dialect for SSL/STARTTLS
  • Waits for SMTP response before returning
  • No async queue, no goroutines, no background workers

Example: If SMTP server takes 5 seconds to respond (or 30s timeout): → HTTP request blocks for 5-30+ seconds → Playwright test times out after 60s

Why Test #248 Fails

Test expectation: "Invite user, get response, user appears in list" Actual behavior: "Invite user → blocks on SMTP → no response → test timeout"

Test File: /projects/Charon/tests/monitoring/uptime-monitoring.spec.ts (for reference) When SMTP is configured: Request hangs indefinitely When SMTP is NOT configured: Request completes quickly (MailService.IsConfigured() = false)

Other Test Failures (Tests #258, #260, #262, #269-270)

Status: Likely Unrelated to Email Blocking

These tests involve:

  • #258: Update permission mode
  • #260: Remove permitted hosts
  • #262: Enable/disable user toggle
  • #269: Update user role to admin
  • #270: Update user role to user

Reason: These endpoints (PUT /users/:id/permissions, PUT /users/:id) do NOT send emails

Hypothesis for other timeouts:

  • Possible slow database queries (missing indexes?)
  • Possible missing database preloading (N+1 queries?)
  • Frontend mocking/test infrastructure issue (not handler code)
  • Transaction deadlocks (concurrent test execution)

Status: Requires separate investigation

Solution Approach for Phase 2.1

Recommendation: Async Email Sending

Change: Convert email sending to background job pattern:

  1. Create user in database
  2. Return response immediately (201 Created)
  3. → Send email asynchronously (goroutine/queue)
  4. → If email fails, log error, user still created

Before:

// User creation + email (both must succeed to return)
tx.Create(&user)      // ✅
SendEmail(...)         // ❌ BLOCKS - no timeout
return JSON(user)      // Only if above completes

After:

// User creation (fast) + async email (non-blocking)
tx.Create(&user)       // ✅ <100ms
go SendEmailAsync(...) // 🔄 Background (non-blocking)
return JSON(user)      // ✅ Immediate response (~150ms total)

Manual Testing Findings

SMTP Configuration Status: NOT configured in test database Result: Invite endpoint returns immediately (emailSent=false skip) Test Environment: Application accessible at http://localhost:8080

Code Verification:

  • POST /users/invite endpoint EXISTS and is properly registered
  • PUT /users/:id/permissions endpoint EXISTS and is properly registered
  • GET /users endpoint EXISTS (for list display)
  • User models properly initialized with permission_mode and permitted_hosts
  • Database schema includes all required fields

Root Cause Summary

Issue Severity Root Cause Impact
Test #248 Timeout CRITICAL Sync SMTP blocking HTTP response InviteUser endpoint completely unavailable when SMTP is slow
Test #258-270 Timeout UNKNOWN Requires further investigation May be database, mocking, or concurrency issues

Recommendations

Immediate (Phase 2.1 Fix)

  1. Refactor InviteUser to async email

    • Create user (fast)
    • Return immediately with 201 Created
    • Send email in background goroutine
    • Endpoint: <100ms response time
  2. Add timeout to SMTP calls

    • If email takes >5s, fail gracefully
    • Never block HTTP response >1s
  3. Add feature flag for optional email

    • Allow invite without email sending
    • Endpoint can pre-generate token for manual sharing

Follow-up (Phase 2.2)

  1. Investigate Tests #258-270 separately (they may be unrelated)
  2. Profile UpdateUserPermissions endpoint (database efficiency?)
  3. Review E2E test mocking (ensure fixtures don't interfere)

Evidence & References

Code files reviewed:

  • /projects/Charon/backend/internal/api/handlers/user_handler.go (InviteUser, UpdateUserPermissions)
  • /projects/Charon/backend/internal/services/mail_service.go (SendEmail, SendInvite)
  • /projects/Charon/backend/internal/models/user.go (User model)
  • /projects/Charon/tests/monitoring/uptime-monitoring.spec.ts (E2E test patterns)

Endpoints verified working:

  • POST /api/v1/users/invite - EXISTS, properly registered
  • PUT /api/v1/users/:id/permissions - EXISTS, properly registered
  • GET /api/v1/users - EXISTS (all users endpoint)

Test Database State:

  • SMTP not configured (safe mode)
  • Users table has admin + test users
  • Permitted hosts associations work
  • Invite tokens generate successfully on user creation

Next Steps

  1. Root cause identified: Synchronous email blocking
  2. → Implement async email sending in InviteUser handler
  3. → Test with E2E suite
  4. → Document performance improvements
  5. → Investigate remaining test failures if needed