- Add 12 tests for proxy host Update() type conversion edge cases - Add 2 DB error tests for security headers handler - Add ID=0 validation test for certificate handler - Coverage improved: boolean fields, negative IDs, invalid strings Fixes coverage gaps reported by Codecov for PR #435
16 KiB
PR #434 Codecov Coverage Gap Remediation Plan
Status: Analysis Complete - REMEDIATION REQUIRED Created: 2025-12-21 Last Updated: 2025-12-21 Objective: Increase patch coverage from 87.31% to meet 85% threshold across 7 files
Executive Summary
Coverage Status: ⚠️ 78 MISSING LINES across 7 files
PR #434: feat: add API-Friendly security header preset for mobile apps
- Branch:
feature/beta-release - Patch Coverage: 87.31% (above 85% threshold ✅)
- Total Missing Lines: 78 lines across 7 files
- Recommendation: Add targeted tests to improve coverage and reduce technical debt
Coverage Gap Summary
| File | Coverage | Missing | Partials | Priority | Effort |
|---|---|---|---|---|---|
handlers/testdb.go |
61.53% | 29 | 1 | P1 | Medium |
handlers/proxy_host_handler.go |
75.00% | 25 | 4 | P1 | High |
handlers/security_headers_handler.go |
93.75% | 8 | 4 | P2 | Low |
handlers/test_helpers.go |
87.50% | 2 | 0 | P3 | Low |
routes/routes.go |
66.66% | 1 | 1 | P3 | Low |
caddy/config.go |
98.82% | 1 | 1 | P4 | Low |
handlers/certificate_handler.go |
50.00% | 1 | 0 | P4 | Low |
Detailed Analysis by File
1. backend/internal/api/handlers/testdb.go (29 Missing, 1 Partial)
File Purpose: Test database utilities providing template DB and migrations for faster test setup.
Current Coverage: 61.53%
Test File: testdb_test.go (exists - 200+ lines)
Uncovered Code Paths
| Lines | Function | Issue | Solution |
|---|---|---|---|
| 26-28 | initTemplateDB() |
Error return path after gorm.Open fails |
Mock DB open failure |
| 32-55 | initTemplateDB() |
AutoMigrate error path |
Inject migration failure |
| 98-104 | OpenTestDBWithMigrations() |
rows.Scan error + empty sql handling |
Test with corrupted template |
| 109-131 | OpenTestDBWithMigrations() |
Fallback AutoMigrate path | Force template DB unavailable |
Test Scenarios to Add
// File: backend/internal/api/handlers/testdb_coverage_test.go
func TestInitTemplateDB_OpenError(t *testing.T) {
// Cannot directly test since initTemplateDB uses sync.Once
// This path is covered by testing GetTemplateDB behavior
// when underlying DB operations fail
}
func TestOpenTestDBWithMigrations_TemplateUnavailable(t *testing.T) {
// Force the template DB to be unavailable
// Verify fallback AutoMigrate is called
// Test by checking table creation works
}
func TestOpenTestDBWithMigrations_ScanError(t *testing.T) {
// Test when rows.Scan returns error
// Should fall through to fallback path
}
func TestOpenTestDBWithMigrations_EmptySQL(t *testing.T) {
// Test when sql string is empty
// Should skip db.Exec call
}
Recommended Actions
- Add
testdb_coverage_test.gowith scenarios above - Complexity: Medium - requires mocking GORM internals or using test doubles
- Alternative: Accept lower coverage since this is test infrastructure code
Note: This file is test-only infrastructure (testdb.go). Coverage gaps here are acceptable since:
- The happy path is already tested
- Error paths are defensive programming
- Testing test utilities creates circular dependencies
Recommendation: P3 - Lower priority, accept current coverage for test utilities.
2. backend/internal/api/handlers/proxy_host_handler.go (25 Missing, 4 Partials)
File Purpose: CRUD operations for proxy hosts including bulk security header updates.
Current Coverage: 75.00%
Test Files:
proxy_host_handler_test.goproxy_host_handler_security_headers_test.go
Uncovered Code Paths (New in PR #434)
| Lines | Function | Issue | Solution |
|---|---|---|---|
| 222-226 | Update() |
enable_standard_headers null handling |
Test with null payload |
| 227-232 | Update() |
forward_auth_enabled bool handling |
Test update with this field |
| 234-237 | Update() |
waf_disabled bool handling |
Test update with this field |
| 286-340 | Update() |
security_header_profile_id type conversions |
Test int, string, float64, default cases |
| 302-305 | Update() |
Failed float64→uint conversion (negative) | Test with -1 value |
| 312-315 | Update() |
Failed int→uint conversion (negative) | Test with -1 value |
| 322-325 | Update() |
Failed string parse | Test with "invalid" string |
| 326-328 | Update() |
Unsupported type default case | Test with bool or array |
| 331-334 | Update() |
Conversion failed response | Implicit test from above |
| 546-549 | BulkUpdateSecurityHeaders() |
Profile lookup DB error (non-404) | Mock DB error |
Test Scenarios to Add
// File: backend/internal/api/handlers/proxy_host_handler_update_test.go
func TestProxyHostUpdate_EnableStandardHeaders_Null(t *testing.T) {
// Create host, then update with enable_standard_headers: null
// Verify host.EnableStandardHeaders becomes nil
}
func TestProxyHostUpdate_EnableStandardHeaders_True(t *testing.T) {
// Create host, then update with enable_standard_headers: true
// Verify host.EnableStandardHeaders is pointer to true
}
func TestProxyHostUpdate_EnableStandardHeaders_False(t *testing.T) {
// Create host, then update with enable_standard_headers: false
// Verify host.EnableStandardHeaders is pointer to false
}
func TestProxyHostUpdate_ForwardAuthEnabled(t *testing.T) {
// Create host with forward_auth_enabled: false
// Update to forward_auth_enabled: true
// Verify change persisted
}
func TestProxyHostUpdate_WAFDisabled(t *testing.T) {
// Create host with waf_disabled: false
// Update to waf_disabled: true
// Verify change persisted
}
func TestProxyHostUpdate_SecurityHeaderProfileID_Int(t *testing.T) {
// Create profile, create host
// Update with security_header_profile_id as int (Go doesn't JSON decode to int, but test anyway)
}
func TestProxyHostUpdate_SecurityHeaderProfileID_NegativeFloat(t *testing.T) {
// Create host
// Update with security_header_profile_id: -1.0 (float64)
// Expect 400 Bad Request
}
func TestProxyHostUpdate_SecurityHeaderProfileID_NegativeInt(t *testing.T) {
// Create host
// Update with security_header_profile_id: -1 (if possible via int type)
// Expect 400 Bad Request
}
func TestProxyHostUpdate_SecurityHeaderProfileID_InvalidString(t *testing.T) {
// Create host
// Update with security_header_profile_id: "not-a-number"
// Expect 400 Bad Request
}
func TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType(t *testing.T) {
// Create host
// Send security_header_profile_id as boolean (true) or array
// Expect 400 Bad Request
}
func TestBulkUpdateSecurityHeaders_DBError_NonNotFound(t *testing.T) {
// Close DB connection to simulate internal error
// Call bulk update with valid profile ID
// Expect 500 Internal Server Error
}
Recommended Actions
- Add
proxy_host_handler_update_test.gowith 11 new test cases - Estimated effort: 2-3 hours
- Impact: Covers 25 lines, brings coverage to ~95%
3. backend/internal/api/handlers/security_headers_handler.go (8 Missing, 4 Partials)
File Purpose: CRUD for security header profiles, presets, CSP validation.
Current Coverage: 93.75%
Test File: security_headers_handler_test.go (extensive - 500+ lines)
Uncovered Code Paths
| Lines | Function | Issue | Solution |
|---|---|---|---|
| 89-91 | GetProfile() |
UUID lookup DB error (non-404) | Close DB before UUID lookup |
| 142-145 | UpdateProfile() |
db.Save() error |
Close DB before save |
| 177-180 | DeleteProfile() |
db.Delete() error |
Already tested in TestDeleteProfile_DeleteDBError |
| 269-271 | validateCSPString() |
Unknown directive warning | Test with unknown-directive |
Test Scenarios to Add
// File: backend/internal/api/handlers/security_headers_handler_coverage_test.go
func TestGetProfile_UUID_DBError_NonNotFound(t *testing.T) {
// Create profile, get UUID
// Close DB connection
// GET /security/headers/profiles/{uuid}
// Expect 500 Internal Server Error
}
func TestUpdateProfile_SaveError(t *testing.T) {
// Create profile (ID = 1)
// Close DB connection
// PUT /security/headers/profiles/1
// Expect 500 Internal Server Error
// Note: Similar to TestUpdateProfile_DBError but for save specifically
}
Note: Most paths are already covered by existing tests. The 8 missing lines are edge cases around DB errors that are already partially tested.
Recommended Actions
- Verify existing tests cover scenarios - some may already be present
- Add 2 additional DB error tests if not covered
- Estimated effort: 30 minutes
4. backend/internal/api/handlers/test_helpers.go (2 Missing)
File Purpose: Polling helpers for test synchronization (waitForCondition).
Current Coverage: 87.50%
Test File: test_helpers_test.go (exists)
Uncovered Code Paths
| Lines | Function | Issue | Solution |
|---|---|---|---|
| 17-18 | waitForCondition() |
t.Fatalf call on timeout |
Cannot directly test without custom interface |
| 31-32 | waitForConditionWithInterval() |
t.Fatalf call on timeout |
Same issue |
Analysis
The missing coverage is in the t.Fatalf() calls which are intentionally not tested because:
t.Fatalf()terminates the test immediately- Testing this would require a custom testing.T interface
- The existing tests use mock implementations to verify timeout behavior
Current tests already cover:
TestWaitForCondition_PassesImmediatelyTestWaitForCondition_PassesAfterIterationsTestWaitForCondition_Timeout(uses mockTestingT)TestWaitForConditionWithInterval_*variants
Recommended Actions
- Accept current coverage - The timeout paths are defensive and covered via mocks
- No additional tests needed - mockTestingT already verifies the behavior
- Estimated effort: None
5. backend/internal/api/routes/routes.go (1 Missing, 1 Partial)
File Purpose: API route registration and middleware wiring.
Current Coverage: 66.66% (but only 1 new line missing)
Test File: routes_test.go (exists)
Uncovered Code Paths
| Lines | Function | Issue | Solution |
|---|---|---|---|
| ~234 | Register() |
secHeadersSvc.EnsurePresetsExist() error logging |
Error is logged but not fatal |
Analysis
The missing line is error handling for EnsurePresetsExist():
if err := secHeadersSvc.EnsurePresetsExist(); err != nil {
logger.Log().WithError(err).Warn("Failed to initialize security header presets")
}
This is non-fatal logging - the route registration continues even if preset initialization fails.
Test Scenarios to Add
// File: backend/internal/api/routes/routes_security_headers_test.go
func TestRegister_EnsurePresetsExist_Error(t *testing.T) {
// This requires mocking SecurityHeadersService
// Or testing with a DB that fails on insert
// Low priority since it's just a warning log
}
Recommended Actions
- Accept current coverage - Error path only logs a warning
- Low impact - Registration continues regardless of error
- Estimated effort: 30 minutes if mocking is needed
6. backend/internal/caddy/config.go (1 Missing, 1 Partial)
File Purpose: Caddy JSON configuration generation.
Current Coverage: 98.82% (excellent)
Test Files: Multiple test files config_security_headers_test.go
Uncovered Code Path
Based on the API-Friendly preset feature, the missing line is likely in buildSecurityHeadersHandler() for an edge case.
Analysis
The existing test TestBuildSecurityHeadersHandler_APIFriendlyPreset covers the new API-Friendly preset. The 1 missing line is likely an edge case in:
- Empty string handling for headers
- Cross-origin policy variations
Recommended Actions
- Review coverage report details to identify exact line
- Likely already covered by
TestBuildSecurityHeadersHandler_APIFriendlyPreset - Estimated effort: 15 minutes to verify
7. backend/internal/api/handlers/certificate_handler.go (1 Missing)
File Purpose: Certificate upload, list, and delete operations.
Current Coverage: 50.00% (only 1 new line)
Test File: certificate_handler_coverage_test.go (exists)
Uncovered Code Path
| Lines | Function | Issue | Solution |
|---|---|---|---|
| ~67 | Delete() |
ID=0 validation check | Already tested |
Analysis
Looking at the test file, TestCertificateHandler_Delete_InvalidID tests the "invalid" ID case but may not specifically test ID=0.
// Validate ID range
if id == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
return
}
Test Scenarios to Add
func TestCertificateHandler_Delete_ZeroID(t *testing.T) {
// DELETE /api/certificates/0
// Expect 400 Bad Request with "invalid id" error
}
Recommended Actions
- Add single test for ID=0 case
- Estimated effort: 10 minutes
Implementation Plan
Priority Order (by impact)
- P1: proxy_host_handler.go - 25 lines, new feature code
- P1: testdb.go - 29 lines, but test-only infrastructure (lower actual priority)
- P2: security_headers_handler.go - 8 lines, minor gaps
- P3: test_helpers.go - Accept current coverage
- P3: routes.go - Accept current coverage (warning log only)
- P4: config.go - Verify existing coverage
- P4: certificate_handler.go - Add 1 test
Estimated Effort
| File | Tests to Add | Time Estimate |
|---|---|---|
proxy_host_handler.go |
11 tests | 2-3 hours |
security_headers_handler.go |
2 tests | 30 minutes |
certificate_handler.go |
1 test | 10 minutes |
testdb.go |
Skip (test utilities) | 0 |
test_helpers.go |
Skip (already covered) | 0 |
routes.go |
Skip (warning log) | 0 |
config.go |
Verify only | 15 minutes |
| Total | 14 tests | ~4 hours |
Test File Locations
New Test Files to Create
backend/internal/api/handlers/proxy_host_handler_update_test.go- Update field coverage
Existing Test Files to Extend
backend/internal/api/handlers/security_headers_handler_test.go- Add 2 DB error testsbackend/internal/api/handlers/certificate_handler_coverage_test.go- Add ID=0 test
Dependencies Between Tests
None identified - all tests can be implemented independently
Acceptance Criteria
- ✅ Patch coverage ≥ 85% (currently 87.31%, already passing)
- ⬜ All new test scenarios pass
- ⬜ No regression in existing tests
- ⬜ Test execution time < 30 seconds total
- ⬜ All tests use
OpenTestDBorOpenTestDBWithMigrationsfor isolation
Mock Requirements
For proxy_host_handler.go Tests
- Standard Gin test router setup (already exists in test files)
- GORM SQLite in-memory DB (use
OpenTestDBWithMigrations) - Mock Caddy manager (nil is acceptable for these tests)
For security_headers_handler.go Tests
- Same as above
- Close DB connection to simulate errors
For certificate_handler.go Tests
- Use existing test setup patterns
- No mocks needed for ID=0 test
Conclusion
Immediate Action Required: None - coverage is above 85% threshold
Recommended Improvements:
- Add 14 targeted tests to improve coverage quality
- Focus on
proxy_host_handler.gowhich has the most new feature code - Accept lower coverage on test infrastructure files (
testdb.go,test_helpers.go)
Total Estimated Effort: ~4 hours for all improvements
Analysis Date: 2025-12-21 Analyzed By: GitHub Copilot Next Action: Implement tests in priority order if coverage improvement is desired