diff --git a/.gitignore b/.gitignore index 040dfe79..64f4c728 100644 --- a/.gitignore +++ b/.gitignore @@ -245,3 +245,4 @@ my-codeql-db/** codeql-linux64.zip backend/main **.out +docs/plans/supply_chain_security_implementation.md.backup diff --git a/backend/test_output.txt b/backend/test_output.txt index 120e28ad..3cde71d3 100644 --- a/backend/test_output.txt +++ b/backend/test_output.txt @@ -1,3 +1,45 @@ -ok github.com/Wikid82/charon/backend/cmd/api (cached) coverage: 0.0% of statements -ok github.com/Wikid82/charon/backend/cmd/seed (cached) coverage: 63.2% of statements +=== RUN TestResetPasswordCommand_Succeeds +time="2026-01-10T03:00:26Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T03:00:26Z" level=info msg="SQLite database integrity check passed" +--- PASS: TestResetPasswordCommand_Succeeds (0.15s) +=== RUN TestMigrateCommand_Succeeds +time="2026-01-10T03:00:27Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T03:00:27Z" level=info msg="SQLite database integrity check passed" +time="2026-01-10T03:00:27Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T03:00:27Z" level=info msg="SQLite database integrity check passed" +--- PASS: TestMigrateCommand_Succeeds (0.08s) +=== RUN TestStartupVerification_MissingTables +time="2026-01-10T03:00:27Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T03:00:27Z" level=info msg="SQLite database integrity check passed" +time="2026-01-10T03:00:27Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T03:00:27Z" level=info msg="SQLite database integrity check passed" + main_test.go:171: Missing table for model *models.SecurityConfig + main_test.go:171: Missing table for model *models.SecurityDecision + main_test.go:171: Missing table for model *models.SecurityAudit + main_test.go:171: Missing table for model *models.SecurityRuleSet + main_test.go:171: Missing table for model *models.CrowdsecPresetEvent + main_test.go:171: Missing table for model *models.CrowdsecConsoleEnrollment +--- PASS: TestStartupVerification_MissingTables (0.05s) +PASS +coverage: 0.0% of statements +ok github.com/Wikid82/charon/backend/cmd/api 0.310s coverage: 0.0% of statements +=== RUN TestSeedMain_Smoke +{"level":"info","msg":"✓ Database migrated successfully","time":"2026-01-10T03:00:27Z"} +{"level":"info","msg":"✓ Created remote server: Local Docker Registry (localhost:5000)","server":"Local Docker Registry","time":"2026-01-10T03:00:27Z"} +{"level":"info","msg":"✓ Created remote server: Development API Server (192.168.1.100:8080)","server":"Development API Server","time":"2026-01-10T03:00:27Z"} +{"level":"info","msg":"✓ Created remote server: Staging Web App (staging.internal:3000)","server":"Staging Web App","time":"2026-01-10T03:00:27Z"} +{"level":"info","msg":"✓ Created remote server: Database Admin (localhost:8081)","server":"Database Admin","time":"2026-01-10T03:00:27Z"} +{"host":"app.local.dev","level":"info","msg":"✓ Created proxy host: app.local.dev -\u003e http://localhost:3000","time":"2026-01-10T03:00:27Z"} +{"host":"api.local.dev","level":"info","msg":"✓ Created proxy host: api.local.dev -\u003e http://192.168.1.100:8080","time":"2026-01-10T03:00:27Z"} +{"host":"docker.local.dev","level":"info","msg":"✓ Created proxy host: docker.local.dev -\u003e http://localhost:5000","time":"2026-01-10T03:00:27Z"} +{"level":"info","msg":"✓ Created setting: app_name = Charon","setting":"app_name","time":"2026-01-10T03:00:27Z"} +{"level":"info","msg":"✓ Created setting: default_scheme = http","setting":"default_scheme","time":"2026-01-10T03:00:27Z"} +{"level":"info","msg":"✓ Created setting: enable_ssl_by_default = false","setting":"enable_ssl_by_default","time":"2026-01-10T03:00:27Z"} +{"level":"info","msg":"✓ Created default user: admin@localhost","time":"2026-01-10T03:00:27Z","user":"admin@localhost"} +{"level":"info","msg":"\n✓ Database seeding completed successfully!","time":"2026-01-10T03:00:27Z"} +{"level":"info","msg":" You can now start the application and see sample data.","time":"2026-01-10T03:00:27Z"} +--- PASS: TestSeedMain_Smoke (0.31s) +PASS +coverage: 63.2% of statements +ok github.com/Wikid82/charon/backend/cmd/seed 0.322s coverage: 63.2% of statements ? github.com/Wikid82/charon/backend/integration [no test files] diff --git a/backend/test_output_qa.txt b/backend/test_output_qa.txt new file mode 100644 index 00000000..d6fd3486 --- /dev/null +++ b/backend/test_output_qa.txt @@ -0,0 +1,14134 @@ +=== RUN TestResetPasswordCommand_Succeeds +time="2026-01-10T02:16:43Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:16:43Z" level=info msg="SQLite database integrity check passed" +--- PASS: TestResetPasswordCommand_Succeeds (1.99s) +=== RUN TestMigrateCommand_Succeeds +time="2026-01-10T02:16:44Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:16:44Z" level=info msg="SQLite database integrity check passed" +time="2026-01-10T02:16:46Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:16:46Z" level=info msg="SQLite database integrity check passed" +--- PASS: TestMigrateCommand_Succeeds (1.27s) +=== RUN TestStartupVerification_MissingTables +time="2026-01-10T02:16:46Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:16:46Z" level=info msg="SQLite database integrity check passed" +time="2026-01-10T02:16:46Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:16:46Z" level=info msg="SQLite database integrity check passed" + main_test.go:171: Missing table for model *models.SecurityConfig + main_test.go:171: Missing table for model *models.SecurityDecision + main_test.go:171: Missing table for model *models.SecurityAudit + main_test.go:171: Missing table for model *models.SecurityRuleSet + main_test.go:171: Missing table for model *models.CrowdsecPresetEvent + main_test.go:171: Missing table for model *models.CrowdsecConsoleEnrollment +--- PASS: TestStartupVerification_MissingTables (0.06s) +PASS +coverage: 0.0% of statements +ok github.com/Wikid82/charon/backend/cmd/api (cached) coverage: 0.0% of statements +=== RUN TestSeedMain_Smoke +{"level":"info","msg":"✓ Database migrated successfully","time":"2026-01-10T02:16:43Z"} +{"level":"info","msg":"✓ Created remote server: Local Docker Registry (localhost:5000)","server":"Local Docker Registry","time":"2026-01-10T02:16:43Z"} +{"level":"info","msg":"✓ Created remote server: Development API Server (192.168.1.100:8080)","server":"Development API Server","time":"2026-01-10T02:16:43Z"} +{"level":"info","msg":"✓ Created remote server: Staging Web App (staging.internal:3000)","server":"Staging Web App","time":"2026-01-10T02:16:43Z"} +{"level":"info","msg":"✓ Created remote server: Database Admin (localhost:8081)","server":"Database Admin","time":"2026-01-10T02:16:43Z"} +{"host":"app.local.dev","level":"info","msg":"✓ Created proxy host: app.local.dev -\u003e http://localhost:3000","time":"2026-01-10T02:16:43Z"} +{"host":"api.local.dev","level":"info","msg":"✓ Created proxy host: api.local.dev -\u003e http://192.168.1.100:8080","time":"2026-01-10T02:16:43Z"} +{"host":"docker.local.dev","level":"info","msg":"✓ Created proxy host: docker.local.dev -\u003e http://localhost:5000","time":"2026-01-10T02:16:43Z"} +{"level":"info","msg":"✓ Created setting: app_name = Charon","setting":"app_name","time":"2026-01-10T02:16:43Z"} +{"level":"info","msg":"✓ Created setting: default_scheme = http","setting":"default_scheme","time":"2026-01-10T02:16:43Z"} +{"level":"info","msg":"✓ Created setting: enable_ssl_by_default = false","setting":"enable_ssl_by_default","time":"2026-01-10T02:16:43Z"} +{"level":"info","msg":"✓ Created default user: admin@localhost","time":"2026-01-10T02:16:43Z","user":"admin@localhost"} +{"level":"info","msg":"\n✓ Database seeding completed successfully!","time":"2026-01-10T02:16:43Z"} +{"level":"info","msg":" You can now start the application and see sample data.","time":"2026-01-10T02:16:43Z"} +--- PASS: TestSeedMain_Smoke (0.28s) +PASS +coverage: 63.2% of statements +ok github.com/Wikid82/charon/backend/cmd/seed (cached) coverage: 63.2% of statements +? github.com/Wikid82/charon/backend/integration [no test files] +=== RUN TestAccessListHandler_SetGeoIPService +--- PASS: TestAccessListHandler_SetGeoIPService (0.01s) +=== RUN TestAccessListHandler_SetGeoIPService_Nil +--- PASS: TestAccessListHandler_SetGeoIPService_Nil (0.01s) +=== RUN TestAccessListHandler_Get_InvalidID +--- PASS: TestAccessListHandler_Get_InvalidID (0.03s) +=== RUN TestAccessListHandler_Update_InvalidID +--- PASS: TestAccessListHandler_Update_InvalidID (0.02s) +=== RUN TestAccessListHandler_Update_InvalidJSON +--- PASS: TestAccessListHandler_Update_InvalidJSON (0.02s) +=== RUN TestAccessListHandler_Delete_InvalidID +--- PASS: TestAccessListHandler_Delete_InvalidID (0.02s) +=== RUN TestAccessListHandler_TestIP_InvalidID +--- PASS: TestAccessListHandler_TestIP_InvalidID (0.02s) +=== RUN TestAccessListHandler_TestIP_MissingIPAddress +--- PASS: TestAccessListHandler_TestIP_MissingIPAddress (0.02s) +=== RUN TestAccessListHandler_List_DBError + +2026/01/10 02:17:36 /projects/Charon/backend/internal/services/access_list_service.go:129 no such table: access_lists +[1.585ms] [rows:0] SELECT * FROM `access_lists` ORDER BY updated_at desc +--- PASS: TestAccessListHandler_List_DBError (0.00s) +=== RUN TestAccessListHandler_Get_DBError + +2026/01/10 02:17:36 /projects/Charon/backend/internal/services/access_list_service.go:105 no such table: access_lists +[2.493ms] [rows:0] SELECT * FROM `access_lists` WHERE `access_lists`.`id` = 1 ORDER BY `access_lists`.`id` LIMIT 1 +--- PASS: TestAccessListHandler_Get_DBError (0.00s) +=== RUN TestAccessListHandler_Delete_InternalError + +2026/01/10 02:17:36 /projects/Charon/backend/internal/services/access_list_service.go:162 no such table: proxy_hosts +[10.015ms] [rows:0] SELECT count(*) FROM `proxy_hosts` WHERE access_list_id = 1 +--- PASS: TestAccessListHandler_Delete_InternalError (0.01s) +=== RUN TestAccessListHandler_Update_InvalidType +--- PASS: TestAccessListHandler_Update_InvalidType (0.03s) +=== RUN TestAccessListHandler_Create_InvalidJSON +--- PASS: TestAccessListHandler_Create_InvalidJSON (0.04s) +=== RUN TestAccessListHandler_TestIP_Blacklist +--- PASS: TestAccessListHandler_TestIP_Blacklist (0.03s) +=== RUN TestAccessListHandler_TestIP_GeoWhitelist +--- PASS: TestAccessListHandler_TestIP_GeoWhitelist (0.03s) +=== RUN TestAccessListHandler_TestIP_LocalNetworkOnly +--- PASS: TestAccessListHandler_TestIP_LocalNetworkOnly (0.02s) +=== RUN TestAccessListHandler_TestIP_InternalError + +2026/01/10 02:17:36 /projects/Charon/backend/internal/services/access_list_service.go:105 no such table: access_lists +[0.993ms] [rows:0] SELECT * FROM `access_lists` WHERE `access_lists`.`id` = 1 ORDER BY `access_lists`.`id` LIMIT 1 +--- PASS: TestAccessListHandler_TestIP_InternalError (0.00s) +=== RUN TestAccessListHandler_Create +=== RUN TestAccessListHandler_Create/create_whitelist_successfully +=== RUN TestAccessListHandler_Create/create_geo_whitelist_successfully +=== RUN TestAccessListHandler_Create/create_local_network_only +=== RUN TestAccessListHandler_Create/fail_with_invalid_type +=== RUN TestAccessListHandler_Create/fail_with_missing_name +--- PASS: TestAccessListHandler_Create (0.02s) + --- PASS: TestAccessListHandler_Create/create_whitelist_successfully (0.00s) + --- PASS: TestAccessListHandler_Create/create_geo_whitelist_successfully (0.00s) + --- PASS: TestAccessListHandler_Create/create_local_network_only (0.00s) + --- PASS: TestAccessListHandler_Create/fail_with_invalid_type (0.00s) + --- PASS: TestAccessListHandler_Create/fail_with_missing_name (0.00s) +=== RUN TestAccessListHandler_List +--- PASS: TestAccessListHandler_List (0.02s) +=== RUN TestAccessListHandler_Get +=== RUN TestAccessListHandler_Get/get_existing_ACL +=== RUN TestAccessListHandler_Get/get_non-existent_ACL + +2026/01/10 02:17:36 /projects/Charon/backend/internal/services/access_list_service.go:105 record not found +[0.120ms] [rows:0] SELECT * FROM `access_lists` WHERE `access_lists`.`id` = 9999 ORDER BY `access_lists`.`id` LIMIT 1 +--- PASS: TestAccessListHandler_Get (0.02s) + --- PASS: TestAccessListHandler_Get/get_existing_ACL (0.00s) + --- PASS: TestAccessListHandler_Get/get_non-existent_ACL (0.00s) +=== RUN TestAccessListHandler_Update +=== RUN TestAccessListHandler_Update/update_successfully +=== RUN TestAccessListHandler_Update/update_non-existent_ACL + +2026/01/10 02:17:36 /projects/Charon/backend/internal/services/access_list_service.go:105 record not found +[0.098ms] [rows:0] SELECT * FROM `access_lists` WHERE `access_lists`.`id` = 9999 ORDER BY `access_lists`.`id` LIMIT 1 +--- PASS: TestAccessListHandler_Update (0.03s) + --- PASS: TestAccessListHandler_Update/update_successfully (0.00s) + --- PASS: TestAccessListHandler_Update/update_non-existent_ACL (0.00s) +=== RUN TestAccessListHandler_Delete +=== RUN TestAccessListHandler_Delete/delete_successfully +=== RUN TestAccessListHandler_Delete/fail_to_delete_ACL_in_use +=== RUN TestAccessListHandler_Delete/delete_non-existent_ACL +--- PASS: TestAccessListHandler_Delete (0.05s) + --- PASS: TestAccessListHandler_Delete/delete_successfully (0.00s) + --- PASS: TestAccessListHandler_Delete/fail_to_delete_ACL_in_use (0.00s) + --- PASS: TestAccessListHandler_Delete/delete_non-existent_ACL (0.00s) +=== RUN TestAccessListHandler_TestIP +=== RUN TestAccessListHandler_TestIP/test_IP_in_whitelist +=== RUN TestAccessListHandler_TestIP/test_IP_not_in_whitelist +=== RUN TestAccessListHandler_TestIP/test_invalid_IP +=== RUN TestAccessListHandler_TestIP/test_non-existent_ACL + +2026/01/10 02:17:36 /projects/Charon/backend/internal/services/access_list_service.go:105 record not found +[0.074ms] [rows:0] SELECT * FROM `access_lists` WHERE `access_lists`.`id` = 9999 ORDER BY `access_lists`.`id` LIMIT 1 +--- PASS: TestAccessListHandler_TestIP (0.04s) + --- PASS: TestAccessListHandler_TestIP/test_IP_in_whitelist (0.00s) + --- PASS: TestAccessListHandler_TestIP/test_IP_not_in_whitelist (0.00s) + --- PASS: TestAccessListHandler_TestIP/test_invalid_IP (0.00s) + --- PASS: TestAccessListHandler_TestIP/test_non-existent_ACL (0.00s) +=== RUN TestAccessListHandler_GetTemplates +--- PASS: TestAccessListHandler_GetTemplates (0.02s) +=== RUN TestImportHandler_Commit_InvalidJSON +--- PASS: TestImportHandler_Commit_InvalidJSON (0.03s) +=== RUN TestImportHandler_Commit_InvalidSessionUUID +--- PASS: TestImportHandler_Commit_InvalidSessionUUID (0.03s) +=== RUN TestImportHandler_Commit_SessionNotFound +--- PASS: TestImportHandler_Commit_SessionNotFound (0.05s) +=== RUN TestRemoteServerHandler_TestConnection_Unreachable +--- PASS: TestRemoteServerHandler_TestConnection_Unreachable (5.01s) +=== RUN TestSecurityHandler_GetConfig_InternalError +--- PASS: TestSecurityHandler_GetConfig_InternalError (0.01s) +=== RUN TestSecurityHandler_UpdateConfig_ApplyCaddyError +--- PASS: TestSecurityHandler_UpdateConfig_ApplyCaddyError (0.01s) +=== RUN TestSecurityHandler_GenerateBreakGlass_Error +--- PASS: TestSecurityHandler_GenerateBreakGlass_Error (0.75s) +=== RUN TestSecurityHandler_ListDecisions_Error +--- PASS: TestSecurityHandler_ListDecisions_Error (0.01s) +=== RUN TestSecurityHandler_ListRuleSets_Error +--- PASS: TestSecurityHandler_ListRuleSets_Error (0.01s) +=== RUN TestSecurityHandler_UpsertRuleSet_Error +--- PASS: TestSecurityHandler_UpsertRuleSet_Error (0.01s) +=== RUN TestSecurityHandler_CreateDecision_LogError +--- PASS: TestSecurityHandler_CreateDecision_LogError (0.01s) +=== RUN TestSecurityHandler_DeleteRuleSet_Error +--- PASS: TestSecurityHandler_DeleteRuleSet_Error (0.01s) +=== RUN TestCrowdsec_ImportConfig_EmptyUpload +--- PASS: TestCrowdsec_ImportConfig_EmptyUpload (0.00s) +=== RUN TestBackupHandler_List_DBError +time="2026-01-10T02:17:42Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestBackupHandler_List_DBError (0.00s) +=== RUN TestImportHandler_UploadMulti_InvalidJSON +--- PASS: TestImportHandler_UploadMulti_InvalidJSON (0.02s) +=== RUN TestImportHandler_UploadMulti_MissingCaddyfile +--- PASS: TestImportHandler_UploadMulti_MissingCaddyfile (0.01s) +=== RUN TestImportHandler_UploadMulti_EmptyContent +--- PASS: TestImportHandler_UploadMulti_EmptyContent (0.02s) +=== RUN TestImportHandler_UploadMulti_PathTraversal +--- PASS: TestImportHandler_UploadMulti_PathTraversal (0.02s) +=== RUN TestLogsHandler_Download_PathTraversal +--- PASS: TestLogsHandler_Download_PathTraversal (0.00s) +=== RUN TestLogsHandler_Download_NotFound +--- PASS: TestLogsHandler_Download_NotFound (0.00s) +=== RUN TestLogsHandler_Download_Success +--- PASS: TestLogsHandler_Download_Success (0.01s) +=== RUN TestImportHandler_Upload_InvalidJSON +time="2026-01-10T02:17:42Z" level=error msg="Import Upload: failed to bind JSON" error="invalid character 'o' in literal null (expecting 'u')" +--- PASS: TestImportHandler_Upload_InvalidJSON (0.02s) +=== RUN TestImportHandler_Upload_EmptyContent +time="2026-01-10T02:17:42Z" level=error msg="Import Upload: failed to bind JSON" error="Key: 'Content' Error:Field validation for 'Content' failed on the 'required' tag" +--- PASS: TestImportHandler_Upload_EmptyContent (0.02s) +=== RUN TestBackupHandler_List_ServiceError +--- PASS: TestBackupHandler_List_ServiceError (0.00s) +=== RUN TestBackupHandler_Delete_PathTraversal +time="2026-01-10T02:17:42Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestBackupHandler_Delete_PathTraversal (0.00s) +=== RUN TestBackupHandler_Delete_InternalError2 +time="2026-01-10T02:17:42Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestBackupHandler_Delete_InternalError2 (0.00s) +=== RUN TestRemoteServerHandler_TestConnection_NotFound2 +--- PASS: TestRemoteServerHandler_TestConnection_NotFound2 (0.00s) +=== RUN TestRemoteServerHandler_TestConnectionCustom_Unreachable2 +--- PASS: TestRemoteServerHandler_TestConnectionCustom_Unreachable2 (5.01s) +=== RUN TestAuthHandler_Register_InvalidJSON +--- PASS: TestAuthHandler_Register_InvalidJSON (0.02s) +=== RUN TestHealthHandler_Basic +--- PASS: TestHealthHandler_Basic (0.00s) +=== RUN TestBackupHandler_Create_Error +time="2026-01-10T02:17:47Z" level=error msg="Failed to create backup" action=create_backup error="database file not found: /tmp/TestBackupHandler_Create_Error62628796/001/data/charon.db" +time="2026-01-10T02:17:47Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestBackupHandler_Create_Error (0.00s) +=== RUN TestSettingsHandler_GetSettings_Error +--- PASS: TestSettingsHandler_GetSettings_Error (0.00s) +=== RUN TestSettingsHandler_UpdateSetting_InvalidJSON +--- PASS: TestSettingsHandler_UpdateSetting_InvalidJSON (0.00s) +=== RUN TestRemoteServerHandler_TestConnection_Reachable +--- PASS: TestRemoteServerHandler_TestConnection_Reachable (0.01s) +=== RUN TestRemoteServerHandler_TestConnection_EmptyHost +--- PASS: TestRemoteServerHandler_TestConnection_EmptyHost (0.00s) +=== RUN TestImportHandler_UploadMulti_ValidCaddyfile +time="2026-01-10T02:17:47Z" level=error msg="Import UploadMulti: import failed" error="caddy adapt failed: exec: \"caddy\": executable file not found in $PATH (output: )" mainCaddyfile=Caddyfile preview="example.com { reverse_proxy localhost:8080 }" +--- PASS: TestImportHandler_UploadMulti_ValidCaddyfile (0.02s) +=== RUN TestImportHandler_UploadMulti_SubdirFile +time="2026-01-10T02:17:47Z" level=error msg="Import UploadMulti: import failed" error="caddy adapt failed: exec: \"caddy\": executable file not found in $PATH (output: )" mainCaddyfile=Caddyfile preview="import sites/*" +--- PASS: TestImportHandler_UploadMulti_SubdirFile (0.02s) +=== RUN Test_getLocalIP_Additional + additional_handlers_test.go:29: getLocalIP returned: 217.15.170.144 +--- PASS: Test_getLocalIP_Additional (0.00s) +=== RUN TestFeatureFlagsHandler_GetFlags_FromShortEnv + +2026/01/10 02:17:47 /projects/Charon/backend/internal/api/handlers/feature_flags_handler.go:48 record not found +[0.064ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:47 /projects/Charon/backend/internal/api/handlers/feature_flags_handler.go:48 record not found +[0.056ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.uptime.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:47 /projects/Charon/backend/internal/api/handlers/feature_flags_handler.go:48 record not found +[0.094ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.crowdsec.console_enrollment" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestFeatureFlagsHandler_GetFlags_FromShortEnv (0.00s) +=== RUN TestFeatureFlagsHandler_UpdateFlags_UnknownFlag +--- PASS: TestFeatureFlagsHandler_UpdateFlags_UnknownFlag (0.00s) +=== RUN TestDomainHandler_List_Additional +--- PASS: TestDomainHandler_List_Additional (0.00s) +=== RUN TestDomainHandler_List_Empty_Additional +--- PASS: TestDomainHandler_List_Empty_Additional (0.00s) +=== RUN TestDomainHandler_Create_Additional +--- PASS: TestDomainHandler_Create_Additional (0.00s) +=== RUN TestDomainHandler_Create_MissingName_Additional +--- PASS: TestDomainHandler_Create_MissingName_Additional (0.00s) +=== RUN TestDomainHandler_Delete_Additional +--- PASS: TestDomainHandler_Delete_Additional (0.00s) +=== RUN TestDomainHandler_Delete_NotFound_Additional + +2026/01/10 02:17:47 /projects/Charon/backend/internal/api/handlers/domain_handler.go:73 record not found +[0.094ms] [rows:0] SELECT * FROM `domains` WHERE uuid = "nonexistent" AND `domains`.`deleted_at` IS NULL ORDER BY `domains`.`id` LIMIT 1 +--- PASS: TestDomainHandler_Delete_NotFound_Additional (0.00s) +=== RUN TestNotificationHandler_List_Additional +--- PASS: TestNotificationHandler_List_Additional (0.00s) +=== RUN TestNotificationHandler_MarkAsRead_Additional +--- PASS: TestNotificationHandler_MarkAsRead_Additional (0.00s) +=== RUN TestNotificationHandler_MarkAllAsRead_Additional +--- PASS: TestNotificationHandler_MarkAllAsRead_Additional (0.00s) +=== RUN TestCrowdsecExec_NewDefaultCrowdsecExecutor +--- PASS: TestCrowdsecExec_NewDefaultCrowdsecExecutor (0.00s) +=== RUN TestDefaultCrowdsecExecutor_isCrowdSecProcess +--- PASS: TestDefaultCrowdsecExecutor_isCrowdSecProcess (0.00s) +=== RUN TestDefaultCrowdsecExecutor_pidFile +--- PASS: TestDefaultCrowdsecExecutor_pidFile (0.00s) +=== RUN TestDefaultCrowdsecExecutor_Status +--- PASS: TestDefaultCrowdsecExecutor_Status (0.00s) +=== RUN Test_isSafePathUnderBase_Additional +=== RUN Test_isSafePathUnderBase_Additional/valid_relative_path_under_base +=== RUN Test_isSafePathUnderBase_Additional/valid_relative_path_with_subdir +=== RUN Test_isSafePathUnderBase_Additional/path_traversal_attempt +=== RUN Test_isSafePathUnderBase_Additional/empty_path +--- PASS: Test_isSafePathUnderBase_Additional (0.00s) + --- PASS: Test_isSafePathUnderBase_Additional/valid_relative_path_under_base (0.00s) + --- PASS: Test_isSafePathUnderBase_Additional/valid_relative_path_with_subdir (0.00s) + --- PASS: Test_isSafePathUnderBase_Additional/path_traversal_attempt (0.00s) + --- PASS: Test_isSafePathUnderBase_Additional/empty_path (0.00s) +=== RUN TestAuditLogHandler_List +=== RUN TestAuditLogHandler_List/List_all_audit_logs +=== RUN TestAuditLogHandler_List/Filter_by_actor +=== RUN TestAuditLogHandler_List/Filter_by_action +=== RUN TestAuditLogHandler_List/Filter_by_event_category +=== RUN TestAuditLogHandler_List/Pagination_-_page_1,_limit_1 +--- PASS: TestAuditLogHandler_List (0.01s) + --- PASS: TestAuditLogHandler_List/List_all_audit_logs (0.00s) + --- PASS: TestAuditLogHandler_List/Filter_by_actor (0.00s) + --- PASS: TestAuditLogHandler_List/Filter_by_action (0.00s) + --- PASS: TestAuditLogHandler_List/Filter_by_event_category (0.00s) + --- PASS: TestAuditLogHandler_List/Pagination_-_page_1,_limit_1 (0.00s) +=== RUN TestAuditLogHandler_Get +=== RUN TestAuditLogHandler_Get/Get_existing_audit_log +=== RUN TestAuditLogHandler_Get/Get_non-existent_audit_log + +2026/01/10 02:17:47 /projects/Charon/backend/internal/services/security_service.go:321 record not found +[0.092ms] [rows:0] SELECT * FROM `security_audits` WHERE uuid = "non-existent-uuid" ORDER BY `security_audits`.`id` LIMIT 1 +=== RUN TestAuditLogHandler_Get/Get_with_empty_UUID +--- PASS: TestAuditLogHandler_Get (0.00s) + --- PASS: TestAuditLogHandler_Get/Get_existing_audit_log (0.00s) + --- PASS: TestAuditLogHandler_Get/Get_non-existent_audit_log (0.00s) + --- PASS: TestAuditLogHandler_Get/Get_with_empty_UUID (0.00s) +=== RUN TestAuditLogHandler_ListByProvider +=== RUN TestAuditLogHandler_ListByProvider/List_audit_logs_for_provider +=== RUN TestAuditLogHandler_ListByProvider/List_audit_logs_for_non-existent_provider +=== RUN TestAuditLogHandler_ListByProvider/Invalid_provider_ID +--- PASS: TestAuditLogHandler_ListByProvider (0.00s) + --- PASS: TestAuditLogHandler_ListByProvider/List_audit_logs_for_provider (0.00s) + --- PASS: TestAuditLogHandler_ListByProvider/List_audit_logs_for_non-existent_provider (0.00s) + --- PASS: TestAuditLogHandler_ListByProvider/Invalid_provider_ID (0.00s) +=== RUN TestAuditLogHandler_ListWithDateFilters +=== RUN TestAuditLogHandler_ListWithDateFilters/Filter_by_start_date +=== RUN TestAuditLogHandler_ListWithDateFilters/Filter_by_end_date +=== RUN TestAuditLogHandler_ListWithDateFilters/Filter_by_date_range +--- PASS: TestAuditLogHandler_ListWithDateFilters (0.01s) + --- PASS: TestAuditLogHandler_ListWithDateFilters/Filter_by_start_date (0.00s) + --- PASS: TestAuditLogHandler_ListWithDateFilters/Filter_by_end_date (0.00s) + --- PASS: TestAuditLogHandler_ListWithDateFilters/Filter_by_date_range (0.00s) +=== RUN TestAuditLogHandler_ServiceErrors +=== RUN TestAuditLogHandler_ServiceErrors/List_fails_when_database_unavailable + +2026/01/10 02:17:47 /projects/Charon/backend/internal/services/security_service.go:305 sql: database is closed +[0.026ms] [rows:0] SELECT count(*) FROM `security_audits` +=== RUN TestAuditLogHandler_ServiceErrors/ListByProvider_fails_when_database_unavailable + +2026/01/10 02:17:47 /projects/Charon/backend/internal/services/security_service.go:339 sql: database is closed +[0.039ms] [rows:0] SELECT count(*) FROM `security_audits` WHERE event_category = "dns_provider" AND resource_id = 123 +=== RUN TestAuditLogHandler_ServiceErrors/Get_fails_when_database_unavailable + +2026/01/10 02:17:47 /projects/Charon/backend/internal/services/security_service.go:321 sql: database is closed +[0.041ms] [rows:0] SELECT * FROM `security_audits` WHERE uuid = "some-uuid" ORDER BY `security_audits`.`id` LIMIT 1 +--- PASS: TestAuditLogHandler_ServiceErrors (0.00s) + --- PASS: TestAuditLogHandler_ServiceErrors/List_fails_when_database_unavailable (0.00s) + --- PASS: TestAuditLogHandler_ServiceErrors/ListByProvider_fails_when_database_unavailable (0.00s) + --- PASS: TestAuditLogHandler_ServiceErrors/Get_fails_when_database_unavailable (0.00s) +=== RUN TestAuditLogHandler_List_PaginationBoundaryEdgeCases +=== RUN TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Negative_page_defaults_to_1 +=== RUN TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Zero_page_defaults_to_1 +=== RUN TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Negative_limit_defaults_to_50 +=== RUN TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Zero_limit_defaults_to_50 +=== RUN TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Limit_over_100_defaults_to_50 +=== RUN TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Non-numeric_page_ignored +=== RUN TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Non-numeric_limit_ignored +--- PASS: TestAuditLogHandler_List_PaginationBoundaryEdgeCases (0.01s) + --- PASS: TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Negative_page_defaults_to_1 (0.00s) + --- PASS: TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Zero_page_defaults_to_1 (0.00s) + --- PASS: TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Negative_limit_defaults_to_50 (0.00s) + --- PASS: TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Zero_limit_defaults_to_50 (0.00s) + --- PASS: TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Limit_over_100_defaults_to_50 (0.00s) + --- PASS: TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Non-numeric_page_ignored (0.00s) + --- PASS: TestAuditLogHandler_List_PaginationBoundaryEdgeCases/Non-numeric_limit_ignored (0.00s) +=== RUN TestAuditLogHandler_ListByProvider_PaginationBoundaryEdgeCases +=== RUN TestAuditLogHandler_ListByProvider_PaginationBoundaryEdgeCases/Negative_page_defaults_to_1 +=== RUN TestAuditLogHandler_ListByProvider_PaginationBoundaryEdgeCases/Zero_limit_defaults_to_50 +=== RUN TestAuditLogHandler_ListByProvider_PaginationBoundaryEdgeCases/Limit_over_100_defaults_to_50 +--- PASS: TestAuditLogHandler_ListByProvider_PaginationBoundaryEdgeCases (0.01s) + --- PASS: TestAuditLogHandler_ListByProvider_PaginationBoundaryEdgeCases/Negative_page_defaults_to_1 (0.00s) + --- PASS: TestAuditLogHandler_ListByProvider_PaginationBoundaryEdgeCases/Zero_limit_defaults_to_50 (0.00s) + --- PASS: TestAuditLogHandler_ListByProvider_PaginationBoundaryEdgeCases/Limit_over_100_defaults_to_50 (0.00s) +=== RUN TestAuditLogHandler_List_InvalidDateFormats +=== RUN TestAuditLogHandler_List_InvalidDateFormats/Invalid_start_date_format +=== RUN TestAuditLogHandler_List_InvalidDateFormats/Invalid_end_date_format +=== RUN TestAuditLogHandler_List_InvalidDateFormats/Both_dates_invalid +--- PASS: TestAuditLogHandler_List_InvalidDateFormats (0.01s) + --- PASS: TestAuditLogHandler_List_InvalidDateFormats/Invalid_start_date_format (0.00s) + --- PASS: TestAuditLogHandler_List_InvalidDateFormats/Invalid_end_date_format (0.00s) + --- PASS: TestAuditLogHandler_List_InvalidDateFormats/Both_dates_invalid (0.00s) +=== RUN TestAuditLogHandler_Get_InternalError + +2026/01/10 02:17:47 /projects/Charon/backend/internal/services/security_service.go:321 sql: database is closed +[0.033ms] [rows:0] SELECT * FROM `security_audits` WHERE uuid = "test-uuid" ORDER BY `security_audits`.`id` LIMIT 1 +--- PASS: TestAuditLogHandler_Get_InternalError (0.00s) +=== RUN TestAuthHandler_Login +=== PAUSE TestAuthHandler_Login +=== RUN TestSetSecureCookie_HTTPS_Strict +--- PASS: TestSetSecureCookie_HTTPS_Strict (0.00s) +=== RUN TestSetSecureCookie_HTTP_Lax +=== PAUSE TestSetSecureCookie_HTTP_Lax +=== RUN TestAuthHandler_Login_Errors +=== PAUSE TestAuthHandler_Login_Errors +=== RUN TestAuthHandler_Register +=== PAUSE TestAuthHandler_Register +=== RUN TestAuthHandler_Register_Duplicate +=== PAUSE TestAuthHandler_Register_Duplicate +=== RUN TestAuthHandler_Logout +=== PAUSE TestAuthHandler_Logout +=== RUN TestAuthHandler_Me +=== PAUSE TestAuthHandler_Me +=== RUN TestAuthHandler_Me_NotFound +=== PAUSE TestAuthHandler_Me_NotFound +=== RUN TestAuthHandler_ChangePassword +=== PAUSE TestAuthHandler_ChangePassword +=== RUN TestAuthHandler_ChangePassword_WrongOld +=== PAUSE TestAuthHandler_ChangePassword_WrongOld +=== RUN TestAuthHandler_ChangePassword_Errors +=== PAUSE TestAuthHandler_ChangePassword_Errors +=== RUN TestNewAuthHandlerWithDB +=== PAUSE TestNewAuthHandlerWithDB +=== RUN TestAuthHandler_Verify_NoCookie +=== PAUSE TestAuthHandler_Verify_NoCookie +=== RUN TestAuthHandler_Verify_InvalidToken +=== PAUSE TestAuthHandler_Verify_InvalidToken +=== RUN TestAuthHandler_Verify_ValidToken +=== PAUSE TestAuthHandler_Verify_ValidToken +=== RUN TestAuthHandler_Verify_BearerToken +=== PAUSE TestAuthHandler_Verify_BearerToken +=== RUN TestAuthHandler_Verify_DisabledUser +=== PAUSE TestAuthHandler_Verify_DisabledUser +=== RUN TestAuthHandler_Verify_ForwardAuthDenied +=== PAUSE TestAuthHandler_Verify_ForwardAuthDenied +=== RUN TestAuthHandler_VerifyStatus_NotAuthenticated +=== PAUSE TestAuthHandler_VerifyStatus_NotAuthenticated +=== RUN TestAuthHandler_VerifyStatus_InvalidToken +=== PAUSE TestAuthHandler_VerifyStatus_InvalidToken +=== RUN TestAuthHandler_VerifyStatus_Authenticated +=== PAUSE TestAuthHandler_VerifyStatus_Authenticated +=== RUN TestAuthHandler_VerifyStatus_DisabledUser +=== PAUSE TestAuthHandler_VerifyStatus_DisabledUser +=== RUN TestAuthHandler_GetAccessibleHosts_Unauthorized +=== PAUSE TestAuthHandler_GetAccessibleHosts_Unauthorized +=== RUN TestAuthHandler_GetAccessibleHosts_AllowAll +=== PAUSE TestAuthHandler_GetAccessibleHosts_AllowAll +=== RUN TestAuthHandler_GetAccessibleHosts_DenyAll +=== PAUSE TestAuthHandler_GetAccessibleHosts_DenyAll +=== RUN TestAuthHandler_GetAccessibleHosts_PermittedHosts +=== PAUSE TestAuthHandler_GetAccessibleHosts_PermittedHosts +=== RUN TestAuthHandler_GetAccessibleHosts_UserNotFound +=== PAUSE TestAuthHandler_GetAccessibleHosts_UserNotFound +=== RUN TestAuthHandler_CheckHostAccess_Unauthorized +=== PAUSE TestAuthHandler_CheckHostAccess_Unauthorized +=== RUN TestAuthHandler_CheckHostAccess_InvalidHostID +=== PAUSE TestAuthHandler_CheckHostAccess_InvalidHostID +=== RUN TestAuthHandler_CheckHostAccess_Allowed +=== PAUSE TestAuthHandler_CheckHostAccess_Allowed +=== RUN TestAuthHandler_CheckHostAccess_Denied +=== PAUSE TestAuthHandler_CheckHostAccess_Denied +=== RUN TestBackupHandlerSanitizesFilename +--- PASS: TestBackupHandlerSanitizesFilename (0.00s) +=== RUN TestBackupLifecycle +--- PASS: TestBackupLifecycle (0.01s) +=== RUN TestBackupHandler_Errors +--- PASS: TestBackupHandler_Errors (0.00s) +=== RUN TestBackupHandler_List_Success +--- PASS: TestBackupHandler_List_Success (0.00s) +=== RUN TestBackupHandler_Create_Success +--- PASS: TestBackupHandler_Create_Success (0.00s) +=== RUN TestBackupHandler_Download_Success +--- PASS: TestBackupHandler_Download_Success (0.00s) +=== RUN TestBackupHandler_PathTraversal +--- PASS: TestBackupHandler_PathTraversal (0.00s) +=== RUN TestBackupHandler_Download_InvalidPath +--- PASS: TestBackupHandler_Download_InvalidPath (0.00s) +=== RUN TestBackupHandler_Create_ServiceError +--- PASS: TestBackupHandler_Create_ServiceError (0.00s) +=== RUN TestBackupHandler_Delete_InternalError +--- PASS: TestBackupHandler_Delete_InternalError (0.00s) +=== RUN TestBackupHandler_Restore_InternalError +--- PASS: TestBackupHandler_Restore_InternalError (0.00s) +=== RUN TestCerberusLogsHandler_NewHandler +=== PAUSE TestCerberusLogsHandler_NewHandler +=== RUN TestCerberusLogsHandler_SuccessfulConnection +=== PAUSE TestCerberusLogsHandler_SuccessfulConnection +=== RUN TestCerberusLogsHandler_ReceiveLogEntries +=== PAUSE TestCerberusLogsHandler_ReceiveLogEntries +=== RUN TestCerberusLogsHandler_SourceFilter +=== PAUSE TestCerberusLogsHandler_SourceFilter +=== RUN TestCerberusLogsHandler_BlockedOnlyFilter +=== PAUSE TestCerberusLogsHandler_BlockedOnlyFilter +=== RUN TestCerberusLogsHandler_IPFilter +=== PAUSE TestCerberusLogsHandler_IPFilter +=== RUN TestCerberusLogsHandler_ClientDisconnect +=== PAUSE TestCerberusLogsHandler_ClientDisconnect +=== RUN TestCerberusLogsHandler_MultipleClients +=== PAUSE TestCerberusLogsHandler_MultipleClients +=== RUN TestCerberusLogsHandler_UpgradeFailure +=== PAUSE TestCerberusLogsHandler_UpgradeFailure +=== RUN TestCertificateHandler_List_DBError + +2026/01/10 02:17:47 /projects/Charon/backend/internal/services/certificate_service.go:198 no such table: ssl_certificates +[3.273ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE provider LIKE "letsencrypt%" + +2026/01/10 02:17:47 /projects/Charon/backend/internal/services/certificate_service.go:226 no such table: ssl_certificates +[0.033ms] [rows:0] SELECT * FROM `ssl_certificates` + +2026/01/10 02:17:47 /projects/Charon/backend/internal/services/certificate_service.go:226 no such table: ssl_certificates +[0.032ms] [rows:0] SELECT * FROM `ssl_certificates` + +2026/01/10 02:17:47 /projects/Charon/backend/internal/services/certificate_service.go:198 no such table: ssl_certificates +[0.044ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE provider LIKE "letsencrypt%" + +2026/01/10 02:17:47 /projects/Charon/backend/internal/services/certificate_service.go:226 no such table: ssl_certificates +[0.031ms] [rows:0] SELECT * FROM `ssl_certificates` +--- PASS: TestCertificateHandler_List_DBError (0.00s) +=== RUN TestCertificateHandler_Delete_InvalidID +--- PASS: TestCertificateHandler_Delete_InvalidID (0.02s) +=== RUN TestCertificateHandler_Delete_NotFound + +2026/01/10 02:17:47 /projects/Charon/backend/internal/services/certificate_service.go:410 record not found +[0.076ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE `ssl_certificates`.`id` = 9999 ORDER BY `ssl_certificates`.`id` LIMIT 1 +--- PASS: TestCertificateHandler_Delete_NotFound (0.01s) +=== RUN TestCertificateHandler_Delete_NoBackupService +--- PASS: TestCertificateHandler_Delete_NoBackupService (0.21s) +=== RUN TestCertificateHandler_Delete_CheckUsageDBError + +2026/01/10 02:17:48 /projects/Charon/backend/internal/services/certificate_service.go:392 no such table: proxy_hosts +[6.492ms] [rows:0] SELECT count(*) FROM `proxy_hosts` WHERE certificate_id = 1 + +2026/01/10 02:17:48 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[5.901ms] [rows:0] SELECT * FROM `proxy_hosts` +--- PASS: TestCertificateHandler_Delete_CheckUsageDBError (0.01s) +=== RUN TestCertificateHandler_List_WithCertificates +--- PASS: TestCertificateHandler_List_WithCertificates (0.01s) +=== RUN TestCertificateHandler_Delete_ZeroID +--- PASS: TestCertificateHandler_Delete_ZeroID (0.02s) +=== RUN TestCertificateHandler_Delete_RequiresAuth +--- PASS: TestCertificateHandler_Delete_RequiresAuth (0.01s) +=== RUN TestCertificateHandler_List_RequiresAuth +--- PASS: TestCertificateHandler_List_RequiresAuth (0.01s) +=== RUN TestCertificateHandler_Upload_RequiresAuth +--- PASS: TestCertificateHandler_Upload_RequiresAuth (0.02s) +=== RUN TestCertificateHandler_Delete_DiskSpaceCheck +--- PASS: TestCertificateHandler_Delete_DiskSpaceCheck (0.01s) +=== RUN TestCertificateHandler_Delete_NotificationRateLimiting +--- PASS: TestCertificateHandler_Delete_NotificationRateLimiting (0.02s) +=== RUN TestDeleteCertificate_InUse +--- PASS: TestDeleteCertificate_InUse (0.02s) +=== RUN TestDeleteCertificate_CreatesBackup + +2026/01/10 02:17:48 /projects/Charon/backend/internal/api/handlers/certificate_handler_test.go:134 record not found +[0.090ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE `ssl_certificates`.`id` = 1 ORDER BY `ssl_certificates`.`id` LIMIT 1 +--- PASS: TestDeleteCertificate_CreatesBackup (0.01s) +=== RUN TestDeleteCertificate_BackupFailure +--- PASS: TestDeleteCertificate_BackupFailure (0.02s) +=== RUN TestDeleteCertificate_InUse_NoBackup +--- PASS: TestDeleteCertificate_InUse_NoBackup (0.01s) +=== RUN TestCertificateHandler_List +--- PASS: TestCertificateHandler_List (0.01s) +=== RUN TestCertificateHandler_Upload_MissingName +--- PASS: TestCertificateHandler_Upload_MissingName (0.01s) +=== RUN TestCertificateHandler_Upload_MissingCertFile +--- PASS: TestCertificateHandler_Upload_MissingCertFile (0.02s) +=== RUN TestCertificateHandler_Upload_MissingKeyFile +--- PASS: TestCertificateHandler_Upload_MissingKeyFile (0.02s) +=== RUN TestCertificateHandler_Upload_Success +--- PASS: TestCertificateHandler_Upload_Success (0.05s) +=== RUN TestDeleteCertificate_InvalidID +--- PASS: TestDeleteCertificate_InvalidID (0.01s) +=== RUN TestDeleteCertificate_ZeroID +--- PASS: TestDeleteCertificate_ZeroID (0.02s) +=== RUN TestDeleteCertificate_LowDiskSpace +--- PASS: TestDeleteCertificate_LowDiskSpace (0.02s) +=== RUN TestDeleteCertificate_DiskSpaceCheckError + +2026/01/10 02:17:48 /projects/Charon/backend/internal/services/certificate_service.go:198 database table is locked: ssl_certificates +[0.359ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE provider LIKE "letsencrypt%" +--- PASS: TestDeleteCertificate_DiskSpaceCheckError (0.02s) +=== RUN TestDeleteCertificate_UsageCheckError + +2026/01/10 02:17:48 /projects/Charon/backend/internal/services/certificate_service.go:392 no such table: proxy_hosts +[8.346ms] [rows:0] SELECT count(*) FROM `proxy_hosts` WHERE certificate_id = 1 + +2026/01/10 02:17:48 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[7.932ms] [rows:0] SELECT * FROM `proxy_hosts` +--- PASS: TestDeleteCertificate_UsageCheckError (0.01s) +=== RUN TestDeleteCertificate_NotificationRateLimit +--- PASS: TestDeleteCertificate_NotificationRateLimit (0.12s) +=== RUN TestSafeIntToUint +=== RUN TestSafeIntToUint/ValidPositive +=== RUN TestSafeIntToUint/Zero +=== RUN TestSafeIntToUint/Negative +--- PASS: TestSafeIntToUint (0.00s) + --- PASS: TestSafeIntToUint/ValidPositive (0.00s) + --- PASS: TestSafeIntToUint/Zero (0.00s) + --- PASS: TestSafeIntToUint/Negative (0.00s) +=== RUN TestSafeFloat64ToUint +=== RUN TestSafeFloat64ToUint/ValidPositive +=== RUN TestSafeFloat64ToUint/Zero +=== RUN TestSafeFloat64ToUint/Negative +=== RUN TestSafeFloat64ToUint/NotInteger +--- PASS: TestSafeFloat64ToUint (0.00s) + --- PASS: TestSafeFloat64ToUint/ValidPositive (0.00s) + --- PASS: TestSafeFloat64ToUint/Zero (0.00s) + --- PASS: TestSafeFloat64ToUint/Negative (0.00s) + --- PASS: TestSafeFloat64ToUint/NotInteger (0.00s) +=== RUN Test_ttlRemainingSeconds +=== RUN Test_ttlRemainingSeconds/zero_retrievedAt_returns_nil +=== RUN Test_ttlRemainingSeconds/zero_ttl_returns_nil +=== RUN Test_ttlRemainingSeconds/negative_ttl_returns_nil +=== RUN Test_ttlRemainingSeconds/expired_ttl_returns_zero +=== RUN Test_ttlRemainingSeconds/valid_remaining_time_returns_positive +--- PASS: Test_ttlRemainingSeconds (0.00s) + --- PASS: Test_ttlRemainingSeconds/zero_retrievedAt_returns_nil (0.00s) + --- PASS: Test_ttlRemainingSeconds/zero_ttl_returns_nil (0.00s) + --- PASS: Test_ttlRemainingSeconds/negative_ttl_returns_nil (0.00s) + --- PASS: Test_ttlRemainingSeconds/expired_ttl_returns_zero (0.00s) + --- PASS: Test_ttlRemainingSeconds/valid_remaining_time_returns_positive (0.00s) +=== RUN Test_mapCrowdsecStatus +=== RUN Test_mapCrowdsecStatus/deadline_exceeded_returns_gateway_timeout +=== RUN Test_mapCrowdsecStatus/context_canceled_returns_gateway_timeout +=== RUN Test_mapCrowdsecStatus/other_error_returns_default_code +=== RUN Test_mapCrowdsecStatus/other_error_returns_bad_request_default +--- PASS: Test_mapCrowdsecStatus (0.00s) + --- PASS: Test_mapCrowdsecStatus/deadline_exceeded_returns_gateway_timeout (0.00s) + --- PASS: Test_mapCrowdsecStatus/context_canceled_returns_gateway_timeout (0.00s) + --- PASS: Test_mapCrowdsecStatus/other_error_returns_default_code (0.00s) + --- PASS: Test_mapCrowdsecStatus/other_error_returns_bad_request_default (0.00s) +=== RUN Test_actorFromContext +=== RUN Test_actorFromContext/with_userID_in_context +=== RUN Test_actorFromContext/without_userID_in_context +=== RUN Test_actorFromContext/with_string_userID +--- PASS: Test_actorFromContext (0.00s) + --- PASS: Test_actorFromContext/with_userID_in_context (0.00s) + --- PASS: Test_actorFromContext/without_userID_in_context (0.00s) + --- PASS: Test_actorFromContext/with_string_userID (0.00s) +=== RUN Test_hubEndpoints +=== RUN Test_hubEndpoints/nil_Hub_returns_nil +--- PASS: Test_hubEndpoints (0.00s) + --- PASS: Test_hubEndpoints/nil_Hub_returns_nil (0.00s) +=== RUN TestRealCommandExecutor_Execute +=== RUN TestRealCommandExecutor_Execute/successful_command +=== RUN TestRealCommandExecutor_Execute/failed_command +=== RUN TestRealCommandExecutor_Execute/context_cancellation +--- PASS: TestRealCommandExecutor_Execute (0.00s) + --- PASS: TestRealCommandExecutor_Execute/successful_command (0.00s) + --- PASS: TestRealCommandExecutor_Execute/failed_command (0.00s) + --- PASS: TestRealCommandExecutor_Execute/context_cancellation (0.00s) +=== RUN Test_isCerberusEnabled +=== RUN Test_isCerberusEnabled/returns_true_when_no_setting_exists_(default) +=== RUN Test_isCerberusEnabled/enabled_when_setting_is_true +=== RUN Test_isCerberusEnabled/disabled_when_setting_is_false +--- PASS: Test_isCerberusEnabled (0.00s) + --- PASS: Test_isCerberusEnabled/returns_true_when_no_setting_exists_(default) (0.00s) + --- PASS: Test_isCerberusEnabled/enabled_when_setting_is_true (0.00s) + --- PASS: Test_isCerberusEnabled/disabled_when_setting_is_false (0.00s) +=== RUN Test_isConsoleEnrollmentEnabled +=== RUN Test_isConsoleEnrollmentEnabled/disabled_when_no_setting_exists +=== RUN Test_isConsoleEnrollmentEnabled/enabled_when_setting_is_true +=== RUN Test_isConsoleEnrollmentEnabled/disabled_when_setting_is_false +--- PASS: Test_isConsoleEnrollmentEnabled (0.00s) + --- PASS: Test_isConsoleEnrollmentEnabled/disabled_when_no_setting_exists (0.00s) + --- PASS: Test_isConsoleEnrollmentEnabled/enabled_when_setting_is_true (0.00s) + --- PASS: Test_isConsoleEnrollmentEnabled/disabled_when_setting_is_false (0.00s) +=== RUN TestCrowdsecHandler_ExportConfig +--- PASS: TestCrowdsecHandler_ExportConfig (0.01s) +=== RUN TestCrowdsecHandler_CheckLAPIHealth +--- PASS: TestCrowdsecHandler_CheckLAPIHealth (0.00s) +=== RUN TestCrowdsecHandler_ConsoleStatus +--- PASS: TestCrowdsecHandler_ConsoleStatus (0.01s) +=== RUN TestCrowdsecHandler_ConsoleEnroll_Disabled +--- PASS: TestCrowdsecHandler_ConsoleEnroll_Disabled (0.00s) +=== RUN TestCrowdsecHandler_DeleteConsoleEnrollment +--- PASS: TestCrowdsecHandler_DeleteConsoleEnrollment (0.00s) +=== RUN TestCrowdsecHandler_BanIP +--- PASS: TestCrowdsecHandler_BanIP (0.01s) +=== RUN TestCrowdsecHandler_UnbanIP +--- PASS: TestCrowdsecHandler_UnbanIP (0.00s) +=== RUN TestCrowdsecHandler_UpdateAcquisitionConfig +--- PASS: TestCrowdsecHandler_UpdateAcquisitionConfig (0.01s) +=== RUN Test_safeIntToUint +=== RUN Test_safeIntToUint/positive_int +=== RUN Test_safeIntToUint/zero +=== RUN Test_safeIntToUint/negative_int +=== RUN Test_safeIntToUint/large_positive +--- PASS: Test_safeIntToUint (0.00s) + --- PASS: Test_safeIntToUint/positive_int (0.00s) + --- PASS: Test_safeIntToUint/zero (0.00s) + --- PASS: Test_safeIntToUint/negative_int (0.00s) + --- PASS: Test_safeIntToUint/large_positive (0.00s) +=== RUN Test_safeFloat64ToUint +=== RUN Test_safeFloat64ToUint/positive_integer_float +=== RUN Test_safeFloat64ToUint/zero +=== RUN Test_safeFloat64ToUint/negative_float +=== RUN Test_safeFloat64ToUint/fractional_float +--- PASS: Test_safeFloat64ToUint (0.00s) + --- PASS: Test_safeFloat64ToUint/positive_integer_float (0.00s) + --- PASS: Test_safeFloat64ToUint/zero (0.00s) + --- PASS: Test_safeFloat64ToUint/negative_float (0.00s) + --- PASS: Test_safeFloat64ToUint/fractional_float (0.00s) +=== RUN TestBackupHandlerQuick +--- PASS: TestBackupHandlerQuick (0.00s) +=== RUN TestListPresetsShowsCachedStatus +--- PASS: TestListPresetsShowsCachedStatus (1.13s) +=== RUN TestCacheKeyPersistence +--- PASS: TestCacheKeyPersistence (0.00s) +=== RUN TestUpdateAcquisitionConfigMissingContent +--- PASS: TestUpdateAcquisitionConfigMissingContent (0.00s) +=== RUN TestUpdateAcquisitionConfigInvalidJSON +--- PASS: TestUpdateAcquisitionConfigInvalidJSON (0.00s) +=== RUN TestGetLAPIDecisionsWithIPFilter +--- PASS: TestGetLAPIDecisionsWithIPFilter (0.00s) +=== RUN TestGetLAPIDecisionsWithScopeFilter +--- PASS: TestGetLAPIDecisionsWithScopeFilter (0.00s) +=== RUN TestGetLAPIDecisionsWithTypeFilter +--- PASS: TestGetLAPIDecisionsWithTypeFilter (0.00s) +=== RUN TestGetLAPIDecisionsWithMultipleFilters +--- PASS: TestGetLAPIDecisionsWithMultipleFilters (0.00s) +=== RUN TestUpdateAcquisitionConfigSuccess +--- PASS: TestUpdateAcquisitionConfigSuccess (0.00s) +=== RUN TestRegisterBouncerScriptPathError +--- PASS: TestRegisterBouncerScriptPathError (0.00s) +=== RUN TestGetLAPIDecisionsEmptyResponse +--- PASS: TestGetLAPIDecisionsEmptyResponse (0.00s) +=== RUN TestGetLAPIDecisionsIPQueryParam +--- PASS: TestGetLAPIDecisionsIPQueryParam (0.00s) +=== RUN TestGetLAPIDecisionsScopeParam +--- PASS: TestGetLAPIDecisionsScopeParam (0.00s) +=== RUN TestGetLAPIDecisionsTypeParam +--- PASS: TestGetLAPIDecisionsTypeParam (0.00s) +=== RUN TestGetLAPIDecisionsCombinedParams +--- PASS: TestGetLAPIDecisionsCombinedParams (0.01s) +=== RUN TestCheckLAPIHealthRequest +--- PASS: TestCheckLAPIHealthRequest (0.00s) +=== RUN TestGetLAPIKeyLookup +--- PASS: TestGetLAPIKeyLookup (0.00s) +=== RUN TestGetLAPIKeyEmpty +--- PASS: TestGetLAPIKeyEmpty (0.00s) +=== RUN TestGetLAPIKeyAlternative +--- PASS: TestGetLAPIKeyAlternative (0.00s) +=== RUN TestStatusRequest +--- PASS: TestStatusRequest (0.00s) +=== RUN TestRegisterBouncerFlow +--- PASS: TestRegisterBouncerFlow (0.00s) +=== RUN TestRegisterBouncerExecutionFailure +--- PASS: TestRegisterBouncerExecutionFailure (0.00s) +=== RUN TestGetAcquisitionConfigNotPresent +--- PASS: TestGetAcquisitionConfigNotPresent (0.00s) +=== RUN TestListDecisions_Success +--- PASS: TestListDecisions_Success (0.00s) +=== RUN TestListDecisions_EmptyList +--- PASS: TestListDecisions_EmptyList (0.00s) +=== RUN TestListDecisions_CscliError +--- PASS: TestListDecisions_CscliError (0.00s) +=== RUN TestListDecisions_InvalidJSON +--- PASS: TestListDecisions_InvalidJSON (0.00s) +=== RUN TestBanIP_Success +--- PASS: TestBanIP_Success (0.00s) +=== RUN TestBanIP_DefaultDuration +--- PASS: TestBanIP_DefaultDuration (0.00s) +=== RUN TestBanIP_MissingIP +--- PASS: TestBanIP_MissingIP (0.00s) +=== RUN TestBanIP_EmptyIP +--- PASS: TestBanIP_EmptyIP (0.00s) +=== RUN TestBanIP_CscliError +--- PASS: TestBanIP_CscliError (0.00s) +=== RUN TestUnbanIP_Success +--- PASS: TestUnbanIP_Success (0.01s) +=== RUN TestUnbanIP_CscliError +--- PASS: TestUnbanIP_CscliError (0.00s) +=== RUN TestListDecisions_MultipleDecisions +--- PASS: TestListDecisions_MultipleDecisions (0.00s) +=== RUN TestBanIP_InvalidJSON +--- PASS: TestBanIP_InvalidJSON (0.00s) +=== RUN TestDefaultCrowdsecExecutorPidFile +--- PASS: TestDefaultCrowdsecExecutorPidFile (0.00s) +=== RUN TestDefaultCrowdsecExecutorStartStatusStop +--- PASS: TestDefaultCrowdsecExecutorStartStatusStop (0.20s) +=== RUN TestDefaultCrowdsecExecutor_Status_NoPidFile +--- PASS: TestDefaultCrowdsecExecutor_Status_NoPidFile (0.00s) +=== RUN TestDefaultCrowdsecExecutor_Status_InvalidPid +--- PASS: TestDefaultCrowdsecExecutor_Status_InvalidPid (0.00s) +=== RUN TestDefaultCrowdsecExecutor_Status_NonExistentProcess +--- PASS: TestDefaultCrowdsecExecutor_Status_NonExistentProcess (0.00s) +=== RUN TestDefaultCrowdsecExecutor_Stop_NoPidFile +--- PASS: TestDefaultCrowdsecExecutor_Stop_NoPidFile (0.00s) +=== RUN TestDefaultCrowdsecExecutor_Stop_InvalidPid +--- PASS: TestDefaultCrowdsecExecutor_Stop_InvalidPid (0.00s) +=== RUN TestDefaultCrowdsecExecutor_Stop_NonExistentProcess +--- PASS: TestDefaultCrowdsecExecutor_Stop_NonExistentProcess (0.00s) +=== RUN TestDefaultCrowdsecExecutor_Stop_Idempotent +--- PASS: TestDefaultCrowdsecExecutor_Stop_Idempotent (0.00s) +=== RUN TestDefaultCrowdsecExecutor_Start_InvalidBinary +--- PASS: TestDefaultCrowdsecExecutor_Start_InvalidBinary (0.00s) +=== RUN TestDefaultCrowdsecExecutor_isCrowdSecProcess_ValidProcess +--- PASS: TestDefaultCrowdsecExecutor_isCrowdSecProcess_ValidProcess (0.00s) +=== RUN TestDefaultCrowdsecExecutor_isCrowdSecProcess_DifferentProcess +--- PASS: TestDefaultCrowdsecExecutor_isCrowdSecProcess_DifferentProcess (0.00s) +=== RUN TestDefaultCrowdsecExecutor_isCrowdSecProcess_NonExistentProcess +--- PASS: TestDefaultCrowdsecExecutor_isCrowdSecProcess_NonExistentProcess (0.00s) +=== RUN TestDefaultCrowdsecExecutor_isCrowdSecProcess_EmptyCmdline +--- PASS: TestDefaultCrowdsecExecutor_isCrowdSecProcess_EmptyCmdline (0.00s) +=== RUN TestDefaultCrowdsecExecutor_Status_PIDReuse_DifferentProcess +--- PASS: TestDefaultCrowdsecExecutor_Status_PIDReuse_DifferentProcess (0.00s) +=== RUN TestDefaultCrowdsecExecutor_Status_PIDReuse_IsCrowdSec +--- PASS: TestDefaultCrowdsecExecutor_Status_PIDReuse_IsCrowdSec (0.00s) +=== RUN TestDefaultCrowdsecExecutor_Stop_SignalError +--- PASS: TestDefaultCrowdsecExecutor_Stop_SignalError (0.00s) +=== RUN TestTTLRemainingSeconds +=== RUN TestTTLRemainingSeconds/zero_retrieved_time +=== RUN TestTTLRemainingSeconds/zero_ttl +=== RUN TestTTLRemainingSeconds/expired_ttl +=== RUN TestTTLRemainingSeconds/valid_ttl +--- PASS: TestTTLRemainingSeconds (0.00s) + --- PASS: TestTTLRemainingSeconds/zero_retrieved_time (0.00s) + --- PASS: TestTTLRemainingSeconds/zero_ttl (0.00s) + --- PASS: TestTTLRemainingSeconds/expired_ttl (0.00s) + --- PASS: TestTTLRemainingSeconds/valid_ttl (0.00s) +=== RUN TestMapCrowdsecStatus +=== RUN TestMapCrowdsecStatus/no_error +=== RUN TestMapCrowdsecStatus/generic_error +--- PASS: TestMapCrowdsecStatus (0.00s) + --- PASS: TestMapCrowdsecStatus/no_error (0.00s) + --- PASS: TestMapCrowdsecStatus/generic_error (0.00s) +=== RUN TestIsConsoleEnrollmentEnabled +=== RUN TestIsConsoleEnrollmentEnabled/enabled_via_env +=== RUN TestIsConsoleEnrollmentEnabled/disabled_via_env +=== RUN TestIsConsoleEnrollmentEnabled/default_when_not_set +--- PASS: TestIsConsoleEnrollmentEnabled (0.00s) + --- PASS: TestIsConsoleEnrollmentEnabled/enabled_via_env (0.00s) + --- PASS: TestIsConsoleEnrollmentEnabled/disabled_via_env (0.00s) + --- PASS: TestIsConsoleEnrollmentEnabled/default_when_not_set (0.00s) +=== RUN TestActorFromContext +=== RUN TestActorFromContext/with_userID +=== RUN TestActorFromContext/without_userID +--- PASS: TestActorFromContext (0.00s) + --- PASS: TestActorFromContext/with_userID (0.00s) + --- PASS: TestActorFromContext/without_userID (0.00s) +=== RUN TestHubEndpoints +--- PASS: TestHubEndpoints (0.00s) +=== RUN TestGetCachedPreset +--- PASS: TestGetCachedPreset (0.00s) +=== RUN TestGetCachedPreset_NotFound +--- PASS: TestGetCachedPreset_NotFound (0.00s) +=== RUN TestGetLAPIDecisions +--- PASS: TestGetLAPIDecisions (0.01s) +=== RUN TestCheckLAPIHealth +--- PASS: TestCheckLAPIHealth (0.00s) +=== RUN TestListDecisions +--- PASS: TestListDecisions (0.00s) +=== RUN TestBanIP +--- PASS: TestBanIP (0.00s) +=== RUN TestUnbanIP +--- PASS: TestUnbanIP (0.00s) +=== RUN TestGetAcquisitionConfig +--- PASS: TestGetAcquisitionConfig (0.00s) +=== RUN TestUpdateAcquisitionConfig +--- PASS: TestUpdateAcquisitionConfig (0.00s) +=== RUN TestGetLAPIKey +--- PASS: TestGetLAPIKey (0.00s) +=== RUN TestCrowdsec_Start_Error +--- PASS: TestCrowdsec_Start_Error (0.00s) +=== RUN TestCrowdsec_Stop_Error +--- PASS: TestCrowdsec_Stop_Error (0.00s) +=== RUN TestCrowdsec_Status_Error +--- PASS: TestCrowdsec_Status_Error (0.00s) +=== RUN TestCrowdsec_ReadFile_MissingPath +--- PASS: TestCrowdsec_ReadFile_MissingPath (0.00s) +=== RUN TestCrowdsec_ReadFile_PathTraversal +--- PASS: TestCrowdsec_ReadFile_PathTraversal (0.00s) +=== RUN TestCrowdsec_ReadFile_NotFound +--- PASS: TestCrowdsec_ReadFile_NotFound (0.01s) +=== RUN TestCrowdsec_WriteFile_InvalidPayload +--- PASS: TestCrowdsec_WriteFile_InvalidPayload (0.00s) +=== RUN TestCrowdsec_WriteFile_MissingPath +--- PASS: TestCrowdsec_WriteFile_MissingPath (0.01s) +=== RUN TestCrowdsec_WriteFile_PathTraversal +--- PASS: TestCrowdsec_WriteFile_PathTraversal (0.00s) +=== RUN TestCrowdsec_ExportConfig_NotFound +--- PASS: TestCrowdsec_ExportConfig_NotFound (0.00s) +=== RUN TestCrowdsec_ListFiles_EmptyDir +--- PASS: TestCrowdsec_ListFiles_EmptyDir (0.00s) +=== RUN TestCrowdsec_ListFiles_NonExistent +--- PASS: TestCrowdsec_ListFiles_NonExistent (0.00s) +=== RUN TestCrowdsec_ImportConfig_NoFile +--- PASS: TestCrowdsec_ImportConfig_NoFile (0.00s) +=== RUN TestCrowdsec_ReadFile_NestedPath +--- PASS: TestCrowdsec_ReadFile_NestedPath (0.01s) +=== RUN TestCrowdsec_WriteFile_Success +--- PASS: TestCrowdsec_WriteFile_Success (0.00s) +=== RUN TestCrowdsec_ListPresets_Disabled +--- PASS: TestCrowdsec_ListPresets_Disabled (0.00s) +=== RUN TestCrowdsec_ListPresets_Success +--- PASS: TestCrowdsec_ListPresets_Success (0.92s) +=== RUN TestCrowdsec_PullPreset_Validation +--- PASS: TestCrowdsec_PullPreset_Validation (0.01s) +=== RUN TestCrowdsec_ApplyPreset_Validation +--- PASS: TestCrowdsec_ApplyPreset_Validation (0.00s) +=== RUN TestCrowdsecEndpoints +=== PAUSE TestCrowdsecEndpoints +=== RUN TestImportConfig +=== PAUSE TestImportConfig +=== RUN TestImportCreatesBackup +=== PAUSE TestImportCreatesBackup +=== RUN TestExportConfig +=== PAUSE TestExportConfig +=== RUN TestListAndReadFile +=== PAUSE TestListAndReadFile +=== RUN TestExportConfigStreamsArchive +=== PAUSE TestExportConfigStreamsArchive +=== RUN TestWriteFileCreatesBackup +=== PAUSE TestWriteFileCreatesBackup +=== RUN TestListPresetsCerberusDisabled +--- PASS: TestListPresetsCerberusDisabled (0.00s) +=== RUN TestReadFileInvalidPath +=== PAUSE TestReadFileInvalidPath +=== RUN TestWriteFileInvalidPath +=== PAUSE TestWriteFileInvalidPath +=== RUN TestWriteFileMissingPath +=== PAUSE TestWriteFileMissingPath +=== RUN TestWriteFileInvalidPayload +=== PAUSE TestWriteFileInvalidPayload +=== RUN TestImportConfigRequiresFile +=== PAUSE TestImportConfigRequiresFile +=== RUN TestImportConfigRejectsEmptyUpload +=== PAUSE TestImportConfigRejectsEmptyUpload +=== RUN TestListFilesMissingDir +=== PAUSE TestListFilesMissingDir +=== RUN TestListFilesReturnsEntries +=== PAUSE TestListFilesReturnsEntries +=== RUN TestIsCerberusEnabledFromDB +=== PAUSE TestIsCerberusEnabledFromDB +=== RUN TestIsCerberusEnabledInvalidEnv +--- PASS: TestIsCerberusEnabledInvalidEnv (0.00s) +=== RUN TestIsCerberusEnabledLegacyEnv +--- PASS: TestIsCerberusEnabledLegacyEnv (0.00s) +=== RUN TestConsoleEnrollDisabled +--- PASS: TestConsoleEnrollDisabled (0.00s) +=== RUN TestConsoleEnrollServiceUnavailable +--- PASS: TestConsoleEnrollServiceUnavailable (0.00s) +=== RUN TestConsoleEnrollInvalidPayload +--- PASS: TestConsoleEnrollInvalidPayload (0.01s) +=== RUN TestConsoleEnrollSuccess +--- PASS: TestConsoleEnrollSuccess (0.01s) +=== RUN TestConsoleEnrollMissingAgentName +--- PASS: TestConsoleEnrollMissingAgentName (0.01s) +=== RUN TestConsoleStatusDisabled +--- PASS: TestConsoleStatusDisabled (0.00s) +=== RUN TestConsoleStatusServiceUnavailable +--- PASS: TestConsoleStatusServiceUnavailable (0.00s) +=== RUN TestConsoleStatusSuccess +--- PASS: TestConsoleStatusSuccess (0.01s) +=== RUN TestConsoleStatusAfterEnroll +--- PASS: TestConsoleStatusAfterEnroll (0.01s) +=== RUN TestIsConsoleEnrollmentEnabledFromDB +=== PAUSE TestIsConsoleEnrollmentEnabledFromDB +=== RUN TestIsConsoleEnrollmentDisabledFromDB +=== PAUSE TestIsConsoleEnrollmentDisabledFromDB +=== RUN TestIsConsoleEnrollmentEnabledFromEnv +--- PASS: TestIsConsoleEnrollmentEnabledFromEnv (0.00s) +=== RUN TestIsConsoleEnrollmentDisabledFromEnv +--- PASS: TestIsConsoleEnrollmentDisabledFromEnv (0.00s) +=== RUN TestIsConsoleEnrollmentInvalidEnv +--- PASS: TestIsConsoleEnrollmentInvalidEnv (0.00s) +=== RUN TestIsConsoleEnrollmentDefaultDisabled +--- PASS: TestIsConsoleEnrollmentDefaultDisabled (0.00s) +=== RUN TestIsConsoleEnrollmentDBTrueVariants +=== RUN TestIsConsoleEnrollmentDBTrueVariants/true +=== RUN TestIsConsoleEnrollmentDBTrueVariants/TRUE +=== RUN TestIsConsoleEnrollmentDBTrueVariants/True +=== RUN TestIsConsoleEnrollmentDBTrueVariants/1 +=== RUN TestIsConsoleEnrollmentDBTrueVariants/yes +=== RUN TestIsConsoleEnrollmentDBTrueVariants/YES +=== RUN TestIsConsoleEnrollmentDBTrueVariants/false +=== RUN TestIsConsoleEnrollmentDBTrueVariants/FALSE +=== RUN TestIsConsoleEnrollmentDBTrueVariants/0 +=== RUN TestIsConsoleEnrollmentDBTrueVariants/no +--- PASS: TestIsConsoleEnrollmentDBTrueVariants (0.04s) + --- PASS: TestIsConsoleEnrollmentDBTrueVariants/true (0.00s) + --- PASS: TestIsConsoleEnrollmentDBTrueVariants/TRUE (0.01s) + --- PASS: TestIsConsoleEnrollmentDBTrueVariants/True (0.01s) + --- PASS: TestIsConsoleEnrollmentDBTrueVariants/1 (0.00s) + --- PASS: TestIsConsoleEnrollmentDBTrueVariants/yes (0.00s) + --- PASS: TestIsConsoleEnrollmentDBTrueVariants/YES (0.00s) + --- PASS: TestIsConsoleEnrollmentDBTrueVariants/false (0.00s) + --- PASS: TestIsConsoleEnrollmentDBTrueVariants/FALSE (0.00s) + --- PASS: TestIsConsoleEnrollmentDBTrueVariants/0 (0.00s) + --- PASS: TestIsConsoleEnrollmentDBTrueVariants/no (0.00s) +=== RUN TestRegisterBouncerScriptNotFound +=== PAUSE TestRegisterBouncerScriptNotFound +=== RUN TestRegisterBouncerSuccess +=== PAUSE TestRegisterBouncerSuccess +=== RUN TestRegisterBouncerExecutionError +=== PAUSE TestRegisterBouncerExecutionError +=== RUN TestGetAcquisitionConfigNotFound +=== PAUSE TestGetAcquisitionConfigNotFound +=== RUN TestGetAcquisitionConfigSuccess +=== PAUSE TestGetAcquisitionConfigSuccess +=== RUN TestDeleteConsoleEnrollmentDisabled +--- PASS: TestDeleteConsoleEnrollmentDisabled (0.00s) +=== RUN TestDeleteConsoleEnrollmentServiceUnavailable +--- PASS: TestDeleteConsoleEnrollmentServiceUnavailable (0.00s) +=== RUN TestDeleteConsoleEnrollmentSuccess +--- PASS: TestDeleteConsoleEnrollmentSuccess (0.00s) +=== RUN TestDeleteConsoleEnrollmentNoRecordSuccess +--- PASS: TestDeleteConsoleEnrollmentNoRecordSuccess (0.00s) +=== RUN TestDeleteConsoleEnrollmentThenReenroll +--- PASS: TestDeleteConsoleEnrollmentThenReenroll (0.01s) +=== RUN TestCrowdsecStart_LAPINotReadyTimeout +=== PAUSE TestCrowdsecStart_LAPINotReadyTimeout +=== RUN TestCrowdsecHandler_Status_Error +=== PAUSE TestCrowdsecHandler_Status_Error +=== RUN TestCrowdsecHandler_Start_ExecutorError +=== PAUSE TestCrowdsecHandler_Start_ExecutorError +=== RUN TestCrowdsecHandler_ExportConfig_DirNotFound +=== PAUSE TestCrowdsecHandler_ExportConfig_DirNotFound +=== RUN TestCrowdsecHandler_ReadFile_NotFound +=== PAUSE TestCrowdsecHandler_ReadFile_NotFound +=== RUN TestCrowdsecHandler_ReadFile_MissingPath +=== PAUSE TestCrowdsecHandler_ReadFile_MissingPath +=== RUN TestCrowdsecHandler_ListDecisions_Success +=== PAUSE TestCrowdsecHandler_ListDecisions_Success +=== RUN TestCrowdsecHandler_ListDecisions_Empty +=== PAUSE TestCrowdsecHandler_ListDecisions_Empty +=== RUN TestCrowdsecHandler_ListDecisions_CscliError +=== PAUSE TestCrowdsecHandler_ListDecisions_CscliError +=== RUN TestCrowdsecHandler_ListDecisions_InvalidJSON +=== PAUSE TestCrowdsecHandler_ListDecisions_InvalidJSON +=== RUN TestCrowdsecHandler_BanIP_Success +=== PAUSE TestCrowdsecHandler_BanIP_Success +=== RUN TestCrowdsecHandler_BanIP_MissingIP +=== PAUSE TestCrowdsecHandler_BanIP_MissingIP +=== RUN TestCrowdsecHandler_BanIP_EmptyIP +=== PAUSE TestCrowdsecHandler_BanIP_EmptyIP +=== RUN TestCrowdsecHandler_BanIP_DefaultDuration +=== PAUSE TestCrowdsecHandler_BanIP_DefaultDuration +=== RUN TestCrowdsecHandler_UnbanIP_Success +=== PAUSE TestCrowdsecHandler_UnbanIP_Success +=== RUN TestCrowdsecHandler_UnbanIP_Error +=== PAUSE TestCrowdsecHandler_UnbanIP_Error +=== RUN TestCrowdsecHandler_BanIP_ExecutionError +=== PAUSE TestCrowdsecHandler_BanIP_ExecutionError +=== RUN TestCrowdsecHandler_CheckLAPIHealth_InvalidURL +=== PAUSE TestCrowdsecHandler_CheckLAPIHealth_InvalidURL +=== RUN TestCrowdsecHandler_GetLAPIDecisions_Fallback +=== PAUSE TestCrowdsecHandler_GetLAPIDecisions_Fallback +=== RUN TestCrowdsecHandler_PullPreset_CerberusDisabled +--- PASS: TestCrowdsecHandler_PullPreset_CerberusDisabled (0.00s) +=== RUN TestCrowdsecHandler_PullPreset_InvalidPayload +--- PASS: TestCrowdsecHandler_PullPreset_InvalidPayload (0.00s) +=== RUN TestCrowdsecHandler_PullPreset_EmptySlug +--- PASS: TestCrowdsecHandler_PullPreset_EmptySlug (0.00s) +=== RUN TestCrowdsecHandler_PullPreset_HubUnavailable +--- PASS: TestCrowdsecHandler_PullPreset_HubUnavailable (0.00s) +=== RUN TestCrowdsecHandler_ApplyPreset_CerberusDisabled +--- PASS: TestCrowdsecHandler_ApplyPreset_CerberusDisabled (0.00s) +=== RUN TestCrowdsecHandler_ApplyPreset_InvalidPayload +--- PASS: TestCrowdsecHandler_ApplyPreset_InvalidPayload (0.01s) +=== RUN TestCrowdsecHandler_ApplyPreset_EmptySlug +--- PASS: TestCrowdsecHandler_ApplyPreset_EmptySlug (0.00s) +=== RUN TestCrowdsecHandler_ApplyPreset_HubUnavailable +--- PASS: TestCrowdsecHandler_ApplyPreset_HubUnavailable (0.00s) +=== RUN TestCrowdsecHandler_UpdateAcquisitionConfig_MissingContent +=== PAUSE TestCrowdsecHandler_UpdateAcquisitionConfig_MissingContent +=== RUN TestCrowdsecHandler_UpdateAcquisitionConfig_InvalidJSON +=== PAUSE TestCrowdsecHandler_UpdateAcquisitionConfig_InvalidJSON +=== RUN TestCrowdsecHandler_ListDecisions_WithConfigYaml +=== PAUSE TestCrowdsecHandler_ListDecisions_WithConfigYaml +=== RUN TestCrowdsecHandler_BanIP_WithConfigYaml +=== PAUSE TestCrowdsecHandler_BanIP_WithConfigYaml +=== RUN TestCrowdsecHandler_UnbanIP_WithConfigYaml +=== PAUSE TestCrowdsecHandler_UnbanIP_WithConfigYaml +=== RUN TestCrowdsecHandler_Status_LAPIReady +=== PAUSE TestCrowdsecHandler_Status_LAPIReady +=== RUN TestCrowdsecHandler_Status_LAPINotReady +=== PAUSE TestCrowdsecHandler_Status_LAPINotReady +=== RUN TestCrowdsecHandler_ListDecisions_WithCreatedAt +=== PAUSE TestCrowdsecHandler_ListDecisions_WithCreatedAt +=== RUN TestCrowdsecHandler_HubEndpoints +=== PAUSE TestCrowdsecHandler_HubEndpoints +=== RUN TestCrowdsecHandler_ConsoleEnroll_ProgressConflict +--- PASS: TestCrowdsecHandler_ConsoleEnroll_ProgressConflict (0.01s) +=== RUN TestCrowdsecHandler_GetCachedPreset_CerberusDisabled +--- PASS: TestCrowdsecHandler_GetCachedPreset_CerberusDisabled (0.00s) +=== RUN TestCrowdsecHandler_GetCachedPreset_HubUnavailable +--- PASS: TestCrowdsecHandler_GetCachedPreset_HubUnavailable (0.00s) +=== RUN TestCrowdsecHandler_GetCachedPreset_EmptySlug +--- PASS: TestCrowdsecHandler_GetCachedPreset_EmptySlug (0.00s) +=== RUN TestGetLAPIDecisions_FallbackToCscli +--- PASS: TestGetLAPIDecisions_FallbackToCscli (0.00s) +=== RUN TestGetLAPIDecisions_EmptyResponse +--- PASS: TestGetLAPIDecisions_EmptyResponse (0.00s) +=== RUN TestCheckLAPIHealth_Handler +--- PASS: TestCheckLAPIHealth_Handler (0.00s) +=== RUN TestGetLAPIKey_FromEnv +--- PASS: TestGetLAPIKey_FromEnv (0.00s) +=== RUN TestGetLAPIKey_Empty +--- PASS: TestGetLAPIKey_Empty (0.00s) +=== RUN TestListPresetsIncludesCacheAndIndex +--- PASS: TestListPresetsIncludesCacheAndIndex (0.01s) +=== RUN TestPullPresetHandlerSuccess +--- PASS: TestPullPresetHandlerSuccess (0.01s) +=== RUN TestApplyPresetHandlerAudits +--- PASS: TestApplyPresetHandlerAudits (0.02s) +=== RUN TestPullPresetHandlerHubError +--- PASS: TestPullPresetHandlerHubError (0.00s) +=== RUN TestPullPresetHandlerTimeout +--- PASS: TestPullPresetHandlerTimeout (0.00s) +=== RUN TestGetCachedPresetNotFound +--- PASS: TestGetCachedPresetNotFound (0.00s) +=== RUN TestGetCachedPresetServiceUnavailable +--- PASS: TestGetCachedPresetServiceUnavailable (0.00s) +=== RUN TestApplyPresetHandlerBackupFailure +--- PASS: TestApplyPresetHandlerBackupFailure (0.00s) +=== RUN TestListPresetsMergesCuratedAndHub +--- PASS: TestListPresetsMergesCuratedAndHub (0.01s) +=== RUN TestGetCachedPresetSuccess +--- PASS: TestGetCachedPresetSuccess (0.00s) +=== RUN TestGetCachedPresetSlugRequired +--- PASS: TestGetCachedPresetSlugRequired (0.00s) +=== RUN TestGetCachedPresetPreviewError +--- PASS: TestGetCachedPresetPreviewError (0.00s) +=== RUN TestPullCuratedPresetSkipsHub +--- PASS: TestPullCuratedPresetSkipsHub (0.00s) +=== RUN TestApplyCuratedPresetSkipsHub +--- PASS: TestApplyCuratedPresetSkipsHub (0.00s) +=== RUN TestPullThenApplyIntegration + crowdsec_pull_apply_integration_test.go:67: User pulls preset + crowdsec_pull_apply_integration_test.go:83: Pull succeeded, cache_key: test/preset-1768011471 + crowdsec_pull_apply_integration_test.go:90: Cache verified, slug: test/preset + crowdsec_pull_apply_integration_test.go:93: User applies preset + crowdsec_pull_apply_integration_test.go:109: Apply succeeded, backup: /tmp/TestPullThenApplyIntegration893046683/002.backup.20260110-021751 +--- PASS: TestPullThenApplyIntegration (0.01s) +=== RUN TestApplyWithoutPullReturnsProperError + crowdsec_pull_apply_integration_test.go:138: User tries to apply preset without pulling first + crowdsec_pull_apply_integration_test.go:154: Proper error message returned: Preset cache missing or expired. Pull the preset again, then retry apply. +--- PASS: TestApplyWithoutPullReturnsProperError (0.01s) +=== RUN TestApplyRollbackWhenCacheMissingAndRepullFails +--- PASS: TestApplyRollbackWhenCacheMissingAndRepullFails (0.01s) +=== RUN TestStartSyncsSettingsTable +--- PASS: TestStartSyncsSettingsTable (60.13s) +=== RUN TestStopSyncsSettingsTable +--- PASS: TestStopSyncsSettingsTable (60.13s) +=== RUN TestStartAndStopStateConsistency +--- PASS: TestStartAndStopStateConsistency (180.39s) +=== RUN TestExistingSettingIsUpdated +--- PASS: TestExistingSettingIsUpdated (60.13s) +=== RUN TestStartFailureRevertsSettings +--- PASS: TestStartFailureRevertsSettings (0.01s) +=== RUN TestStatusResponseFormat +--- PASS: TestStatusResponseFormat (0.01s) +=== RUN TestCrowdsecHandler_Stop_Success +--- PASS: TestCrowdsecHandler_Stop_Success (0.01s) +=== RUN TestCrowdsecHandler_Stop_Error +--- PASS: TestCrowdsecHandler_Stop_Error (0.00s) +=== RUN TestCrowdsecHandler_Stop_NoSecurityConfig +--- PASS: TestCrowdsecHandler_Stop_NoSecurityConfig (0.00s) +=== RUN TestGetLAPIDecisions_WithMockServer +--- PASS: TestGetLAPIDecisions_WithMockServer (0.01s) +=== RUN TestGetLAPIDecisions_Unauthorized +--- PASS: TestGetLAPIDecisions_Unauthorized (0.01s) +=== RUN TestGetLAPIDecisions_NullResponse +--- PASS: TestGetLAPIDecisions_NullResponse (0.00s) +=== RUN TestGetLAPIDecisions_NonJSONContentType +--- PASS: TestGetLAPIDecisions_NonJSONContentType (0.01s) +=== RUN TestCheckLAPIHealth_WithMockServer +--- PASS: TestCheckLAPIHealth_WithMockServer (0.00s) +=== RUN TestCheckLAPIHealth_FallbackToDecisions +--- PASS: TestCheckLAPIHealth_FallbackToDecisions (0.01s) +=== RUN TestGetLAPIKey_AllEnvVars +=== RUN TestGetLAPIKey_AllEnvVars/CROWDSEC_API_KEY +=== RUN TestGetLAPIKey_AllEnvVars/CROWDSEC_BOUNCER_API_KEY +=== RUN TestGetLAPIKey_AllEnvVars/CERBERUS_SECURITY_CROWDSEC_API_KEY +=== RUN TestGetLAPIKey_AllEnvVars/CHARON_SECURITY_CROWDSEC_API_KEY +=== RUN TestGetLAPIKey_AllEnvVars/CPM_SECURITY_CROWDSEC_API_KEY +--- PASS: TestGetLAPIKey_AllEnvVars (0.00s) + --- PASS: TestGetLAPIKey_AllEnvVars/CROWDSEC_API_KEY (0.00s) + --- PASS: TestGetLAPIKey_AllEnvVars/CROWDSEC_BOUNCER_API_KEY (0.00s) + --- PASS: TestGetLAPIKey_AllEnvVars/CERBERUS_SECURITY_CROWDSEC_API_KEY (0.00s) + --- PASS: TestGetLAPIKey_AllEnvVars/CHARON_SECURITY_CROWDSEC_API_KEY (0.00s) + --- PASS: TestGetLAPIKey_AllEnvVars/CPM_SECURITY_CROWDSEC_API_KEY (0.00s) +=== RUN TestDBHealthHandler_Check_Healthy +--- PASS: TestDBHealthHandler_Check_Healthy (0.00s) +=== RUN TestDBHealthHandler_Check_WithBackupService +--- PASS: TestDBHealthHandler_Check_WithBackupService (0.00s) +=== RUN TestDBHealthHandler_Check_WALMode +--- PASS: TestDBHealthHandler_Check_WALMode (0.01s) +=== RUN TestDBHealthHandler_ResponseJSONTags +--- PASS: TestDBHealthHandler_ResponseJSONTags (0.00s) +=== RUN TestNewDBHealthHandler +--- PASS: TestNewDBHealthHandler (0.00s) +=== RUN TestDBHealthHandler_Check_CorruptedDatabase + +2026/01/10 02:23:52 /projects/Charon/backend/internal/database/database.go:57 database disk image is malformed +[0.125ms] [rows:1] PRAGMA quick_check + +2026/01/10 02:23:52 /projects/Charon/backend/internal/database/errors.go:63 database disk image is malformed +[0.092ms] [rows:1] PRAGMA quick_check +--- PASS: TestDBHealthHandler_Check_CorruptedDatabase (0.01s) +=== RUN TestDBHealthHandler_Check_BackupServiceError +--- PASS: TestDBHealthHandler_Check_BackupServiceError (0.00s) +=== RUN TestDBHealthHandler_Check_BackupTimeZero +--- PASS: TestDBHealthHandler_Check_BackupTimeZero (0.00s) +=== RUN TestNewDNSDetectionHandler +--- PASS: TestNewDNSDetectionHandler (0.00s) +=== RUN TestDetect_Success +=== RUN TestDetect_Success/successful_detection_without_configured_provider +=== RUN TestDetect_Success/successful_detection_with_configured_provider +=== RUN TestDetect_Success/detection_not_found +--- PASS: TestDetect_Success (0.00s) + --- PASS: TestDetect_Success/successful_detection_without_configured_provider (0.00s) + --- PASS: TestDetect_Success/successful_detection_with_configured_provider (0.00s) + --- PASS: TestDetect_Success/detection_not_found (0.00s) +=== RUN TestDetect_ValidationErrors +=== RUN TestDetect_ValidationErrors/missing_domain +=== RUN TestDetect_ValidationErrors/invalid_JSON +--- PASS: TestDetect_ValidationErrors (0.00s) + --- PASS: TestDetect_ValidationErrors/missing_domain (0.00s) + --- PASS: TestDetect_ValidationErrors/invalid_JSON (0.00s) +=== RUN TestDetect_ServiceError +--- PASS: TestDetect_ServiceError (0.00s) +=== RUN TestGetPatterns +--- PASS: TestGetPatterns (0.00s) +=== RUN TestDetect_WildcardDomain +--- PASS: TestDetect_WildcardDomain (0.00s) +=== RUN TestDetect_LowConfidence +--- PASS: TestDetect_LowConfidence (0.00s) +=== RUN TestDetect_DNSLookupError +--- PASS: TestDetect_DNSLookupError (0.00s) +=== RUN TestDetectRequest_Binding +=== RUN TestDetectRequest_Binding/valid_request +=== RUN TestDetectRequest_Binding/missing_domain +=== RUN TestDetectRequest_Binding/empty_domain +=== RUN TestDetectRequest_Binding/invalid_JSON +--- PASS: TestDetectRequest_Binding (0.00s) + --- PASS: TestDetectRequest_Binding/valid_request (0.00s) + --- PASS: TestDetectRequest_Binding/missing_domain (0.00s) + --- PASS: TestDetectRequest_Binding/empty_domain (0.00s) + --- PASS: TestDetectRequest_Binding/invalid_JSON (0.00s) +=== RUN TestDNSProviderHandler_List +=== RUN TestDNSProviderHandler_List/success +=== RUN TestDNSProviderHandler_List/service_error +--- PASS: TestDNSProviderHandler_List (0.00s) + --- PASS: TestDNSProviderHandler_List/success (0.00s) + --- PASS: TestDNSProviderHandler_List/service_error (0.00s) +=== RUN TestDNSProviderHandler_Get +=== RUN TestDNSProviderHandler_Get/success +=== RUN TestDNSProviderHandler_Get/not_found +=== RUN TestDNSProviderHandler_Get/invalid_id +--- PASS: TestDNSProviderHandler_Get (0.00s) + --- PASS: TestDNSProviderHandler_Get/success (0.00s) + --- PASS: TestDNSProviderHandler_Get/not_found (0.00s) + --- PASS: TestDNSProviderHandler_Get/invalid_id (0.00s) +=== RUN TestDNSProviderHandler_Create +=== RUN TestDNSProviderHandler_Create/success +=== RUN TestDNSProviderHandler_Create/validation_error +=== RUN TestDNSProviderHandler_Create/invalid_provider_type +=== RUN TestDNSProviderHandler_Create/invalid_credentials +--- PASS: TestDNSProviderHandler_Create (0.00s) + --- PASS: TestDNSProviderHandler_Create/success (0.00s) + --- PASS: TestDNSProviderHandler_Create/validation_error (0.00s) + --- PASS: TestDNSProviderHandler_Create/invalid_provider_type (0.00s) + --- PASS: TestDNSProviderHandler_Create/invalid_credentials (0.00s) +=== RUN TestDNSProviderHandler_Update +=== RUN TestDNSProviderHandler_Update/success +=== RUN TestDNSProviderHandler_Update/not_found +--- PASS: TestDNSProviderHandler_Update (0.00s) + --- PASS: TestDNSProviderHandler_Update/success (0.00s) + --- PASS: TestDNSProviderHandler_Update/not_found (0.00s) +=== RUN TestDNSProviderHandler_Delete +=== RUN TestDNSProviderHandler_Delete/success +=== RUN TestDNSProviderHandler_Delete/not_found +--- PASS: TestDNSProviderHandler_Delete (0.00s) + --- PASS: TestDNSProviderHandler_Delete/success (0.00s) + --- PASS: TestDNSProviderHandler_Delete/not_found (0.00s) +=== RUN TestDNSProviderHandler_Test +=== RUN TestDNSProviderHandler_Test/success +=== RUN TestDNSProviderHandler_Test/not_found +--- PASS: TestDNSProviderHandler_Test (0.00s) + --- PASS: TestDNSProviderHandler_Test/success (0.00s) + --- PASS: TestDNSProviderHandler_Test/not_found (0.00s) +=== RUN TestDNSProviderHandler_TestCredentials +=== RUN TestDNSProviderHandler_TestCredentials/success +=== RUN TestDNSProviderHandler_TestCredentials/validation_error +--- PASS: TestDNSProviderHandler_TestCredentials (0.00s) + --- PASS: TestDNSProviderHandler_TestCredentials/success (0.00s) + --- PASS: TestDNSProviderHandler_TestCredentials/validation_error (0.00s) +=== RUN TestDNSProviderHandler_GetTypes +--- PASS: TestDNSProviderHandler_GetTypes (0.00s) +=== RUN TestDNSProviderHandler_CredentialsNeverExposed +=== RUN TestDNSProviderHandler_CredentialsNeverExposed/Get_endpoint +=== RUN TestDNSProviderHandler_CredentialsNeverExposed/List_endpoint +--- PASS: TestDNSProviderHandler_CredentialsNeverExposed (0.00s) + --- PASS: TestDNSProviderHandler_CredentialsNeverExposed/Get_endpoint (0.00s) + --- PASS: TestDNSProviderHandler_CredentialsNeverExposed/List_endpoint (0.00s) +=== RUN TestDNSProviderHandler_UpdateInvalidID +--- PASS: TestDNSProviderHandler_UpdateInvalidID (0.00s) +=== RUN TestDNSProviderHandler_DeleteInvalidID +--- PASS: TestDNSProviderHandler_DeleteInvalidID (0.00s) +=== RUN TestDNSProviderHandler_TestInvalidID +--- PASS: TestDNSProviderHandler_TestInvalidID (0.00s) +=== RUN TestDNSProviderHandler_CreateEncryptionFailure +--- PASS: TestDNSProviderHandler_CreateEncryptionFailure (0.00s) +=== RUN TestDNSProviderHandler_UpdateEncryptionFailure +--- PASS: TestDNSProviderHandler_UpdateEncryptionFailure (0.00s) +=== RUN TestDNSProviderHandler_GetServiceError +--- PASS: TestDNSProviderHandler_GetServiceError (0.00s) +=== RUN TestDNSProviderHandler_DeleteServiceError +--- PASS: TestDNSProviderHandler_DeleteServiceError (0.00s) +=== RUN TestDNSProviderHandler_TestServiceError +--- PASS: TestDNSProviderHandler_TestServiceError (0.00s) +=== RUN TestDNSProviderHandler_TestCredentialsServiceError +--- PASS: TestDNSProviderHandler_TestCredentialsServiceError (0.00s) +=== RUN TestDNSProviderHandler_UpdateInvalidCredentials +--- PASS: TestDNSProviderHandler_UpdateInvalidCredentials (0.00s) +=== RUN TestDNSProviderHandler_UpdateBindJSONError +--- PASS: TestDNSProviderHandler_UpdateBindJSONError (0.00s) +=== RUN TestDNSProviderHandler_UpdateGenericError +--- PASS: TestDNSProviderHandler_UpdateGenericError (0.00s) +=== RUN TestDNSProviderHandler_CreateGenericError +--- PASS: TestDNSProviderHandler_CreateGenericError (0.00s) +=== RUN TestDockerHandler_ListContainers_InvalidHostRejected +--- PASS: TestDockerHandler_ListContainers_InvalidHostRejected (0.00s) +=== RUN TestDockerHandler_ListContainers_DockerUnavailableMappedTo503 +--- PASS: TestDockerHandler_ListContainers_DockerUnavailableMappedTo503 (0.00s) +=== RUN TestDockerHandler_ListContainers_ServerIDResolvesToTCPHost +--- PASS: TestDockerHandler_ListContainers_ServerIDResolvesToTCPHost (0.00s) +=== RUN TestDockerHandler_ListContainers_ServerIDNotFoundReturns404 +--- PASS: TestDockerHandler_ListContainers_ServerIDNotFoundReturns404 (0.00s) +=== RUN TestDockerHandler_ListContainers_Local +--- PASS: TestDockerHandler_ListContainers_Local (0.00s) +=== RUN TestDockerHandler_ListContainers_RemoteServerSuccess +--- PASS: TestDockerHandler_ListContainers_RemoteServerSuccess (0.00s) +=== RUN TestDockerHandler_ListContainers_RemoteServerNotFound +--- PASS: TestDockerHandler_ListContainers_RemoteServerNotFound (0.00s) +=== RUN TestDockerHandler_ListContainers_InvalidHost +=== RUN TestDockerHandler_ListContainers_InvalidHost/arbitrary_IP +=== RUN TestDockerHandler_ListContainers_InvalidHost/tcp_URL +=== RUN TestDockerHandler_ListContainers_InvalidHost/unix_socket +=== RUN TestDockerHandler_ListContainers_InvalidHost/http_URL +--- PASS: TestDockerHandler_ListContainers_InvalidHost (0.00s) + --- PASS: TestDockerHandler_ListContainers_InvalidHost/arbitrary_IP (0.00s) + --- PASS: TestDockerHandler_ListContainers_InvalidHost/tcp_URL (0.00s) + --- PASS: TestDockerHandler_ListContainers_InvalidHost/unix_socket (0.00s) + --- PASS: TestDockerHandler_ListContainers_InvalidHost/http_URL (0.00s) +=== RUN TestDockerHandler_ListContainers_DockerUnavailable +=== RUN TestDockerHandler_ListContainers_DockerUnavailable/daemon_not_running +=== RUN TestDockerHandler_ListContainers_DockerUnavailable/socket_permission_denied +=== RUN TestDockerHandler_ListContainers_DockerUnavailable/socket_not_found +--- PASS: TestDockerHandler_ListContainers_DockerUnavailable (0.00s) + --- PASS: TestDockerHandler_ListContainers_DockerUnavailable/daemon_not_running (0.00s) + --- PASS: TestDockerHandler_ListContainers_DockerUnavailable/socket_permission_denied (0.00s) + --- PASS: TestDockerHandler_ListContainers_DockerUnavailable/socket_not_found (0.00s) +=== RUN TestDockerHandler_ListContainers_GenericError +=== RUN TestDockerHandler_ListContainers_GenericError/API_error +=== RUN TestDockerHandler_ListContainers_GenericError/context_cancelled +=== RUN TestDockerHandler_ListContainers_GenericError/unknown_error +--- PASS: TestDockerHandler_ListContainers_GenericError (0.00s) + --- PASS: TestDockerHandler_ListContainers_GenericError/API_error (0.00s) + --- PASS: TestDockerHandler_ListContainers_GenericError/context_cancelled (0.00s) + --- PASS: TestDockerHandler_ListContainers_GenericError/unknown_error (0.00s) +=== RUN TestDomainLifecycle +--- PASS: TestDomainLifecycle (0.01s) +=== RUN TestDomainErrors +--- PASS: TestDomainErrors (0.01s) +=== RUN TestDomainDelete_NotFound + +2026/01/10 02:23:52 /projects/Charon/backend/internal/api/handlers/domain_handler.go:73 record not found +[0.094ms] [rows:0] SELECT * FROM `domains` WHERE uuid = "nonexistent-uuid" AND `domains`.`deleted_at` IS NULL ORDER BY `domains`.`id` LIMIT 1 +--- PASS: TestDomainDelete_NotFound (0.01s) +=== RUN TestDomainCreate_Duplicate + +2026/01/10 02:23:52 /projects/Charon/backend/internal/api/handlers/domain_handler.go:49 UNIQUE constraint failed: domains.name +[0.211ms] [rows:0] INSERT INTO `domains` (`uuid`,`name`,`created_at`,`updated_at`,`deleted_at`) VALUES ("072e12ad-e893-484b-809c-c89d1c1a1dd5","duplicate.com","2026-01-10 02:23:52.233","2026-01-10 02:23:52.233",NULL) RETURNING `id` +--- PASS: TestDomainCreate_Duplicate (0.01s) +=== RUN TestDomainList_Empty +--- PASS: TestDomainList_Empty (0.01s) +=== RUN TestDomainCreate_LongName +--- PASS: TestDomainCreate_LongName (0.01s) +=== RUN TestEncryptionHandler_GetStatus +=== RUN TestEncryptionHandler_GetStatus/admin_can_get_status +=== RUN TestEncryptionHandler_GetStatus/non-admin_cannot_get_status +=== RUN TestEncryptionHandler_GetStatus/status_shows_next_key_when_configured +=== RUN TestEncryptionHandler_GetStatus/status_error_when_database_unavailable + +2026/01/10 02:23:52 /projects/Charon/backend/internal/crypto/rotation_service.go:272 sql: database is closed +[0.041ms] [rows:0] SELECT `key_version` FROM `dns_providers` +--- PASS: TestEncryptionHandler_GetStatus (0.04s) + --- PASS: TestEncryptionHandler_GetStatus/admin_can_get_status (0.00s) + --- PASS: TestEncryptionHandler_GetStatus/non-admin_cannot_get_status (0.00s) + --- PASS: TestEncryptionHandler_GetStatus/status_shows_next_key_when_configured (0.00s) + --- PASS: TestEncryptionHandler_GetStatus/status_error_when_database_unavailable (0.00s) +=== RUN TestEncryptionHandler_Rotate +=== RUN TestEncryptionHandler_Rotate/admin_can_trigger_rotation +=== RUN TestEncryptionHandler_Rotate/non-admin_cannot_trigger_rotation +=== RUN TestEncryptionHandler_Rotate/rotation_fails_without_next_key +--- PASS: TestEncryptionHandler_Rotate (0.06s) + --- PASS: TestEncryptionHandler_Rotate/admin_can_trigger_rotation (0.02s) + --- PASS: TestEncryptionHandler_Rotate/non-admin_cannot_trigger_rotation (0.00s) + --- PASS: TestEncryptionHandler_Rotate/rotation_fails_without_next_key (0.00s) +=== RUN TestEncryptionHandler_GetHistory +=== RUN TestEncryptionHandler_GetHistory/admin_can_get_history +=== RUN TestEncryptionHandler_GetHistory/non-admin_cannot_get_history +=== RUN TestEncryptionHandler_GetHistory/supports_pagination +=== RUN TestEncryptionHandler_GetHistory/history_error_when_service_fails + +2026/01/10 02:23:52 /projects/Charon/backend/internal/services/security_service.go:305 sql: database is closed +[0.049ms] [rows:0] SELECT count(*) FROM `security_audits` WHERE event_category = "encryption" +--- PASS: TestEncryptionHandler_GetHistory (0.07s) + --- PASS: TestEncryptionHandler_GetHistory/admin_can_get_history (0.00s) + --- PASS: TestEncryptionHandler_GetHistory/non-admin_cannot_get_history (0.00s) + --- PASS: TestEncryptionHandler_GetHistory/supports_pagination (0.00s) + --- PASS: TestEncryptionHandler_GetHistory/history_error_when_service_fails (0.03s) +=== RUN TestEncryptionHandler_Validate +=== RUN TestEncryptionHandler_Validate/admin_can_validate_keys +=== RUN TestEncryptionHandler_Validate/non-admin_cannot_validate_keys +=== RUN TestEncryptionHandler_Validate/validation_fails_with_invalid_key_configuration +--- PASS: TestEncryptionHandler_Validate (0.05s) + --- PASS: TestEncryptionHandler_Validate/admin_can_validate_keys (0.01s) + --- PASS: TestEncryptionHandler_Validate/non-admin_cannot_validate_keys (0.00s) + --- PASS: TestEncryptionHandler_Validate/validation_fails_with_invalid_key_configuration (0.00s) +=== RUN TestEncryptionHandler_IntegrationFlow +=== RUN TestEncryptionHandler_IntegrationFlow/complete_rotation_workflow +--- PASS: TestEncryptionHandler_IntegrationFlow (0.10s) + --- PASS: TestEncryptionHandler_IntegrationFlow/complete_rotation_workflow (0.06s) +=== RUN TestEncryptionHandler_HelperFunctions +=== RUN TestEncryptionHandler_HelperFunctions/isAdmin_with_invalid_role_type +=== RUN TestEncryptionHandler_HelperFunctions/getActorFromGinContext_with_string_user_id +=== RUN TestEncryptionHandler_HelperFunctions/getActorFromGinContext_with_uint_user_id +=== RUN TestEncryptionHandler_HelperFunctions/getActorFromGinContext_without_user_id_returns_system +--- PASS: TestEncryptionHandler_HelperFunctions (0.00s) + --- PASS: TestEncryptionHandler_HelperFunctions/isAdmin_with_invalid_role_type (0.00s) + --- PASS: TestEncryptionHandler_HelperFunctions/getActorFromGinContext_with_string_user_id (0.00s) + --- PASS: TestEncryptionHandler_HelperFunctions/getActorFromGinContext_with_uint_user_id (0.00s) + --- PASS: TestEncryptionHandler_HelperFunctions/getActorFromGinContext_without_user_id_returns_system (0.00s) +=== RUN TestEncryptionHandler_RefreshKey_RotatesCredentials +--- PASS: TestEncryptionHandler_RefreshKey_RotatesCredentials (0.06s) +=== RUN TestEncryptionHandler_RefreshKey_FailsWithoutProvider +--- PASS: TestEncryptionHandler_RefreshKey_FailsWithoutProvider (0.03s) +=== RUN TestEncryptionHandler_RefreshKey_InvalidOldKey +Failed to rotate provider 1 (Test Provider): failed to decrypt credentials: failed to decrypt with version 1 or any fallback version +--- PASS: TestEncryptionHandler_RefreshKey_InvalidOldKey (0.06s) +=== RUN TestEncryptionHandler_GetActorFromGinContext_InvalidType +--- PASS: TestEncryptionHandler_GetActorFromGinContext_InvalidType (0.00s) +=== RUN TestEncryptionHandler_RotateWithPartialFailures +Failed to rotate provider 2 (Invalid Provider): failed to decrypt credentials: failed to decrypt with version 1 or any fallback version +--- PASS: TestEncryptionHandler_RotateWithPartialFailures (0.05s) +=== RUN TestEncryptionHandler_isAdmin_NoRoleSet +--- PASS: TestEncryptionHandler_isAdmin_NoRoleSet (0.00s) +=== RUN TestEncryptionHandler_isAdmin_NonAdminRole +--- PASS: TestEncryptionHandler_isAdmin_NonAdminRole (0.00s) +=== RUN TestFeatureFlagsHandler_GetFlags_DBPrecedence +--- PASS: TestFeatureFlagsHandler_GetFlags_DBPrecedence (0.00s) +=== RUN TestFeatureFlagsHandler_GetFlags_EnvFallback +--- PASS: TestFeatureFlagsHandler_GetFlags_EnvFallback (0.00s) +=== RUN TestFeatureFlagsHandler_GetFlags_EnvShortForm +--- PASS: TestFeatureFlagsHandler_GetFlags_EnvShortForm (0.00s) +=== RUN TestFeatureFlagsHandler_GetFlags_EnvNumeric +--- PASS: TestFeatureFlagsHandler_GetFlags_EnvNumeric (0.00s) +=== RUN TestFeatureFlagsHandler_GetFlags_DefaultTrue +--- PASS: TestFeatureFlagsHandler_GetFlags_DefaultTrue (0.00s) +=== RUN TestFeatureFlagsHandler_GetFlags_AllDefaultFlagsPresent +--- PASS: TestFeatureFlagsHandler_GetFlags_AllDefaultFlagsPresent (0.00s) +=== RUN TestFeatureFlagsHandler_UpdateFlags_Success +--- PASS: TestFeatureFlagsHandler_UpdateFlags_Success (0.00s) +=== RUN TestFeatureFlagsHandler_UpdateFlags_Upsert +--- PASS: TestFeatureFlagsHandler_UpdateFlags_Upsert (0.00s) +=== RUN TestFeatureFlagsHandler_UpdateFlags_InvalidJSON +--- PASS: TestFeatureFlagsHandler_UpdateFlags_InvalidJSON (0.00s) +=== RUN TestFeatureFlagsHandler_UpdateFlags_OnlyAllowedKeys +--- PASS: TestFeatureFlagsHandler_UpdateFlags_OnlyAllowedKeys (0.00s) +=== RUN TestFeatureFlagsHandler_UpdateFlags_EmptyPayload +--- PASS: TestFeatureFlagsHandler_UpdateFlags_EmptyPayload (0.00s) +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/lowercase_true +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/uppercase_TRUE +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/mixed_case_True +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/numeric_1 +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/yes +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/YES_uppercase +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/lowercase_false +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/numeric_0 +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/no +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/empty_string +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/random_string +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/whitespace_padded_true +=== RUN TestFeatureFlagsHandler_GetFlags_DBValueVariants/whitespace_padded_false +--- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants (0.04s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/lowercase_true (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/uppercase_TRUE (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/mixed_case_True (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/numeric_1 (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/yes (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/YES_uppercase (0.01s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/lowercase_false (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/numeric_0 (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/no (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/empty_string (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/random_string (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/whitespace_padded_true (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_DBValueVariants/whitespace_padded_false (0.00s) +=== RUN TestFeatureFlagsHandler_GetFlags_EnvValueVariants +=== RUN TestFeatureFlagsHandler_GetFlags_EnvValueVariants/true_string +=== RUN TestFeatureFlagsHandler_GetFlags_EnvValueVariants/TRUE_uppercase +=== RUN TestFeatureFlagsHandler_GetFlags_EnvValueVariants/1_numeric +=== RUN TestFeatureFlagsHandler_GetFlags_EnvValueVariants/false_string +=== RUN TestFeatureFlagsHandler_GetFlags_EnvValueVariants/FALSE_uppercase +=== RUN TestFeatureFlagsHandler_GetFlags_EnvValueVariants/0_numeric +=== RUN TestFeatureFlagsHandler_GetFlags_EnvValueVariants/invalid_value_defaults_to_numeric_check +--- PASS: TestFeatureFlagsHandler_GetFlags_EnvValueVariants (0.03s) + --- PASS: TestFeatureFlagsHandler_GetFlags_EnvValueVariants/true_string (0.01s) + --- PASS: TestFeatureFlagsHandler_GetFlags_EnvValueVariants/TRUE_uppercase (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_EnvValueVariants/1_numeric (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_EnvValueVariants/false_string (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_EnvValueVariants/FALSE_uppercase (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_EnvValueVariants/0_numeric (0.00s) + --- PASS: TestFeatureFlagsHandler_GetFlags_EnvValueVariants/invalid_value_defaults_to_numeric_check (0.00s) +=== RUN TestFeatureFlagsHandler_UpdateFlags_BoolValues +=== RUN TestFeatureFlagsHandler_UpdateFlags_BoolValues/true +=== RUN TestFeatureFlagsHandler_UpdateFlags_BoolValues/false +--- PASS: TestFeatureFlagsHandler_UpdateFlags_BoolValues (0.00s) + --- PASS: TestFeatureFlagsHandler_UpdateFlags_BoolValues/true (0.00s) + --- PASS: TestFeatureFlagsHandler_UpdateFlags_BoolValues/false (0.00s) +=== RUN TestFeatureFlagsHandler_NewFeatureFlagsHandler +--- PASS: TestFeatureFlagsHandler_NewFeatureFlagsHandler (0.00s) +=== RUN TestFeatureFlags_GetAndUpdate +--- PASS: TestFeatureFlags_GetAndUpdate (0.00s) +=== RUN TestFeatureFlags_EnvFallback +--- PASS: TestFeatureFlags_EnvFallback (0.00s) +=== RUN TestHealthHandler +--- PASS: TestHealthHandler (0.00s) +=== RUN TestGetLocalIP + health_handler_test.go:36: getLocalIP returned: "217.15.170.144" +--- PASS: TestGetLocalIP (0.00s) +=== RUN TestIsSafePathUnderBase +--- PASS: TestIsSafePathUnderBase (0.00s) +=== RUN TestImportUploadSanitizesFilename +--- PASS: TestImportUploadSanitizesFilename (0.00s) +=== RUN TestLogsHandler_Read_FilterBySearch +--- PASS: TestLogsHandler_Read_FilterBySearch (0.00s) +=== RUN TestLogsHandler_Read_FilterByHost +--- PASS: TestLogsHandler_Read_FilterByHost (0.00s) +=== RUN TestLogsHandler_Read_FilterByLevel +--- PASS: TestLogsHandler_Read_FilterByLevel (0.00s) +=== RUN TestLogsHandler_Read_FilterByStatus +--- PASS: TestLogsHandler_Read_FilterByStatus (0.00s) +=== RUN TestLogsHandler_Read_SortAsc +--- PASS: TestLogsHandler_Read_SortAsc (0.00s) +=== RUN TestLogsHandler_List_DirectoryIsFile +--- PASS: TestLogsHandler_List_DirectoryIsFile (0.00s) +=== RUN TestLogsHandler_Download_TempFileError +--- PASS: TestLogsHandler_Download_TempFileError (0.00s) +=== RUN TestLogsLifecycle +--- PASS: TestLogsLifecycle (0.00s) +=== RUN TestLogsHandler_PathTraversal +--- PASS: TestLogsHandler_PathTraversal (0.00s) +=== RUN TestLogsWebSocketHandler_SuccessfulConnection +--- PASS: TestLogsWebSocketHandler_SuccessfulConnection (0.00s) +=== RUN TestLogsWebSocketHandler_ReceiveLogEntries +--- PASS: TestLogsWebSocketHandler_ReceiveLogEntries (0.00s) +=== RUN TestLogsWebSocketHandler_LevelFilter +--- PASS: TestLogsWebSocketHandler_LevelFilter (0.15s) +=== RUN TestLogsWebSocketHandler_SourceFilter +--- PASS: TestLogsWebSocketHandler_SourceFilter (0.00s) +=== RUN TestLogsWebSocketHandler_CombinedFilters +--- PASS: TestLogsWebSocketHandler_CombinedFilters (0.00s) +=== RUN TestLogsWebSocketHandler_CaseInsensitiveFilters +--- PASS: TestLogsWebSocketHandler_CaseInsensitiveFilters (0.00s) +=== RUN TestLogsWebSocketHandler_UpgradeFailure +--- PASS: TestLogsWebSocketHandler_UpgradeFailure (0.00s) +=== RUN TestLogsWebSocketHandler_ClientDisconnect +--- PASS: TestLogsWebSocketHandler_ClientDisconnect (0.02s) +=== RUN TestLogsWebSocketHandler_ChannelClosed +--- PASS: TestLogsWebSocketHandler_ChannelClosed (0.00s) +=== RUN TestLogsWebSocketHandler_MultipleConnections +--- PASS: TestLogsWebSocketHandler_MultipleConnections (0.01s) +=== RUN TestLogsWebSocketHandler_HighVolumeLogging +--- PASS: TestLogsWebSocketHandler_HighVolumeLogging (0.04s) +=== RUN TestLogsWebSocketHandler_EmptyLogFields +--- PASS: TestLogsWebSocketHandler_EmptyLogFields (0.02s) +=== RUN TestLogsWebSocketHandler_SubscriberIDUniqueness +--- PASS: TestLogsWebSocketHandler_SubscriberIDUniqueness (0.02s) +=== RUN TestLogsWebSocketHandler_WithRealLogger +--- PASS: TestLogsWebSocketHandler_WithRealLogger (0.00s) +=== RUN TestLogsWebSocketHandler_ConnectionLifecycle +--- PASS: TestLogsWebSocketHandler_ConnectionLifecycle (0.02s) +=== RUN TestDomainHandler_List_Error +--- PASS: TestDomainHandler_List_Error (0.00s) +=== RUN TestDomainHandler_Create_InvalidJSON +--- PASS: TestDomainHandler_Create_InvalidJSON (0.00s) +=== RUN TestDomainHandler_Create_DBError +--- PASS: TestDomainHandler_Create_DBError (0.00s) +=== RUN TestDomainHandler_Delete_Error +--- PASS: TestDomainHandler_Delete_Error (0.00s) +=== RUN TestRemoteServerHandler_List_Error +--- PASS: TestRemoteServerHandler_List_Error (0.00s) +=== RUN TestRemoteServerHandler_List_EnabledOnly +--- PASS: TestRemoteServerHandler_List_EnabledOnly (0.00s) +=== RUN TestRemoteServerHandler_Update_NotFound +--- PASS: TestRemoteServerHandler_Update_NotFound (0.00s) +=== RUN TestRemoteServerHandler_Update_InvalidJSON +--- PASS: TestRemoteServerHandler_Update_InvalidJSON (0.00s) +=== RUN TestRemoteServerHandler_TestConnection_NotFound +--- PASS: TestRemoteServerHandler_TestConnection_NotFound (0.00s) +=== RUN TestRemoteServerHandler_TestConnectionCustom_InvalidJSON +--- PASS: TestRemoteServerHandler_TestConnectionCustom_InvalidJSON (0.00s) +=== RUN TestRemoteServerHandler_TestConnectionCustom_Unreachable +--- PASS: TestRemoteServerHandler_TestConnectionCustom_Unreachable (5.00s) +=== RUN TestUptimeHandler_List_Error +--- PASS: TestUptimeHandler_List_Error (0.02s) +=== RUN TestUptimeHandler_GetHistory_Error +--- PASS: TestUptimeHandler_GetHistory_Error (0.02s) +=== RUN TestUptimeHandler_Update_InvalidJSON +--- PASS: TestUptimeHandler_Update_InvalidJSON (0.02s) +=== RUN TestUptimeHandler_Sync_Error +--- PASS: TestUptimeHandler_Sync_Error (0.02s) +=== RUN TestUptimeHandler_Delete_Error +--- PASS: TestUptimeHandler_Delete_Error (0.02s) +=== RUN TestUptimeHandler_CheckMonitor_NotFound +--- PASS: TestUptimeHandler_CheckMonitor_NotFound (0.02s) +=== RUN TestNotificationHandler_List_Error +--- PASS: TestNotificationHandler_List_Error (0.01s) +=== RUN TestNotificationHandler_List_UnreadOnly +--- PASS: TestNotificationHandler_List_UnreadOnly (0.01s) +=== RUN TestNotificationHandler_MarkAsRead_Error +--- PASS: TestNotificationHandler_MarkAsRead_Error (0.01s) +=== RUN TestNotificationHandler_MarkAllAsRead_Error +--- PASS: TestNotificationHandler_MarkAllAsRead_Error (0.01s) +=== RUN TestNotificationProviderHandler_List_Error +--- PASS: TestNotificationProviderHandler_List_Error (0.01s) +=== RUN TestNotificationProviderHandler_Create_InvalidJSON +--- PASS: TestNotificationProviderHandler_Create_InvalidJSON (0.01s) +=== RUN TestNotificationProviderHandler_Create_DBError +--- PASS: TestNotificationProviderHandler_Create_DBError (0.01s) +=== RUN TestNotificationProviderHandler_Create_InvalidTemplate +--- PASS: TestNotificationProviderHandler_Create_InvalidTemplate (0.01s) +=== RUN TestNotificationProviderHandler_Update_InvalidJSON +--- PASS: TestNotificationProviderHandler_Update_InvalidJSON (0.01s) +=== RUN TestNotificationProviderHandler_Update_InvalidTemplate +--- PASS: TestNotificationProviderHandler_Update_InvalidTemplate (0.01s) +=== RUN TestNotificationProviderHandler_Update_DBError +--- PASS: TestNotificationProviderHandler_Update_DBError (0.00s) +=== RUN TestNotificationProviderHandler_Delete_Error +--- PASS: TestNotificationProviderHandler_Delete_Error (0.01s) +=== RUN TestNotificationProviderHandler_Test_InvalidJSON +--- PASS: TestNotificationProviderHandler_Test_InvalidJSON (0.00s) +=== RUN TestNotificationProviderHandler_Templates +--- PASS: TestNotificationProviderHandler_Templates (0.00s) +=== RUN TestNotificationProviderHandler_Preview_InvalidJSON +--- PASS: TestNotificationProviderHandler_Preview_InvalidJSON (0.00s) +=== RUN TestNotificationProviderHandler_Preview_WithData +--- PASS: TestNotificationProviderHandler_Preview_WithData (0.01s) +=== RUN TestNotificationProviderHandler_Preview_InvalidTemplate +--- PASS: TestNotificationProviderHandler_Preview_InvalidTemplate (0.01s) +=== RUN TestNotificationTemplateHandler_List_Error +--- PASS: TestNotificationTemplateHandler_List_Error (0.01s) +=== RUN TestNotificationTemplateHandler_Create_BadJSON +--- PASS: TestNotificationTemplateHandler_Create_BadJSON (0.00s) +=== RUN TestNotificationTemplateHandler_Create_DBError +--- PASS: TestNotificationTemplateHandler_Create_DBError (0.01s) +=== RUN TestNotificationTemplateHandler_Update_BadJSON +--- PASS: TestNotificationTemplateHandler_Update_BadJSON (0.01s) +=== RUN TestNotificationTemplateHandler_Update_DBError +--- PASS: TestNotificationTemplateHandler_Update_DBError (0.01s) +=== RUN TestNotificationTemplateHandler_Delete_Error +--- PASS: TestNotificationTemplateHandler_Delete_Error (0.01s) +=== RUN TestNotificationTemplateHandler_Preview_BadJSON +--- PASS: TestNotificationTemplateHandler_Preview_BadJSON (0.01s) +=== RUN TestNotificationTemplateHandler_Preview_TemplateNotFound +--- PASS: TestNotificationTemplateHandler_Preview_TemplateNotFound (0.01s) +=== RUN TestNotificationTemplateHandler_Preview_WithStoredTemplate +--- PASS: TestNotificationTemplateHandler_Preview_WithStoredTemplate (0.01s) +=== RUN TestNotificationTemplateHandler_Preview_InvalidTemplate +--- PASS: TestNotificationTemplateHandler_Preview_InvalidTemplate (0.01s) +=== RUN TestNotificationTemplateHandler_CRUDAndPreview +--- PASS: TestNotificationTemplateHandler_CRUDAndPreview (0.01s) +=== RUN TestNotificationTemplateHandler_Create_InvalidJSON +--- PASS: TestNotificationTemplateHandler_Create_InvalidJSON (0.00s) +=== RUN TestNotificationTemplateHandler_Update_InvalidJSON +--- PASS: TestNotificationTemplateHandler_Update_InvalidJSON (0.00s) +=== RUN TestNotificationTemplateHandler_Preview_InvalidJSON +--- PASS: TestNotificationTemplateHandler_Preview_InvalidJSON (0.00s) +=== RUN TestPerf_GetStatus_AssertThreshold + perf_assert_test.go:107: GetStatus avg=0.386ms p95=0.520ms max=2.700ms +--- PASS: TestPerf_GetStatus_AssertThreshold (0.20s) +=== RUN TestPerf_GetStatus_Parallel_AssertThreshold + perf_assert_test.go:150: GetStatus Parallel avg=0.584ms p95=1.505ms max=4.662ms +--- PASS: TestPerf_GetStatus_Parallel_AssertThreshold (0.14s) +=== RUN TestPerf_ListDecisions_AssertThreshold + perf_assert_test.go:179: ListDecisions avg=2.203ms p95=2.787ms max=6.314ms +--- PASS: TestPerf_ListDecisions_AssertThreshold (0.68s) +=== RUN TestPluginHandler_NewPluginHandler +--- PASS: TestPluginHandler_NewPluginHandler (0.00s) +=== RUN TestPluginHandler_ListPlugins +--- PASS: TestPluginHandler_ListPlugins (0.06s) +=== RUN TestPluginHandler_GetPlugin_InvalidID +--- PASS: TestPluginHandler_GetPlugin_InvalidID (0.00s) +=== RUN TestPluginHandler_GetPlugin_NotFound +--- PASS: TestPluginHandler_GetPlugin_NotFound (0.00s) +=== RUN TestPluginHandler_GetPlugin_Success +--- PASS: TestPluginHandler_GetPlugin_Success (0.00s) +=== RUN TestPluginHandler_EnablePlugin_InvalidID +--- PASS: TestPluginHandler_EnablePlugin_InvalidID (0.00s) +=== RUN TestPluginHandler_EnablePlugin_NotFound +--- PASS: TestPluginHandler_EnablePlugin_NotFound (0.00s) +=== RUN TestPluginHandler_EnablePlugin_AlreadyEnabled +--- PASS: TestPluginHandler_EnablePlugin_AlreadyEnabled (0.00s) +=== RUN TestPluginHandler_EnablePlugin_Success +--- PASS: TestPluginHandler_EnablePlugin_Success (0.00s) +=== RUN TestPluginHandler_DisablePlugin_InvalidID +--- PASS: TestPluginHandler_DisablePlugin_InvalidID (0.00s) +=== RUN TestPluginHandler_DisablePlugin_NotFound +--- PASS: TestPluginHandler_DisablePlugin_NotFound (0.00s) +=== RUN TestPluginHandler_DisablePlugin_AlreadyDisabled +--- PASS: TestPluginHandler_DisablePlugin_AlreadyDisabled (0.00s) +=== RUN TestPluginHandler_DisablePlugin_InUse +--- PASS: TestPluginHandler_DisablePlugin_InUse (0.00s) +=== RUN TestPluginHandler_DisablePlugin_Success +--- PASS: TestPluginHandler_DisablePlugin_Success (0.00s) +=== RUN TestPluginHandler_ReloadPlugins_Success +--- PASS: TestPluginHandler_ReloadPlugins_Success (0.00s) +=== RUN TestPluginHandler_ListPlugins_WithBuiltInProviders +--- PASS: TestPluginHandler_ListPlugins_WithBuiltInProviders (0.00s) +=== RUN TestPluginHandler_ListPlugins_ExternalLoadedPlugin +--- PASS: TestPluginHandler_ListPlugins_ExternalLoadedPlugin (0.01s) +=== RUN TestPluginHandler_GetPlugin_WithProvider +--- PASS: TestPluginHandler_GetPlugin_WithProvider (0.00s) +=== RUN TestPluginHandler_EnablePlugin_WithLoadError +--- PASS: TestPluginHandler_EnablePlugin_WithLoadError (0.00s) +=== RUN TestPluginHandler_DisablePlugin_WithUnloadError +--- PASS: TestPluginHandler_DisablePlugin_WithUnloadError (0.01s) +=== RUN TestPluginHandler_DisablePlugin_MultipleProviders +--- PASS: TestPluginHandler_DisablePlugin_MultipleProviders (0.00s) +=== RUN TestPluginHandler_ReloadPlugins_WithErrors +--- PASS: TestPluginHandler_ReloadPlugins_WithErrors (0.00s) +=== RUN TestPluginHandler_ListPlugins_FailedPluginWithLoadedAt +--- PASS: TestPluginHandler_ListPlugins_FailedPluginWithLoadedAt (0.00s) +=== RUN TestPluginHandler_GetPlugin_WithLoadedAt +--- PASS: TestPluginHandler_GetPlugin_WithLoadedAt (0.01s) +=== RUN TestPluginHandler_Count + plugin_handler_test.go:851: Total plugin handler tests: Aim for 15-20 tests +--- PASS: TestPluginHandler_Count (0.00s) +=== RUN TestPluginHandler_EnablePlugin_DBUpdateError +--- PASS: TestPluginHandler_EnablePlugin_DBUpdateError (0.00s) +=== RUN TestPluginHandler_DisablePlugin_DBUpdateError +--- PASS: TestPluginHandler_DisablePlugin_DBUpdateError (0.00s) +=== RUN TestPluginHandler_GetPlugin_DBInternalError +--- PASS: TestPluginHandler_GetPlugin_DBInternalError (0.00s) +=== RUN TestPluginHandler_EnablePlugin_FirstDBLookupError +--- PASS: TestPluginHandler_EnablePlugin_FirstDBLookupError (0.00s) +=== RUN TestPluginHandler_DisablePlugin_FirstDBLookupError +--- PASS: TestPluginHandler_DisablePlugin_FirstDBLookupError (0.00s) +=== RUN TestPluginHandler_EnablePlugin_DatabaseUpdateError +--- PASS: TestPluginHandler_EnablePlugin_DatabaseUpdateError (0.00s) +=== RUN TestPluginHandler_DisablePlugin_DatabaseUpdateError +--- PASS: TestPluginHandler_DisablePlugin_DatabaseUpdateError (0.00s) +=== RUN TestPluginHandler_GetPlugin_DatabaseError +--- PASS: TestPluginHandler_GetPlugin_DatabaseError (0.00s) +=== RUN TestPluginHandler_EnablePlugin_DatabaseFirstError +--- PASS: TestPluginHandler_EnablePlugin_DatabaseFirstError (0.00s) +=== RUN TestPluginHandler_DisablePlugin_DatabaseFirstError +--- PASS: TestPluginHandler_DisablePlugin_DatabaseFirstError (0.00s) +=== RUN TestEncryptionHandler_Validate_NonAdminAccess +--- PASS: TestEncryptionHandler_Validate_NonAdminAccess (0.03s) +=== RUN TestEncryptionHandler_GetHistory_PaginationBoundary +--- PASS: TestEncryptionHandler_GetHistory_PaginationBoundary (0.03s) +=== RUN TestEncryptionHandler_GetStatus_VersionInfo +--- PASS: TestEncryptionHandler_GetStatus_VersionInfo (0.03s) +=== RUN TestSettingsHandler_TestPublicURL_RoleNotExists +--- PASS: TestSettingsHandler_TestPublicURL_RoleNotExists (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_InvalidURLFormat +--- PASS: TestSettingsHandler_TestPublicURL_InvalidURLFormat (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_PrivateIPBlocked_Coverage +--- PASS: TestSettingsHandler_TestPublicURL_PrivateIPBlocked_Coverage (0.00s) +=== RUN TestSettingsHandler_ValidatePublicURL_WithTrailingSlash +--- PASS: TestSettingsHandler_ValidatePublicURL_WithTrailingSlash (0.00s) +=== RUN TestSettingsHandler_ValidatePublicURL_MissingScheme +--- PASS: TestSettingsHandler_ValidatePublicURL_MissingScheme (0.00s) +=== RUN TestAuditLogHandler_List_PaginationEdgeCases + +2026/01/10 02:23:59 /projects/Charon/backend/internal/api/handlers/pr_coverage_test.go:408 UNIQUE constraint failed: security_audits.uuid +[0.315ms] [rows:0] INSERT INTO `security_audits` (`uuid`,`actor`,`action`,`event_category`,`resource_id`,`resource_uuid`,`details`,`ip_address`,`user_agent`,`created_at`) VALUES ("","user1","action_1","test",NULL,"","{}","","","2026-01-10 02:23:59.873") RETURNING `id` + +2026/01/10 02:23:59 /projects/Charon/backend/internal/api/handlers/pr_coverage_test.go:408 UNIQUE constraint failed: security_audits.uuid +[0.249ms] [rows:0] INSERT INTO `security_audits` (`uuid`,`actor`,`action`,`event_category`,`resource_id`,`resource_uuid`,`details`,`ip_address`,`user_agent`,`created_at`) VALUES ("","user1","action_2","test",NULL,"","{}","","","2026-01-10 02:23:59.873") RETURNING `id` + +2026/01/10 02:23:59 /projects/Charon/backend/internal/api/handlers/pr_coverage_test.go:408 UNIQUE constraint failed: security_audits.uuid +[0.385ms] [rows:0] INSERT INTO `security_audits` (`uuid`,`actor`,`action`,`event_category`,`resource_id`,`resource_uuid`,`details`,`ip_address`,`user_agent`,`created_at`) VALUES ("","user1","action_3","test",NULL,"","{}","","","2026-01-10 02:23:59.873") RETURNING `id` + +2026/01/10 02:23:59 /projects/Charon/backend/internal/api/handlers/pr_coverage_test.go:408 UNIQUE constraint failed: security_audits.uuid +[0.424ms] [rows:0] INSERT INTO `security_audits` (`uuid`,`actor`,`action`,`event_category`,`resource_id`,`resource_uuid`,`details`,`ip_address`,`user_agent`,`created_at`) VALUES ("","user1","action_4","test",NULL,"","{}","","","2026-01-10 02:23:59.874") RETURNING `id` + +2026/01/10 02:23:59 /projects/Charon/backend/internal/api/handlers/pr_coverage_test.go:408 UNIQUE constraint failed: security_audits.uuid +[0.194ms] [rows:0] INSERT INTO `security_audits` (`uuid`,`actor`,`action`,`event_category`,`resource_id`,`resource_uuid`,`details`,`ip_address`,`user_agent`,`created_at`) VALUES ("","user1","action_5","test",NULL,"","{}","","","2026-01-10 02:23:59.875") RETURNING `id` + +2026/01/10 02:23:59 /projects/Charon/backend/internal/api/handlers/pr_coverage_test.go:408 UNIQUE constraint failed: security_audits.uuid +[0.239ms] [rows:0] INSERT INTO `security_audits` (`uuid`,`actor`,`action`,`event_category`,`resource_id`,`resource_uuid`,`details`,`ip_address`,`user_agent`,`created_at`) VALUES ("","user1","action_6","test",NULL,"","{}","","","2026-01-10 02:23:59.875") RETURNING `id` + +2026/01/10 02:23:59 /projects/Charon/backend/internal/api/handlers/pr_coverage_test.go:408 UNIQUE constraint failed: security_audits.uuid +[0.169ms] [rows:0] INSERT INTO `security_audits` (`uuid`,`actor`,`action`,`event_category`,`resource_id`,`resource_uuid`,`details`,`ip_address`,`user_agent`,`created_at`) VALUES ("","user1","action_7","test",NULL,"","{}","","","2026-01-10 02:23:59.875") RETURNING `id` + +2026/01/10 02:23:59 /projects/Charon/backend/internal/api/handlers/pr_coverage_test.go:408 UNIQUE constraint failed: security_audits.uuid +[0.214ms] [rows:0] INSERT INTO `security_audits` (`uuid`,`actor`,`action`,`event_category`,`resource_id`,`resource_uuid`,`details`,`ip_address`,`user_agent`,`created_at`) VALUES ("","user1","action_8","test",NULL,"","{}","","","2026-01-10 02:23:59.875") RETURNING `id` + +2026/01/10 02:23:59 /projects/Charon/backend/internal/api/handlers/pr_coverage_test.go:408 UNIQUE constraint failed: security_audits.uuid +[0.157ms] [rows:0] INSERT INTO `security_audits` (`uuid`,`actor`,`action`,`event_category`,`resource_id`,`resource_uuid`,`details`,`ip_address`,`user_agent`,`created_at`) VALUES ("","user1","action_9","test",NULL,"","{}","","","2026-01-10 02:23:59.876") RETURNING `id` +--- PASS: TestAuditLogHandler_List_PaginationEdgeCases (0.04s) +=== RUN TestAuditLogHandler_List_CategoryFilter + +2026/01/10 02:23:59 /projects/Charon/backend/internal/api/handlers/pr_coverage_test.go:447 UNIQUE constraint failed: security_audits.uuid +[0.321ms] [rows:0] INSERT INTO `security_audits` (`uuid`,`actor`,`action`,`event_category`,`resource_id`,`resource_uuid`,`details`,`ip_address`,`user_agent`,`created_at`) VALUES ("","user2","action2","security",NULL,"","{}","","","2026-01-10 02:23:59.909") RETURNING `id` +--- PASS: TestAuditLogHandler_List_CategoryFilter (0.03s) +=== RUN TestAuditLogHandler_ListByProvider_DatabaseError + +2026/01/10 02:23:59 /projects/Charon/backend/internal/services/security_service.go:339 sql: database is closed +[0.025ms] [rows:0] SELECT count(*) FROM `security_audits` WHERE event_category = "dns_provider" AND resource_id = 1 +--- PASS: TestAuditLogHandler_ListByProvider_DatabaseError (0.03s) +=== RUN TestAuditLogHandler_ListByProvider_InvalidProviderID +--- PASS: TestAuditLogHandler_ListByProvider_InvalidProviderID (0.03s) +=== RUN TestGetActorFromGinContext_InvalidUserIDType +--- PASS: TestGetActorFromGinContext_InvalidUserIDType (0.00s) +=== RUN TestIsAdmin_NonAdminRole +--- PASS: TestIsAdmin_NonAdminRole (0.00s) +=== RUN TestCredentialHandler_Update_InvalidProviderType +--- PASS: TestCredentialHandler_Update_InvalidProviderType (0.01s) +=== RUN TestCredentialHandler_List_DatabaseClosed + +2026/01/10 02:23:59 /projects/Charon/backend/internal/services/credential_service.go:86 sql: database is closed +[0.032ms] [rows:0] SELECT * FROM `dns_providers` WHERE `dns_providers`.`id` = 1 ORDER BY `dns_providers`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_List_DatabaseClosed (0.00s) +=== RUN TestSettingsHandler_MaskPasswordForTestFunction +=== RUN TestSettingsHandler_MaskPasswordForTestFunction/empty_string +=== RUN TestSettingsHandler_MaskPasswordForTestFunction/non-empty_password +=== RUN TestSettingsHandler_MaskPasswordForTestFunction/already_masked +=== RUN TestSettingsHandler_MaskPasswordForTestFunction/single_char +--- PASS: TestSettingsHandler_MaskPasswordForTestFunction (0.00s) + --- PASS: TestSettingsHandler_MaskPasswordForTestFunction/empty_string (0.00s) + --- PASS: TestSettingsHandler_MaskPasswordForTestFunction/non-empty_password (0.00s) + --- PASS: TestSettingsHandler_MaskPasswordForTestFunction/already_masked (0.00s) + --- PASS: TestSettingsHandler_MaskPasswordForTestFunction/single_char (0.00s) +=== RUN TestCredentialHandler_Update_NotFoundError + +2026/01/10 02:23:59 /projects/Charon/backend/internal/services/credential_service.go:111 record not found +[0.132ms] [rows:0] SELECT * FROM `dns_provider_credentials` WHERE id = 9999 AND dns_provider_id = 1 ORDER BY `dns_provider_credentials`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_Update_NotFoundError (0.01s) +=== RUN TestCredentialHandler_Update_MalformedJSON +--- PASS: TestCredentialHandler_Update_MalformedJSON (0.01s) +=== RUN TestCredentialHandler_Update_BadCredentialID +--- PASS: TestCredentialHandler_Update_BadCredentialID (0.00s) +=== RUN TestCredentialHandler_Delete_NotFoundError + +2026/01/10 02:24:00 /projects/Charon/backend/internal/services/credential_service.go:111 record not found +[0.118ms] [rows:0] SELECT * FROM `dns_provider_credentials` WHERE id = 9999 AND dns_provider_id = 1 ORDER BY `dns_provider_credentials`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_Delete_NotFoundError (0.01s) +=== RUN TestCredentialHandler_Delete_BadCredentialID +--- PASS: TestCredentialHandler_Delete_BadCredentialID (0.02s) +=== RUN TestCredentialHandler_Test_BadCredentialID +--- PASS: TestCredentialHandler_Test_BadCredentialID (0.01s) +=== RUN TestCredentialHandler_EnableMultiCredentials_BadProviderID +--- PASS: TestCredentialHandler_EnableMultiCredentials_BadProviderID (0.01s) +=== RUN TestEncryptionHandler_Validate_AdminSuccess +--- PASS: TestEncryptionHandler_Validate_AdminSuccess (0.09s) +=== RUN TestBulkUpdateSecurityHeaders_Success +--- PASS: TestBulkUpdateSecurityHeaders_Success (0.03s) +=== RUN TestBulkUpdateSecurityHeaders_RemoveProfile +--- PASS: TestBulkUpdateSecurityHeaders_RemoveProfile (0.02s) +=== RUN TestBulkUpdateSecurityHeaders_InvalidProfileID + +2026/01/10 02:24:00 /projects/Charon/backend/internal/api/handlers/proxy_host_handler.go:615 record not found +[0.101ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 99999 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestBulkUpdateSecurityHeaders_InvalidProfileID (0.02s) +=== RUN TestBulkUpdateSecurityHeaders_EmptyUUIDs +--- PASS: TestBulkUpdateSecurityHeaders_EmptyUUIDs (0.02s) +=== RUN TestBulkUpdateSecurityHeaders_PartialFailure + +2026/01/10 02:24:00 /projects/Charon/backend/internal/api/handlers/proxy_host_handler.go:638 record not found +[0.138ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "non-existent-uuid" ORDER BY `proxy_hosts`.`id` LIMIT 1 +--- PASS: TestBulkUpdateSecurityHeaders_PartialFailure (0.03s) +=== RUN TestBulkUpdateSecurityHeaders_TransactionRollback + +2026/01/10 02:24:00 /projects/Charon/backend/internal/api/handlers/proxy_host_handler.go:638 record not found +[0.144ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "invalid-uuid-1" ORDER BY `proxy_hosts`.`id` LIMIT 1 + +2026/01/10 02:24:00 /projects/Charon/backend/internal/api/handlers/proxy_host_handler.go:638 record not found +[0.137ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "invalid-uuid-2" ORDER BY `proxy_hosts`.`id` LIMIT 1 +--- PASS: TestBulkUpdateSecurityHeaders_TransactionRollback (0.03s) +=== RUN TestBulkUpdateSecurityHeaders_InvalidJSON +--- PASS: TestBulkUpdateSecurityHeaders_InvalidJSON (0.02s) +=== RUN TestBulkUpdateSecurityHeaders_MixedProfileStates +--- PASS: TestBulkUpdateSecurityHeaders_MixedProfileStates (0.04s) +=== RUN TestBulkUpdateSecurityHeaders_SingleHost +--- PASS: TestBulkUpdateSecurityHeaders_SingleHost (0.02s) +=== RUN TestProxyHostLifecycle +=== PAUSE TestProxyHostLifecycle +=== RUN TestProxyHostDelete_WithUptimeCleanup +=== PAUSE TestProxyHostDelete_WithUptimeCleanup +=== RUN TestProxyHostErrors +=== PAUSE TestProxyHostErrors +=== RUN TestProxyHostValidation +=== PAUSE TestProxyHostValidation +=== RUN TestProxyHostCreate_AdvancedConfig_InvalidJSON +=== PAUSE TestProxyHostCreate_AdvancedConfig_InvalidJSON +=== RUN TestProxyHostCreate_AdvancedConfig_Normalization +=== PAUSE TestProxyHostCreate_AdvancedConfig_Normalization +=== RUN TestProxyHostUpdate_CertificateID_Null +=== PAUSE TestProxyHostUpdate_CertificateID_Null +=== RUN TestProxyHostConnection +=== PAUSE TestProxyHostConnection +=== RUN TestProxyHostHandler_List_Error +=== PAUSE TestProxyHostHandler_List_Error +=== RUN TestProxyHostWithCaddyIntegration +=== PAUSE TestProxyHostWithCaddyIntegration +=== RUN TestProxyHostHandler_BulkUpdateACL_Success +=== PAUSE TestProxyHostHandler_BulkUpdateACL_Success +=== RUN TestProxyHostHandler_BulkUpdateACL_RemoveACL +=== PAUSE TestProxyHostHandler_BulkUpdateACL_RemoveACL +=== RUN TestProxyHostHandler_BulkUpdateACL_PartialFailure +=== PAUSE TestProxyHostHandler_BulkUpdateACL_PartialFailure +=== RUN TestProxyHostHandler_BulkUpdateACL_EmptyUUIDs +=== PAUSE TestProxyHostHandler_BulkUpdateACL_EmptyUUIDs +=== RUN TestProxyHostHandler_BulkUpdateACL_InvalidJSON +=== PAUSE TestProxyHostHandler_BulkUpdateACL_InvalidJSON +=== RUN TestProxyHostUpdate_AdvancedConfig_ClearAndBackup +=== PAUSE TestProxyHostUpdate_AdvancedConfig_ClearAndBackup +=== RUN TestProxyHostUpdate_AdvancedConfig_InvalidJSON +=== PAUSE TestProxyHostUpdate_AdvancedConfig_InvalidJSON +=== RUN TestProxyHostUpdate_SetCertificateID +=== PAUSE TestProxyHostUpdate_SetCertificateID +=== RUN TestProxyHostUpdate_AdvancedConfig_SetBackup +=== PAUSE TestProxyHostUpdate_AdvancedConfig_SetBackup +=== RUN TestProxyHostUpdate_ForwardPort_StringValue +=== PAUSE TestProxyHostUpdate_ForwardPort_StringValue +=== RUN TestProxyHostUpdate_Locations_InvalidPayload +=== PAUSE TestProxyHostUpdate_Locations_InvalidPayload +=== RUN TestProxyHostUpdate_SetBooleansAndApplication +=== PAUSE TestProxyHostUpdate_SetBooleansAndApplication +=== RUN TestProxyHostUpdate_Locations_Replace +=== PAUSE TestProxyHostUpdate_Locations_Replace +=== RUN TestProxyHostCreate_WithCertificateAndLocations +=== PAUSE TestProxyHostCreate_WithCertificateAndLocations +=== RUN TestProxyHostCreate_WithSecurityHeaderProfile +=== PAUSE TestProxyHostCreate_WithSecurityHeaderProfile +=== RUN TestProxyHostUpdate_AssignSecurityHeaderProfile +=== PAUSE TestProxyHostUpdate_AssignSecurityHeaderProfile +=== RUN TestProxyHostUpdate_ChangeSecurityHeaderProfile +=== PAUSE TestProxyHostUpdate_ChangeSecurityHeaderProfile +=== RUN TestProxyHostUpdate_RemoveSecurityHeaderProfile +=== PAUSE TestProxyHostUpdate_RemoveSecurityHeaderProfile +=== RUN TestProxyHostUpdate_InvalidSecurityHeaderProfileID +=== PAUSE TestProxyHostUpdate_InvalidSecurityHeaderProfileID +=== RUN TestProxyHostUpdate_SecurityHeaderProfile_StrictToBasic +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfile_StrictToBasic +=== RUN TestProxyHostUpdate_SecurityHeaderProfile_ToNone +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfile_ToNone +=== RUN TestProxyHostUpdate_SecurityHeaderProfile_FromNoneToValid +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfile_FromNoneToValid +=== RUN TestProxyHostUpdate_SecurityHeaderProfile_InvalidString +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfile_InvalidString +=== RUN TestProxyHostUpdate_SecurityHeaderProfile_InvalidFloat +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfile_InvalidFloat +=== RUN TestProxyHostUpdate_SecurityHeaderProfile_ValidString +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfile_ValidString +=== RUN TestProxyHostUpdate_SecurityHeaderProfile_UnsupportedType +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfile_UnsupportedType +=== RUN TestUpdate_EnableStandardHeaders +=== PAUSE TestUpdate_EnableStandardHeaders +=== RUN TestUpdate_ForwardAuthEnabled +=== PAUSE TestUpdate_ForwardAuthEnabled +=== RUN TestUpdate_WAFDisabled +=== PAUSE TestUpdate_WAFDisabled +=== RUN TestUpdate_IntegrationCaddyConfig +=== PAUSE TestUpdate_IntegrationCaddyConfig +=== RUN TestUpdate_ExistingHostsBackwardCompatibility +=== PAUSE TestUpdate_ExistingHostsBackwardCompatibility +=== RUN TestProxyHostHandler_BulkUpdateSecurityHeaders_Success +=== PAUSE TestProxyHostHandler_BulkUpdateSecurityHeaders_Success +=== RUN TestProxyHostHandler_BulkUpdateSecurityHeaders_RemoveProfile +=== PAUSE TestProxyHostHandler_BulkUpdateSecurityHeaders_RemoveProfile +=== RUN TestProxyHostHandler_BulkUpdateSecurityHeaders_PartialFailure +=== PAUSE TestProxyHostHandler_BulkUpdateSecurityHeaders_PartialFailure +=== RUN TestProxyHostHandler_BulkUpdateSecurityHeaders_EmptyUUIDs +=== PAUSE TestProxyHostHandler_BulkUpdateSecurityHeaders_EmptyUUIDs +=== RUN TestProxyHostHandler_BulkUpdateSecurityHeaders_InvalidJSON +=== PAUSE TestProxyHostHandler_BulkUpdateSecurityHeaders_InvalidJSON +=== RUN TestProxyHostHandler_BulkUpdateSecurityHeaders_ProfileNotFound +=== PAUSE TestProxyHostHandler_BulkUpdateSecurityHeaders_ProfileNotFound +=== RUN TestProxyHostHandler_BulkUpdateSecurityHeaders_AllFail +=== PAUSE TestProxyHostHandler_BulkUpdateSecurityHeaders_AllFail +=== RUN TestProxyHostUpdate_NegativeIntCertificateID +=== PAUSE TestProxyHostUpdate_NegativeIntCertificateID +=== RUN TestProxyHostUpdate_AccessListID_StringValue +=== PAUSE TestProxyHostUpdate_AccessListID_StringValue +=== RUN TestProxyHostUpdate_AccessListID_IntValue +=== PAUSE TestProxyHostUpdate_AccessListID_IntValue +=== RUN TestProxyHostUpdate_CertificateID_IntValue +=== PAUSE TestProxyHostUpdate_CertificateID_IntValue +=== RUN TestProxyHostUpdate_CertificateID_StringValue +=== PAUSE TestProxyHostUpdate_CertificateID_StringValue +=== RUN TestProxyHostUpdate_EnableStandardHeaders_Null +=== PAUSE TestProxyHostUpdate_EnableStandardHeaders_Null +=== RUN TestProxyHostUpdate_EnableStandardHeaders_True +=== PAUSE TestProxyHostUpdate_EnableStandardHeaders_True +=== RUN TestProxyHostUpdate_EnableStandardHeaders_False +=== PAUSE TestProxyHostUpdate_EnableStandardHeaders_False +=== RUN TestProxyHostUpdate_ForwardAuthEnabled +=== PAUSE TestProxyHostUpdate_ForwardAuthEnabled +=== RUN TestProxyHostUpdate_WAFDisabled +=== PAUSE TestProxyHostUpdate_WAFDisabled +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_NegativeFloat +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfileID_NegativeFloat +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_NegativeInt +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfileID_NegativeInt +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_InvalidString +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfileID_InvalidString +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_ValidAssignment +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfileID_ValidAssignment +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_SetToNull +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfileID_SetToNull +=== RUN TestBulkUpdateSecurityHeaders_DBError_NonNotFound +=== PAUSE TestBulkUpdateSecurityHeaders_DBError_NonNotFound +=== RUN TestSanitizeForLog +--- PASS: TestSanitizeForLog (0.00s) +=== RUN TestSecurityHandler_GetGeoIPStatus_NotInitialized +--- PASS: TestSecurityHandler_GetGeoIPStatus_NotInitialized (0.00s) +=== RUN TestSecurityHandler_GetGeoIPStatus_Initialized_NotLoaded +--- PASS: TestSecurityHandler_GetGeoIPStatus_Initialized_NotLoaded (0.00s) +=== RUN TestSecurityHandler_ReloadGeoIP_NotInitialized +--- PASS: TestSecurityHandler_ReloadGeoIP_NotInitialized (0.00s) +=== RUN TestSecurityHandler_ReloadGeoIP_LoadError +time="2026-01-10T02:24:00Z" level=error msg="Failed to reload GeoIP database" error="open : no such file or directory" +--- PASS: TestSecurityHandler_ReloadGeoIP_LoadError (0.00s) +=== RUN TestSecurityHandler_LookupGeoIP_MissingIPAddress +--- PASS: TestSecurityHandler_LookupGeoIP_MissingIPAddress (0.00s) +=== RUN TestSecurityHandler_LookupGeoIP_ServiceUnavailable +--- PASS: TestSecurityHandler_LookupGeoIP_ServiceUnavailable (0.00s) +=== RUN TestSecurityHandler_GetConfigAndUpdateConfig + +2026/01/10 02:24:00 /projects/Charon/backend/internal/services/security_service.go:70 record not found +[0.082ms] [rows:0] SELECT * FROM `security_configs` ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:00 /projects/Charon/backend/internal/services/security_service.go:106 record not found +[0.107ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityHandler_GetConfigAndUpdateConfig (0.00s) +=== RUN TestSecurityHandler_GetStatus_SQLInjection +--- PASS: TestSecurityHandler_GetStatus_SQLInjection (0.03s) +=== RUN TestSecurityHandler_CreateDecision_SQLInjection +=== RUN TestSecurityHandler_CreateDecision_SQLInjection/payload_0 +=== RUN TestSecurityHandler_CreateDecision_SQLInjection/payload_1 +=== RUN TestSecurityHandler_CreateDecision_SQLInjection/payload_2 +=== RUN TestSecurityHandler_CreateDecision_SQLInjection/payload_3 +--- PASS: TestSecurityHandler_CreateDecision_SQLInjection (0.02s) + --- PASS: TestSecurityHandler_CreateDecision_SQLInjection/payload_0 (0.00s) + --- PASS: TestSecurityHandler_CreateDecision_SQLInjection/payload_1 (0.00s) + --- PASS: TestSecurityHandler_CreateDecision_SQLInjection/payload_2 (0.00s) + --- PASS: TestSecurityHandler_CreateDecision_SQLInjection/payload_3 (0.00s) +=== RUN TestSecurityHandler_UpsertRuleSet_MassivePayload +--- PASS: TestSecurityHandler_UpsertRuleSet_MassivePayload (0.57s) +=== RUN TestSecurityHandler_UpsertRuleSet_EmptyName +--- PASS: TestSecurityHandler_UpsertRuleSet_EmptyName (0.01s) +=== RUN TestSecurityHandler_CreateDecision_EmptyFields +=== RUN TestSecurityHandler_CreateDecision_EmptyFields/empty_ip +=== RUN TestSecurityHandler_CreateDecision_EmptyFields/empty_action +=== RUN TestSecurityHandler_CreateDecision_EmptyFields/both_empty +=== RUN TestSecurityHandler_CreateDecision_EmptyFields/valid +--- PASS: TestSecurityHandler_CreateDecision_EmptyFields (0.02s) + --- PASS: TestSecurityHandler_CreateDecision_EmptyFields/empty_ip (0.00s) + --- PASS: TestSecurityHandler_CreateDecision_EmptyFields/empty_action (0.00s) + --- PASS: TestSecurityHandler_CreateDecision_EmptyFields/both_empty (0.00s) + --- PASS: TestSecurityHandler_CreateDecision_EmptyFields/valid (0.00s) +=== RUN TestSecurityHandler_GetStatus_SettingsOverride +--- PASS: TestSecurityHandler_GetStatus_SettingsOverride (0.02s) +=== RUN TestSecurityHandler_GetStatus_DisabledViaSettings +--- PASS: TestSecurityHandler_GetStatus_DisabledViaSettings (0.02s) +=== RUN TestSecurityAudit_DeleteRuleSet_InvalidID +=== RUN TestSecurityAudit_DeleteRuleSet_InvalidID/empty_id +=== RUN TestSecurityAudit_DeleteRuleSet_InvalidID/non_numeric +=== RUN TestSecurityAudit_DeleteRuleSet_InvalidID/negative +=== RUN TestSecurityAudit_DeleteRuleSet_InvalidID/sql_injection +=== RUN TestSecurityAudit_DeleteRuleSet_InvalidID/not_found +--- PASS: TestSecurityAudit_DeleteRuleSet_InvalidID (0.02s) + --- PASS: TestSecurityAudit_DeleteRuleSet_InvalidID/empty_id (0.00s) + --- PASS: TestSecurityAudit_DeleteRuleSet_InvalidID/non_numeric (0.00s) + --- PASS: TestSecurityAudit_DeleteRuleSet_InvalidID/negative (0.00s) + --- PASS: TestSecurityAudit_DeleteRuleSet_InvalidID/sql_injection (0.00s) + --- PASS: TestSecurityAudit_DeleteRuleSet_InvalidID/not_found (0.00s) +=== RUN TestSecurityHandler_UpsertRuleSet_XSSInContent +--- PASS: TestSecurityHandler_UpsertRuleSet_XSSInContent (0.03s) +=== RUN TestSecurityHandler_UpdateConfig_RateLimitBounds +=== RUN TestSecurityHandler_UpdateConfig_RateLimitBounds/valid_limits +=== RUN TestSecurityHandler_UpdateConfig_RateLimitBounds/zero_requests +=== RUN TestSecurityHandler_UpdateConfig_RateLimitBounds/negative_burst +=== RUN TestSecurityHandler_UpdateConfig_RateLimitBounds/huge_values +--- PASS: TestSecurityHandler_UpdateConfig_RateLimitBounds (0.02s) + --- PASS: TestSecurityHandler_UpdateConfig_RateLimitBounds/valid_limits (0.00s) + --- PASS: TestSecurityHandler_UpdateConfig_RateLimitBounds/zero_requests (0.00s) + --- PASS: TestSecurityHandler_UpdateConfig_RateLimitBounds/negative_burst (0.00s) + --- PASS: TestSecurityHandler_UpdateConfig_RateLimitBounds/huge_values (0.00s) +=== RUN TestSecurityHandler_GetStatus_NilDB +--- PASS: TestSecurityHandler_GetStatus_NilDB (0.00s) +=== RUN TestSecurityHandler_Enable_WithoutWhitelist +--- PASS: TestSecurityHandler_Enable_WithoutWhitelist (0.01s) +=== RUN TestSecurityHandler_Disable_RequiresToken +--- PASS: TestSecurityHandler_Disable_RequiresToken (0.03s) +=== RUN TestSecurityHandler_GetStatus_CrowdSecModeValidation +=== RUN TestSecurityHandler_GetStatus_CrowdSecModeValidation/mode_remote +=== RUN TestSecurityHandler_GetStatus_CrowdSecModeValidation/mode_external +=== RUN TestSecurityHandler_GetStatus_CrowdSecModeValidation/mode_cloud +=== RUN TestSecurityHandler_GetStatus_CrowdSecModeValidation/mode_api +=== RUN TestSecurityHandler_GetStatus_CrowdSecModeValidation/mode_../../../etc/passwd +--- PASS: TestSecurityHandler_GetStatus_CrowdSecModeValidation (0.03s) + --- PASS: TestSecurityHandler_GetStatus_CrowdSecModeValidation/mode_remote (0.01s) + --- PASS: TestSecurityHandler_GetStatus_CrowdSecModeValidation/mode_external (0.00s) + --- PASS: TestSecurityHandler_GetStatus_CrowdSecModeValidation/mode_cloud (0.00s) + --- PASS: TestSecurityHandler_GetStatus_CrowdSecModeValidation/mode_api (0.00s) + --- PASS: TestSecurityHandler_GetStatus_CrowdSecModeValidation/mode_../../../etc/passwd (0.00s) +=== RUN TestSecurityHandler_GetStatus_Clean +--- PASS: TestSecurityHandler_GetStatus_Clean (0.00s) +=== RUN TestSecurityHandler_Cerberus_DBOverride +--- PASS: TestSecurityHandler_Cerberus_DBOverride (0.01s) +=== RUN TestSecurityHandler_ACL_DBOverride +--- PASS: TestSecurityHandler_ACL_DBOverride (0.02s) +=== RUN TestSecurityHandler_GenerateBreakGlass_ReturnsToken +--- PASS: TestSecurityHandler_GenerateBreakGlass_ReturnsToken (1.09s) +=== RUN TestSecurityHandler_ACL_DisabledWhenCerberusOff +--- PASS: TestSecurityHandler_ACL_DisabledWhenCerberusOff (0.01s) +=== RUN TestSecurityHandler_CrowdSec_Mode_DBOverride +--- PASS: TestSecurityHandler_CrowdSec_Mode_DBOverride (0.01s) +=== RUN TestSecurityHandler_CrowdSec_ExternalMappedToDisabled_DBOverride +--- PASS: TestSecurityHandler_CrowdSec_ExternalMappedToDisabled_DBOverride (0.01s) +=== RUN TestSecurityHandler_ExternalModeMappedToDisabled +--- PASS: TestSecurityHandler_ExternalModeMappedToDisabled (0.00s) +=== RUN TestSecurityHandler_Enable_Disable_WithAdminWhitelistAndToken +--- PASS: TestSecurityHandler_Enable_Disable_WithAdminWhitelistAndToken (1.52s) +=== RUN TestSecurityHandler_UpdateConfig_Success +--- PASS: TestSecurityHandler_UpdateConfig_Success (0.02s) +=== RUN TestSecurityHandler_UpdateConfig_DefaultName +--- PASS: TestSecurityHandler_UpdateConfig_DefaultName (0.02s) +=== RUN TestSecurityHandler_UpdateConfig_InvalidPayload +--- PASS: TestSecurityHandler_UpdateConfig_InvalidPayload (0.01s) +=== RUN TestSecurityHandler_GetConfig_Success +--- PASS: TestSecurityHandler_GetConfig_Success (0.01s) +=== RUN TestSecurityHandler_GetConfig_NotFound +--- PASS: TestSecurityHandler_GetConfig_NotFound (0.01s) +=== RUN TestSecurityHandler_ListDecisions_Success +--- PASS: TestSecurityHandler_ListDecisions_Success (0.01s) +=== RUN TestSecurityHandler_ListDecisions_WithLimit +--- PASS: TestSecurityHandler_ListDecisions_WithLimit (0.01s) +=== RUN TestSecurityHandler_CreateDecision_Success +--- PASS: TestSecurityHandler_CreateDecision_Success (0.01s) +=== RUN TestSecurityHandler_CreateDecision_MissingIP +--- PASS: TestSecurityHandler_CreateDecision_MissingIP (0.01s) +=== RUN TestSecurityHandler_CreateDecision_MissingAction +--- PASS: TestSecurityHandler_CreateDecision_MissingAction (0.01s) +=== RUN TestSecurityHandler_CreateDecision_InvalidPayload +--- PASS: TestSecurityHandler_CreateDecision_InvalidPayload (0.01s) +=== RUN TestSecurityHandler_ListRuleSets_Success +--- PASS: TestSecurityHandler_ListRuleSets_Success (0.01s) +=== RUN TestSecurityHandler_UpsertRuleSet_Success +--- PASS: TestSecurityHandler_UpsertRuleSet_Success (0.01s) +=== RUN TestSecurityHandler_UpsertRuleSet_MissingName +--- PASS: TestSecurityHandler_UpsertRuleSet_MissingName (0.00s) +=== RUN TestSecurityHandler_UpsertRuleSet_InvalidPayload +--- PASS: TestSecurityHandler_UpsertRuleSet_InvalidPayload (0.01s) +=== RUN TestSecurityHandler_DeleteRuleSet_Success +--- PASS: TestSecurityHandler_DeleteRuleSet_Success (0.01s) +=== RUN TestSecurityHandler_DeleteRuleSet_NotFound +--- PASS: TestSecurityHandler_DeleteRuleSet_NotFound (0.01s) +=== RUN TestSecurityHandler_DeleteRuleSet_InvalidID +--- PASS: TestSecurityHandler_DeleteRuleSet_InvalidID (0.01s) +=== RUN TestSecurityHandler_DeleteRuleSet_EmptyID +--- PASS: TestSecurityHandler_DeleteRuleSet_EmptyID (0.00s) +=== RUN TestSecurityHandler_Enable_NoConfigNoWhitelist +--- PASS: TestSecurityHandler_Enable_NoConfigNoWhitelist (0.01s) +=== RUN TestSecurityHandler_Enable_WithWhitelist +--- PASS: TestSecurityHandler_Enable_WithWhitelist (0.01s) +=== RUN TestSecurityHandler_Enable_IPNotInWhitelist +--- PASS: TestSecurityHandler_Enable_IPNotInWhitelist (0.01s) +=== RUN TestSecurityHandler_Enable_WithValidBreakGlassToken +--- PASS: TestSecurityHandler_Enable_WithValidBreakGlassToken (1.51s) +=== RUN TestSecurityHandler_Enable_WithInvalidBreakGlassToken +--- PASS: TestSecurityHandler_Enable_WithInvalidBreakGlassToken (0.01s) +=== RUN TestSecurityHandler_Disable_FromLocalhost +--- PASS: TestSecurityHandler_Disable_FromLocalhost (0.01s) +=== RUN TestSecurityHandler_Disable_FromRemoteWithToken +--- PASS: TestSecurityHandler_Disable_FromRemoteWithToken (1.49s) +=== RUN TestSecurityHandler_Disable_FromRemoteNoToken +--- PASS: TestSecurityHandler_Disable_FromRemoteNoToken (0.01s) +=== RUN TestSecurityHandler_Disable_FromRemoteInvalidToken +--- PASS: TestSecurityHandler_Disable_FromRemoteInvalidToken (0.01s) +=== RUN TestSecurityHandler_GenerateBreakGlass_NoConfig +--- PASS: TestSecurityHandler_GenerateBreakGlass_NoConfig (0.79s) +=== RUN TestSecurityHandler_Disable_FromIPv6Localhost +--- PASS: TestSecurityHandler_Disable_FromIPv6Localhost (0.01s) +=== RUN TestSecurityHandler_Enable_WithCIDRWhitelist +--- PASS: TestSecurityHandler_Enable_WithCIDRWhitelist (0.01s) +=== RUN TestSecurityHandler_Enable_WithExactIPWhitelist +--- PASS: TestSecurityHandler_Enable_WithExactIPWhitelist (0.01s) +=== RUN TestSecurityHandler_GetStatus_Fixed +=== RUN TestSecurityHandler_GetStatus_Fixed/All_Disabled +=== RUN TestSecurityHandler_GetStatus_Fixed/All_Enabled +--- PASS: TestSecurityHandler_GetStatus_Fixed (0.00s) + --- PASS: TestSecurityHandler_GetStatus_Fixed/All_Disabled (0.00s) + --- PASS: TestSecurityHandler_GetStatus_Fixed/All_Enabled (0.00s) +=== RUN TestSecurityHandler_CreateAndListDecisionAndRulesets + +2026/01/10 02:24:08 /projects/Charon/backend/internal/services/security_service.go:366 record not found +[0.186ms] [rows:0] SELECT * FROM `security_rule_sets` WHERE name = "owasp-crs" ORDER BY `security_rule_sets`.`id` LIMIT 1 +--- PASS: TestSecurityHandler_CreateAndListDecisionAndRulesets (0.27s) +=== RUN TestSecurityHandler_UpsertDeleteTriggersApplyConfig + +2026/01/10 02:24:08 /projects/Charon/backend/internal/services/security_service.go:251 attempt to write a readonly database +[0.701ms] [rows:0] INSERT INTO `security_audits` (`uuid`,`actor`,`action`,`event_category`,`resource_id`,`resource_uuid`,`details`,`ip_address`,`user_agent`,`created_at`) VALUES ("6418b3c6-0b2e-46da-8fee-112ba3795600","192.0.2.1","delete_ruleset","",NULL,"","1","","","2026-01-10 02:24:08.191") RETURNING `id` +Failed to write audit log: attempt to write a readonly database + +2026/01/10 02:24:08 /projects/Charon/backend/internal/services/security_service.go:366 record not found +[0.101ms] [rows:0] SELECT * FROM `security_rule_sets` WHERE name = "owasp-crs" ORDER BY `security_rule_sets`.`id` LIMIT 1 + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:90 no such table: dns_providers +[0.044ms] [rows:0] SELECT * FROM `dns_providers` WHERE enabled = true + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.706ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.083ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.094ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.097ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.067ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:90 no such table: dns_providers +[0.106ms] [rows:0] SELECT * FROM `dns_providers` WHERE enabled = true + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.098ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.102ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.065ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.065ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:08 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.059ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestSecurityHandler_UpsertDeleteTriggersApplyConfig (0.04s) +=== RUN TestSecurityHandler_GetStatus_RespectsSettingsTable +=== RUN TestSecurityHandler_GetStatus_RespectsSettingsTable/WAF_enabled_via_settings_overrides_disabled_config +=== RUN TestSecurityHandler_GetStatus_RespectsSettingsTable/Rate_Limit_enabled_via_settings_overrides_disabled_config +=== RUN TestSecurityHandler_GetStatus_RespectsSettingsTable/CrowdSec_enabled_via_settings_overrides_disabled_config +=== RUN TestSecurityHandler_GetStatus_RespectsSettingsTable/All_modules_enabled_via_settings +=== RUN TestSecurityHandler_GetStatus_RespectsSettingsTable/WAF_disabled_via_settings_overrides_enabled_config +=== RUN TestSecurityHandler_GetStatus_RespectsSettingsTable/No_settings_-_falls_back_to_config_(enabled) +--- PASS: TestSecurityHandler_GetStatus_RespectsSettingsTable (0.04s) + --- PASS: TestSecurityHandler_GetStatus_RespectsSettingsTable/WAF_enabled_via_settings_overrides_disabled_config (0.01s) + --- PASS: TestSecurityHandler_GetStatus_RespectsSettingsTable/Rate_Limit_enabled_via_settings_overrides_disabled_config (0.01s) + --- PASS: TestSecurityHandler_GetStatus_RespectsSettingsTable/CrowdSec_enabled_via_settings_overrides_disabled_config (0.01s) + --- PASS: TestSecurityHandler_GetStatus_RespectsSettingsTable/All_modules_enabled_via_settings (0.01s) + --- PASS: TestSecurityHandler_GetStatus_RespectsSettingsTable/WAF_disabled_via_settings_overrides_enabled_config (0.01s) + --- PASS: TestSecurityHandler_GetStatus_RespectsSettingsTable/No_settings_-_falls_back_to_config_(enabled) (0.01s) +=== RUN TestSecurityHandler_GetStatus_WAFModeFromSettings +--- PASS: TestSecurityHandler_GetStatus_WAFModeFromSettings (0.01s) +=== RUN TestSecurityHandler_GetStatus_RateLimitModeFromSettings +--- PASS: TestSecurityHandler_GetStatus_RateLimitModeFromSettings (0.01s) +=== RUN TestSecurityHandler_GetWAFExclusions_Empty +--- PASS: TestSecurityHandler_GetWAFExclusions_Empty (0.01s) +=== RUN TestSecurityHandler_GetWAFExclusions_WithExclusions +--- PASS: TestSecurityHandler_GetWAFExclusions_WithExclusions (0.01s) +=== RUN TestSecurityHandler_GetWAFExclusions_InvalidJSON +time="2026-01-10T02:24:08Z" level=warning msg="Failed to parse WAF exclusions" error="invalid character 'i' looking for beginning of value" +--- PASS: TestSecurityHandler_GetWAFExclusions_InvalidJSON (0.01s) +=== RUN TestSecurityHandler_AddWAFExclusion_Success +--- PASS: TestSecurityHandler_AddWAFExclusion_Success (0.02s) +=== RUN TestSecurityHandler_AddWAFExclusion_WithTarget +--- PASS: TestSecurityHandler_AddWAFExclusion_WithTarget (0.01s) +=== RUN TestSecurityHandler_AddWAFExclusion_ToExistingConfig +--- PASS: TestSecurityHandler_AddWAFExclusion_ToExistingConfig (0.02s) +=== RUN TestSecurityHandler_AddWAFExclusion_Duplicate +--- PASS: TestSecurityHandler_AddWAFExclusion_Duplicate (0.01s) +=== RUN TestSecurityHandler_AddWAFExclusion_DuplicateWithDifferentTarget +--- PASS: TestSecurityHandler_AddWAFExclusion_DuplicateWithDifferentTarget (0.02s) +=== RUN TestSecurityHandler_AddWAFExclusion_MissingRuleID +--- PASS: TestSecurityHandler_AddWAFExclusion_MissingRuleID (0.01s) +=== RUN TestSecurityHandler_AddWAFExclusion_InvalidRuleID +--- PASS: TestSecurityHandler_AddWAFExclusion_InvalidRuleID (0.01s) +=== RUN TestSecurityHandler_AddWAFExclusion_NegativeRuleID +--- PASS: TestSecurityHandler_AddWAFExclusion_NegativeRuleID (0.01s) +=== RUN TestSecurityHandler_AddWAFExclusion_InvalidPayload +--- PASS: TestSecurityHandler_AddWAFExclusion_InvalidPayload (0.01s) +=== RUN TestSecurityHandler_DeleteWAFExclusion_Success +--- PASS: TestSecurityHandler_DeleteWAFExclusion_Success (0.01s) +=== RUN TestSecurityHandler_DeleteWAFExclusion_WithTarget +--- PASS: TestSecurityHandler_DeleteWAFExclusion_WithTarget (0.02s) +=== RUN TestSecurityHandler_DeleteWAFExclusion_NotFound +--- PASS: TestSecurityHandler_DeleteWAFExclusion_NotFound (0.01s) +=== RUN TestSecurityHandler_DeleteWAFExclusion_NoConfig +--- PASS: TestSecurityHandler_DeleteWAFExclusion_NoConfig (0.01s) +=== RUN TestSecurityHandler_DeleteWAFExclusion_InvalidRuleID +--- PASS: TestSecurityHandler_DeleteWAFExclusion_InvalidRuleID (0.01s) +=== RUN TestSecurityHandler_DeleteWAFExclusion_ZeroRuleID +--- PASS: TestSecurityHandler_DeleteWAFExclusion_ZeroRuleID (0.01s) +=== RUN TestSecurityHandler_DeleteWAFExclusion_NegativeRuleID +--- PASS: TestSecurityHandler_DeleteWAFExclusion_NegativeRuleID (0.01s) +=== RUN TestSecurityHandler_WAFExclusion_FullWorkflow +--- PASS: TestSecurityHandler_WAFExclusion_FullWorkflow (0.01s) +=== RUN TestProxyHost_WAFDisabled_DefaultFalse +--- PASS: TestProxyHost_WAFDisabled_DefaultFalse (0.02s) +=== RUN TestProxyHost_WAFDisabled_SetTrue +--- PASS: TestProxyHost_WAFDisabled_SetTrue (0.02s) +=== RUN TestSecurityConfig_WAFParanoiaLevel_Default +--- PASS: TestSecurityConfig_WAFParanoiaLevel_Default (0.01s) +=== RUN TestSecurityConfig_WAFParanoiaLevel_CustomValue +--- PASS: TestSecurityConfig_WAFParanoiaLevel_CustomValue (0.01s) +=== RUN TestSecurityConfig_WAFExclusions_Empty +--- PASS: TestSecurityConfig_WAFExclusions_Empty (0.01s) +=== RUN TestSecurityConfig_WAFExclusions_JSONArray +--- PASS: TestSecurityConfig_WAFExclusions_JSONArray (0.01s) +=== RUN TestListProfiles +--- PASS: TestListProfiles (0.01s) +=== RUN TestGetProfile_ByID +--- PASS: TestGetProfile_ByID (0.02s) +=== RUN TestGetProfile_ByUUID +--- PASS: TestGetProfile_ByUUID (0.01s) +=== RUN TestGetProfile_NotFound + +2026/01/10 02:24:08 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:72 record not found +[0.131ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 99999 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestGetProfile_NotFound (0.01s) +=== RUN TestCreateProfile +--- PASS: TestCreateProfile (0.01s) +=== RUN TestCreateProfile_MissingName +--- PASS: TestCreateProfile_MissingName (0.01s) +=== RUN TestUpdateProfile +--- PASS: TestUpdateProfile (0.01s) +=== RUN TestUpdateProfile_CannotModifyPreset +--- PASS: TestUpdateProfile_CannotModifyPreset (0.01s) +=== RUN TestDeleteProfile +--- PASS: TestDeleteProfile (0.01s) +=== RUN TestDeleteProfile_CannotDeletePreset +--- PASS: TestDeleteProfile_CannotDeletePreset (0.02s) +=== RUN TestDeleteProfile_InUse +--- PASS: TestDeleteProfile_InUse (0.01s) +=== RUN TestGetPresets +--- PASS: TestGetPresets (0.01s) +=== RUN TestApplyPreset +--- PASS: TestApplyPreset (0.02s) +=== RUN TestApplyPreset_InvalidType +--- PASS: TestApplyPreset_InvalidType (0.01s) +=== RUN TestCalculateScore +--- PASS: TestCalculateScore (0.01s) +=== RUN TestValidateCSP_Valid +--- PASS: TestValidateCSP_Valid (0.01s) +=== RUN TestValidateCSP_Invalid +--- PASS: TestValidateCSP_Invalid (0.01s) +=== RUN TestValidateCSP_UnsafeDirectives +--- PASS: TestValidateCSP_UnsafeDirectives (0.01s) +=== RUN TestBuildCSP +--- PASS: TestBuildCSP (0.01s) +=== RUN TestListProfiles_DBError + +2026/01/10 02:24:08 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:56 sql: database is closed +[0.032ms] [rows:0] SELECT * FROM `security_header_profiles` ORDER BY is_preset DESC, name ASC +--- PASS: TestListProfiles_DBError (0.01s) +=== RUN TestGetProfile_UUID_NotFound + +2026/01/10 02:24:08 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:82 record not found +[0.115ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "non-existent-uuid-12345" ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestGetProfile_UUID_NotFound (0.01s) +=== RUN TestGetProfile_ID_DBError + +2026/01/10 02:24:08 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:72 sql: database is closed +[0.050ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 1 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestGetProfile_ID_DBError (0.01s) +=== RUN TestGetProfile_UUID_DBError + +2026/01/10 02:24:08 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:82 sql: database is closed +[0.038ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "some-uuid-format" ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestGetProfile_UUID_DBError (0.01s) +=== RUN TestCreateProfile_InvalidJSON +--- PASS: TestCreateProfile_InvalidJSON (0.01s) +=== RUN TestCreateProfile_DBError +--- PASS: TestCreateProfile_DBError (0.02s) +=== RUN TestUpdateProfile_InvalidID +--- PASS: TestUpdateProfile_InvalidID (0.01s) +=== RUN TestUpdateProfile_NotFound + +2026/01/10 02:24:08 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:136 record not found +[0.141ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 99999 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestUpdateProfile_NotFound (0.01s) +=== RUN TestUpdateProfile_InvalidJSON +--- PASS: TestUpdateProfile_InvalidJSON (0.01s) +=== RUN TestUpdateProfile_DBError + +2026/01/10 02:24:08 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:136 sql: database is closed +[0.037ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 1 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestUpdateProfile_DBError (0.02s) +=== RUN TestUpdateProfile_LookupDBError + +2026/01/10 02:24:09 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:136 sql: database is closed +[0.041ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 1 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestUpdateProfile_LookupDBError (0.01s) +=== RUN TestDeleteProfile_InvalidID +--- PASS: TestDeleteProfile_InvalidID (0.01s) +=== RUN TestDeleteProfile_NotFound + +2026/01/10 02:24:09 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:183 record not found +[0.119ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 99999 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestDeleteProfile_NotFound (0.01s) +=== RUN TestDeleteProfile_LookupDBError + +2026/01/10 02:24:09 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:183 sql: database is closed +[0.055ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 1 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestDeleteProfile_LookupDBError (0.02s) +=== RUN TestDeleteProfile_CountDBError + +2026/01/10 02:24:09 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:200 no such table: proxy_hosts +[4.018ms] [rows:0] SELECT count(*) FROM `proxy_hosts` WHERE security_header_profile_id = 1 +--- PASS: TestDeleteProfile_CountDBError (0.01s) +=== RUN TestDeleteProfile_DeleteDBError + +2026/01/10 02:24:09 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:183 sql: database is closed +[0.063ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 1 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestDeleteProfile_DeleteDBError (0.01s) +=== RUN TestApplyPreset_InvalidJSON +--- PASS: TestApplyPreset_InvalidJSON (0.01s) +=== RUN TestCalculateScore_InvalidJSON +--- PASS: TestCalculateScore_InvalidJSON (0.02s) +=== RUN TestValidateCSP_InvalidJSON +--- PASS: TestValidateCSP_InvalidJSON (0.01s) +=== RUN TestValidateCSP_EmptyCSP +--- PASS: TestValidateCSP_EmptyCSP (0.01s) +=== RUN TestValidateCSP_UnknownDirective +--- PASS: TestValidateCSP_UnknownDirective (0.01s) +=== RUN TestBuildCSP_InvalidJSON +--- PASS: TestBuildCSP_InvalidJSON (0.02s) +=== RUN TestGetProfile_UUID_DBError_NonNotFound + +2026/01/10 02:24:09 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:82 sql: database is closed +[0.042ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "550e8400-e29b-41d4-a716-446655440000" ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestGetProfile_UUID_DBError_NonNotFound (0.01s) +=== RUN TestUpdateProfile_SaveError + +2026/01/10 02:24:09 /projects/Charon/backend/internal/api/handlers/security_headers_handler.go:136 sql: database is closed +[0.039ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 1 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestUpdateProfile_SaveError (0.01s) +=== RUN TestNewSecurityNotificationHandler +=== PAUSE TestNewSecurityNotificationHandler +=== RUN TestSecurityNotificationHandler_GetSettings_Success +=== PAUSE TestSecurityNotificationHandler_GetSettings_Success +=== RUN TestSecurityNotificationHandler_GetSettings_ServiceError +=== PAUSE TestSecurityNotificationHandler_GetSettings_ServiceError +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidJSON +=== PAUSE TestSecurityNotificationHandler_UpdateSettings_InvalidJSON +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel +=== PAUSE TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF +=== PAUSE TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF +=== RUN TestSecurityNotificationHandler_UpdateSettings_PrivateIPWebhook +=== PAUSE TestSecurityNotificationHandler_UpdateSettings_PrivateIPWebhook +=== RUN TestSecurityNotificationHandler_UpdateSettings_ServiceError +=== PAUSE TestSecurityNotificationHandler_UpdateSettings_ServiceError +=== RUN TestSecurityNotificationHandler_UpdateSettings_Success +=== PAUSE TestSecurityNotificationHandler_UpdateSettings_Success +=== RUN TestSecurityNotificationHandler_UpdateSettings_EmptyWebhookURL +=== PAUSE TestSecurityNotificationHandler_UpdateSettings_EmptyWebhookURL +=== RUN TestSecurityHandler_Priority_SettingsOverSecurityConfig +=== RUN TestSecurityHandler_Priority_SettingsOverSecurityConfig/Settings_table_overrides_SecurityConfig_DB +=== RUN TestSecurityHandler_Priority_SettingsOverSecurityConfig/SecurityConfig_DB_overrides_static_config +=== RUN TestSecurityHandler_Priority_SettingsOverSecurityConfig/Static_config_used_when_no_DB_overrides +--- PASS: TestSecurityHandler_Priority_SettingsOverSecurityConfig (0.04s) + --- PASS: TestSecurityHandler_Priority_SettingsOverSecurityConfig/Settings_table_overrides_SecurityConfig_DB (0.01s) + --- PASS: TestSecurityHandler_Priority_SettingsOverSecurityConfig/SecurityConfig_DB_overrides_static_config (0.01s) + --- PASS: TestSecurityHandler_Priority_SettingsOverSecurityConfig/Static_config_used_when_no_DB_overrides (0.01s) +=== RUN TestSecurityHandler_Priority_AllModules +--- PASS: TestSecurityHandler_Priority_AllModules (0.01s) +=== RUN TestSecurityHandler_GetRateLimitPresets +--- PASS: TestSecurityHandler_GetRateLimitPresets (0.00s) +=== RUN TestSecurityHandler_GetRateLimitPresets_StandardPreset +--- PASS: TestSecurityHandler_GetRateLimitPresets_StandardPreset (0.00s) +=== RUN TestSecurityHandler_GetRateLimitPresets_LoginPreset +--- PASS: TestSecurityHandler_GetRateLimitPresets_LoginPreset (0.00s) +=== RUN TestGetClientIPHeadersAndRemoteAddr +--- PASS: TestGetClientIPHeadersAndRemoteAddr (0.00s) +=== RUN TestGetMyIPHandler +=== RUN TestGetMyIPHandler/with_CF_header +=== RUN TestGetMyIPHandler/with_X-Forwarded-For_header +=== RUN TestGetMyIPHandler/with_X-Real-IP_header +=== RUN TestGetMyIPHandler/direct_connection +--- PASS: TestGetMyIPHandler (0.00s) + --- PASS: TestGetMyIPHandler/with_CF_header (0.00s) + --- PASS: TestGetMyIPHandler/with_X-Forwarded-For_header (0.00s) + --- PASS: TestGetMyIPHandler/with_X-Real-IP_header (0.00s) + --- PASS: TestGetMyIPHandler/direct_connection (0.00s) +=== RUN TestWaitForCondition_PassesImmediately +--- PASS: TestWaitForCondition_PassesImmediately (0.00s) +=== RUN TestWaitForCondition_PassesAfterIterations +--- PASS: TestWaitForCondition_PassesAfterIterations (0.02s) +=== RUN TestWaitForConditionWithInterval_PassesImmediately +--- PASS: TestWaitForConditionWithInterval_PassesImmediately (0.00s) +=== RUN TestWaitForConditionWithInterval_CustomInterval +--- PASS: TestWaitForConditionWithInterval_CustomInterval (0.06s) +=== RUN TestWaitForCondition_Timeout +--- PASS: TestWaitForCondition_Timeout (0.03s) +=== RUN TestWaitForConditionWithInterval_Timeout +--- PASS: TestWaitForConditionWithInterval_Timeout (0.06s) +=== RUN TestWaitForCondition_ZeroTimeout +--- PASS: TestWaitForCondition_ZeroTimeout (0.00s) +=== RUN TestGetTemplateDB +--- PASS: TestGetTemplateDB (0.00s) +=== RUN TestGetTemplateDB_HasTables +--- PASS: TestGetTemplateDB_HasTables (0.00s) +=== RUN TestOpenTestDB +--- PASS: TestOpenTestDB (0.00s) +=== RUN TestOpenTestDB_Uniqueness +--- PASS: TestOpenTestDB_Uniqueness (0.00s) +=== RUN TestOpenTestDBWithMigrations +--- PASS: TestOpenTestDBWithMigrations (0.00s) +=== RUN TestOpenTestDBWithMigrations_CanInsertData +--- PASS: TestOpenTestDBWithMigrations_CanInsertData (0.02s) +=== RUN TestOpenTestDBWithMigrations_MultipleModels +--- PASS: TestOpenTestDBWithMigrations_MultipleModels (0.01s) +=== RUN TestOpenTestDBWithMigrations_FallbackPath +--- PASS: TestOpenTestDBWithMigrations_FallbackPath (0.01s) +=== RUN TestOpenTestDB_ParallelSafety +=== PAUSE TestOpenTestDB_ParallelSafety +=== RUN TestOpenTestDBWithMigrations_ParallelSafety +=== RUN TestOpenTestDBWithMigrations_ParallelSafety/parallel-migrations-0 +=== RUN TestOpenTestDBWithMigrations_ParallelSafety/parallel-migrations-1 +=== RUN TestOpenTestDBWithMigrations_ParallelSafety/parallel-migrations-2 +--- PASS: TestOpenTestDBWithMigrations_ParallelSafety (0.02s) + --- PASS: TestOpenTestDBWithMigrations_ParallelSafety/parallel-migrations-0 (0.01s) + --- PASS: TestOpenTestDBWithMigrations_ParallelSafety/parallel-migrations-1 (0.01s) + --- PASS: TestOpenTestDBWithMigrations_ParallelSafety/parallel-migrations-2 (0.01s) +=== RUN TestUpdateHandler_Check +--- PASS: TestUpdateHandler_Check (0.00s) +=== RUN TestUserHandler_GetSetupStatus_Error +--- PASS: TestUserHandler_GetSetupStatus_Error (0.02s) +=== RUN TestUserHandler_Setup_CheckStatusError +--- PASS: TestUserHandler_Setup_CheckStatusError (0.02s) +=== RUN TestUserHandler_Setup_AlreadyCompleted +--- PASS: TestUserHandler_Setup_AlreadyCompleted (0.74s) +=== RUN TestUserHandler_Setup_InvalidJSON +--- PASS: TestUserHandler_Setup_InvalidJSON (0.02s) +=== RUN TestUserHandler_RegenerateAPIKey_Unauthorized +--- PASS: TestUserHandler_RegenerateAPIKey_Unauthorized (0.02s) +=== RUN TestUserHandler_RegenerateAPIKey_DBError +--- PASS: TestUserHandler_RegenerateAPIKey_DBError (0.02s) +=== RUN TestUserHandler_GetProfile_Unauthorized +--- PASS: TestUserHandler_GetProfile_Unauthorized (0.02s) +=== RUN TestUserHandler_GetProfile_NotFound +--- PASS: TestUserHandler_GetProfile_NotFound (0.02s) +=== RUN TestUserHandler_UpdateProfile_Unauthorized +--- PASS: TestUserHandler_UpdateProfile_Unauthorized (0.01s) +=== RUN TestUserHandler_UpdateProfile_InvalidJSON +--- PASS: TestUserHandler_UpdateProfile_InvalidJSON (0.02s) +=== RUN TestUserHandler_UpdateProfile_UserNotFound +--- PASS: TestUserHandler_UpdateProfile_UserNotFound (0.02s) +=== RUN TestUserHandler_UpdateProfile_EmailConflict +--- PASS: TestUserHandler_UpdateProfile_EmailConflict (1.48s) +=== RUN TestUserHandler_UpdateProfile_EmailChangeNoPassword +--- PASS: TestUserHandler_UpdateProfile_EmailChangeNoPassword (0.75s) +=== RUN TestUserHandler_UpdateProfile_WrongPassword +--- PASS: TestUserHandler_UpdateProfile_WrongPassword (1.47s) +=== RUN TestUserHandler_GetSetupStatus +--- PASS: TestUserHandler_GetSetupStatus (0.02s) +=== RUN TestUserHandler_Setup +--- PASS: TestUserHandler_Setup (0.74s) +=== RUN TestUserHandler_Setup_DBError +--- PASS: TestUserHandler_Setup_DBError (0.00s) +=== RUN TestUserHandler_RegenerateAPIKey +--- PASS: TestUserHandler_RegenerateAPIKey (0.01s) +=== RUN TestUserHandler_GetProfile +--- PASS: TestUserHandler_GetProfile (0.02s) +=== RUN TestUserHandler_RegisterRoutes +--- PASS: TestUserHandler_RegisterRoutes (0.01s) +=== RUN TestUserHandler_Errors + +2026/01/10 02:24:14 /projects/Charon/backend/internal/api/handlers/user_handler.go:171 record not found +[0.103ms] [rows:0] SELECT * FROM `users` WHERE `users`.`id` = 99999 ORDER BY `users`.`id` LIMIT 1 + +2026/01/10 02:24:14 /projects/Charon/backend/internal/api/handlers/user_handler.go:154 no such table: users +[0.155ms] [rows:0] UPDATE `users` SET `api_key`="e55dd8a0-c3ca-4679-a77b-509d128435d1",`updated_at`="2026-01-10 02:24:14.927" WHERE id = 99999 +--- PASS: TestUserHandler_Errors (0.02s) +=== RUN TestUserHandler_UpdateProfile +=== RUN TestUserHandler_UpdateProfile/Success_Name_Only +=== RUN TestUserHandler_UpdateProfile/Success_Email_Change +=== RUN TestUserHandler_UpdateProfile/Fail_Email_Change_No_Password +=== RUN TestUserHandler_UpdateProfile/Fail_Email_Change_Wrong_Password +=== RUN TestUserHandler_UpdateProfile/Fail_Email_In_Use +--- PASS: TestUserHandler_UpdateProfile (2.36s) + --- PASS: TestUserHandler_UpdateProfile/Success_Name_Only (0.00s) + --- PASS: TestUserHandler_UpdateProfile/Success_Email_Change (0.77s) + --- PASS: TestUserHandler_UpdateProfile/Fail_Email_Change_No_Password (0.00s) + --- PASS: TestUserHandler_UpdateProfile/Fail_Email_Change_Wrong_Password (0.73s) + --- PASS: TestUserHandler_UpdateProfile/Fail_Email_In_Use (0.00s) +=== RUN TestUserHandler_UpdateProfile_Errors + +2026/01/10 02:24:17 /projects/Charon/backend/internal/api/handlers/user_handler.go:207 record not found +[0.135ms] [rows:0] SELECT * FROM `users` WHERE `users`.`id` = 999 ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_UpdateProfile_Errors (0.01s) +=== RUN TestUserHandler_ListUsers_NonAdmin +--- PASS: TestUserHandler_ListUsers_NonAdmin (0.02s) +=== RUN TestUserHandler_ListUsers_Admin +--- PASS: TestUserHandler_ListUsers_Admin (0.01s) +=== RUN TestUserHandler_CreateUser_NonAdmin +--- PASS: TestUserHandler_CreateUser_NonAdmin (0.01s) +=== RUN TestUserHandler_CreateUser_Admin +--- PASS: TestUserHandler_CreateUser_Admin (0.73s) +=== RUN TestUserHandler_CreateUser_InvalidJSON +--- PASS: TestUserHandler_CreateUser_InvalidJSON (0.01s) +=== RUN TestUserHandler_CreateUser_DuplicateEmail +--- PASS: TestUserHandler_CreateUser_DuplicateEmail (0.01s) +=== RUN TestUserHandler_CreateUser_WithPermittedHosts +--- PASS: TestUserHandler_CreateUser_WithPermittedHosts (0.73s) +=== RUN TestUserHandler_GetUser_NonAdmin +--- PASS: TestUserHandler_GetUser_NonAdmin (0.01s) +=== RUN TestUserHandler_GetUser_InvalidID +--- PASS: TestUserHandler_GetUser_InvalidID (0.02s) +=== RUN TestUserHandler_GetUser_NotFound + +2026/01/10 02:24:18 /projects/Charon/backend/internal/api/handlers/user_handler.go:571 record not found +[0.080ms] [rows:0] SELECT * FROM `users` WHERE `users`.`id` = 999 ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_GetUser_NotFound (0.02s) +=== RUN TestUserHandler_GetUser_Success +--- PASS: TestUserHandler_GetUser_Success (0.01s) +=== RUN TestUserHandler_UpdateUser_NonAdmin +--- PASS: TestUserHandler_UpdateUser_NonAdmin (0.02s) +=== RUN TestUserHandler_UpdateUser_InvalidID +--- PASS: TestUserHandler_UpdateUser_InvalidID (0.01s) +=== RUN TestUserHandler_UpdateUser_InvalidJSON +--- PASS: TestUserHandler_UpdateUser_InvalidJSON (0.01s) +=== RUN TestUserHandler_UpdateUser_NotFound + +2026/01/10 02:24:18 /projects/Charon/backend/internal/api/handlers/user_handler.go:623 record not found +[0.103ms] [rows:0] SELECT * FROM `users` WHERE `users`.`id` = 999 ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_UpdateUser_NotFound (0.02s) +=== RUN TestUserHandler_UpdateUser_Success +--- PASS: TestUserHandler_UpdateUser_Success (0.02s) +=== RUN TestUserHandler_DeleteUser_NonAdmin +--- PASS: TestUserHandler_DeleteUser_NonAdmin (0.01s) +=== RUN TestUserHandler_DeleteUser_InvalidID +--- PASS: TestUserHandler_DeleteUser_InvalidID (0.02s) +=== RUN TestUserHandler_DeleteUser_NotFound + +2026/01/10 02:24:19 /projects/Charon/backend/internal/api/handlers/user_handler.go:693 record not found +[0.096ms] [rows:0] SELECT * FROM `users` WHERE `users`.`id` = 999 ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_DeleteUser_NotFound (0.02s) +=== RUN TestUserHandler_DeleteUser_Success +--- PASS: TestUserHandler_DeleteUser_Success (0.02s) +=== RUN TestUserHandler_DeleteUser_CannotDeleteSelf +--- PASS: TestUserHandler_DeleteUser_CannotDeleteSelf (0.02s) +=== RUN TestUserHandler_UpdateUserPermissions_NonAdmin +--- PASS: TestUserHandler_UpdateUserPermissions_NonAdmin (0.01s) +=== RUN TestUserHandler_UpdateUserPermissions_InvalidID +--- PASS: TestUserHandler_UpdateUserPermissions_InvalidID (0.02s) +=== RUN TestUserHandler_UpdateUserPermissions_InvalidJSON +--- PASS: TestUserHandler_UpdateUserPermissions_InvalidJSON (0.01s) +=== RUN TestUserHandler_UpdateUserPermissions_NotFound + +2026/01/10 02:24:19 /projects/Charon/backend/internal/api/handlers/user_handler.go:734 record not found +[0.079ms] [rows:0] SELECT * FROM `users` WHERE `users`.`id` = 999 ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_UpdateUserPermissions_NotFound (0.01s) +=== RUN TestUserHandler_UpdateUserPermissions_Success +--- PASS: TestUserHandler_UpdateUserPermissions_Success (0.02s) +=== RUN TestUserHandler_ValidateInvite_MissingToken +--- PASS: TestUserHandler_ValidateInvite_MissingToken (0.01s) +=== RUN TestUserHandler_ValidateInvite_InvalidToken + +2026/01/10 02:24:19 /projects/Charon/backend/internal/api/handlers/user_handler.go:783 record not found +[0.091ms] [rows:0] SELECT * FROM `users` WHERE invite_token = "invalidtoken" ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_ValidateInvite_InvalidToken (0.01s) +=== RUN TestUserHandler_ValidateInvite_ExpiredToken +--- PASS: TestUserHandler_ValidateInvite_ExpiredToken (0.02s) +=== RUN TestUserHandler_ValidateInvite_AlreadyAccepted +--- PASS: TestUserHandler_ValidateInvite_AlreadyAccepted (0.01s) +=== RUN TestUserHandler_ValidateInvite_Success +--- PASS: TestUserHandler_ValidateInvite_Success (0.02s) +=== RUN TestUserHandler_AcceptInvite_InvalidJSON +--- PASS: TestUserHandler_AcceptInvite_InvalidJSON (0.02s) +=== RUN TestUserHandler_AcceptInvite_InvalidToken + +2026/01/10 02:24:19 /projects/Charon/backend/internal/api/handlers/user_handler.go:822 record not found +[0.115ms] [rows:0] SELECT * FROM `users` WHERE invite_token = "invalidtoken" ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_AcceptInvite_InvalidToken (0.02s) +=== RUN TestUserHandler_AcceptInvite_Success +--- PASS: TestUserHandler_AcceptInvite_Success (0.78s) +=== RUN TestGenerateSecureToken +--- PASS: TestGenerateSecureToken (0.00s) +=== RUN TestUserHandler_InviteUser_NonAdmin +--- PASS: TestUserHandler_InviteUser_NonAdmin (0.02s) +=== RUN TestUserHandler_InviteUser_InvalidJSON +--- PASS: TestUserHandler_InviteUser_InvalidJSON (0.02s) +=== RUN TestUserHandler_InviteUser_DuplicateEmail +--- PASS: TestUserHandler_InviteUser_DuplicateEmail (0.02s) +=== RUN TestUserHandler_InviteUser_Success + +2026/01/10 02:24:20 /projects/Charon/backend/internal/api/handlers/user_handler.go:422 record not found +[0.088ms] [rows:0] SELECT * FROM `users` WHERE email = "newinvite@example.com" ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_InviteUser_Success (0.02s) +=== RUN TestUserHandler_InviteUser_WithPermittedHosts + +2026/01/10 02:24:20 /projects/Charon/backend/internal/api/handlers/user_handler.go:422 record not found +[0.098ms] [rows:0] SELECT * FROM `users` WHERE email = "invitee-perms@example.com" ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_InviteUser_WithPermittedHosts (0.02s) +=== RUN TestUserHandler_InviteUser_WithSMTPConfigured + +2026/01/10 02:24:20 /projects/Charon/backend/internal/api/handlers/user_handler.go:422 record not found +[0.107ms] [rows:0] SELECT * FROM `users` WHERE email = "smtp-test@example.com" ORDER BY `users`.`id` LIMIT 1 + +2026/01/10 02:24:20 /projects/Charon/backend/internal/utils/url.go:17 record not found +[0.081ms] [rows:0] SELECT * FROM `settings` WHERE key = "app.public_url" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestUserHandler_InviteUser_WithSMTPConfigured (0.03s) +=== RUN TestUserHandler_InviteUser_WithSMTPConfigured_DefaultAppName + +2026/01/10 02:24:20 /projects/Charon/backend/internal/api/handlers/user_handler.go:422 record not found +[0.117ms] [rows:0] SELECT * FROM `users` WHERE email = "smtp-test-default@example.com" ORDER BY `users`.`id` LIMIT 1 + +2026/01/10 02:24:20 /projects/Charon/backend/internal/utils/url.go:17 record not found +[0.087ms] [rows:0] SELECT * FROM `settings` WHERE key = "app.public_url" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:20 /projects/Charon/backend/internal/api/handlers/user_handler.go:549 record not found +[0.075ms] [rows:0] SELECT * FROM `settings` WHERE key = "app_name" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestUserHandler_InviteUser_WithSMTPConfigured_DefaultAppName (0.02s) +=== RUN TestUserHandler_AcceptInvite_ExpiredToken +--- PASS: TestUserHandler_AcceptInvite_ExpiredToken (0.02s) +=== RUN TestUserHandler_AcceptInvite_AlreadyAccepted +--- PASS: TestUserHandler_AcceptInvite_AlreadyAccepted (0.02s) +=== RUN TestUserHandler_PreviewInviteURL_NonAdmin +--- PASS: TestUserHandler_PreviewInviteURL_NonAdmin (0.01s) +=== RUN TestUserHandler_PreviewInviteURL_InvalidJSON +--- PASS: TestUserHandler_PreviewInviteURL_InvalidJSON (0.01s) +=== RUN TestUserHandler_PreviewInviteURL_Success_Unconfigured + +2026/01/10 02:24:20 /projects/Charon/backend/internal/utils/url.go:17 record not found +[0.102ms] [rows:0] SELECT * FROM `settings` WHERE key = "app.public_url" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:20 /projects/Charon/backend/internal/api/handlers/user_handler.go:529 record not found +[0.069ms] [rows:0] SELECT * FROM `settings` WHERE key = "app.public_url" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestUserHandler_PreviewInviteURL_Success_Unconfigured (0.02s) +=== RUN TestUserHandler_PreviewInviteURL_Success_Configured +--- PASS: TestUserHandler_PreviewInviteURL_Success_Configured (0.02s) +=== RUN TestGetAppName_Default + +2026/01/10 02:24:20 /projects/Charon/backend/internal/api/handlers/user_handler.go:549 record not found +[0.093ms] [rows:0] SELECT * FROM `settings` WHERE key = "app_name" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestGetAppName_Default (0.01s) +=== RUN TestGetAppName_FromSettings +--- PASS: TestGetAppName_FromSettings (0.02s) +=== RUN TestGetAppName_EmptyValue +--- PASS: TestGetAppName_EmptyValue (0.01s) +=== RUN TestUserHandler_UpdateUser_EmailConflict +--- PASS: TestUserHandler_UpdateUser_EmailConflict (0.02s) +=== RUN TestUserHandler_CreateUser_EmailNormalization +--- PASS: TestUserHandler_CreateUser_EmailNormalization (0.74s) +=== RUN TestUserHandler_InviteUser_EmailNormalization + +2026/01/10 02:24:21 /projects/Charon/backend/internal/api/handlers/user_handler.go:422 record not found +[0.102ms] [rows:0] SELECT * FROM `users` WHERE email = "invite@example.com" ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_InviteUser_EmailNormalization (0.02s) +=== RUN TestUserHandler_CreateUser_DefaultPermissionMode +--- PASS: TestUserHandler_CreateUser_DefaultPermissionMode (0.76s) +=== RUN TestUserHandler_InviteUser_DefaultPermissionMode + +2026/01/10 02:24:21 /projects/Charon/backend/internal/api/handlers/user_handler.go:422 record not found +[0.081ms] [rows:0] SELECT * FROM `users` WHERE email = "defaultinvite@example.com" ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_InviteUser_DefaultPermissionMode (0.02s) +=== RUN TestUserHandler_CreateUser_DefaultRole +--- PASS: TestUserHandler_CreateUser_DefaultRole (0.81s) +=== RUN TestUserHandler_InviteUser_DefaultRole + +2026/01/10 02:24:22 /projects/Charon/backend/internal/api/handlers/user_handler.go:422 record not found +[0.104ms] [rows:0] SELECT * FROM `users` WHERE email = "defaultroleinvite@example.com" ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestUserHandler_InviteUser_DefaultRole (0.02s) +=== RUN TestUserHandler_CreateUser_EmptyPermittedHosts +--- PASS: TestUserHandler_CreateUser_EmptyPermittedHosts (0.75s) +=== RUN TestUserHandler_CreateUser_NonExistentPermittedHosts +--- PASS: TestUserHandler_CreateUser_NonExistentPermittedHosts (0.71s) +=== RUN TestUserLoginAfterEmailChange +--- PASS: TestUserLoginAfterEmailChange (3.64s) +=== RUN TestWebSocketStatusHandler_GetConnections +--- PASS: TestWebSocketStatusHandler_GetConnections (0.00s) +=== RUN TestWebSocketStatusHandler_GetConnectionsEmpty +--- PASS: TestWebSocketStatusHandler_GetConnectionsEmpty (0.00s) +=== RUN TestWebSocketStatusHandler_GetStats +--- PASS: TestWebSocketStatusHandler_GetStats (0.00s) +=== RUN TestWebSocketStatusHandler_GetStatsEmpty +--- PASS: TestWebSocketStatusHandler_GetStatsEmpty (0.00s) +=== RUN TestCredentialHandler_Create +--- PASS: TestCredentialHandler_Create (0.01s) +=== RUN TestCredentialHandler_Create_InvalidProviderID +--- PASS: TestCredentialHandler_Create_InvalidProviderID (0.01s) +=== RUN TestCredentialHandler_List +--- PASS: TestCredentialHandler_List (0.06s) +=== RUN TestCredentialHandler_Get +--- PASS: TestCredentialHandler_Get (0.01s) +=== RUN TestCredentialHandler_Get_NotFound + +2026/01/10 02:24:27 /projects/Charon/backend/internal/services/credential_service.go:111 record not found +[0.111ms] [rows:0] SELECT * FROM `dns_provider_credentials` WHERE id = 9999 AND dns_provider_id = 1 ORDER BY `dns_provider_credentials`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_Get_NotFound (0.01s) +=== RUN TestCredentialHandler_Update +--- PASS: TestCredentialHandler_Update (0.02s) +=== RUN TestCredentialHandler_Delete + +2026/01/10 02:24:27 /projects/Charon/backend/internal/services/credential_service.go:349 database table is locked +[0.198ms] [rows:0] DELETE FROM `dns_provider_credentials` WHERE `dns_provider_credentials`.`id` = 1 + credential_handler_test.go:271: + Error Trace: /projects/Charon/backend/internal/api/handlers/credential_handler_test.go:271 + Error: Not equal: + expected: 204 + actual : 500 + Test: TestCredentialHandler_Delete + credential_handler_test.go:275: + Error Trace: /projects/Charon/backend/internal/api/handlers/credential_handler_test.go:275 + Error: Expected error with "credential not found" in chain but got nil. + Test: TestCredentialHandler_Delete +--- FAIL: TestCredentialHandler_Delete (0.01s) +=== RUN TestCredentialHandler_Test + +2026/01/10 02:24:27 /projects/Charon/backend/internal/services/credential_service.go:431 database table is locked +[0.284ms] [rows:0] UPDATE `dns_provider_credentials` SET `uuid`="f40f619f-7684-4790-b783-ac5e44294763",`dns_provider_id`=1,`label`="Test",`zone_filter`="",`enabled`=true,`credentials_encrypted`="NBrnkSpfbFdR/MjU6p0DHJ+vnHw83TDcFg9uM543VBlnMlK1Sdw/IvMOMEVHGmAumg==",`key_version`=1,`propagation_timeout`=120,`polling_interval`=5,`last_used_at`=NULL,`success_count`=1,`failure_count`=0,`last_error`="",`created_at`="2026-01-10 02:24:27.934",`updated_at`="2026-01-10 02:24:27.935" WHERE `id` = 1 +--- PASS: TestCredentialHandler_Test (0.01s) +=== RUN TestCredentialHandler_EnableMultiCredentials +--- PASS: TestCredentialHandler_EnableMultiCredentials (0.01s) +=== RUN TestCredentialHandler_List_InvalidProviderID +--- PASS: TestCredentialHandler_List_InvalidProviderID (0.01s) +=== RUN TestCredentialHandler_List_ProviderNotFound + +2026/01/10 02:24:27 /projects/Charon/backend/internal/services/credential_service.go:86 record not found +[0.095ms] [rows:0] SELECT * FROM `dns_providers` WHERE `dns_providers`.`id` = 9999 ORDER BY `dns_providers`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_List_ProviderNotFound (0.01s) +=== RUN TestCredentialHandler_List_MultiCredentialNotEnabled +--- PASS: TestCredentialHandler_List_MultiCredentialNotEnabled (0.00s) +=== RUN TestCredentialHandler_Create_ProviderNotFound + +2026/01/10 02:24:27 /projects/Charon/backend/internal/services/credential_service.go:127 record not found +[0.098ms] [rows:0] SELECT * FROM `dns_providers` WHERE `dns_providers`.`id` = 9999 ORDER BY `dns_providers`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_Create_ProviderNotFound (0.01s) +=== RUN TestCredentialHandler_Create_MultiCredentialNotEnabled +--- PASS: TestCredentialHandler_Create_MultiCredentialNotEnabled (0.01s) +=== RUN TestCredentialHandler_Create_InvalidJSON +--- PASS: TestCredentialHandler_Create_InvalidJSON (0.00s) +=== RUN TestCredentialHandler_Create_MissingRequiredFields +--- PASS: TestCredentialHandler_Create_MissingRequiredFields (0.01s) +=== RUN TestCredentialHandler_Create_InvalidProviderType +--- PASS: TestCredentialHandler_Create_InvalidProviderType (0.01s) +=== RUN TestCredentialHandler_Get_InvalidProviderID +--- PASS: TestCredentialHandler_Get_InvalidProviderID (0.01s) +=== RUN TestCredentialHandler_Get_InvalidCredentialID +--- PASS: TestCredentialHandler_Get_InvalidCredentialID (0.00s) +=== RUN TestCredentialHandler_Update_InvalidProviderID +--- PASS: TestCredentialHandler_Update_InvalidProviderID (0.01s) +=== RUN TestCredentialHandler_Update_InvalidCredentialID +--- PASS: TestCredentialHandler_Update_InvalidCredentialID (0.01s) +=== RUN TestCredentialHandler_Update_NotFound + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/credential_service.go:111 record not found +[0.103ms] [rows:0] SELECT * FROM `dns_provider_credentials` WHERE id = 9999 AND dns_provider_id = 1 ORDER BY `dns_provider_credentials`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_Update_NotFound (0.01s) +=== RUN TestCredentialHandler_Update_InvalidJSON +--- PASS: TestCredentialHandler_Update_InvalidJSON (0.01s) +=== RUN TestCredentialHandler_Delete_InvalidProviderID +--- PASS: TestCredentialHandler_Delete_InvalidProviderID (0.00s) +=== RUN TestCredentialHandler_Delete_InvalidCredentialID +--- PASS: TestCredentialHandler_Delete_InvalidCredentialID (0.00s) +=== RUN TestCredentialHandler_Delete_NotFound + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/credential_service.go:111 record not found +[0.136ms] [rows:0] SELECT * FROM `dns_provider_credentials` WHERE id = 9999 AND dns_provider_id = 1 ORDER BY `dns_provider_credentials`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_Delete_NotFound (0.00s) +=== RUN TestCredentialHandler_Test_InvalidProviderID +--- PASS: TestCredentialHandler_Test_InvalidProviderID (0.00s) +=== RUN TestCredentialHandler_Test_InvalidCredentialID +--- PASS: TestCredentialHandler_Test_InvalidCredentialID (0.00s) +=== RUN TestCredentialHandler_Test_NotFound + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/credential_service.go:111 record not found +[0.094ms] [rows:0] SELECT * FROM `dns_provider_credentials` WHERE id = 9999 AND dns_provider_id = 1 ORDER BY `dns_provider_credentials`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_Test_NotFound (0.01s) +=== RUN TestCredentialHandler_EnableMultiCredentials_InvalidProviderID +--- PASS: TestCredentialHandler_EnableMultiCredentials_InvalidProviderID (0.00s) +=== RUN TestCredentialHandler_EnableMultiCredentials_ProviderNotFound + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/credential_service.go:555 record not found +[0.115ms] [rows:0] SELECT * FROM `dns_providers` WHERE `dns_providers`.`id` = 9999 ORDER BY `dns_providers`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_EnableMultiCredentials_ProviderNotFound (0.01s) +=== RUN TestCredentialHandler_Create_EncryptionError +--- PASS: TestCredentialHandler_Create_EncryptionError (0.01s) +=== RUN TestCredentialHandler_Update_EncryptionError +--- PASS: TestCredentialHandler_Update_EncryptionError (0.02s) +=== RUN TestCredentialHandler_Update_InvalidProviderType +--- PASS: TestCredentialHandler_Update_InvalidProviderType (0.02s) +=== RUN TestCredentialHandler_Update_InvalidCredentials +--- PASS: TestCredentialHandler_Update_InvalidCredentials (0.02s) +=== RUN TestCredentialHandler_Create_EmptyLabel +--- PASS: TestCredentialHandler_Create_EmptyLabel (0.01s) +=== RUN TestCredentialHandler_Update_WithZoneFilter +--- PASS: TestCredentialHandler_Update_WithZoneFilter (0.02s) +=== RUN TestCredentialHandler_Delete_ProviderNotFound + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/credential_service.go:111 record not found +[0.109ms] [rows:0] SELECT * FROM `dns_provider_credentials` WHERE id = 1 AND dns_provider_id = 9999 ORDER BY `dns_provider_credentials`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_Delete_ProviderNotFound (0.01s) +=== RUN TestCredentialHandler_Test_ProviderNotFound + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/credential_service.go:111 record not found +[0.088ms] [rows:0] SELECT * FROM `dns_provider_credentials` WHERE id = 1 AND dns_provider_id = 9999 ORDER BY `dns_provider_credentials`.`id` LIMIT 1 +--- PASS: TestCredentialHandler_Test_ProviderNotFound (0.01s) +=== RUN TestRemoteServerHandler_List +=== PAUSE TestRemoteServerHandler_List +=== RUN TestRemoteServerHandler_Create +=== PAUSE TestRemoteServerHandler_Create +=== RUN TestRemoteServerHandler_TestConnection +=== PAUSE TestRemoteServerHandler_TestConnection +=== RUN TestRemoteServerHandler_Get +=== PAUSE TestRemoteServerHandler_Get +=== RUN TestRemoteServerHandler_Update +=== PAUSE TestRemoteServerHandler_Update +=== RUN TestRemoteServerHandler_Delete +=== PAUSE TestRemoteServerHandler_Delete +=== RUN TestProxyHostHandler_List +=== PAUSE TestProxyHostHandler_List +=== RUN TestProxyHostHandler_Create +=== PAUSE TestProxyHostHandler_Create +=== RUN TestProxyHostHandler_PartialUpdate_DoesNotWipeFields +=== PAUSE TestProxyHostHandler_PartialUpdate_DoesNotWipeFields +=== RUN TestHealthHandler +=== PAUSE TestHealthHandler +=== RUN TestRemoteServerHandler_Errors +=== PAUSE TestRemoteServerHandler_Errors +=== RUN TestImportHandler_GetStatus + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:60 record not found +[0.180ms] [rows:0] SELECT * FROM `import_sessions` WHERE status IN ("pending","reviewing") ORDER BY created_at DESC,`import_sessions`.`id` LIMIT 1 + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:60 record not found +[0.101ms] [rows:0] SELECT * FROM `import_sessions` WHERE status IN ("pending","reviewing") ORDER BY created_at DESC,`import_sessions`.`id` LIMIT 1 + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:70 record not found +[0.077ms] [rows:0] SELECT * FROM `import_sessions` WHERE source_file = "/tmp/TestImportHandler_GetStatus2696916650/001/mounted.caddyfile" AND status = "committed" ORDER BY committed_at DESC,`import_sessions`.`id` LIMIT 1 +--- PASS: TestImportHandler_GetStatus (0.02s) +=== RUN TestImportHandler_GetPreview + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:122 record not found +[0.193ms] [rows:0] SELECT * FROM `import_sessions` WHERE status IN ("pending","reviewing") ORDER BY created_at DESC,`import_sessions`.`id` LIMIT 1 +--- PASS: TestImportHandler_GetPreview (0.02s) +=== RUN TestImportHandler_Cancel +--- PASS: TestImportHandler_Cancel (0.01s) +=== RUN TestImportHandler_Commit +--- PASS: TestImportHandler_Commit (0.01s) +=== RUN TestImportHandler_Upload +--- PASS: TestImportHandler_Upload (0.02s) +=== RUN TestImportHandler_GetPreview_WithContent +--- PASS: TestImportHandler_GetPreview_WithContent (0.02s) +=== RUN TestImportHandler_Commit_Errors + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:583 record not found +[0.080ms] [rows:0] SELECT * FROM `import_sessions` WHERE uuid = "non-existent" AND status = "reviewing" ORDER BY `import_sessions`.`id` LIMIT 1 +--- PASS: TestImportHandler_Commit_Errors (0.01s) +=== RUN TestImportHandler_Cancel_Errors + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:734 record not found +[0.123ms] [rows:0] SELECT * FROM `import_sessions` WHERE uuid = "non-existent" ORDER BY `import_sessions`.`id` LIMIT 1 +--- PASS: TestImportHandler_Cancel_Errors (0.01s) +=== RUN TestCheckMountedImport +--- PASS: TestCheckMountedImport (0.01s) +=== RUN TestImportHandler_Upload_Failure +--- PASS: TestImportHandler_Upload_Failure (0.02s) +=== RUN TestImportHandler_Upload_Conflict +--- PASS: TestImportHandler_Upload_Conflict (0.02s) +=== RUN TestImportHandler_GetPreview_BackupContent +--- PASS: TestImportHandler_GetPreview_BackupContent (0.02s) +=== RUN TestImportHandler_RegisterRoutes + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:60 record not found +[0.152ms] [rows:0] SELECT * FROM `import_sessions` WHERE status IN ("pending","reviewing") ORDER BY created_at DESC,`import_sessions`.`id` LIMIT 1 +--- PASS: TestImportHandler_RegisterRoutes (0.02s) +=== RUN TestImportHandler_GetPreview_TransientMount + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:122 record not found +[0.162ms] [rows:0] SELECT * FROM `import_sessions` WHERE status IN ("pending","reviewing") ORDER BY created_at DESC,`import_sessions`.`id` LIMIT 1 + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:167 record not found +[0.074ms] [rows:0] SELECT * FROM `import_sessions` WHERE source_file = "/tmp/TestImportHandler_GetPreview_TransientMount3200069616/001/mounted.caddyfile" AND status = "committed" ORDER BY committed_at DESC,`import_sessions`.`id` LIMIT 1 +--- PASS: TestImportHandler_GetPreview_TransientMount (0.02s) +=== RUN TestImportHandler_Commit_TransientUpload + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:583 record not found +[0.101ms] [rows:0] SELECT * FROM `import_sessions` WHERE uuid = "07b56185-d75c-46c0-8e2d-ece5943f0c14" AND status = "reviewing" ORDER BY `import_sessions`.`id` LIMIT 1 +--- PASS: TestImportHandler_Commit_TransientUpload (0.02s) +=== RUN TestImportHandler_Commit_TransientMount + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:583 record not found +[0.112ms] [rows:0] SELECT * FROM `import_sessions` WHERE uuid = "7b3daafe-be1a-4222-a08f-4b5b10b631e8" AND status = "reviewing" ORDER BY `import_sessions`.`id` LIMIT 1 +--- PASS: TestImportHandler_Commit_TransientMount (0.02s) +=== RUN TestImportHandler_Cancel_TransientUpload + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:734 record not found +[0.108ms] [rows:0] SELECT * FROM `import_sessions` WHERE uuid = "49cca609-8bee-4ea1-bb1f-0381031e0894" ORDER BY `import_sessions`.`id` LIMIT 1 +--- PASS: TestImportHandler_Cancel_TransientUpload (0.03s) +=== RUN TestImportHandler_Errors + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:583 record not found +[0.156ms] [rows:0] SELECT * FROM `import_sessions` WHERE uuid = "non-existent" AND status = "reviewing" ORDER BY `import_sessions`.`id` LIMIT 1 + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/import_handler.go:734 record not found +[0.063ms] [rows:0] SELECT * FROM `import_sessions` WHERE uuid = "non-existent" ORDER BY `import_sessions`.`id` LIMIT 1 +--- PASS: TestImportHandler_Errors (0.01s) +=== RUN TestImportHandler_DetectImports +=== RUN TestImportHandler_DetectImports/no_imports +=== RUN TestImportHandler_DetectImports/single_import +=== RUN TestImportHandler_DetectImports/multiple_imports +=== RUN TestImportHandler_DetectImports/import_with_comment +--- PASS: TestImportHandler_DetectImports (0.02s) + --- PASS: TestImportHandler_DetectImports/no_imports (0.00s) + --- PASS: TestImportHandler_DetectImports/single_import (0.00s) + --- PASS: TestImportHandler_DetectImports/multiple_imports (0.00s) + --- PASS: TestImportHandler_DetectImports/import_with_comment (0.00s) +=== RUN TestImportHandler_DetectImports_InvalidJSON +--- PASS: TestImportHandler_DetectImports_InvalidJSON (0.01s) +=== RUN TestImportHandler_UploadMulti +=== RUN TestImportHandler_UploadMulti/single_Caddyfile +=== RUN TestImportHandler_UploadMulti/Caddyfile_with_site_files +=== RUN TestImportHandler_UploadMulti/missing_Caddyfile +=== RUN TestImportHandler_UploadMulti/path_traversal_in_filename +=== RUN TestImportHandler_UploadMulti/empty_file_content +--- PASS: TestImportHandler_UploadMulti (0.03s) + --- PASS: TestImportHandler_UploadMulti/single_Caddyfile (0.00s) + --- PASS: TestImportHandler_UploadMulti/Caddyfile_with_site_files (0.00s) + --- PASS: TestImportHandler_UploadMulti/missing_Caddyfile (0.00s) + --- PASS: TestImportHandler_UploadMulti/path_traversal_in_filename (0.00s) + --- PASS: TestImportHandler_UploadMulti/empty_file_content (0.00s) +=== RUN TestNotificationHandler_List +--- PASS: TestNotificationHandler_List (0.01s) +=== RUN TestNotificationHandler_MarkAsRead +--- PASS: TestNotificationHandler_MarkAsRead (0.01s) +=== RUN TestNotificationHandler_MarkAllAsRead +--- PASS: TestNotificationHandler_MarkAllAsRead (0.01s) +=== RUN TestNotificationHandler_MarkAllAsRead_Error +--- PASS: TestNotificationHandler_MarkAllAsRead_Error (0.01s) +=== RUN TestNotificationHandler_DBError +--- PASS: TestNotificationHandler_DBError (0.01s) +=== RUN TestNotificationProviderHandler_CRUD +[GIN] 2026/01/10 - 02:24:28 | 201 | 455.583µs | | POST "/api/v1/notifications/providers" +[GIN] 2026/01/10 - 02:24:28 | 200 | 237.03µs | | GET "/api/v1/notifications/providers" +[GIN] 2026/01/10 - 02:24:28 | 200 | 395.081µs | | PUT "/api/v1/notifications/providers/19f9ccc0-a4f6-421b-9541-c556bdee06d9" +[GIN] 2026/01/10 - 02:24:28 | 200 | 153.631µs | | DELETE "/api/v1/notifications/providers/19f9ccc0-a4f6-421b-9541-c556bdee06d9" +--- PASS: TestNotificationProviderHandler_CRUD (0.01s) +=== RUN TestNotificationProviderHandler_Templates +[GIN] 2026/01/10 - 02:24:28 | 200 | 84.391µs | | GET "/api/v1/notifications/templates" +--- PASS: TestNotificationProviderHandler_Templates (0.01s) +=== RUN TestNotificationProviderHandler_Test +[GIN] 2026/01/10 - 02:24:28 | 400 | 535.722µs | | POST "/api/v1/notifications/providers/test" +--- PASS: TestNotificationProviderHandler_Test (0.00s) +=== RUN TestNotificationProviderHandler_Errors +[GIN] 2026/01/10 - 02:24:28 | 400 | 38.21µs | | POST "/api/v1/notifications/providers" +[GIN] 2026/01/10 - 02:24:28 | 400 | 20.67µs | | PUT "/api/v1/notifications/providers/123" +[GIN] 2026/01/10 - 02:24:28 | 400 | 19.43µs | | POST "/api/v1/notifications/providers/test" +--- PASS: TestNotificationProviderHandler_Errors (0.00s) +=== RUN TestNotificationProviderHandler_InvalidCustomTemplate_Rejects +[GIN] 2026/01/10 - 02:24:28 | 400 | 203.791µs | | POST "/api/v1/notifications/providers" +[GIN] 2026/01/10 - 02:24:28 | 201 | 416.732µs | | POST "/api/v1/notifications/providers" +[GIN] 2026/01/10 - 02:24:28 | 400 | 137.501µs | | PUT "/api/v1/notifications/providers/c3d48686-6348-4440-8417-eaec4cd16093" +--- PASS: TestNotificationProviderHandler_InvalidCustomTemplate_Rejects (0.01s) +=== RUN TestNotificationProviderHandler_Preview +[GIN] 2026/01/10 - 02:24:28 | 200 | 415.902µs | | POST "/api/v1/notifications/providers/preview" +[GIN] 2026/01/10 - 02:24:28 | 400 | 248.891µs | | POST "/api/v1/notifications/providers/preview" +--- PASS: TestNotificationProviderHandler_Preview (0.00s) +=== RUN TestRemoteServerHandler_TestConnectionCustom +[GIN] 2026/01/10 - 02:24:28 | 200 | 1.952616ms | | POST "/api/v1/remote-servers/test" +--- PASS: TestRemoteServerHandler_TestConnectionCustom (0.03s) +=== RUN TestRemoteServerHandler_FullCRUD +[GIN] 2026/01/10 - 02:24:28 | 201 | 1.055584ms | | POST "/api/v1/remote-servers" +[GIN] 2026/01/10 - 02:24:28 | 200 | 274.871µs | | GET "/api/v1/remote-servers" +[GIN] 2026/01/10 - 02:24:28 | 200 | 233.741µs | | GET "/api/v1/remote-servers/5a619327-d8de-4d58-917a-f6d159264bbc" +[GIN] 2026/01/10 - 02:24:28 | 200 | 675.653µs | | PUT "/api/v1/remote-servers/5a619327-d8de-4d58-917a-f6d159264bbc" +[GIN] 2026/01/10 - 02:24:28 | 204 | 504.062µs | | DELETE "/api/v1/remote-servers/5a619327-d8de-4d58-917a-f6d159264bbc" +[GIN] 2026/01/10 - 02:24:28 | 400 | 32.881µs | | POST "/api/v1/remote-servers" +[GIN] 2026/01/10 - 02:24:28 | 404 | 131.18µs | | PUT "/api/v1/remote-servers/non-existent-uuid" +[GIN] 2026/01/10 - 02:24:28 | 404 | 113.17µs | | DELETE "/api/v1/remote-servers/non-existent-uuid" +--- PASS: TestRemoteServerHandler_FullCRUD (0.03s) +=== RUN TestSettingsHandler_GetSettings +--- PASS: TestSettingsHandler_GetSettings (0.00s) +=== RUN TestSettingsHandler_GetSettings_DatabaseError + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/settings_handler.go:30 sql: database is closed +[0.026ms] [rows:0] SELECT * FROM `settings` +--- PASS: TestSettingsHandler_GetSettings_DatabaseError (0.00s) +=== RUN TestSettingsHandler_UpdateSettings +--- PASS: TestSettingsHandler_UpdateSettings (0.00s) +=== RUN TestSettingsHandler_UpdateSetting_DatabaseError + +2026/01/10 02:24:28 /projects/Charon/backend/internal/api/handlers/settings_handler.go:72 sql: database is closed +[0.026ms] [rows:0] SELECT * FROM `settings` WHERE `settings`.`key` = "test_key" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestSettingsHandler_UpdateSetting_DatabaseError (0.00s) +=== RUN TestSettingsHandler_Errors +--- PASS: TestSettingsHandler_Errors (0.00s) +=== RUN TestSettingsHandler_GetSMTPConfig +--- PASS: TestSettingsHandler_GetSMTPConfig (0.00s) +=== RUN TestSettingsHandler_GetSMTPConfig_Empty +--- PASS: TestSettingsHandler_GetSMTPConfig_Empty (0.01s) +=== RUN TestSettingsHandler_GetSMTPConfig_DatabaseError + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/mail_service.go:91 sql: database is closed +[0.019ms] [rows:0] SELECT * FROM `settings` WHERE category = "smtp" +--- PASS: TestSettingsHandler_GetSMTPConfig_DatabaseError (0.00s) +=== RUN TestSettingsHandler_UpdateSMTPConfig_NonAdmin +--- PASS: TestSettingsHandler_UpdateSMTPConfig_NonAdmin (0.00s) +=== RUN TestSettingsHandler_UpdateSMTPConfig_InvalidJSON +--- PASS: TestSettingsHandler_UpdateSMTPConfig_InvalidJSON (0.00s) +=== RUN TestSettingsHandler_UpdateSMTPConfig_Success + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/mail_service.go:142 record not found +[0.047ms] [rows:0] SELECT * FROM `settings` WHERE key = "smtp_host" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/mail_service.go:142 record not found +[0.078ms] [rows:0] SELECT * FROM `settings` WHERE key = "smtp_port" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/mail_service.go:142 record not found +[0.104ms] [rows:0] SELECT * FROM `settings` WHERE key = "smtp_username" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/mail_service.go:142 record not found +[0.069ms] [rows:0] SELECT * FROM `settings` WHERE key = "smtp_password" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/mail_service.go:142 record not found +[0.054ms] [rows:0] SELECT * FROM `settings` WHERE key = "smtp_from_address" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/mail_service.go:142 record not found +[0.080ms] [rows:0] SELECT * FROM `settings` WHERE key = "smtp_encryption" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestSettingsHandler_UpdateSMTPConfig_Success (0.00s) +=== RUN TestSettingsHandler_UpdateSMTPConfig_KeepExistingPassword + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/mail_service.go:142 record not found +[0.066ms] [rows:0] SELECT * FROM `settings` WHERE key = "smtp_username" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestSettingsHandler_UpdateSMTPConfig_KeepExistingPassword (0.00s) +=== RUN TestSettingsHandler_TestSMTPConfig_NonAdmin +--- PASS: TestSettingsHandler_TestSMTPConfig_NonAdmin (0.00s) +=== RUN TestSettingsHandler_TestSMTPConfig_NotConfigured +--- PASS: TestSettingsHandler_TestSMTPConfig_NotConfigured (0.00s) +=== RUN TestSettingsHandler_TestSMTPConfig_Success +--- PASS: TestSettingsHandler_TestSMTPConfig_Success (0.00s) +=== RUN TestSettingsHandler_SendTestEmail_NonAdmin +--- PASS: TestSettingsHandler_SendTestEmail_NonAdmin (0.00s) +=== RUN TestSettingsHandler_SendTestEmail_InvalidJSON +--- PASS: TestSettingsHandler_SendTestEmail_InvalidJSON (0.00s) +=== RUN TestSettingsHandler_SendTestEmail_NotConfigured +--- PASS: TestSettingsHandler_SendTestEmail_NotConfigured (0.00s) +=== RUN TestSettingsHandler_SendTestEmail_Success +--- PASS: TestSettingsHandler_SendTestEmail_Success (0.00s) +=== RUN TestMaskPassword +--- PASS: TestMaskPassword (0.00s) +=== RUN TestSettingsHandler_ValidatePublicURL_NonAdmin +--- PASS: TestSettingsHandler_ValidatePublicURL_NonAdmin (0.00s) +=== RUN TestSettingsHandler_ValidatePublicURL_InvalidFormat +=== RUN TestSettingsHandler_ValidatePublicURL_InvalidFormat/Missing_scheme +=== RUN TestSettingsHandler_ValidatePublicURL_InvalidFormat/Invalid_scheme +=== RUN TestSettingsHandler_ValidatePublicURL_InvalidFormat/URL_with_path +--- PASS: TestSettingsHandler_ValidatePublicURL_InvalidFormat (0.00s) + --- PASS: TestSettingsHandler_ValidatePublicURL_InvalidFormat/Missing_scheme (0.00s) + --- PASS: TestSettingsHandler_ValidatePublicURL_InvalidFormat/Invalid_scheme (0.00s) + --- PASS: TestSettingsHandler_ValidatePublicURL_InvalidFormat/URL_with_path (0.00s) +=== RUN TestSettingsHandler_ValidatePublicURL_Success +=== RUN TestSettingsHandler_ValidatePublicURL_Success/HTTPS_URL +=== RUN TestSettingsHandler_ValidatePublicURL_Success/HTTP_URL +=== RUN TestSettingsHandler_ValidatePublicURL_Success/URL_with_port +=== RUN TestSettingsHandler_ValidatePublicURL_Success/URL_with_trailing_slash +--- PASS: TestSettingsHandler_ValidatePublicURL_Success (0.00s) + --- PASS: TestSettingsHandler_ValidatePublicURL_Success/HTTPS_URL (0.00s) + --- PASS: TestSettingsHandler_ValidatePublicURL_Success/HTTP_URL (0.00s) + --- PASS: TestSettingsHandler_ValidatePublicURL_Success/URL_with_port (0.00s) + --- PASS: TestSettingsHandler_ValidatePublicURL_Success/URL_with_trailing_slash (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_NonAdmin +--- PASS: TestSettingsHandler_TestPublicURL_NonAdmin (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_NoRole +--- PASS: TestSettingsHandler_TestPublicURL_NoRole (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_InvalidJSON +--- PASS: TestSettingsHandler_TestPublicURL_InvalidJSON (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_InvalidURL +--- PASS: TestSettingsHandler_TestPublicURL_InvalidURL (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_PrivateIPBlocked +=== RUN TestSettingsHandler_TestPublicURL_PrivateIPBlocked/localhost +=== RUN TestSettingsHandler_TestPublicURL_PrivateIPBlocked/127.0.0.1 +=== RUN TestSettingsHandler_TestPublicURL_PrivateIPBlocked/Private_10.x +=== RUN TestSettingsHandler_TestPublicURL_PrivateIPBlocked/Private_192.168.x +=== RUN TestSettingsHandler_TestPublicURL_PrivateIPBlocked/AWS_metadata +--- PASS: TestSettingsHandler_TestPublicURL_PrivateIPBlocked (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_PrivateIPBlocked/localhost (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_PrivateIPBlocked/127.0.0.1 (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_PrivateIPBlocked/Private_10.x (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_PrivateIPBlocked/Private_192.168.x (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_PrivateIPBlocked/AWS_metadata (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_Success +2026/01/10 02:24:28 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:24:28Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011868760076519","result":"allowed","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestSettingsHandler_TestPublicURL_Success (0.08s) +=== RUN TestSettingsHandler_TestPublicURL_DNSFailure +--- PASS: TestSettingsHandler_TestPublicURL_DNSFailure (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_ConnectivityError +--- PASS: TestSettingsHandler_TestPublicURL_ConnectivityError (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_SSRFProtection +=== RUN TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_RFC_1918_-_10.x +=== RUN TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_RFC_1918_-_192.168.x +=== RUN TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_RFC_1918_-_172.16.x +=== RUN TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_localhost +=== RUN TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_127.0.0.1 +=== RUN TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_cloud_metadata +=== RUN TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_link-local +--- PASS: TestSettingsHandler_TestPublicURL_SSRFProtection (0.01s) + --- PASS: TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_RFC_1918_-_10.x (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_RFC_1918_-_192.168.x (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_RFC_1918_-_172.16.x (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_localhost (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_127.0.0.1 (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_cloud_metadata (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_SSRFProtection/blocks_link-local (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_EmbeddedCredentials +--- PASS: TestSettingsHandler_TestPublicURL_EmbeddedCredentials (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_EmptyURL +=== RUN TestSettingsHandler_TestPublicURL_EmptyURL/empty_string +=== RUN TestSettingsHandler_TestPublicURL_EmptyURL/missing_field +--- PASS: TestSettingsHandler_TestPublicURL_EmptyURL (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_EmptyURL/empty_string (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_EmptyURL/missing_field (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_InvalidScheme +=== RUN TestSettingsHandler_TestPublicURL_InvalidScheme/ftp_scheme +=== RUN TestSettingsHandler_TestPublicURL_InvalidScheme/file_scheme +=== RUN TestSettingsHandler_TestPublicURL_InvalidScheme/javascript_scheme +--- PASS: TestSettingsHandler_TestPublicURL_InvalidScheme (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_InvalidScheme/ftp_scheme (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_InvalidScheme/file_scheme (0.00s) + --- PASS: TestSettingsHandler_TestPublicURL_InvalidScheme/javascript_scheme (0.00s) +=== RUN TestSettingsHandler_ValidatePublicURL_InvalidJSON +--- PASS: TestSettingsHandler_ValidatePublicURL_InvalidJSON (0.00s) +=== RUN TestSettingsHandler_ValidatePublicURL_URLWithWarning +--- PASS: TestSettingsHandler_ValidatePublicURL_URLWithWarning (0.00s) +=== RUN TestSettingsHandler_UpdateSMTPConfig_DatabaseError + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/mail_service.go:91 sql: database is closed +[0.037ms] [rows:0] SELECT * FROM `settings` WHERE category = "smtp" + +2026/01/10 02:24:28 /projects/Charon/backend/internal/services/mail_service.go:142 sql: database is closed +[0.026ms] [rows:0] SELECT * FROM `settings` WHERE key = "smtp_host" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestSettingsHandler_UpdateSMTPConfig_DatabaseError (0.00s) +=== RUN TestSettingsHandler_TestPublicURL_IPv6LocalhostBlocked +--- PASS: TestSettingsHandler_TestPublicURL_IPv6LocalhostBlocked (0.00s) +=== RUN TestUptimeHandler_List +[GIN] 2026/01/10 - 02:24:28 | 200 | 437.171µs | | GET "/api/v1/uptime" +--- PASS: TestUptimeHandler_List (0.03s) +=== RUN TestUptimeHandler_GetHistory +[GIN] 2026/01/10 - 02:24:28 | 200 | 266.771µs | | GET "/api/v1/uptime/monitor-1/history" +--- PASS: TestUptimeHandler_GetHistory (0.02s) +=== RUN TestUptimeHandler_CheckMonitor +[GIN] 2026/01/10 - 02:24:28 | 200 | 272.301µs | | POST "/api/v1/uptime/check-mon-1/check" +--- PASS: TestUptimeHandler_CheckMonitor (0.03s) +=== RUN TestUptimeHandler_CheckMonitor_NotFound +[GIN] 2026/01/10 - 02:24:28 | 404 | 286.201µs | | POST "/api/v1/uptime/nonexistent/check" +--- PASS: TestUptimeHandler_CheckMonitor_NotFound (0.02s) +=== RUN TestUptimeHandler_Update +=== RUN TestUptimeHandler_Update/success +[GIN] 2026/01/10 - 02:24:28 | 200 | 589.321µs | | PUT "/api/v1/uptime/monitor-update" +=== RUN TestUptimeHandler_Update/invalid_json +[GIN] 2026/01/10 - 02:24:29 | 400 | 159.921µs | | PUT "/api/v1/uptime/monitor-1" +=== RUN TestUptimeHandler_Update/not_found +[GIN] 2026/01/10 - 02:24:29 | 500 | 239.771µs | | PUT "/api/v1/uptime/nonexistent" +--- PASS: TestUptimeHandler_Update (0.08s) + --- PASS: TestUptimeHandler_Update/success (0.03s) + --- PASS: TestUptimeHandler_Update/invalid_json (0.02s) + --- PASS: TestUptimeHandler_Update/not_found (0.02s) +=== RUN TestUptimeHandler_DeleteAndSync +=== RUN TestUptimeHandler_DeleteAndSync/delete_monitor +[GIN] 2026/01/10 - 02:24:29 | 200 | 455.851µs | | DELETE "/api/v1/uptime/mon-delete" +=== RUN TestUptimeHandler_DeleteAndSync/sync_creates_monitor_for_proxy_host +[GIN] 2026/01/10 - 02:24:29 | 200 | 1.287745ms | | POST "/api/v1/uptime/sync" +=== RUN TestUptimeHandler_DeleteAndSync/update_enabled_via_PUT +[GIN] 2026/01/10 - 02:24:29 | 200 | 564.043µs | | PUT "/api/v1/uptime/mon-enable" +--- PASS: TestUptimeHandler_DeleteAndSync (0.07s) + --- PASS: TestUptimeHandler_DeleteAndSync/delete_monitor (0.02s) + --- PASS: TestUptimeHandler_DeleteAndSync/sync_creates_monitor_for_proxy_host (0.02s) + --- PASS: TestUptimeHandler_DeleteAndSync/update_enabled_via_PUT (0.02s) +=== RUN TestUptimeHandler_Sync_Success +[GIN] 2026/01/10 - 02:24:29 | 200 | 265.551µs | | POST "/api/v1/uptime/sync" +--- PASS: TestUptimeHandler_Sync_Success (0.03s) +=== RUN TestUptimeHandler_Delete_Error +[GIN] 2026/01/10 - 02:24:29 | 500 | 178.732µs | | DELETE "/api/v1/uptime/nonexistent" +--- PASS: TestUptimeHandler_Delete_Error (0.02s) +=== RUN TestUptimeHandler_List_Error +[GIN] 2026/01/10 - 02:24:29 | 500 | 230.432µs | | GET "/api/v1/uptime" +--- PASS: TestUptimeHandler_List_Error (0.02s) +=== RUN TestUptimeHandler_GetHistory_Error +[GIN] 2026/01/10 - 02:24:29 | 500 | 175.441µs | | GET "/api/v1/uptime/monitor-1/history" +--- PASS: TestUptimeHandler_GetHistory_Error (0.02s) +=== CONT TestAuthHandler_Login +=== CONT TestCrowdsecHandler_Status_LAPINotReady +=== CONT TestExportConfigStreamsArchive +=== CONT TestProxyHostHandler_BulkUpdateSecurityHeaders_RemoveProfile +--- PASS: TestExportConfigStreamsArchive (0.02s) +=== CONT TestExportConfig +--- PASS: TestCrowdsecHandler_Status_LAPINotReady (0.03s) +=== CONT TestImportCreatesBackup +--- PASS: TestExportConfig (0.01s) +=== CONT TestListAndReadFile +--- PASS: TestImportCreatesBackup (0.01s) +=== CONT TestRemoteServerHandler_Errors +--- PASS: TestListAndReadFile (0.00s) +=== CONT TestHealthHandler +--- PASS: TestHealthHandler (0.00s) +=== CONT TestProxyHostHandler_PartialUpdate_DoesNotWipeFields +--- PASS: TestProxyHostHandler_BulkUpdateSecurityHeaders_RemoveProfile (0.07s) +--- PASS: TestRemoteServerHandler_Errors (0.04s) +=== CONT TestProxyHostHandler_Create +=== CONT TestProxyHostHandler_List +--- PASS: TestProxyHostHandler_PartialUpdate_DoesNotWipeFields (0.06s) +=== CONT TestRemoteServerHandler_Update +--- PASS: TestProxyHostHandler_Create (0.03s) +=== CONT TestRemoteServerHandler_Get +--- PASS: TestProxyHostHandler_List (0.05s) +=== CONT TestRemoteServerHandler_TestConnection +--- PASS: TestRemoteServerHandler_Update (0.03s) +=== CONT TestRemoteServerHandler_Create +--- PASS: TestRemoteServerHandler_Get (0.03s) +=== CONT TestRemoteServerHandler_Delete +--- PASS: TestRemoteServerHandler_TestConnection (0.03s) +=== CONT TestOpenTestDB_ParallelSafety +=== RUN TestOpenTestDB_ParallelSafety/parallel-0 +=== PAUSE TestOpenTestDB_ParallelSafety/parallel-0 +=== RUN TestOpenTestDB_ParallelSafety/parallel-1 +=== PAUSE TestOpenTestDB_ParallelSafety/parallel-1 +=== RUN TestOpenTestDB_ParallelSafety/parallel-2 +=== PAUSE TestOpenTestDB_ParallelSafety/parallel-2 +=== RUN TestOpenTestDB_ParallelSafety/parallel-3 +=== PAUSE TestOpenTestDB_ParallelSafety/parallel-3 +=== RUN TestOpenTestDB_ParallelSafety/parallel-4 +=== PAUSE TestOpenTestDB_ParallelSafety/parallel-4 +=== CONT TestRemoteServerHandler_List +--- PASS: TestRemoteServerHandler_Create (0.04s) +=== CONT TestSecurityNotificationHandler_UpdateSettings_Success +--- PASS: TestSecurityNotificationHandler_UpdateSettings_Success (0.00s) +--- PASS: TestRemoteServerHandler_Delete (0.03s) +=== CONT TestSecurityNotificationHandler_UpdateSettings_ServiceError +--- PASS: TestSecurityNotificationHandler_UpdateSettings_ServiceError (0.00s) +=== CONT TestSecurityNotificationHandler_UpdateSettings_EmptyWebhookURL +--- PASS: TestSecurityNotificationHandler_UpdateSettings_EmptyWebhookURL (0.00s) +=== CONT TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/AWS_Metadata +=== CONT TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel/trace +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/GCP_Metadata +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel/critical +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel/fatal +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel/unknown +--- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel/trace (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel/critical (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel/fatal (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidMinLogLevel/unknown (0.00s) +=== CONT TestSecurityNotificationHandler_UpdateSettings_PrivateIPWebhook +=== RUN TestSecurityNotificationHandler_UpdateSettings_PrivateIPWebhook/http://127.0.0.1/hook +=== RUN TestSecurityNotificationHandler_UpdateSettings_PrivateIPWebhook/http://localhost/webhook +=== RUN TestSecurityNotificationHandler_UpdateSettings_PrivateIPWebhook/http://[::1]/api +--- PASS: TestSecurityNotificationHandler_UpdateSettings_PrivateIPWebhook (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_PrivateIPWebhook/http://127.0.0.1/hook (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_PrivateIPWebhook/http://localhost/webhook (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_PrivateIPWebhook/http://[::1]/api (0.00s) +=== CONT TestSecurityNotificationHandler_GetSettings_ServiceError +--- PASS: TestSecurityNotificationHandler_GetSettings_ServiceError (0.00s) +=== CONT TestSecurityNotificationHandler_GetSettings_Success +--- PASS: TestSecurityNotificationHandler_GetSettings_Success (0.00s) +=== CONT TestSecurityNotificationHandler_UpdateSettings_InvalidJSON +--- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidJSON (0.00s) +=== CONT TestBulkUpdateSecurityHeaders_DBError_NonNotFound +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/Azure_Metadata +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/Private_IP_10.x +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/Private_IP_172.16.x +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/Private_IP_192.168.x +=== RUN TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/Link-local +--- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF (0.01s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/AWS_Metadata (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/GCP_Metadata (0.01s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/Azure_Metadata (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/Private_IP_10.x (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/Private_IP_172.16.x (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/Private_IP_192.168.x (0.00s) + --- PASS: TestSecurityNotificationHandler_UpdateSettings_InvalidWebhookURL_SSRF/Link-local (0.00s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfileID_SetToNull +--- PASS: TestRemoteServerHandler_List (0.03s) +=== CONT TestNewSecurityNotificationHandler +--- PASS: TestNewSecurityNotificationHandler (0.00s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType + +2026/01/10 02:24:29 /projects/Charon/backend/internal/api/handlers/proxy_host_handler.go:615 sql: database is closed +[0.029ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 1 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestBulkUpdateSecurityHeaders_DBError_NonNotFound (0.02s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfileID_InvalidString +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/boolean_true +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/boolean_true +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/boolean_false +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/boolean_false +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/array +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/array +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/object +=== PAUSE TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/object +=== CONT TestProxyHostUpdate_SecurityHeaderProfileID_ValidAssignment +--- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_SetToNull (0.05s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfileID_NegativeFloat +--- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_InvalidString (0.04s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfileID_NegativeInt +--- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_NegativeFloat (0.02s) +=== CONT TestProxyHostUpdate_ForwardAuthEnabled +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_ValidAssignment/as_float64 +=== RUN TestProxyHostUpdate_SecurityHeaderProfileID_ValidAssignment/as_string +--- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_NegativeInt (0.03s) +=== CONT TestProxyHostUpdate_EnableStandardHeaders_False +--- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_ValidAssignment (0.05s) + --- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_ValidAssignment/as_float64 (0.00s) + --- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_ValidAssignment/as_string (0.01s) +=== CONT TestProxyHostUpdate_WAFDisabled +--- PASS: TestProxyHostUpdate_ForwardAuthEnabled (0.04s) +=== CONT TestProxyHostUpdate_EnableStandardHeaders_True +--- PASS: TestProxyHostUpdate_WAFDisabled (0.03s) +=== CONT TestProxyHostUpdate_CertificateID_StringValue +--- PASS: TestProxyHostUpdate_EnableStandardHeaders_False (0.03s) +=== CONT TestProxyHostUpdate_EnableStandardHeaders_Null +--- PASS: TestProxyHostUpdate_EnableStandardHeaders_True (0.02s) +=== CONT TestProxyHostUpdate_CertificateID_IntValue +--- PASS: TestProxyHostUpdate_EnableStandardHeaders_Null (0.03s) +=== CONT TestProxyHostUpdate_AccessListID_IntValue +--- PASS: TestProxyHostUpdate_CertificateID_StringValue (0.03s) +=== CONT TestProxyHostUpdate_NegativeIntCertificateID +--- PASS: TestProxyHostUpdate_CertificateID_IntValue (0.03s) +=== CONT TestProxyHostHandler_BulkUpdateSecurityHeaders_AllFail +--- PASS: TestProxyHostUpdate_NegativeIntCertificateID (0.02s) +=== CONT TestProxyHostHandler_BulkUpdateSecurityHeaders_ProfileNotFound +--- PASS: TestProxyHostUpdate_AccessListID_IntValue (0.04s) +=== CONT TestProxyHostUpdate_AccessListID_StringValue + +2026/01/10 02:24:29 /projects/Charon/backend/internal/api/handlers/proxy_host_handler.go:615 record not found +[0.111ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE `security_header_profiles`.`id` = 99999 ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestProxyHostHandler_BulkUpdateSecurityHeaders_ProfileNotFound (0.04s) +=== CONT TestProxyHostHandler_BulkUpdateSecurityHeaders_EmptyUUIDs + +2026/01/10 02:24:29 /projects/Charon/backend/internal/api/handlers/proxy_host_handler.go:638 record not found +[0.104ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "8c5c2bdf-0760-4ddb-9980-c72c43b5fe33" ORDER BY `proxy_hosts`.`id` LIMIT 1 + +2026/01/10 02:24:29 /projects/Charon/backend/internal/api/handlers/proxy_host_handler.go:638 record not found +[0.100ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "ea888497-0fec-4c2e-a9ba-9017bc322ac3" ORDER BY `proxy_hosts`.`id` LIMIT 1 +--- PASS: TestProxyHostHandler_BulkUpdateSecurityHeaders_AllFail (0.05s) +=== CONT TestProxyHostHandler_BulkUpdateSecurityHeaders_InvalidJSON +--- PASS: TestProxyHostUpdate_AccessListID_StringValue (0.04s) +=== CONT TestProxyHostHandler_BulkUpdateSecurityHeaders_PartialFailure +--- PASS: TestProxyHostHandler_BulkUpdateSecurityHeaders_EmptyUUIDs (0.03s) +=== CONT TestUpdate_ExistingHostsBackwardCompatibility +--- PASS: TestProxyHostHandler_BulkUpdateSecurityHeaders_InvalidJSON (0.03s) +=== CONT TestUpdate_IntegrationCaddyConfig + +2026/01/10 02:24:29 /projects/Charon/backend/internal/api/handlers/proxy_host_handler.go:638 record not found +[0.170ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "d6bdf056-cc3d-4ff3-bc3f-ce1e4dde3499" ORDER BY `proxy_hosts`.`id` LIMIT 1 +--- PASS: TestProxyHostHandler_BulkUpdateSecurityHeaders_PartialFailure (0.06s) +=== CONT TestUpdate_WAFDisabled + +2026/01/10 02:24:29 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.069ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:29 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.114ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:29 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[2.830ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:29 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.088ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:29 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.079ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:29 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.076ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:29 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.037ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:29 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[1.014ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:24:29 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[3.027ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestUpdate_WAFDisabled (0.04s) +=== CONT TestProxyHostHandler_BulkUpdateSecurityHeaders_Success +--- PASS: TestUpdate_IntegrationCaddyConfig (0.08s) +=== CONT TestUpdate_ForwardAuthEnabled +--- PASS: TestUpdate_ForwardAuthEnabled (0.04s) +=== CONT TestUpdate_EnableStandardHeaders +--- PASS: TestUpdate_ExistingHostsBackwardCompatibility (0.14s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfile_UnsupportedType +--- PASS: TestProxyHostHandler_BulkUpdateSecurityHeaders_Success (0.07s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfile_InvalidFloat +--- PASS: TestUpdate_EnableStandardHeaders (0.06s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfile_InvalidString +--- PASS: TestProxyHostUpdate_SecurityHeaderProfile_InvalidFloat (0.04s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfile_ValidString +--- PASS: TestProxyHostUpdate_SecurityHeaderProfile_UnsupportedType (0.07s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfile_FromNoneToValid +--- PASS: TestProxyHostUpdate_SecurityHeaderProfile_InvalidString (0.03s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfile_ToNone +--- PASS: TestProxyHostUpdate_SecurityHeaderProfile_ValidString (0.05s) +=== CONT TestProxyHostUpdate_InvalidSecurityHeaderProfileID +--- PASS: TestProxyHostUpdate_SecurityHeaderProfile_FromNoneToValid (0.05s) +=== CONT TestProxyHostUpdate_RemoveSecurityHeaderProfile +--- PASS: TestProxyHostUpdate_SecurityHeaderProfile_ToNone (0.05s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfile_StrictToBasic +--- PASS: TestProxyHostUpdate_InvalidSecurityHeaderProfileID (0.10s) +=== CONT TestProxyHostUpdate_AssignSecurityHeaderProfile +--- PASS: TestProxyHostUpdate_RemoveSecurityHeaderProfile (0.10s) +=== CONT TestProxyHostUpdate_ChangeSecurityHeaderProfile +--- PASS: TestProxyHostUpdate_SecurityHeaderProfile_StrictToBasic (0.10s) +=== CONT TestProxyHostCreate_WithCertificateAndLocations +--- PASS: TestProxyHostUpdate_AssignSecurityHeaderProfile (0.04s) +=== CONT TestProxyHostCreate_WithSecurityHeaderProfile +--- PASS: TestProxyHostCreate_WithCertificateAndLocations (0.03s) +=== CONT TestProxyHostUpdate_SetBooleansAndApplication +--- PASS: TestProxyHostCreate_WithSecurityHeaderProfile (0.04s) +=== CONT TestProxyHostUpdate_Locations_InvalidPayload +--- PASS: TestProxyHostUpdate_ChangeSecurityHeaderProfile (0.06s) +=== CONT TestProxyHostUpdate_Locations_Replace +--- PASS: TestProxyHostUpdate_SetBooleansAndApplication (0.04s) +=== CONT TestProxyHostUpdate_ForwardPort_StringValue +--- PASS: TestProxyHostUpdate_Locations_InvalidPayload (0.02s) +=== CONT TestProxyHostUpdate_AdvancedConfig_SetBackup +--- PASS: TestProxyHostUpdate_ForwardPort_StringValue (0.03s) +=== CONT TestProxyHostUpdate_SetCertificateID +--- PASS: TestProxyHostUpdate_Locations_Replace (0.05s) +=== CONT TestProxyHostUpdate_AdvancedConfig_InvalidJSON +--- PASS: TestProxyHostUpdate_AdvancedConfig_SetBackup (0.03s) +=== CONT TestProxyHostUpdate_AdvancedConfig_ClearAndBackup +--- PASS: TestProxyHostUpdate_SetCertificateID (0.03s) +=== CONT TestProxyHostHandler_BulkUpdateACL_EmptyUUIDs +--- PASS: TestProxyHostUpdate_AdvancedConfig_InvalidJSON (0.03s) +=== CONT TestProxyHostHandler_BulkUpdateACL_PartialFailure +--- PASS: TestProxyHostUpdate_AdvancedConfig_ClearAndBackup (0.03s) +=== CONT TestProxyHostHandler_BulkUpdateACL_InvalidJSON +--- PASS: TestProxyHostHandler_BulkUpdateACL_InvalidJSON (0.03s) +=== CONT TestProxyHostHandler_BulkUpdateACL_Success +--- PASS: TestProxyHostHandler_BulkUpdateACL_EmptyUUIDs (0.04s) +=== CONT TestProxyHostWithCaddyIntegration + +2026/01/10 02:24:30 /projects/Charon/backend/internal/services/proxyhost_service.go:117 record not found +[0.088ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "07e9e0fb-b069-425f-a470-33bdf2cf6e2e" ORDER BY `proxy_hosts`.`id` LIMIT 1 +--- PASS: TestProxyHostHandler_BulkUpdateACL_PartialFailure (0.03s) +=== CONT TestProxyHostHandler_BulkUpdateACL_RemoveACL + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.273ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.126ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.092ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.104ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.033ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.826ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.760ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestProxyHostHandler_BulkUpdateACL_Success (0.03s) +=== CONT TestProxyHostConnection +--- PASS: TestProxyHostHandler_BulkUpdateACL_RemoveACL (0.04s) +=== CONT TestProxyHostHandler_List_Error + +2026/01/10 02:24:30 /projects/Charon/backend/internal/services/notification_service.go:96 no such table: notification_providers +[1.014ms] [rows:0] SELECT * FROM `notification_providers` WHERE enabled = true + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.059ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.118ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.039ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.311ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.097ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.093ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.039ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.037ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.040ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.069ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.124ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.064ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.095ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.131ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.095ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.039ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.032ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.058ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc + +2026/01/10 02:24:30 /projects/Charon/backend/internal/services/notification_service.go:96 no such table: notification_providers +[0.069ms] [rows:0] SELECT * FROM `notification_providers` WHERE enabled = true +--- PASS: TestProxyHostWithCaddyIntegration (0.06s) +=== CONT TestProxyHostCreate_AdvancedConfig_Normalization +--- PASS: TestProxyHostConnection (0.03s) +=== CONT TestProxyHostUpdate_CertificateID_Null + +2026/01/10 02:24:30 /projects/Charon/backend/internal/services/proxyhost_service.go:126 sql: database is closed +[0.031ms] [rows:0] SELECT * FROM `proxy_hosts` ORDER BY updated_at desc +--- PASS: TestProxyHostHandler_List_Error (0.02s) +=== CONT TestProxyHostValidation +--- PASS: TestProxyHostUpdate_CertificateID_Null (0.04s) +=== CONT TestProxyHostCreate_AdvancedConfig_InvalidJSON +--- PASS: TestProxyHostCreate_AdvancedConfig_Normalization (0.04s) +=== CONT TestProxyHostDelete_WithUptimeCleanup +--- PASS: TestProxyHostValidation (0.05s) +=== CONT TestProxyHostLifecycle +--- PASS: TestProxyHostCreate_AdvancedConfig_InvalidJSON (0.02s) +=== CONT TestCrowdsecHandler_HubEndpoints + +2026/01/10 02:24:30 /projects/Charon/backend/internal/services/notification_service.go:96 no such table: notification_providers +[3.430ms] [rows:0] SELECT * FROM `notification_providers` WHERE enabled = true + +2026/01/10 02:24:30 /projects/Charon/backend/internal/api/handlers/proxy_host_handler_test.go:143 record not found +[0.133ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "ph-delete-1" ORDER BY `proxy_hosts`.`id` LIMIT 1 +--- PASS: TestProxyHostDelete_WithUptimeCleanup (0.03s) +=== CONT TestProxyHostErrors +--- PASS: TestCrowdsecHandler_HubEndpoints (0.01s) +=== CONT TestCrowdsecHandler_Status_LAPIReady +--- PASS: TestCrowdsecHandler_Status_LAPIReady (0.01s) +=== CONT TestCrowdsecHandler_ListDecisions_WithCreatedAt +--- PASS: TestCrowdsecHandler_ListDecisions_WithCreatedAt (0.01s) +=== CONT TestCrowdsecHandler_BanIP_WithConfigYaml +--- PASS: TestCrowdsecHandler_BanIP_WithConfigYaml (0.00s) +=== CONT TestCrowdsecHandler_UnbanIP_WithConfigYaml + +2026/01/10 02:24:30 /projects/Charon/backend/internal/services/proxyhost_service.go:117 record not found +[0.108ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "baa89836-1f8a-4d50-9840-30e46e95211b" ORDER BY `proxy_hosts`.`id` LIMIT 1 +--- PASS: TestProxyHostLifecycle (0.03s) +=== CONT TestCrowdsecHandler_UpdateAcquisitionConfig_InvalidJSON +--- PASS: TestCrowdsecHandler_UnbanIP_WithConfigYaml (0.01s) +=== CONT TestCrowdsecHandler_UpdateAcquisitionConfig_MissingContent +--- PASS: TestCrowdsecHandler_UpdateAcquisitionConfig_InvalidJSON (0.00s) +=== CONT TestCrowdsecHandler_ListDecisions_WithConfigYaml +--- PASS: TestCrowdsecHandler_UpdateAcquisitionConfig_MissingContent (0.00s) +=== CONT TestCrowdsecHandler_CheckLAPIHealth_InvalidURL +--- PASS: TestCrowdsecHandler_ListDecisions_WithConfigYaml (0.01s) +=== CONT TestCrowdsecHandler_BanIP_ExecutionError +--- PASS: TestCrowdsecHandler_BanIP_ExecutionError (0.11s) +=== CONT TestCrowdsecHandler_GetLAPIDecisions_Fallback +--- PASS: TestCrowdsecHandler_CheckLAPIHealth_InvalidURL (0.12s) +=== CONT TestCrowdsecHandler_UnbanIP_Error + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.094ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.068ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.028ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.068ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.075ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.069ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.025ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[3.112ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.784ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestCrowdsecHandler_UnbanIP_Error (0.01s) +=== CONT TestCrowdsecHandler_BanIP_DefaultDuration +--- PASS: TestCrowdsecHandler_GetLAPIDecisions_Fallback (0.02s) +=== CONT TestCrowdsecHandler_UnbanIP_Success +--- PASS: TestCrowdsecHandler_BanIP_DefaultDuration (0.01s) +=== CONT TestCrowdsecHandler_BanIP_EmptyIP + +2026/01/10 02:24:30 /projects/Charon/backend/internal/services/proxyhost_service.go:117 record not found +[0.123ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "non-existent-uuid" ORDER BY `proxy_hosts`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/services/proxyhost_service.go:117 record not found +[0.119ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "non-existent-uuid" ORDER BY `proxy_hosts`.`id` LIMIT 1 +--- PASS: TestCrowdsecHandler_UnbanIP_Success (0.01s) +=== CONT TestCrowdsecHandler_BanIP_MissingIP + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.092ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.105ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.086ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.084ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.093ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.092ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.045ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.081ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.037ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestCrowdsecHandler_BanIP_EmptyIP (0.01s) +=== CONT TestCrowdsecHandler_ListDecisions_InvalidJSON +--- PASS: TestCrowdsecHandler_BanIP_MissingIP (0.01s) +=== CONT TestCrowdsecHandler_BanIP_Success + +2026/01/10 02:24:30 /projects/Charon/backend/internal/services/proxyhost_service.go:117 record not found +[0.159ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE uuid = "non-existent-uuid" ORDER BY `proxy_hosts`.`id` LIMIT 1 +--- PASS: TestCrowdsecHandler_ListDecisions_InvalidJSON (0.01s) +=== CONT TestCrowdsecHandler_ListDecisions_Empty +=== CONT TestCrowdsecHandler_ListDecisions_Success +--- PASS: TestCrowdsecHandler_ListDecisions_Empty (0.01s) + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.740ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.082ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestCrowdsecHandler_BanIP_Success (0.01s) +=== CONT TestCrowdsecHandler_ReadFile_MissingPath + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.082ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.076ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.104ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.110ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.057ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.063ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:24:30 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.069ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestCrowdsecHandler_ListDecisions_Success (0.01s) +=== CONT TestCrowdsecHandler_ListDecisions_CscliError +--- PASS: TestCrowdsecHandler_ReadFile_MissingPath (0.01s) +--- PASS: TestCrowdsecHandler_ListDecisions_CscliError (0.00s) +=== CONT TestCrowdsecHandler_Start_ExecutorError +=== CONT TestCrowdsecHandler_ExportConfig_DirNotFound +--- PASS: TestCrowdsecHandler_ExportConfig_DirNotFound (0.01s) +=== CONT TestCrowdsecHandler_ReadFile_NotFound +--- PASS: TestProxyHostErrors (0.20s) +=== CONT TestCrowdsecStart_LAPINotReadyTimeout +--- PASS: TestCrowdsecHandler_ReadFile_NotFound (0.00s) +=== CONT TestCrowdsecHandler_Status_Error +--- PASS: TestCrowdsecHandler_Start_ExecutorError (0.01s) +=== CONT TestGetAcquisitionConfigNotFound +--- PASS: TestGetAcquisitionConfigNotFound (0.00s) +=== CONT TestRegisterBouncerExecutionError +--- PASS: TestCrowdsecHandler_Status_Error (0.00s) +=== CONT TestGetAcquisitionConfigSuccess +--- PASS: TestRegisterBouncerExecutionError (0.00s) +=== CONT TestRegisterBouncerScriptNotFound +--- PASS: TestGetAcquisitionConfigSuccess (0.00s) +=== CONT TestRegisterBouncerSuccess +--- PASS: TestRegisterBouncerSuccess (0.00s) +=== CONT TestIsConsoleEnrollmentDisabledFromDB +--- PASS: TestRegisterBouncerScriptNotFound (0.00s) +=== CONT TestIsConsoleEnrollmentEnabledFromDB +--- PASS: TestIsConsoleEnrollmentDisabledFromDB (0.01s) +=== CONT TestIsCerberusEnabledFromDB +--- PASS: TestIsConsoleEnrollmentEnabledFromDB (0.01s) +=== CONT TestListFilesMissingDir +--- PASS: TestListFilesMissingDir (0.00s) +=== CONT TestImportConfigRejectsEmptyUpload +=== CONT TestListFilesReturnsEntries +--- PASS: TestImportConfigRejectsEmptyUpload (0.00s) +=== CONT TestWriteFileInvalidPayload +--- PASS: TestWriteFileInvalidPayload (0.00s) +=== CONT TestCrowdsecEndpoints +--- PASS: TestIsCerberusEnabledFromDB (0.00s) +--- PASS: TestListFilesReturnsEntries (0.00s) +=== CONT TestCerberusLogsHandler_UpgradeFailure +--- PASS: TestCerberusLogsHandler_UpgradeFailure (0.00s) +=== CONT TestCerberusLogsHandler_MultipleClients +--- PASS: TestCerberusLogsHandler_MultipleClients (0.40s) +=== CONT TestImportConfig +--- PASS: TestImportConfig (0.00s) +=== CONT TestCerberusLogsHandler_ClientDisconnect +--- PASS: TestCerberusLogsHandler_ClientDisconnect (0.10s) +=== CONT TestCerberusLogsHandler_IPFilter +--- PASS: TestAuthHandler_Login (1.84s) +=== CONT TestCerberusLogsHandler_SourceFilter +--- PASS: TestCerberusLogsHandler_IPFilter (0.30s) +=== CONT TestCerberusLogsHandler_BlockedOnlyFilter +=== CONT TestCerberusLogsHandler_ReceiveLogEntries +--- PASS: TestCerberusLogsHandler_SourceFilter (0.30s) +--- PASS: TestCerberusLogsHandler_BlockedOnlyFilter (0.30s) +=== CONT TestCerberusLogsHandler_NewHandler +--- PASS: TestCerberusLogsHandler_NewHandler (0.00s) +=== CONT TestAuthHandler_CheckHostAccess_Denied +--- PASS: TestAuthHandler_CheckHostAccess_Denied (0.02s) +=== CONT TestAuthHandler_CheckHostAccess_Allowed +--- PASS: TestCerberusLogsHandler_ReceiveLogEntries (0.30s) +=== CONT TestCerberusLogsHandler_SuccessfulConnection +--- PASS: TestCerberusLogsHandler_SuccessfulConnection (0.00s) +=== CONT TestAuthHandler_CheckHostAccess_Unauthorized +--- PASS: TestAuthHandler_CheckHostAccess_Allowed (0.02s) +=== CONT TestWriteFileMissingPath +--- PASS: TestWriteFileMissingPath (0.00s) +=== CONT TestImportConfigRequiresFile +--- PASS: TestImportConfigRequiresFile (0.00s) +=== CONT TestWriteFileInvalidPath +--- PASS: TestWriteFileInvalidPath (0.00s) +=== CONT TestReadFileInvalidPath +--- PASS: TestReadFileInvalidPath (0.00s) +=== CONT TestWriteFileCreatesBackup +--- PASS: TestAuthHandler_CheckHostAccess_Unauthorized (0.02s) +=== CONT TestAuthHandler_GetAccessibleHosts_UserNotFound +--- PASS: TestWriteFileCreatesBackup (0.00s) +=== CONT TestAuthHandler_GetAccessibleHosts_PermittedHosts + +2026/01/10 02:24:31 /projects/Charon/backend/internal/api/handlers/auth_handler.go:334 record not found +[0.106ms] [rows:0] SELECT * FROM `users` WHERE `users`.`id` = 99999 ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestAuthHandler_GetAccessibleHosts_UserNotFound (0.02s) +=== CONT TestAuthHandler_GetAccessibleHosts_DenyAll +--- PASS: TestAuthHandler_GetAccessibleHosts_PermittedHosts (0.03s) +=== CONT TestAuthHandler_GetAccessibleHosts_AllowAll +--- PASS: TestAuthHandler_GetAccessibleHosts_DenyAll (0.02s) +=== CONT TestAuthHandler_GetAccessibleHosts_Unauthorized +--- PASS: TestAuthHandler_GetAccessibleHosts_AllowAll (0.02s) +=== CONT TestAuthHandler_VerifyStatus_DisabledUser +--- PASS: TestAuthHandler_GetAccessibleHosts_Unauthorized (0.02s) +=== CONT TestAuthHandler_VerifyStatus_Authenticated +--- PASS: TestAuthHandler_VerifyStatus_Authenticated (0.73s) +=== CONT TestAuthHandler_VerifyStatus_InvalidToken +--- PASS: TestAuthHandler_VerifyStatus_DisabledUser (0.75s) +=== CONT TestAuthHandler_CheckHostAccess_InvalidHostID +--- PASS: TestAuthHandler_VerifyStatus_InvalidToken (0.02s) +=== CONT TestAuthHandler_VerifyStatus_NotAuthenticated +--- PASS: TestAuthHandler_CheckHostAccess_InvalidHostID (0.02s) +=== CONT TestAuthHandler_Verify_ForwardAuthDenied +--- PASS: TestAuthHandler_VerifyStatus_NotAuthenticated (0.03s) +=== CONT TestAuthHandler_Verify_DisabledUser +--- PASS: TestAuthHandler_Verify_ForwardAuthDenied (0.75s) +=== CONT TestAuthHandler_Verify_ValidToken +--- PASS: TestAuthHandler_Verify_DisabledUser (0.77s) +=== CONT TestAuthHandler_Verify_InvalidToken +--- PASS: TestAuthHandler_Verify_InvalidToken (0.02s) +=== CONT TestAuthHandler_Verify_BearerToken +--- PASS: TestAuthHandler_Verify_ValidToken (0.75s) +=== CONT TestNewAuthHandlerWithDB +--- PASS: TestNewAuthHandlerWithDB (0.01s) +=== CONT TestAuthHandler_Verify_NoCookie +--- PASS: TestAuthHandler_Verify_NoCookie (0.02s) +=== CONT TestAuthHandler_ChangePassword_Errors +--- PASS: TestAuthHandler_ChangePassword_Errors (0.02s) +=== CONT TestAuthHandler_ChangePassword_WrongOld +--- PASS: TestAuthHandler_Verify_BearerToken (0.75s) +=== CONT TestAuthHandler_ChangePassword +--- PASS: TestAuthHandler_ChangePassword_WrongOld (1.71s) +=== CONT TestAuthHandler_Me +--- PASS: TestAuthHandler_Me (0.03s) +=== CONT TestAuthHandler_Me_NotFound + +2026/01/10 02:24:35 /projects/Charon/backend/internal/services/auth_service.go:147 record not found +[0.110ms] [rows:0] SELECT * FROM `users` WHERE `users`.`id` = 999 ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestAuthHandler_Me_NotFound (0.02s) +=== CONT TestAuthHandler_Logout +--- PASS: TestAuthHandler_Logout (0.02s) +=== CONT TestAuthHandler_Register +--- PASS: TestAuthHandler_Register (0.78s) +=== CONT TestAuthHandler_Login_Errors + +2026/01/10 02:24:36 /projects/Charon/backend/internal/services/auth_service.go:64 record not found +[0.126ms] [rows:0] SELECT * FROM `users` WHERE email = "nonexistent@example.com" ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestAuthHandler_Login_Errors (0.02s) +=== CONT TestAuthHandler_Register_Duplicate +--- PASS: TestAuthHandler_ChangePassword (3.20s) +=== CONT TestSetSecureCookie_HTTP_Lax +--- PASS: TestSetSecureCookie_HTTP_Lax (0.00s) +=== CONT TestOpenTestDB_ParallelSafety/parallel-3 +=== CONT TestOpenTestDB_ParallelSafety/parallel-4 +=== CONT TestOpenTestDB_ParallelSafety/parallel-2 +=== CONT TestOpenTestDB_ParallelSafety/parallel-1 +=== CONT TestOpenTestDB_ParallelSafety/parallel-0 +--- PASS: TestOpenTestDB_ParallelSafety (0.00s) + --- PASS: TestOpenTestDB_ParallelSafety/parallel-3 (0.00s) + --- PASS: TestOpenTestDB_ParallelSafety/parallel-4 (0.00s) + --- PASS: TestOpenTestDB_ParallelSafety/parallel-2 (0.00s) + --- PASS: TestOpenTestDB_ParallelSafety/parallel-1 (0.00s) + --- PASS: TestOpenTestDB_ParallelSafety/parallel-0 (0.00s) +=== CONT TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/boolean_false +=== CONT TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/array +=== CONT TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/object +=== CONT TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/boolean_true +--- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType (0.04s) + --- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/boolean_false (0.00s) + --- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/array (0.00s) + --- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/object (0.00s) + --- PASS: TestProxyHostUpdate_SecurityHeaderProfileID_UnsupportedType/boolean_true (0.00s) + +2026/01/10 02:24:37 /projects/Charon/backend/internal/services/auth_service.go:54 UNIQUE constraint failed: users.email +[0.517ms] [rows:0] INSERT INTO `users` (`uuid`,`email`,`api_key`,`password_hash`,`name`,`role`,`enabled`,`failed_login_attempts`,`locked_until`,`last_login`,`invite_token`,`invite_expires`,`invited_at`,`invited_by`,`invite_status`,`permission_mode`,`created_at`,`updated_at`) VALUES ("6fce0af7-2cd7-43c4-bd3c-4b55b59b90f9","dup@example.com","a727f701-f3cc-478a-b4ce-b0e4725a470f","$2a$10$wfJbDIjJM9wSS4ZzXClUTe1qVO5jZMtW9QpArDQ09rMPsAnaa/S5S","Dup User","user",true,0,NULL,NULL,"",NULL,NULL,NULL,"","allow_all","2026-01-10 02:24:36.614","2026-01-10 02:24:36.614") RETURNING `id` +--- PASS: TestAuthHandler_Register_Duplicate (0.76s) +--- PASS: TestCrowdsecStart_LAPINotReadyTimeout (60.12s) +--- PASS: TestCrowdsecEndpoints (60.14s) +FAIL +coverage: 86.9% of statements +FAIL github.com/Wikid82/charon/backend/internal/api/handlers 474.816s +=== RUN TestAuthMiddleware_MissingHeader +--- PASS: TestAuthMiddleware_MissingHeader (0.00s) +=== RUN TestRequireRole_Success +--- PASS: TestRequireRole_Success (0.00s) +=== RUN TestRequireRole_Forbidden +--- PASS: TestRequireRole_Forbidden (0.00s) +=== RUN TestAuthMiddleware_Cookie +--- PASS: TestAuthMiddleware_Cookie (1.22s) +=== RUN TestAuthMiddleware_ValidToken +--- PASS: TestAuthMiddleware_ValidToken (0.85s) +=== RUN TestAuthMiddleware_PrefersAuthorizationHeader +--- PASS: TestAuthMiddleware_PrefersAuthorizationHeader (0.82s) +=== RUN TestAuthMiddleware_InvalidToken +--- PASS: TestAuthMiddleware_InvalidToken (0.03s) +=== RUN TestRequireRole_MissingRoleInContext +--- PASS: TestRequireRole_MissingRoleInContext (0.00s) +=== RUN TestAuthMiddleware_QueryParamFallback +--- PASS: TestAuthMiddleware_QueryParamFallback (0.87s) +=== RUN TestAuthMiddleware_PrefersCookieOverQueryParam +--- PASS: TestAuthMiddleware_PrefersCookieOverQueryParam (1.64s) +=== RUN TestRecoveryLogsStacktraceVerbose +--- PASS: TestRecoveryLogsStacktraceVerbose (0.00s) +=== RUN TestRecoveryLogsBriefWhenNotVerbose +--- PASS: TestRecoveryLogsBriefWhenNotVerbose (0.00s) +=== RUN TestRecoveryDoesNotLogSensitiveHeaders +--- PASS: TestRecoveryDoesNotLogSensitiveHeaders (0.00s) +=== RUN TestRecoveryTruncatesLongPanicMessage +--- PASS: TestRecoveryTruncatesLongPanicMessage (0.00s) +=== RUN TestRecoveryNoPanicNormalFlow +--- PASS: TestRecoveryNoPanicNormalFlow (0.00s) +=== RUN TestRecoveryPanicWithNilValue +--- PASS: TestRecoveryPanicWithNilValue (0.00s) +=== RUN TestRequestIDAddsHeaderAndLogger +--- PASS: TestRequestIDAddsHeaderAndLogger (0.00s) +=== RUN TestRequestLoggerSanitizesPath +--- PASS: TestRequestLoggerSanitizesPath (0.00s) +=== RUN TestRequestLoggerIncludesRequestID +--- PASS: TestRequestLoggerIncludesRequestID (0.00s) +=== RUN TestSanitizeHeaders +=== RUN TestSanitizeHeaders/nil_headers +=== RUN TestSanitizeHeaders/redacts_sensitive_headers +=== RUN TestSanitizeHeaders/sanitizes_and_truncates_values +--- PASS: TestSanitizeHeaders (0.00s) + --- PASS: TestSanitizeHeaders/nil_headers (0.00s) + --- PASS: TestSanitizeHeaders/redacts_sensitive_headers (0.00s) + --- PASS: TestSanitizeHeaders/sanitizes_and_truncates_values (0.00s) +=== RUN TestSanitizePath +--- PASS: TestSanitizePath (0.00s) +=== RUN TestSecurityHeaders +=== RUN TestSecurityHeaders/production_mode_sets_HSTS +=== RUN TestSecurityHeaders/development_mode_skips_HSTS +=== RUN TestSecurityHeaders/sets_X-Frame-Options +=== RUN TestSecurityHeaders/sets_X-Content-Type-Options +=== RUN TestSecurityHeaders/sets_X-XSS-Protection +=== RUN TestSecurityHeaders/sets_Referrer-Policy +=== RUN TestSecurityHeaders/sets_Content-Security-Policy +=== RUN TestSecurityHeaders/development_mode_CSP_allows_unsafe-eval +=== RUN TestSecurityHeaders/sets_Permissions-Policy +=== RUN TestSecurityHeaders/sets_Cross-Origin-Opener-Policy_in_production +=== RUN TestSecurityHeaders/skips_Cross-Origin-Opener-Policy_in_development +=== RUN TestSecurityHeaders/sets_Cross-Origin-Resource-Policy +--- PASS: TestSecurityHeaders (0.00s) + --- PASS: TestSecurityHeaders/production_mode_sets_HSTS (0.00s) + --- PASS: TestSecurityHeaders/development_mode_skips_HSTS (0.00s) + --- PASS: TestSecurityHeaders/sets_X-Frame-Options (0.00s) + --- PASS: TestSecurityHeaders/sets_X-Content-Type-Options (0.00s) + --- PASS: TestSecurityHeaders/sets_X-XSS-Protection (0.00s) + --- PASS: TestSecurityHeaders/sets_Referrer-Policy (0.00s) + --- PASS: TestSecurityHeaders/sets_Content-Security-Policy (0.00s) + --- PASS: TestSecurityHeaders/development_mode_CSP_allows_unsafe-eval (0.00s) + --- PASS: TestSecurityHeaders/sets_Permissions-Policy (0.00s) + --- PASS: TestSecurityHeaders/sets_Cross-Origin-Opener-Policy_in_production (0.00s) + --- PASS: TestSecurityHeaders/skips_Cross-Origin-Opener-Policy_in_development (0.00s) + --- PASS: TestSecurityHeaders/sets_Cross-Origin-Resource-Policy (0.00s) +=== RUN TestSecurityHeadersCustomCSP +--- PASS: TestSecurityHeadersCustomCSP (0.00s) +=== RUN TestDefaultSecurityHeadersConfig +--- PASS: TestDefaultSecurityHeadersConfig (0.00s) +=== RUN TestSecurityHeaders_COOP_DevelopmentMode +--- PASS: TestSecurityHeaders_COOP_DevelopmentMode (0.00s) +=== RUN TestSecurityHeaders_COOP_ProductionMode +--- PASS: TestSecurityHeaders_COOP_ProductionMode (0.00s) +=== RUN TestBuildCSP +=== RUN TestBuildCSP/production_CSP +=== RUN TestBuildCSP/development_CSP +--- PASS: TestBuildCSP (0.00s) + --- PASS: TestBuildCSP/production_CSP (0.00s) + --- PASS: TestBuildCSP/development_CSP (0.00s) +=== RUN TestBuildPermissionsPolicy +--- PASS: TestBuildPermissionsPolicy (0.00s) +PASS +coverage: 99.1% of statements +ok github.com/Wikid82/charon/backend/internal/api/middleware (cached) coverage: 99.1% of statements +=== RUN TestRegister +time="2026-01-10T02:16:51Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:51Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:51Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" + +2026/01/10 02:16:51 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.131ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-basic" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:16:51 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.138ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-api-friendly" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:16:51 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.123ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-strict" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:16:51 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.164ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-paranoid" ORDER BY `security_header_profiles`.`id` LIMIT 1 +time="2026-01-10T02:16:51Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:51Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:51Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +--- PASS: TestRegister (0.13s) +=== RUN TestRegister_WithDevelopmentEnvironment +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:51Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:51Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:51Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:51Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:51Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:51Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +--- PASS: TestRegister_WithDevelopmentEnvironment (0.48s) +=== RUN TestRegister_WithProductionEnvironment +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:51Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:51Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:51Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:51Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:51Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:51Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +--- PASS: TestRegister_WithProductionEnvironment (0.31s) +=== RUN TestRegister_AutoMigrateFailure +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: disk sync complete" count=0 + +2026/01/10 02:16:51 /projects/Charon/backend/internal/api/routes/routes.go:42 sql: database is closed +[0.005ms] [rows:0] CREATE TABLE `ssl_certificates` (`id` integer PRIMARY KEY AUTOINCREMENT,`uuid` text,`name` text,`provider` text,`domains` text,`certificate` text,`private_key` text,`expires_at` datetime,`auto_renew` numeric DEFAULT false,`created_at` datetime,`updated_at` datetime) +--- PASS: TestRegister_AutoMigrateFailure (0.04s) +=== RUN TestRegisterImportHandler +--- PASS: TestRegisterImportHandler (0.00s) +=== RUN TestRegister_RoutesRegistration +time="2026-01-10T02:16:52Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:52Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:52Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:52Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:52Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:52Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_RoutesRegistration (0.27s) +=== RUN TestRegister_ProxyHostsRequireAuth +time="2026-01-10T02:16:52Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:52Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:52Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:52Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:52Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:52Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data + +2026/01/10 02:16:52 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.099ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:52 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.062ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_ProxyHostsRequireAuth (0.17s) +=== RUN TestRegister_DNSProviders_NotRegisteredWhenEncryptionKeyMissing +time="2026-01-10T02:16:52Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:52Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:52Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:52Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:52Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:52Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +--- PASS: TestRegister_DNSProviders_NotRegisteredWhenEncryptionKeyMissing (0.15s) +=== RUN TestRegister_DNSProviders_NotRegisteredWhenEncryptionKeyInvalid +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:52Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:52Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:52Z" level=error msg="Failed to initialize encryption service - DNS provider features will be unavailable" error="invalid base64 key: illegal base64 data at input byte 3" +time="2026-01-10T02:16:52Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:52Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:52Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +--- PASS: TestRegister_DNSProviders_NotRegisteredWhenEncryptionKeyInvalid (0.18s) +=== RUN TestRegister_DNSProviders_RegisteredWhenEncryptionKeyValid +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:52Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:52Z" level=info msg="Backup service cron scheduler started" +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +time="2026-01-10T02:16:52Z" level=warning msg="Failed to initialize rotation service - key rotation features will be unavailable" error="CHARON_ENCRYPTION_KEY is required" +time="2026-01-10T02:16:52Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:52Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:52Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +--- PASS: TestRegister_DNSProviders_RegisteredWhenEncryptionKeyValid (0.19s) +=== RUN TestRegister_AllRoutesRegistered +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:52Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:53Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:53Z" level=info msg="Backup service cron scheduler started" +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +time="2026-01-10T02:16:53Z" level=warning msg="Failed to initialize rotation service - key rotation features will be unavailable" error="CHARON_ENCRYPTION_KEY is required" +time="2026-01-10T02:16:53Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:53Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:53Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_AllRoutesRegistered (0.17s) +=== RUN TestRegister_MiddlewareApplied +time="2026-01-10T02:16:53Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:53Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:53Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:53Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:53Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:53Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_MiddlewareApplied (0.18s) +=== RUN TestRegister_AuthenticatedRoutes +time="2026-01-10T02:16:53Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:53Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:53Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:53Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:53Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:53Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +=== RUN TestRegister_AuthenticatedRoutes/GET_/api/v1/backups + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.172ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.086ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +=== RUN TestRegister_AuthenticatedRoutes/POST_/api/v1/backups + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.269ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.094ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +=== RUN TestRegister_AuthenticatedRoutes/GET_/api/v1/logs + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.110ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: disk sync complete" count=0 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.531ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +=== RUN TestRegister_AuthenticatedRoutes/GET_/api/v1/settings + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.170ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.098ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +=== RUN TestRegister_AuthenticatedRoutes/GET_/api/v1/notifications + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.137ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.080ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +=== RUN TestRegister_AuthenticatedRoutes/GET_/api/v1/users + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.089ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.059ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +=== RUN TestRegister_AuthenticatedRoutes/GET_/api/v1/auth/me + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.094ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.076ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +=== RUN TestRegister_AuthenticatedRoutes/POST_/api/v1/auth/logout + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.086ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.067ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +=== RUN TestRegister_AuthenticatedRoutes/GET_/api/v1/uptime/monitors + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.104ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.062ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestRegister_AuthenticatedRoutes (0.20s) + --- PASS: TestRegister_AuthenticatedRoutes/GET_/api/v1/backups (0.00s) + --- PASS: TestRegister_AuthenticatedRoutes/POST_/api/v1/backups (0.00s) + --- PASS: TestRegister_AuthenticatedRoutes/GET_/api/v1/logs (0.00s) + --- PASS: TestRegister_AuthenticatedRoutes/GET_/api/v1/settings (0.00s) + --- PASS: TestRegister_AuthenticatedRoutes/GET_/api/v1/notifications (0.00s) + --- PASS: TestRegister_AuthenticatedRoutes/GET_/api/v1/users (0.00s) + --- PASS: TestRegister_AuthenticatedRoutes/GET_/api/v1/auth/me (0.00s) + --- PASS: TestRegister_AuthenticatedRoutes/POST_/api/v1/auth/logout (0.00s) + --- PASS: TestRegister_AuthenticatedRoutes/GET_/api/v1/uptime/monitors (0.00s) +=== RUN TestRegister_AdminRoutes +time="2026-01-10T02:16:53Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:53Z" level=info msg="Backup service cron scheduler started" +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +time="2026-01-10T02:16:53Z" level=warning msg="Failed to initialize rotation service - key rotation features will be unavailable" error="CHARON_ENCRYPTION_KEY is required" +time="2026-01-10T02:16:53Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:53Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:53Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.190ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.096ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.086ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: disk sync complete" count=0 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.120ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestRegister_AdminRoutes (0.17s) +=== RUN TestRegister_PublicRoutes +time="2026-01-10T02:16:53Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:53Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:53Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:53Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:53Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:53Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +=== RUN TestRegister_PublicRoutes/GET_/api/v1/health +=== RUN TestRegister_PublicRoutes/GET_/metrics +=== RUN TestRegister_PublicRoutes/GET_/api/v1/setup + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.312ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.098ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +=== RUN TestRegister_PublicRoutes/GET_/api/v1/auth/status + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.188ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates + +2026/01/10 02:16:53 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.359ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestRegister_PublicRoutes (0.20s) + --- PASS: TestRegister_PublicRoutes/GET_/api/v1/health (0.00s) + --- PASS: TestRegister_PublicRoutes/GET_/metrics (0.00s) + --- PASS: TestRegister_PublicRoutes/GET_/api/v1/setup (0.00s) + --- PASS: TestRegister_PublicRoutes/GET_/api/v1/auth/status (0.00s) +=== RUN TestRegister_HealthEndpoint +time="2026-01-10T02:16:53Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:54Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:54Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:54Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:54Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:54Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:54Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +--- PASS: TestRegister_HealthEndpoint (0.21s) +=== RUN TestRegister_MetricsEndpoint +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:54Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:54Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:54Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:54Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:54Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:54Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_MetricsEndpoint (0.19s) +=== RUN TestRegister_DBHealthEndpoint +time="2026-01-10T02:16:54Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:54Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:54Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:54Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:54Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:54Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_DBHealthEndpoint (0.18s) +=== RUN TestRegister_LoginEndpoint +time="2026-01-10T02:16:54Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:54Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:54Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:54Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:54Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:54Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates + +2026/01/10 02:16:54 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.237ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: disk sync complete" count=0 + +2026/01/10 02:16:54 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.640ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestRegister_LoginEndpoint (0.21s) +=== RUN TestRegister_SetupEndpoint +time="2026-01-10T02:16:54Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:54Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:54Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:54Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:54Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:54Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates + +2026/01/10 02:16:54 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.124ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:54 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.149ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestRegister_SetupEndpoint (0.22s) +time="2026-01-10T02:16:54Z" level=info msg="CertificateService: disk sync complete" count=0 +=== RUN TestRegister_WithEncryptionRoutes +time="2026-01-10T02:16:55Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:55Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:55Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:55Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:55Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +--- PASS: TestRegister_WithEncryptionRoutes (0.21s) +=== RUN TestRegister_UptimeCheckEndpoint +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:55Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:55Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:55Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:55Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:55Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:55Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates + +2026/01/10 02:16:55 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.092ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:55 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.494ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_UptimeCheckEndpoint (0.17s) +=== RUN TestRegister_CrowdSecRoutes +time="2026-01-10T02:16:55Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:55Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:55Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:55Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:55Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:55Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +--- PASS: TestRegister_CrowdSecRoutes (0.19s) +=== RUN TestRegister_SecurityRoutes +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:55Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:55Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:55Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:55Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:55Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:55Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +--- PASS: TestRegister_SecurityRoutes (0.18s) +=== RUN TestRegister_AccessListRoutes +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:55Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:55Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:55Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:55Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:55Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:55Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +--- PASS: TestRegister_AccessListRoutes (0.17s) +=== RUN TestRegister_CertificateRoutes +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:55Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:55Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:55Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:55Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:55Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:55Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +--- PASS: TestRegister_CertificateRoutes (0.17s) +=== RUN TestRegister_NilHandlers +time="2026-01-10T02:16:55Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:56Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:56Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:56Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:56Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:56Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:56Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_NilHandlers (0.20s) +=== RUN TestRegister_MiddlewareOrder +time="2026-01-10T02:16:56Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:56Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:56Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:56Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:56Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:56Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +--- PASS: TestRegister_MiddlewareOrder (0.17s) +=== RUN TestRegister_GzipCompression +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:56Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:56Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:56Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:56Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:56Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:56Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_GzipCompression (0.20s) +=== RUN TestRegister_CerberusMiddleware +time="2026-01-10T02:16:56Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:56Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:56Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:56Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:56Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:56Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +--- PASS: TestRegister_CerberusMiddleware (0.17s) +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +=== RUN TestRegister_FeatureFlagsEndpoint +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:56Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:56Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:56Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:56Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:56Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:56Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data + +2026/01/10 02:16:56 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.094ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:56 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.074ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestRegister_FeatureFlagsEndpoint (0.23s) +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:56Z" level=info msg="CertificateService: disk sync complete" count=0 +=== RUN TestRegister_WebSocketRoutes +time="2026-01-10T02:16:57Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:57Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:57Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:57Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:57Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:57Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +--- PASS: TestRegister_WebSocketRoutes (0.19s) +=== RUN TestRegister_NotificationRoutes +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:57Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:57Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:57Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:57Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:57Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:57Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +--- PASS: TestRegister_NotificationRoutes (0.18s) +=== RUN TestRegister_DomainRoutes +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:57Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:57Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:57Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:57Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:57Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:57Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_DomainRoutes (0.20s) +=== RUN TestRegister_VerifyAuthEndpoint +time="2026-01-10T02:16:57Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:57Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:57Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:57Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:57Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:57Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data + +2026/01/10 02:16:57 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.165ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:57 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.115ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestRegister_VerifyAuthEndpoint (0.21s) +=== RUN TestRegister_SMTPRoutes +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:57Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:57Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:57Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:57Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:57Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:57Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +--- PASS: TestRegister_SMTPRoutes (0.23s) +=== RUN TestRegisterImportHandler_RoutesExist +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:57Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegisterImportHandler_RoutesExist (0.00s) +=== RUN TestRegister_EncryptionRoutesWithValidKey +time="2026-01-10T02:16:58Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:58Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:58Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:58Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:58Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_EncryptionRoutesWithValidKey (0.26s) +=== RUN TestRegister_WAFExclusionRoutes +time="2026-01-10T02:16:58Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:58Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:58Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:58Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:58Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:58Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +--- PASS: TestRegister_WAFExclusionRoutes (0.18s) +=== RUN TestRegister_BreakGlassRoute +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:58Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:58Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:58Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:58Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:58Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:58Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_BreakGlassRoute (0.20s) +=== RUN TestRegister_RateLimitPresetsRoute +time="2026-01-10T02:16:58Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:58Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:58Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:58Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:58Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:58Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=/data +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: scanning cert directory" certRoot=/data/certificates +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/data/certificates +time="2026-01-10T02:16:58Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestRegister_RateLimitPresetsRoute (0.16s) +=== RUN TestRegisterImportHandler +--- PASS: TestRegisterImportHandler (0.02s) +PASS +coverage: 87.1% of statements +ok github.com/Wikid82/charon/backend/internal/api/routes (cached) coverage: 87.1% of statements +=== RUN TestIntegration_WAF_BlockAndMonitor +time="2026-01-10T02:16:51Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:51Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:51Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" + +2026/01/10 02:16:51 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.091ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-basic" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:16:51 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.134ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-api-friendly" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:16:51 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.127ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-strict" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:16:51 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.094ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-paranoid" ORDER BY `security_header_profiles`.`id` LIMIT 1 +time="2026-01-10T02:16:51Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:51Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:51Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=data/caddy/data +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: scanning cert directory" certRoot=data/caddy/data/certificates +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: cert directory does not exist" certRoot=data/caddy/data/certificates +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: disk sync complete" count=0 +time="2026-01-10T02:16:51Z" level=info msg="Cleaning up invalid Let's Encrypt certificate associations..." +time="2026-01-10T02:16:51Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:16:51Z" level=warning msg="CHARON_ENCRYPTION_KEY not set - DNS provider and plugin features will be unavailable" +time="2026-01-10T02:16:51Z" level=info msg="GeoIP database not found - geo-blocking features will be unavailable" path=/app/data/geoip/GeoLite2-Country.mmdb +time="2026-01-10T02:16:51Z" level=info msg="LogWatcher started" path=/var/log/caddy/access.log +time="2026-01-10T02:16:51Z" level=info msg="Using Caddy data directory for certificates scan" caddy_data_dir=data/caddy/data +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: scanning cert directory" certRoot=data/caddy/data/certificates +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: cert directory does not exist" certRoot=data/caddy/data/certificates +time="2026-01-10T02:16:51Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestIntegration_WAF_BlockAndMonitor (0.55s) +=== RUN TestInviteToken_MustBeUnguessable +--- PASS: TestInviteToken_MustBeUnguessable (1.00s) +=== RUN TestInviteToken_ExpiredCannotBeUsed +--- PASS: TestInviteToken_ExpiredCannotBeUsed (0.84s) +=== RUN TestInviteToken_CannotBeReused +--- PASS: TestInviteToken_CannotBeReused (1.67s) +=== RUN TestInviteUser_EmailValidation +=== RUN TestInviteUser_EmailValidation/empty_email +=== RUN TestInviteUser_EmailValidation/invalid_email_no_@ +=== RUN TestInviteUser_EmailValidation/invalid_email_no_domain +=== RUN TestInviteUser_EmailValidation/sql_injection_attempt +=== RUN TestInviteUser_EmailValidation/script_injection +=== RUN TestInviteUser_EmailValidation/valid_email +--- PASS: TestInviteUser_EmailValidation (0.83s) + --- PASS: TestInviteUser_EmailValidation/empty_email (0.00s) + --- PASS: TestInviteUser_EmailValidation/invalid_email_no_@ (0.00s) + --- PASS: TestInviteUser_EmailValidation/invalid_email_no_domain (0.00s) + --- PASS: TestInviteUser_EmailValidation/sql_injection_attempt (0.00s) + --- PASS: TestInviteUser_EmailValidation/script_injection (0.00s) + --- PASS: TestInviteUser_EmailValidation/valid_email (0.00s) +=== RUN TestAcceptInvite_PasswordValidation +=== RUN TestAcceptInvite_PasswordValidation/empty_password +=== RUN TestAcceptInvite_PasswordValidation/too_short +=== RUN TestAcceptInvite_PasswordValidation/7_chars +=== RUN TestAcceptInvite_PasswordValidation/8_chars_valid +--- PASS: TestAcceptInvite_PasswordValidation (1.71s) + --- PASS: TestAcceptInvite_PasswordValidation/empty_password (0.00s) + --- PASS: TestAcceptInvite_PasswordValidation/too_short (0.00s) + --- PASS: TestAcceptInvite_PasswordValidation/7_chars (0.00s) + --- PASS: TestAcceptInvite_PasswordValidation/8_chars_valid (0.85s) +=== RUN TestUserEndpoints_RequireAdmin +=== RUN TestUserEndpoints_RequireAdmin/GET_/api/users +=== RUN TestUserEndpoints_RequireAdmin/POST_/api/users +=== RUN TestUserEndpoints_RequireAdmin/POST_/api/users/invite +=== RUN TestUserEndpoints_RequireAdmin/GET_/api/users/1 +=== RUN TestUserEndpoints_RequireAdmin/PUT_/api/users/1 +=== RUN TestUserEndpoints_RequireAdmin/DELETE_/api/users/1 +=== RUN TestUserEndpoints_RequireAdmin/PUT_/api/users/1/permissions +--- PASS: TestUserEndpoints_RequireAdmin (0.91s) + --- PASS: TestUserEndpoints_RequireAdmin/GET_/api/users (0.00s) + --- PASS: TestUserEndpoints_RequireAdmin/POST_/api/users (0.00s) + --- PASS: TestUserEndpoints_RequireAdmin/POST_/api/users/invite (0.00s) + --- PASS: TestUserEndpoints_RequireAdmin/GET_/api/users/1 (0.00s) + --- PASS: TestUserEndpoints_RequireAdmin/PUT_/api/users/1 (0.00s) + --- PASS: TestUserEndpoints_RequireAdmin/DELETE_/api/users/1 (0.00s) + --- PASS: TestUserEndpoints_RequireAdmin/PUT_/api/users/1/permissions (0.00s) +=== RUN TestSMTPEndpoints_RequireAdmin +=== RUN TestSMTPEndpoints_RequireAdmin/POST_/api/settings/smtp +=== RUN TestSMTPEndpoints_RequireAdmin/POST_/api/settings/smtp/test +=== RUN TestSMTPEndpoints_RequireAdmin/POST_/api/settings/smtp/test-email +--- PASS: TestSMTPEndpoints_RequireAdmin (0.82s) + --- PASS: TestSMTPEndpoints_RequireAdmin/POST_/api/settings/smtp (0.00s) + --- PASS: TestSMTPEndpoints_RequireAdmin/POST_/api/settings/smtp/test (0.00s) + --- PASS: TestSMTPEndpoints_RequireAdmin/POST_/api/settings/smtp/test-email (0.00s) +=== RUN TestSMTPConfig_PasswordMasked +--- PASS: TestSMTPConfig_PasswordMasked (0.78s) +=== RUN TestSMTPConfig_PortValidation +=== RUN TestSMTPConfig_PortValidation/port_0_invalid +=== RUN TestSMTPConfig_PortValidation/port_-1_invalid +=== RUN TestSMTPConfig_PortValidation/port_65536_invalid +=== RUN TestSMTPConfig_PortValidation/port_587_valid +=== RUN TestSMTPConfig_PortValidation/port_465_valid +=== RUN TestSMTPConfig_PortValidation/port_25_valid +--- PASS: TestSMTPConfig_PortValidation (0.99s) + --- PASS: TestSMTPConfig_PortValidation/port_0_invalid (0.00s) + --- PASS: TestSMTPConfig_PortValidation/port_-1_invalid (0.00s) + --- PASS: TestSMTPConfig_PortValidation/port_65536_invalid (0.00s) + --- PASS: TestSMTPConfig_PortValidation/port_587_valid (0.01s) + --- PASS: TestSMTPConfig_PortValidation/port_465_valid (0.00s) + --- PASS: TestSMTPConfig_PortValidation/port_25_valid (0.00s) +=== RUN TestSMTPConfig_EncryptionValidation +=== RUN TestSMTPConfig_EncryptionValidation/empty_encryption_invalid +=== RUN TestSMTPConfig_EncryptionValidation/invalid_encryption +=== RUN TestSMTPConfig_EncryptionValidation/tls_lowercase_valid +=== RUN TestSMTPConfig_EncryptionValidation/starttls_valid +=== RUN TestSMTPConfig_EncryptionValidation/none_valid +--- PASS: TestSMTPConfig_EncryptionValidation (2.40s) + --- PASS: TestSMTPConfig_EncryptionValidation/empty_encryption_invalid (0.00s) + --- PASS: TestSMTPConfig_EncryptionValidation/invalid_encryption (0.00s) + --- PASS: TestSMTPConfig_EncryptionValidation/tls_lowercase_valid (0.00s) + --- PASS: TestSMTPConfig_EncryptionValidation/starttls_valid (0.00s) + --- PASS: TestSMTPConfig_EncryptionValidation/none_valid (0.00s) +=== RUN TestInviteUser_DuplicateEmailBlocked +--- PASS: TestInviteUser_DuplicateEmailBlocked (0.93s) +=== RUN TestInviteUser_EmailCaseInsensitive +--- PASS: TestInviteUser_EmailCaseInsensitive (0.82s) +=== RUN TestDeleteUser_CannotDeleteSelf +--- PASS: TestDeleteUser_CannotDeleteSelf (0.83s) +=== RUN TestUpdatePermissions_ValidModes +=== RUN TestUpdatePermissions_ValidModes/allow_all_valid +=== RUN TestUpdatePermissions_ValidModes/deny_all_valid +=== RUN TestUpdatePermissions_ValidModes/invalid_mode +=== RUN TestUpdatePermissions_ValidModes/empty_mode +--- PASS: TestUpdatePermissions_ValidModes (0.85s) + --- PASS: TestUpdatePermissions_ValidModes/allow_all_valid (0.00s) + --- PASS: TestUpdatePermissions_ValidModes/deny_all_valid (0.00s) + --- PASS: TestUpdatePermissions_ValidModes/invalid_mode (0.00s) + --- PASS: TestUpdatePermissions_ValidModes/empty_mode (0.00s) +=== RUN TestPublicEndpoints_NoAuthRequired +--- PASS: TestPublicEndpoints_NoAuthRequired (0.81s) +PASS +coverage: [no statements] +ok github.com/Wikid82/charon/backend/internal/api/tests (cached) coverage: [no statements] +=== RUN TestClient_Load_Success +--- PASS: TestClient_Load_Success (0.01s) +=== RUN TestClient_Load_Failure +--- PASS: TestClient_Load_Failure (0.01s) +=== RUN TestClient_GetConfig_Success +--- PASS: TestClient_GetConfig_Success (0.00s) +=== RUN TestClient_Ping_Success +--- PASS: TestClient_Ping_Success (0.00s) +=== RUN TestClient_Ping_Unreachable +--- PASS: TestClient_Ping_Unreachable (0.00s) +=== RUN TestClient_Load_CreateRequestFailure +--- PASS: TestClient_Load_CreateRequestFailure (0.00s) +=== RUN TestClient_Ping_CreateRequestFailure +--- PASS: TestClient_Ping_CreateRequestFailure (0.00s) +=== RUN TestClient_GetConfig_Failure +--- PASS: TestClient_GetConfig_Failure (0.00s) +=== RUN TestClient_GetConfig_InvalidJSON +--- PASS: TestClient_GetConfig_InvalidJSON (0.00s) +=== RUN TestClient_Ping_Failure +--- PASS: TestClient_Ping_Failure (0.00s) +=== RUN TestClient_RequestCreationErrors +--- PASS: TestClient_RequestCreationErrors (0.00s) +=== RUN TestClient_NetworkErrors +--- PASS: TestClient_NetworkErrors (0.00s) +=== RUN TestClient_Load_MarshalFailure +--- PASS: TestClient_Load_MarshalFailure (0.00s) +=== RUN TestClient_Ping_TransportError +--- PASS: TestClient_Ping_TransportError (0.00s) +=== RUN TestClient_GetConfig_BaseURLNil_ReturnsError +--- PASS: TestClient_GetConfig_BaseURLNil_ReturnsError (0.00s) +=== RUN TestClient_RequestCreationErrors_FromInvalidResolvedURL +--- PASS: TestClient_RequestCreationErrors_FromInvalidResolvedURL (0.00s) +=== RUN TestBuildACLHandler_GeoBlacklist +--- PASS: TestBuildACLHandler_GeoBlacklist (0.00s) +=== RUN TestBuildACLHandler_UnknownTypeReturnsNil +--- PASS: TestBuildACLHandler_UnknownTypeReturnsNil (0.00s) +=== RUN TestBuildACLHandler_GeoWhitelist +--- PASS: TestBuildACLHandler_GeoWhitelist (0.00s) +=== RUN TestBuildACLHandler_LocalNetwork +--- PASS: TestBuildACLHandler_LocalNetwork (0.00s) +=== RUN TestBuildACLHandler_IPRules +--- PASS: TestBuildACLHandler_IPRules (0.00s) +=== RUN TestBuildACLHandler_InvalidIPJSON +--- PASS: TestBuildACLHandler_InvalidIPJSON (0.00s) +=== RUN TestBuildACLHandler_NoIPRulesReturnsNil +--- PASS: TestBuildACLHandler_NoIPRulesReturnsNil (0.00s) +=== RUN TestBuildACLHandler_Whitelist +--- PASS: TestBuildACLHandler_Whitelist (0.00s) +=== RUN TestBuildCrowdSecHandler_Disabled +--- PASS: TestBuildCrowdSecHandler_Disabled (0.00s) +=== RUN TestBuildCrowdSecHandler_EnabledWithoutConfig +--- PASS: TestBuildCrowdSecHandler_EnabledWithoutConfig (0.00s) +=== RUN TestBuildCrowdSecHandler_EnabledWithEmptyAPIURL +--- PASS: TestBuildCrowdSecHandler_EnabledWithEmptyAPIURL (0.00s) +=== RUN TestBuildCrowdSecHandler_EnabledWithCustomAPIURL +--- PASS: TestBuildCrowdSecHandler_EnabledWithCustomAPIURL (0.00s) +=== RUN TestBuildCrowdSecHandler_JSONFormat +--- PASS: TestBuildCrowdSecHandler_JSONFormat (0.00s) +=== RUN TestBuildCrowdSecHandler_WithHost +--- PASS: TestBuildCrowdSecHandler_WithHost (0.00s) +=== RUN TestGenerateConfig_WithCrowdSec +--- PASS: TestGenerateConfig_WithCrowdSec (0.00s) +=== RUN TestGenerateConfig_CrowdSecDisabled +--- PASS: TestGenerateConfig_CrowdSecDisabled (0.00s) +=== RUN TestGenerateConfig_CatchAllFrontend +--- PASS: TestGenerateConfig_CatchAllFrontend (0.00s) +=== RUN TestGenerateConfig_AdvancedInvalidJSON +time="2026-01-10T02:16:57Z" level=warning msg="Failed to parse advanced_config for host" error="invalid character 'i' looking for beginning of object key string" host=adv1 +--- PASS: TestGenerateConfig_AdvancedInvalidJSON (0.00s) +=== RUN TestGenerateConfig_AdvancedArrayHandler +--- PASS: TestGenerateConfig_AdvancedArrayHandler (0.00s) +=== RUN TestGenerateConfig_LowercaseDomains +--- PASS: TestGenerateConfig_LowercaseDomains (0.00s) +=== RUN TestGenerateConfig_AdvancedObjectHandler +--- PASS: TestGenerateConfig_AdvancedObjectHandler (0.00s) +=== RUN TestGenerateConfig_AdvancedHeadersStringToArray +--- PASS: TestGenerateConfig_AdvancedHeadersStringToArray (0.00s) +=== RUN TestGenerateConfig_ACLWhitelistIncluded +--- PASS: TestGenerateConfig_ACLWhitelistIncluded (0.00s) +=== RUN TestGenerateConfig_SkipsEmptyDomainEntries +--- PASS: TestGenerateConfig_SkipsEmptyDomainEntries (0.00s) +=== RUN TestGenerateConfig_AdvancedNoHandlerKey +time="2026-01-10T02:16:57Z" level=warning msg="advanced_config for host is not a handler object" host=adv3 +--- PASS: TestGenerateConfig_AdvancedNoHandlerKey (0.00s) +=== RUN TestGenerateConfig_AdvancedUnexpectedJSONStructure +time="2026-01-10T02:16:57Z" level=warning msg="advanced_config for host has unexpected JSON structure" host=adv4 +--- PASS: TestGenerateConfig_AdvancedUnexpectedJSONStructure (0.00s) +=== RUN TestBuildACLHandler_UnknownIPTypeReturnsNil +--- PASS: TestBuildACLHandler_UnknownIPTypeReturnsNil (0.00s) +=== RUN TestGenerateConfig_SecurityPipeline_Order +--- PASS: TestGenerateConfig_SecurityPipeline_Order (0.00s) +=== RUN TestGenerateConfig_SecurityPipeline_OmitWhenDisabled +--- PASS: TestGenerateConfig_SecurityPipeline_OmitWhenDisabled (0.00s) +=== RUN TestGetAccessLogPath +=== RUN TestGetAccessLogPath/CrowdSecEnabled_UsesStandardPath +=== RUN TestGetAccessLogPath/Production_UsesStandardPath +=== RUN TestGetAccessLogPath/Development_UsesRelativePath +=== RUN TestGetAccessLogPath/NoEnv_CrowdSecEnabled_UsesStandardPath +--- PASS: TestGetAccessLogPath (0.00s) + --- PASS: TestGetAccessLogPath/CrowdSecEnabled_UsesStandardPath (0.00s) + --- PASS: TestGetAccessLogPath/Production_UsesStandardPath (0.00s) + --- PASS: TestGetAccessLogPath/Development_UsesRelativePath (0.00s) + --- PASS: TestGetAccessLogPath/NoEnv_CrowdSecEnabled_UsesStandardPath (0.00s) +=== RUN TestGenerateConfig_LoggingConfigured +--- PASS: TestGenerateConfig_LoggingConfigured (0.00s) +=== RUN TestGenerateConfig_ZerosslAndBothProviders +--- PASS: TestGenerateConfig_ZerosslAndBothProviders (0.00s) +=== RUN TestGenerateConfig_SecurityPipeline_Order_Locations +--- PASS: TestGenerateConfig_SecurityPipeline_Order_Locations (0.00s) +=== RUN TestGenerateConfig_ACLLogWarning +--- PASS: TestGenerateConfig_ACLLogWarning (0.00s) +=== RUN TestGenerateConfig_ACLHandlerIncluded +--- PASS: TestGenerateConfig_ACLHandlerIncluded (0.00s) +=== RUN TestGenerateConfig_DecisionsBlockWithAdminExclusion + config_generate_additional_test.go:147: handles: [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "Access denied: Blocked by security decision", + "handler": "static_response", + "status_code": 403 + } + ], + "match": [ + { + "remote_ip": { + "ranges": [ + "1.2.3.4" + ] + } + }, + { + "not": [ + { + "remote_ip": { + "ranges": [ + "10.0.0.1/32" + ] + } + } + ] + } + ], + "terminal": true + } + ] + }, + { + "flush_interval": -1, + "handler": "reverse_proxy", + "headers": { + "request": { + "set": { + "X-Forwarded-Host": [ + "{http.request.host}" + ], + "X-Forwarded-Port": [ + "{http.request.port}" + ], + "X-Forwarded-Proto": [ + "{http.request.scheme}" + ], + "X-Real-IP": [ + "{http.request.remote.host}" + ] + } + } + }, + "upstreams": [ + { + "dial": "app:8080" + } + ] + } + ] +--- PASS: TestGenerateConfig_DecisionsBlockWithAdminExclusion (0.00s) +=== RUN TestGenerateConfig_WAFModeAndRulesetReference +--- PASS: TestGenerateConfig_WAFModeAndRulesetReference (0.00s) +=== RUN TestGenerateConfig_WAFModeDisabledSkipsHandler +--- PASS: TestGenerateConfig_WAFModeDisabledSkipsHandler (0.00s) +=== RUN TestGenerateConfig_WAFSelectedSetsContentAndMode +--- PASS: TestGenerateConfig_WAFSelectedSetsContentAndMode (0.00s) +=== RUN TestGenerateConfig_DecisionAdminPartsEmpty +--- PASS: TestGenerateConfig_DecisionAdminPartsEmpty (0.00s) +=== RUN TestNormalizeHeaderOps_PreserveStringArray +--- PASS: TestNormalizeHeaderOps_PreserveStringArray (0.00s) +=== RUN TestGenerateConfig_WAFUsesRuleSet +--- PASS: TestGenerateConfig_WAFUsesRuleSet (0.00s) +=== RUN TestGenerateConfig_WAFUsesRuleSetFromAdvancedConfig +--- PASS: TestGenerateConfig_WAFUsesRuleSetFromAdvancedConfig (0.00s) +=== RUN TestGenerateConfig_WAFUsesRuleSetFromAdvancedConfig_Array +--- PASS: TestGenerateConfig_WAFUsesRuleSetFromAdvancedConfig_Array (0.00s) +=== RUN TestGenerateConfig_WAFUsesRulesetFromSecCfgFallback +--- PASS: TestGenerateConfig_WAFUsesRulesetFromSecCfgFallback (0.00s) +=== RUN TestGenerateConfig_RateLimitFromSecCfg +--- PASS: TestGenerateConfig_RateLimitFromSecCfg (0.00s) +=== RUN TestGenerateConfig_CrowdSecHandlerFromSecCfg +--- PASS: TestGenerateConfig_CrowdSecHandlerFromSecCfg (0.00s) +=== RUN TestGenerateConfig_EmptyHostsAndNoFrontend +--- PASS: TestGenerateConfig_EmptyHostsAndNoFrontend (0.00s) +=== RUN TestGenerateConfig_SkipsInvalidCustomCert +--- PASS: TestGenerateConfig_SkipsInvalidCustomCert (0.00s) +=== RUN TestGenerateConfig_SkipsDuplicateDomains +--- PASS: TestGenerateConfig_SkipsDuplicateDomains (0.00s) +=== RUN TestGenerateConfig_LoadPEMSetsTLSWhenNoACME +--- PASS: TestGenerateConfig_LoadPEMSetsTLSWhenNoACME (0.00s) +=== RUN TestGenerateConfig_DefaultAcmeStaging +--- PASS: TestGenerateConfig_DefaultAcmeStaging (0.00s) +=== RUN TestGenerateConfig_ACLHandlerBuildError +--- PASS: TestGenerateConfig_ACLHandlerBuildError (0.00s) +=== RUN TestGenerateConfig_SkipHostDomainEmptyAndDisabled +--- PASS: TestGenerateConfig_SkipHostDomainEmptyAndDisabled (0.00s) +=== RUN TestGenerateConfig_CustomCertsAndTLS +--- PASS: TestGenerateConfig_CustomCertsAndTLS (0.00s) +=== RUN TestGenerateConfig_DNSChallenge_LetsEncrypt_StagingCAAndPropagationTimeout +--- PASS: TestGenerateConfig_DNSChallenge_LetsEncrypt_StagingCAAndPropagationTimeout (0.00s) +=== RUN TestGenerateConfig_DNSChallenge_ZeroSSL_IssuerShape +--- PASS: TestGenerateConfig_DNSChallenge_ZeroSSL_IssuerShape (0.00s) +=== RUN TestGenerateConfig_DNSChallenge_SkipsPolicyWhenProviderConfigMissing +--- PASS: TestGenerateConfig_DNSChallenge_SkipsPolicyWhenProviderConfigMissing (0.00s) +=== RUN TestGenerateConfig_HTTPChallenge_ExcludesIPDomains +--- PASS: TestGenerateConfig_HTTPChallenge_ExcludesIPDomains (0.00s) +=== RUN TestGetCrowdSecAPIKey_EnvPriority +--- PASS: TestGetCrowdSecAPIKey_EnvPriority (0.00s) +=== RUN TestHasWildcard_TrueFalse +--- PASS: TestHasWildcard_TrueFalse (0.00s) +=== RUN TestGenerateConfig_MultiCredential_ZoneSpecificPolicies +--- PASS: TestGenerateConfig_MultiCredential_ZoneSpecificPolicies (0.00s) +=== RUN TestGenerateConfig_MultiCredential_ZeroSSL_Issuer +--- PASS: TestGenerateConfig_MultiCredential_ZeroSSL_Issuer (0.00s) +=== RUN TestGenerateConfig_MultiCredential_BothIssuers +--- PASS: TestGenerateConfig_MultiCredential_BothIssuers (0.00s) +=== RUN TestGenerateConfig_MultiCredential_ACMEStaging +--- PASS: TestGenerateConfig_MultiCredential_ACMEStaging (0.00s) +=== RUN TestGenerateConfig_MultiCredential_NoMatchingDomains +--- PASS: TestGenerateConfig_MultiCredential_NoMatchingDomains (0.00s) +=== RUN TestGenerateConfig_MultiCredential_ProviderTypeNotFound +--- PASS: TestGenerateConfig_MultiCredential_ProviderTypeNotFound (0.00s) +=== RUN TestGenerateConfig_MultiCredential_SupportsMultiCredential_UsesZoneConfigAndStagingBothIssuers +--- PASS: TestGenerateConfig_MultiCredential_SupportsMultiCredential_UsesZoneConfigAndStagingBothIssuers (0.00s) +=== RUN TestGenerateConfig_DNSChallenge_SingleCredential_BothIssuers_ACMEStaging +--- PASS: TestGenerateConfig_DNSChallenge_SingleCredential_BothIssuers_ACMEStaging (0.00s) +=== RUN TestGenerateConfig_DNSChallenge_SingleCredential_ProviderTypeNotFound_SkipsPolicy +--- PASS: TestGenerateConfig_DNSChallenge_SingleCredential_ProviderTypeNotFound_SkipsPolicy (0.00s) +=== RUN TestGenerateConfig_DefaultPolicy_LetsEncrypt_StagingCA +--- PASS: TestGenerateConfig_DefaultPolicy_LetsEncrypt_StagingCA (0.00s) +=== RUN TestGenerateConfig_DefaultPolicy_ZeroSSL_Issuer +--- PASS: TestGenerateConfig_DefaultPolicy_ZeroSSL_Issuer (0.00s) +=== RUN TestGenerateConfig_DefaultPolicy_BothIssuers_StagingCA +--- PASS: TestGenerateConfig_DefaultPolicy_BothIssuers_StagingCA (0.00s) +=== RUN TestGenerateConfig_IPSubjects_InitializesTLSAppAndAutomation +--- PASS: TestGenerateConfig_IPSubjects_InitializesTLSAppAndAutomation (0.00s) +=== RUN TestGetAccessLogPath_DockerEnv_UsesCrowdSecPath +--- PASS: TestGetAccessLogPath_DockerEnv_UsesCrowdSecPath (0.00s) +=== RUN TestBuildSecurityHeadersHandler_AllEnabled +--- PASS: TestBuildSecurityHeadersHandler_AllEnabled (0.00s) +=== RUN TestBuildSecurityHeadersHandler_HSTSOnly +--- PASS: TestBuildSecurityHeadersHandler_HSTSOnly (0.00s) +=== RUN TestBuildSecurityHeadersHandler_CSPOnly +--- PASS: TestBuildSecurityHeadersHandler_CSPOnly (0.00s) +=== RUN TestBuildSecurityHeadersHandler_CSPReportOnly +--- PASS: TestBuildSecurityHeadersHandler_CSPReportOnly (0.00s) +=== RUN TestBuildSecurityHeadersHandler_NoProfile +--- PASS: TestBuildSecurityHeadersHandler_NoProfile (0.00s) +=== RUN TestBuildSecurityHeadersHandler_Disabled +--- PASS: TestBuildSecurityHeadersHandler_Disabled (0.00s) +=== RUN TestBuildSecurityHeadersHandler_NilHost +--- PASS: TestBuildSecurityHeadersHandler_NilHost (0.00s) +=== RUN TestBuildCSPString +=== RUN TestBuildCSPString/simple_CSP +=== RUN TestBuildCSPString/multiple_directives +=== RUN TestBuildCSPString/empty_string +=== RUN TestBuildCSPString/invalid_JSON +--- PASS: TestBuildCSPString (0.00s) + --- PASS: TestBuildCSPString/simple_CSP (0.00s) + --- PASS: TestBuildCSPString/multiple_directives (0.00s) + --- PASS: TestBuildCSPString/empty_string (0.00s) + --- PASS: TestBuildCSPString/invalid_JSON (0.00s) +=== RUN TestBuildPermissionsPolicyString +=== RUN TestBuildPermissionsPolicyString/single_feature_no_allowlist +=== RUN TestBuildPermissionsPolicyString/single_feature_with_self +=== RUN TestBuildPermissionsPolicyString/multiple_features +=== RUN TestBuildPermissionsPolicyString/empty_string +=== RUN TestBuildPermissionsPolicyString/invalid_JSON +--- PASS: TestBuildPermissionsPolicyString (0.00s) + --- PASS: TestBuildPermissionsPolicyString/single_feature_no_allowlist (0.00s) + --- PASS: TestBuildPermissionsPolicyString/single_feature_with_self (0.00s) + --- PASS: TestBuildPermissionsPolicyString/multiple_features (0.00s) + --- PASS: TestBuildPermissionsPolicyString/empty_string (0.00s) + --- PASS: TestBuildPermissionsPolicyString/invalid_JSON (0.00s) +=== RUN TestGetDefaultSecurityHeaderProfile +--- PASS: TestGetDefaultSecurityHeaderProfile (0.00s) +=== RUN TestBuildSecurityHeadersHandler_PermissionsPolicy +--- PASS: TestBuildSecurityHeadersHandler_PermissionsPolicy (0.00s) +=== RUN TestBuildSecurityHeadersHandler_InvalidCSPJSON +--- PASS: TestBuildSecurityHeadersHandler_InvalidCSPJSON (0.00s) +=== RUN TestBuildSecurityHeadersHandler_InvalidPermissionsJSON +--- PASS: TestBuildSecurityHeadersHandler_InvalidPermissionsJSON (0.00s) +=== RUN TestBuildSecurityHeadersHandler_APIFriendlyPreset +--- PASS: TestBuildSecurityHeadersHandler_APIFriendlyPreset (0.00s) +=== RUN TestGenerateConfig_Empty +--- PASS: TestGenerateConfig_Empty (0.00s) +=== RUN TestGenerateConfig_SingleHost +--- PASS: TestGenerateConfig_SingleHost (0.00s) +=== RUN TestGenerateConfig_MultipleHosts +--- PASS: TestGenerateConfig_MultipleHosts (0.00s) +=== RUN TestGenerateConfig_WebSocketEnabled +--- PASS: TestGenerateConfig_WebSocketEnabled (0.00s) +=== RUN TestGenerateConfig_EmptyDomain +--- PASS: TestGenerateConfig_EmptyDomain (0.00s) +=== RUN TestGenerateConfig_Logging +--- PASS: TestGenerateConfig_Logging (0.00s) +=== RUN TestGenerateConfig_IPHostsSkipAutoHTTPS +--- PASS: TestGenerateConfig_IPHostsSkipAutoHTTPS (0.00s) +=== RUN TestGenerateConfig_Advanced +--- PASS: TestGenerateConfig_Advanced (0.00s) +=== RUN TestGenerateConfig_ACMEStaging +--- PASS: TestGenerateConfig_ACMEStaging (0.00s) +=== RUN TestBuildACLHandler_WhitelistAndBlacklistAdminMerge +--- PASS: TestBuildACLHandler_WhitelistAndBlacklistAdminMerge (0.00s) +=== RUN TestBuildACLHandler_GeoAndLocalNetwork +--- PASS: TestBuildACLHandler_GeoAndLocalNetwork (0.00s) +=== RUN TestBuildACLHandler_AdminWhitelistParsing +--- PASS: TestBuildACLHandler_AdminWhitelistParsing (0.00s) +=== RUN TestBuildRateLimitHandler_Disabled +--- PASS: TestBuildRateLimitHandler_Disabled (0.00s) +=== RUN TestBuildRateLimitHandler_InvalidValues +--- PASS: TestBuildRateLimitHandler_InvalidValues (0.00s) +=== RUN TestBuildRateLimitHandler_ValidConfig +--- PASS: TestBuildRateLimitHandler_ValidConfig (0.00s) +=== RUN TestBuildRateLimitHandler_JSONFormat +--- PASS: TestBuildRateLimitHandler_JSONFormat (0.00s) +=== RUN TestGenerateConfig_WithRateLimiting +--- PASS: TestGenerateConfig_WithRateLimiting (0.00s) +=== RUN TestBuildRateLimitHandler_UsesBurst +--- PASS: TestBuildRateLimitHandler_UsesBurst (0.00s) +=== RUN TestBuildRateLimitHandler_DefaultBurst +--- PASS: TestBuildRateLimitHandler_DefaultBurst (0.00s) +=== RUN TestGetAccessLogPath_CrowdSecEnabled +--- PASS: TestGetAccessLogPath_CrowdSecEnabled (0.00s) +=== RUN TestGetAccessLogPath_DockerEnv +--- PASS: TestGetAccessLogPath_DockerEnv (0.00s) +=== RUN TestGetAccessLogPath_Development +--- PASS: TestGetAccessLogPath_Development (0.00s) +=== RUN TestBuildPermissionsPolicyString_EmptyAllowlist +--- PASS: TestBuildPermissionsPolicyString_EmptyAllowlist (0.00s) +=== RUN TestBuildPermissionsPolicyString_SelfAndStar +--- PASS: TestBuildPermissionsPolicyString_SelfAndStar (0.00s) +=== RUN TestBuildPermissionsPolicyString_DomainValues +--- PASS: TestBuildPermissionsPolicyString_DomainValues (0.00s) +=== RUN TestBuildPermissionsPolicyString_Mixed +--- PASS: TestBuildPermissionsPolicyString_Mixed (0.00s) +=== RUN TestBuildPermissionsPolicyString_InvalidJSON +--- PASS: TestBuildPermissionsPolicyString_InvalidJSON (0.00s) +=== RUN TestBuildCSPString_EmptyDirective +--- PASS: TestBuildCSPString_EmptyDirective (0.00s) +=== RUN TestBuildCSPString_InvalidJSON +--- PASS: TestBuildCSPString_InvalidJSON (0.00s) +=== RUN TestBuildSecurityHeadersHandler_CompleteProfile +--- PASS: TestBuildSecurityHeadersHandler_CompleteProfile (0.00s) +=== RUN TestGenerateConfig_SSLProviderZeroSSL +--- PASS: TestGenerateConfig_SSLProviderZeroSSL (0.00s) +=== RUN TestGenerateConfig_SSLProviderBoth +--- PASS: TestGenerateConfig_SSLProviderBoth (0.00s) +=== RUN TestGenerateConfig_DuplicateDomains +--- PASS: TestGenerateConfig_DuplicateDomains (0.00s) +=== RUN TestGenerateConfig_WithCrowdSecApp +--- PASS: TestGenerateConfig_WithCrowdSecApp (0.00s) +=== RUN TestGenerateConfig_CrowdSecHandlerAdded +--- PASS: TestGenerateConfig_CrowdSecHandlerAdded (0.00s) +=== RUN TestGenerateConfig_WithSecurityDecisions +--- PASS: TestGenerateConfig_WithSecurityDecisions (0.00s) +=== RUN TestBuildRateLimitHandler_BypassList +--- PASS: TestBuildRateLimitHandler_BypassList (0.00s) +=== RUN TestBuildRateLimitHandler_BypassList_PlainIPs +--- PASS: TestBuildRateLimitHandler_BypassList_PlainIPs (0.00s) +=== RUN TestBuildRateLimitHandler_BypassList_InvalidEntries +--- PASS: TestBuildRateLimitHandler_BypassList_InvalidEntries (0.00s) +=== RUN TestBuildRateLimitHandler_BypassList_Empty +--- PASS: TestBuildRateLimitHandler_BypassList_Empty (0.00s) +=== RUN TestBuildRateLimitHandler_BypassList_AllInvalid +--- PASS: TestBuildRateLimitHandler_BypassList_AllInvalid (0.00s) +=== RUN TestParseBypassCIDRs +=== RUN TestParseBypassCIDRs/empty +=== RUN TestParseBypassCIDRs/single_cidr +=== RUN TestParseBypassCIDRs/multiple_cidrs +=== RUN TestParseBypassCIDRs/plain_ipv4 +=== RUN TestParseBypassCIDRs/plain_ipv6 +=== RUN TestParseBypassCIDRs/mixed +=== RUN TestParseBypassCIDRs/with_spaces +=== RUN TestParseBypassCIDRs/all_invalid +--- PASS: TestParseBypassCIDRs (0.00s) + --- PASS: TestParseBypassCIDRs/empty (0.00s) + --- PASS: TestParseBypassCIDRs/single_cidr (0.00s) + --- PASS: TestParseBypassCIDRs/multiple_cidrs (0.00s) + --- PASS: TestParseBypassCIDRs/plain_ipv4 (0.00s) + --- PASS: TestParseBypassCIDRs/plain_ipv6 (0.00s) + --- PASS: TestParseBypassCIDRs/mixed (0.00s) + --- PASS: TestParseBypassCIDRs/with_spaces (0.00s) + --- PASS: TestParseBypassCIDRs/all_invalid (0.00s) +=== RUN TestBuildWAFHandler_ParanoiaLevel +=== RUN TestBuildWAFHandler_ParanoiaLevel/level_1_default +=== RUN TestBuildWAFHandler_ParanoiaLevel/level_1_explicit +=== RUN TestBuildWAFHandler_ParanoiaLevel/level_2 +=== RUN TestBuildWAFHandler_ParanoiaLevel/level_3 +=== RUN TestBuildWAFHandler_ParanoiaLevel/level_4_max +=== RUN TestBuildWAFHandler_ParanoiaLevel/level_invalid_high +=== RUN TestBuildWAFHandler_ParanoiaLevel/level_invalid_neg +--- PASS: TestBuildWAFHandler_ParanoiaLevel (0.00s) + --- PASS: TestBuildWAFHandler_ParanoiaLevel/level_1_default (0.00s) + --- PASS: TestBuildWAFHandler_ParanoiaLevel/level_1_explicit (0.00s) + --- PASS: TestBuildWAFHandler_ParanoiaLevel/level_2 (0.00s) + --- PASS: TestBuildWAFHandler_ParanoiaLevel/level_3 (0.00s) + --- PASS: TestBuildWAFHandler_ParanoiaLevel/level_4_max (0.00s) + --- PASS: TestBuildWAFHandler_ParanoiaLevel/level_invalid_high (0.00s) + --- PASS: TestBuildWAFHandler_ParanoiaLevel/level_invalid_neg (0.00s) +=== RUN TestBuildWAFHandler_Exclusions +--- PASS: TestBuildWAFHandler_Exclusions (0.00s) +=== RUN TestBuildWAFHandler_ExclusionsWithTarget +--- PASS: TestBuildWAFHandler_ExclusionsWithTarget (0.00s) +=== RUN TestBuildWAFHandler_PerHostDisabled +--- PASS: TestBuildWAFHandler_PerHostDisabled (0.00s) +=== RUN TestBuildWAFHandler_MonitorMode +--- PASS: TestBuildWAFHandler_MonitorMode (0.00s) +=== RUN TestBuildWAFHandler_GlobalDisabled +--- PASS: TestBuildWAFHandler_GlobalDisabled (0.00s) +=== RUN TestBuildWAFHandler_NoRuleset +--- PASS: TestBuildWAFHandler_NoRuleset (0.00s) +=== RUN TestParseWAFExclusions +=== RUN TestParseWAFExclusions/empty +=== RUN TestParseWAFExclusions/single_exclusion +=== RUN TestParseWAFExclusions/multiple_exclusions +=== RUN TestParseWAFExclusions/invalid_json +--- PASS: TestParseWAFExclusions (0.00s) + --- PASS: TestParseWAFExclusions/empty (0.00s) + --- PASS: TestParseWAFExclusions/single_exclusion (0.00s) + --- PASS: TestParseWAFExclusions/multiple_exclusions (0.00s) + --- PASS: TestParseWAFExclusions/invalid_json (0.00s) +=== RUN TestGenerateConfig_WithWAFPerHostDisabled +--- PASS: TestGenerateConfig_WithWAFPerHostDisabled (0.00s) +=== RUN TestGenerateConfig_WithDisabledHost +--- PASS: TestGenerateConfig_WithDisabledHost (0.00s) +=== RUN TestGenerateConfig_WithFrontendDir +--- PASS: TestGenerateConfig_WithFrontendDir (0.00s) +=== RUN TestGenerateConfig_CustomCertificate +--- PASS: TestGenerateConfig_CustomCertificate (0.00s) +=== RUN TestGenerateConfig_CustomCertificateMissingData +--- PASS: TestGenerateConfig_CustomCertificateMissingData (0.00s) +=== RUN TestGenerateConfig_LetsEncryptCertificateNotLoaded +--- PASS: TestGenerateConfig_LetsEncryptCertificateNotLoaded (0.00s) +=== RUN TestGenerateConfig_NormalizeAdvancedConfig +--- PASS: TestGenerateConfig_NormalizeAdvancedConfig (0.00s) +=== RUN TestGenerateConfig_NoACMEEmailNoTLS +--- PASS: TestGenerateConfig_NoACMEEmailNoTLS (0.00s) +=== RUN TestGenerateConfig_SecurityDecisionsWithAdminWhitelist +--- PASS: TestGenerateConfig_SecurityDecisionsWithAdminWhitelist (0.00s) +=== RUN TestBuildSecurityHeadersHandler_DefaultProfile +--- PASS: TestBuildSecurityHeadersHandler_DefaultProfile (0.00s) +=== RUN TestHasWildcard +=== RUN TestHasWildcard/no_wildcard +=== RUN TestHasWildcard/with_wildcard +=== RUN TestHasWildcard/only_wildcard +=== RUN TestHasWildcard/empty +--- PASS: TestHasWildcard (0.00s) + --- PASS: TestHasWildcard/no_wildcard (0.00s) + --- PASS: TestHasWildcard/with_wildcard (0.00s) + --- PASS: TestHasWildcard/only_wildcard (0.00s) + --- PASS: TestHasWildcard/empty (0.00s) +=== RUN TestDedupeDomains +=== RUN TestDedupeDomains/no_dupes +=== RUN TestDedupeDomains/with_dupes +=== RUN TestDedupeDomains/all_dupes +=== RUN TestDedupeDomains/empty +--- PASS: TestDedupeDomains (0.00s) + --- PASS: TestDedupeDomains/no_dupes (0.00s) + --- PASS: TestDedupeDomains/with_dupes (0.00s) + --- PASS: TestDedupeDomains/all_dupes (0.00s) + --- PASS: TestDedupeDomains/empty (0.00s) +=== RUN TestNormalizeAdvancedConfig_NestedRoutes +--- PASS: TestNormalizeAdvancedConfig_NestedRoutes (0.00s) +=== RUN TestNormalizeAdvancedConfig_ArrayInput +--- PASS: TestNormalizeAdvancedConfig_ArrayInput (0.00s) +=== RUN TestGetCrowdSecAPIKey +--- PASS: TestGetCrowdSecAPIKey (0.00s) +=== RUN TestBuildWAFHandler_PathTraversalAttack +=== RUN TestBuildWAFHandler_PathTraversalAttack/Path_traversal_in_ruleset_name +=== RUN TestBuildWAFHandler_PathTraversalAttack/Null_byte_injection +=== RUN TestBuildWAFHandler_PathTraversalAttack/URL_encoded_traversal +--- PASS: TestBuildWAFHandler_PathTraversalAttack (0.00s) + --- PASS: TestBuildWAFHandler_PathTraversalAttack/Path_traversal_in_ruleset_name (0.00s) + --- PASS: TestBuildWAFHandler_PathTraversalAttack/Null_byte_injection (0.00s) + --- PASS: TestBuildWAFHandler_PathTraversalAttack/URL_encoded_traversal (0.00s) +=== RUN TestBuildWAFHandler_SQLInjectionInRulesetName +=== RUN TestBuildWAFHandler_SQLInjectionInRulesetName/';_DROP_TABLE_rulesets;_-- +=== RUN TestBuildWAFHandler_SQLInjectionInRulesetName/1'_OR_'1'='1 +=== RUN TestBuildWAFHandler_SQLInjectionInRulesetName/UNION_SELECT_*_FROM_users-- +=== RUN TestBuildWAFHandler_SQLInjectionInRulesetName/admin'/* +--- PASS: TestBuildWAFHandler_SQLInjectionInRulesetName (0.00s) + --- PASS: TestBuildWAFHandler_SQLInjectionInRulesetName/';_DROP_TABLE_rulesets;_-- (0.00s) + --- PASS: TestBuildWAFHandler_SQLInjectionInRulesetName/1'_OR_'1'='1 (0.00s) + --- PASS: TestBuildWAFHandler_SQLInjectionInRulesetName/UNION_SELECT_*_FROM_users-- (0.00s) + --- PASS: TestBuildWAFHandler_SQLInjectionInRulesetName/admin'/* (0.00s) +=== RUN TestBuildWAFHandler_XSSInAdvancedConfig +=== RUN TestBuildWAFHandler_XSSInAdvancedConfig/{"ruleset_name":""} +=== RUN TestBuildWAFHandler_XSSInAdvancedConfig/{"ruleset_name":""} +=== RUN TestBuildWAFHandler_XSSInAdvancedConfig/{"ruleset_name":"javascript:alert(1)"} +=== RUN TestBuildWAFHandler_XSSInAdvancedConfig/{"ruleset_name":""} +--- PASS: TestBuildWAFHandler_XSSInAdvancedConfig (0.00s) + --- PASS: TestBuildWAFHandler_XSSInAdvancedConfig/{"ruleset_name":""} (0.00s) + --- PASS: TestBuildWAFHandler_XSSInAdvancedConfig/{"ruleset_name":""} (0.00s) + --- PASS: TestBuildWAFHandler_XSSInAdvancedConfig/{"ruleset_name":"javascript:alert(1)"} (0.00s) + --- PASS: TestBuildWAFHandler_XSSInAdvancedConfig/{"ruleset_name":""} (0.00s) +=== RUN TestBuildWAFHandler_HugePayload +--- PASS: TestBuildWAFHandler_HugePayload (0.00s) +=== RUN TestBuildWAFHandler_EmptyAndWhitespaceInputs +=== RUN TestBuildWAFHandler_EmptyAndWhitespaceInputs/Empty_string_WAFRulesSource +=== RUN TestBuildWAFHandler_EmptyAndWhitespaceInputs/Whitespace-only_WAFRulesSource +=== RUN TestBuildWAFHandler_EmptyAndWhitespaceInputs/Tab_and_newline_in_WAFRulesSource +--- PASS: TestBuildWAFHandler_EmptyAndWhitespaceInputs (0.00s) + --- PASS: TestBuildWAFHandler_EmptyAndWhitespaceInputs/Empty_string_WAFRulesSource (0.00s) + --- PASS: TestBuildWAFHandler_EmptyAndWhitespaceInputs/Whitespace-only_WAFRulesSource (0.00s) + --- PASS: TestBuildWAFHandler_EmptyAndWhitespaceInputs/Tab_and_newline_in_WAFRulesSource (0.00s) +=== RUN TestBuildWAFHandler_ConcurrentRulesetSelection +--- PASS: TestBuildWAFHandler_ConcurrentRulesetSelection (0.00s) +=== RUN TestBuildWAFHandler_NilSecCfg +--- PASS: TestBuildWAFHandler_NilSecCfg (0.00s) +=== RUN TestBuildWAFHandler_NilHost +--- PASS: TestBuildWAFHandler_NilHost (0.00s) +=== RUN TestBuildWAFHandler_SpecialCharactersInRulesetName +=== RUN TestBuildWAFHandler_SpecialCharactersInRulesetName/ruleset_with_spaces +=== RUN TestBuildWAFHandler_SpecialCharactersInRulesetName/ruleset/with/slashes +=== RUN TestBuildWAFHandler_SpecialCharactersInRulesetName/UPPERCASE-RULESET +=== RUN TestBuildWAFHandler_SpecialCharactersInRulesetName/ruleset_with_underscores +=== RUN TestBuildWAFHandler_SpecialCharactersInRulesetName/ruleset.with.dots +--- PASS: TestBuildWAFHandler_SpecialCharactersInRulesetName (0.00s) + --- PASS: TestBuildWAFHandler_SpecialCharactersInRulesetName/ruleset_with_spaces (0.00s) + --- PASS: TestBuildWAFHandler_SpecialCharactersInRulesetName/ruleset/with/slashes (0.00s) + --- PASS: TestBuildWAFHandler_SpecialCharactersInRulesetName/UPPERCASE-RULESET (0.00s) + --- PASS: TestBuildWAFHandler_SpecialCharactersInRulesetName/ruleset_with_underscores (0.00s) + --- PASS: TestBuildWAFHandler_SpecialCharactersInRulesetName/ruleset.with.dots (0.00s) +=== RUN TestBuildWAFHandler_RulesetSelectionPriority +=== RUN TestBuildWAFHandler_RulesetSelectionPriority/WAFRulesSource_takes_priority_over_owasp-crs +=== RUN TestBuildWAFHandler_RulesetSelectionPriority/hostRulesetName_takes_priority_over_owasp-crs +=== RUN TestBuildWAFHandler_RulesetSelectionPriority/host.Application_takes_priority_over_owasp-crs +=== RUN TestBuildWAFHandler_RulesetSelectionPriority/owasp-crs_used_as_fallback_when_no_other_match +=== RUN TestBuildWAFHandler_RulesetSelectionPriority/WAFRulesSource_takes_priority_over_host.Application_and_owasp-crs +--- PASS: TestBuildWAFHandler_RulesetSelectionPriority (0.00s) + --- PASS: TestBuildWAFHandler_RulesetSelectionPriority/WAFRulesSource_takes_priority_over_owasp-crs (0.00s) + --- PASS: TestBuildWAFHandler_RulesetSelectionPriority/hostRulesetName_takes_priority_over_owasp-crs (0.00s) + --- PASS: TestBuildWAFHandler_RulesetSelectionPriority/host.Application_takes_priority_over_owasp-crs (0.00s) + --- PASS: TestBuildWAFHandler_RulesetSelectionPriority/owasp-crs_used_as_fallback_when_no_other_match (0.00s) + --- PASS: TestBuildWAFHandler_RulesetSelectionPriority/WAFRulesSource_takes_priority_over_host.Application_and_owasp-crs (0.00s) +=== RUN TestBuildWAFHandler_NoDirectivesReturnsNil +=== RUN TestBuildWAFHandler_NoDirectivesReturnsNil/Empty_rulesets_returns_nil +=== RUN TestBuildWAFHandler_NoDirectivesReturnsNil/Ruleset_exists_but_no_path_mapping_returns_nil +=== RUN TestBuildWAFHandler_NoDirectivesReturnsNil/WAFRulesSource_specified_but_not_in_rulesets_or_paths_returns_nil +=== RUN TestBuildWAFHandler_NoDirectivesReturnsNil/Empty_path_in_rulesetPaths_returns_nil +--- PASS: TestBuildWAFHandler_NoDirectivesReturnsNil (0.00s) + --- PASS: TestBuildWAFHandler_NoDirectivesReturnsNil/Empty_rulesets_returns_nil (0.00s) + --- PASS: TestBuildWAFHandler_NoDirectivesReturnsNil/Ruleset_exists_but_no_path_mapping_returns_nil (0.00s) + --- PASS: TestBuildWAFHandler_NoDirectivesReturnsNil/WAFRulesSource_specified_but_not_in_rulesets_or_paths_returns_nil (0.00s) + --- PASS: TestBuildWAFHandler_NoDirectivesReturnsNil/Empty_path_in_rulesetPaths_returns_nil (0.00s) +=== RUN TestBuildWAFHandler_DisabledModes +=== RUN TestBuildWAFHandler_DisabledModes/wafEnabled_false_returns_nil +=== RUN TestBuildWAFHandler_DisabledModes/WAFMode_disabled_returns_nil +--- PASS: TestBuildWAFHandler_DisabledModes (0.00s) + --- PASS: TestBuildWAFHandler_DisabledModes/wafEnabled_false_returns_nil (0.00s) + --- PASS: TestBuildWAFHandler_DisabledModes/WAFMode_disabled_returns_nil (0.00s) +=== RUN TestBuildWAFHandler_HandlerStructure +--- PASS: TestBuildWAFHandler_HandlerStructure (0.00s) +=== RUN TestBuildWAFHandler_AdvancedConfigParsing +=== RUN TestBuildWAFHandler_AdvancedConfigParsing/Valid_ruleset_name_in_advanced_config +=== RUN TestBuildWAFHandler_AdvancedConfigParsing/Invalid_JSON_falls_back_to_owasp-crs +=== RUN TestBuildWAFHandler_AdvancedConfigParsing/Empty_advanced_config_falls_back_to_owasp-crs +=== RUN TestBuildWAFHandler_AdvancedConfigParsing/Empty_ruleset_name_string_falls_back_to_owasp-crs +=== RUN TestBuildWAFHandler_AdvancedConfigParsing/Non-string_ruleset_name_falls_back_to_owasp-crs +--- PASS: TestBuildWAFHandler_AdvancedConfigParsing (0.00s) + --- PASS: TestBuildWAFHandler_AdvancedConfigParsing/Valid_ruleset_name_in_advanced_config (0.00s) + --- PASS: TestBuildWAFHandler_AdvancedConfigParsing/Invalid_JSON_falls_back_to_owasp-crs (0.00s) + --- PASS: TestBuildWAFHandler_AdvancedConfigParsing/Empty_advanced_config_falls_back_to_owasp-crs (0.00s) + --- PASS: TestBuildWAFHandler_AdvancedConfigParsing/Empty_ruleset_name_string_falls_back_to_owasp-crs (0.00s) + --- PASS: TestBuildWAFHandler_AdvancedConfigParsing/Non-string_ruleset_name_falls_back_to_owasp-crs (0.00s) +=== RUN TestImporter_ExtractHosts_DialWithoutPortDefaultsTo80 +--- PASS: TestImporter_ExtractHosts_DialWithoutPortDefaultsTo80 (0.00s) +=== RUN TestImporter_ExtractHosts_DetectsWebsocketFromHeaders +--- PASS: TestImporter_ExtractHosts_DetectsWebsocketFromHeaders (0.00s) +=== RUN TestImporter_ImportFile_ParseOutputInvalidJSON +--- PASS: TestImporter_ImportFile_ParseOutputInvalidJSON (0.00s) +=== RUN TestImporter_ImportFile_ExecutorError +--- PASS: TestImporter_ImportFile_ExecutorError (0.00s) +=== RUN TestImporter_ExtractHosts_TLSConnectionPolicyAndDialWithoutPort +--- PASS: TestImporter_ExtractHosts_TLSConnectionPolicyAndDialWithoutPort (0.00s) +=== RUN TestExtractHandlers_Subroute_WithUnsupportedSubhandle +--- PASS: TestExtractHandlers_Subroute_WithUnsupportedSubhandle (0.00s) +=== RUN TestExtractHandlers_Subroute_WithNonMapRoutes +--- PASS: TestExtractHandlers_Subroute_WithNonMapRoutes (0.00s) +=== RUN TestImporter_ExtractHosts_UpstreamsNonMapAndWarnings +--- PASS: TestImporter_ExtractHosts_UpstreamsNonMapAndWarnings (0.00s) +=== RUN TestBackupCaddyfile_ReadFailure +--- PASS: TestBackupCaddyfile_ReadFailure (0.00s) +=== RUN TestExtractHandlers_Subroute_EmptyAndHandleNotArray +--- PASS: TestExtractHandlers_Subroute_EmptyAndHandleNotArray (0.00s) +=== RUN TestImporter_ExtractHosts_ReverseProxyNoUpstreams +--- PASS: TestImporter_ExtractHosts_ReverseProxyNoUpstreams (0.00s) +=== RUN TestBackupCaddyfile_Success +--- PASS: TestBackupCaddyfile_Success (0.00s) +=== RUN TestExtractHandlers_Subroute_WithHeadersUpstreams +--- PASS: TestExtractHandlers_Subroute_WithHeadersUpstreams (0.00s) +=== RUN TestImporter_ExtractHosts_DuplicateHost +--- PASS: TestImporter_ExtractHosts_DuplicateHost (0.00s) +=== RUN TestBackupCaddyfile_WriteFailure +--- PASS: TestBackupCaddyfile_WriteFailure (0.00s) +=== RUN TestImporter_ExtractHosts_SSLForcedByDomainScheme +--- PASS: TestImporter_ExtractHosts_SSLForcedByDomainScheme (0.00s) +=== RUN TestImporter_ExtractHosts_MultipleHostsInMatch +--- PASS: TestImporter_ExtractHosts_MultipleHostsInMatch (0.00s) +=== RUN TestImporter_ExtractHosts_UpgradeHeaderAsString +--- PASS: TestImporter_ExtractHosts_UpgradeHeaderAsString (0.00s) +=== RUN TestImporter_ExtractHosts_SscanfFailureOnPort +--- PASS: TestImporter_ExtractHosts_SscanfFailureOnPort (0.00s) +=== RUN TestImporter_ExtractHosts_PartsSscanfFail +--- PASS: TestImporter_ExtractHosts_PartsSscanfFail (0.00s) +=== RUN TestImporter_ExtractHosts_PartsEmptyPortField +--- PASS: TestImporter_ExtractHosts_PartsEmptyPortField (0.00s) +=== RUN TestImporter_ExtractHosts_ForceSplitFallback_PartsNumericPort +--- PASS: TestImporter_ExtractHosts_ForceSplitFallback_PartsNumericPort (0.00s) +=== RUN TestImporter_ExtractHosts_ForceSplitFallback_PartsSscanfFail +--- PASS: TestImporter_ExtractHosts_ForceSplitFallback_PartsSscanfFail (0.00s) +=== RUN TestBackupCaddyfile_WriteErrorDeterministic +--- PASS: TestBackupCaddyfile_WriteErrorDeterministic (0.00s) +=== RUN TestParseCaddyfile_InvalidPath +--- PASS: TestParseCaddyfile_InvalidPath (0.00s) +=== RUN TestBackupCaddyfile_InvalidOriginalPath +--- PASS: TestBackupCaddyfile_InvalidOriginalPath (0.00s) +=== RUN TestExtractHandlers_Subroute +--- PASS: TestExtractHandlers_Subroute (0.00s) +=== RUN TestNewImporter +--- PASS: TestNewImporter (0.00s) +=== RUN TestImporter_ParseCaddyfile_NotFound +--- PASS: TestImporter_ParseCaddyfile_NotFound (0.00s) +=== RUN TestImporter_ParseCaddyfile_Success +--- PASS: TestImporter_ParseCaddyfile_Success (0.00s) +=== RUN TestImporter_ParseCaddyfile_Failure +--- PASS: TestImporter_ParseCaddyfile_Failure (0.00s) +=== RUN TestImporter_ExtractHosts +--- PASS: TestImporter_ExtractHosts (0.00s) +=== RUN TestImporter_ImportFile +--- PASS: TestImporter_ImportFile (0.00s) +=== RUN TestConvertToProxyHosts +--- PASS: TestConvertToProxyHosts (0.00s) +=== RUN TestImporter_ValidateCaddyBinary +--- PASS: TestImporter_ValidateCaddyBinary (0.00s) +=== RUN TestBackupCaddyfile +--- PASS: TestBackupCaddyfile (0.00s) +=== RUN TestDefaultExecutor_Execute +--- PASS: TestDefaultExecutor_Execute (0.01s) +=== RUN TestManager_ListSnapshots_ReadDirError +--- PASS: TestManager_ListSnapshots_ReadDirError (0.00s) +=== RUN TestManager_RotateSnapshots_NoOp +--- PASS: TestManager_RotateSnapshots_NoOp (0.00s) +=== RUN TestManager_Rollback_NoSnapshots +--- PASS: TestManager_Rollback_NoSnapshots (0.00s) +=== RUN TestManager_Rollback_UnmarshalError +--- PASS: TestManager_Rollback_UnmarshalError (0.00s) +=== RUN TestManager_Rollback_LoadSnapshotFail +--- PASS: TestManager_Rollback_LoadSnapshotFail (0.00s) +=== RUN TestManager_SaveSnapshot_WriteError +--- PASS: TestManager_SaveSnapshot_WriteError (0.00s) +=== RUN TestBackupCaddyfile_MkdirAllFailure +--- PASS: TestBackupCaddyfile_MkdirAllFailure (0.00s) +=== RUN TestManager_SaveSnapshot_Success +--- PASS: TestManager_SaveSnapshot_Success (0.00s) +=== RUN TestManager_ApplyConfig_WithSettings + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.870ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.071ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.058ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.080ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.029ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.660ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.009ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_WithSettings (0.05s) +=== RUN TestManager_RotateSnapshots_ListDirError +--- PASS: TestManager_RotateSnapshots_ListDirError (0.00s) +=== RUN TestManager_RotateSnapshots_DeletesOld +--- PASS: TestManager_RotateSnapshots_DeletesOld (0.00s) +=== RUN TestManager_ApplyConfig_RotateSnapshotsWarning + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.118ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.097ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[3.490ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.104ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.075ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.065ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.031ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.993ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.807ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_RotateSnapshotsWarning (0.04s) +=== RUN TestManager_ApplyConfig_LoadFailsAndRollbackFails + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.049ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.081ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.990ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.068ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.048ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.042ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.039ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.713ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.297ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_LoadFailsAndRollbackFails (0.05s) +=== RUN TestManager_ApplyConfig_SaveSnapshotFails + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.057ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.046ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.847ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.051ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.039ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.035ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.022ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[1.168ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.519ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_SaveSnapshotFails (0.03s) +=== RUN TestManager_ApplyConfig_LoadFailsThenRollbackSucceeds + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.053ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.071ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.532ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.106ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.279ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.089ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.052ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[2.196ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.848ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_LoadFailsThenRollbackSucceeds (0.04s) +=== RUN TestManager_SaveSnapshot_MarshalError +--- PASS: TestManager_SaveSnapshot_MarshalError (0.00s) +=== RUN TestManager_RotateSnapshots_DeleteError +--- PASS: TestManager_RotateSnapshots_DeleteError (0.00s) +=== RUN TestManager_ApplyConfig_GenerateConfigFails + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.076ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.229ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.226ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.086ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.106ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.079ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.047ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[3.675ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.945ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_GenerateConfigFails (0.03s) +=== RUN TestManager_ApplyConfig_WarnsWhenCerberusEnabledWithoutAdminWhitelist + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.084ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.076ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.109ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.071ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.054ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[2.722ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.750ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_WarnsWhenCerberusEnabledWithoutAdminWhitelist (0.03s) +=== RUN TestManager_ApplyConfig_ValidateFails + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.058ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.053ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[3.064ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.049ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.046ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.028ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[2.537ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.732ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_ValidateFails (0.02s) +=== RUN TestManager_Rollback_ReadFileError +--- PASS: TestManager_Rollback_ReadFileError (0.00s) +=== RUN TestManager_ApplyConfig_RotateSnapshotsWarning_Stderr + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.065ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.070ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.128ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.181ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.109ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.099ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.041ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[2.253ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.361ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_RotateSnapshotsWarning_Stderr (0.03s) +=== RUN TestManager_ApplyConfig_PassesAdminWhitelistToGenerateConfig + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.088ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.089ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.078ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.055ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.048ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.890ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.535ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_PassesAdminWhitelistToGenerateConfig (0.03s) +=== RUN TestManager_ApplyConfig_PassesRuleSetsToGenerateConfig + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.072ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.050ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.045ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.042ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.039ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.990ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_PassesRuleSetsToGenerateConfig (0.05s) +=== RUN TestManager_ApplyConfig_IncludesWAFHandlerWithRuleset + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.062ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.102ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.102ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.068ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.044ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.668ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc + manager_additional_test.go:727: generated config: {"admin":{"listen":"0.0.0.0:2019"},"apps":{"http":{"servers":{"charon_server":{"listen":[":80",":443"],"routes":[{"match":[{"host":["ruleset.example.com"]}],"handle":[{"directives":"SecRuleEngine On\nSecRequestBodyAccess On\nSecResponseBodyAccess Off\nSecAction \"id:900000,phase:1,nolog,pass,t:none,setvar:tx.paranoia_level=1\"\nInclude /tmp/TestManager_ApplyConfig_IncludesWAFHandlerWithRuleset1320949653/001/coraza/rulesets/owasp-crs-05ec1bde.conf\n","handler":"waf"},{"handler":"headers","response":{"set":{"Cross-Origin-Opener-Policy":["same-origin"],"Cross-Origin-Resource-Policy":["same-origin"],"Referrer-Policy":["strict-origin-when-cross-origin"],"Strict-Transport-Security":["max-age=31536000"],"X-Content-Type-Options":["nosniff"],"X-Frame-Options":["SAMEORIGIN"],"X-XSS-Protection":["1; mode=block"]}}},{"handler":"vars"},{"flush_interval":-1,"handler":"reverse_proxy","headers":{"request":{"set":{"X-Forwarded-Host":["{http.request.host}"],"X-Forwarded-Port":["{http.request.port}"],"X-Forwarded-Proto":["{http.request.scheme}"],"X-Real-IP":["{http.request.remote.host}"]}}},"upstreams":[{"dial":"127.0.0.1:8080"}]}],"terminal":true}],"automatic_https":{},"logs":{"default_logger_name":"access_log"},"trusted_proxies":{"source":"static","ranges":["127.0.0.1/32","::1/128","172.16.0.0/12","10.0.0.0/8","192.168.0.0/16"]}}}}},"logging":{"logs":{"access":{"writer":{"output":"file","filename":"/tmp/TestManager_ApplyConfig_IncludesWAFHandlerWithRuleset1320949653/logs/access.log","roll":true,"roll_size_mb":10,"roll_keep":5,"roll_keep_days":7},"encoder":{"format":"json"},"level":"INFO","include":["http.log.access.access_log"]}}},"storage":{"module":"file_system","root":"/tmp/TestManager_ApplyConfig_IncludesWAFHandlerWithRuleset1320949653/001/data"}} +--- PASS: TestManager_ApplyConfig_IncludesWAFHandlerWithRuleset (0.03s) +=== RUN TestManager_ApplyConfig_RulesetWriteFileFailure + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.104ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.102ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.096ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.079ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.069ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.334ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_RulesetWriteFileFailure (0.04s) +=== RUN TestManager_ApplyConfig_RulesetDirMkdirFailure + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.054ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.054ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.053ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.057ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.052ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.739ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_RulesetDirMkdirFailure (0.04s) +=== RUN TestManager_ApplyConfig_ReappliesOnFlagChange + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.065ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.078ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.281ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.079ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.068ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.074ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.035ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[1.490ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.567ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.050ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.066ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.048ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.078ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.075ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.038ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.036ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.023ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.062ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.068ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.030ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.043ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.072ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.043ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.032ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.050ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_ReappliesOnFlagChange (0.05s) +=== RUN TestManager_ApplyConfig_PrependsSecRuleEngineDirectives + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.042ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.044ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.366ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.165ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.059ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.861ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_PrependsSecRuleEngineDirectives (0.04s) +=== RUN TestManager_ApplyConfig_DoesNotPrependIfSecRuleEngineExists + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.067ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.071ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.064ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.057ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[2.184ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_DoesNotPrependIfSecRuleEngineExists (0.03s) +=== RUN TestManager_ApplyConfig_DebugMarshalFailure + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.045ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.049ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.222ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.053ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.076ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.035ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_DebugMarshalFailure (0.03s) +=== RUN TestManager_ApplyConfig_WAFModeMonitorUsesDetectionOnly + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.041ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.046ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.039ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.059ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.039ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[2.842ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_WAFModeMonitorUsesDetectionOnly (0.04s) +=== RUN TestManager_ApplyConfig_PerRulesetModeOverride + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.053ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.055ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.059ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.123ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_PerRulesetModeOverride (0.04s) +=== RUN TestManager_ApplyConfig_RulesetFileCleanup + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.064ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.081ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.067ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.067ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.073ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.760ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_RulesetFileCleanup (0.03s) +=== RUN TestManager_ApplyConfig_RulesetCleanupReadDirError + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.093ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.069ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.053ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.044ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.037ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.134ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_RulesetCleanupReadDirError (0.03s) +=== RUN TestManager_ApplyConfig_RulesetCleanupRemoveError + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.055ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.063ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.052ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.050ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.048ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.576ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_RulesetCleanupRemoveError (0.03s) +=== RUN TestManager_ApplyConfig_WAFModeBlockExplicit + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.066ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.085ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.082ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.079ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.062ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.136ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_WAFModeBlockExplicit (0.03s) +=== RUN TestManager_ApplyConfig_RulesetNamePathTraversal + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.094ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.069ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.076ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.062ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[2.064ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_RulesetNamePathTraversal (0.03s) +=== RUN TestExtractBaseDomain_EmptyInput +--- PASS: TestExtractBaseDomain_EmptyInput (0.00s) +=== RUN TestExtractBaseDomain_OnlyCommas +--- PASS: TestExtractBaseDomain_OnlyCommas (0.00s) +=== RUN TestExtractBaseDomain_SingleDomain +--- PASS: TestExtractBaseDomain_SingleDomain (0.00s) +=== RUN TestExtractBaseDomain_WildcardDomain +--- PASS: TestExtractBaseDomain_WildcardDomain (0.00s) +=== RUN TestExtractBaseDomain_MultipleDomains +--- PASS: TestExtractBaseDomain_MultipleDomains (0.00s) +=== RUN TestExtractBaseDomain_MultipleDomainsWithWildcard +--- PASS: TestExtractBaseDomain_MultipleDomainsWithWildcard (0.00s) +=== RUN TestExtractBaseDomain_WithWhitespace +--- PASS: TestExtractBaseDomain_WithWhitespace (0.00s) +=== RUN TestExtractBaseDomain_CaseNormalization +--- PASS: TestExtractBaseDomain_CaseNormalization (0.00s) +=== RUN TestExtractBaseDomain_Subdomain +--- PASS: TestExtractBaseDomain_Subdomain (0.00s) +=== RUN TestExtractBaseDomain_MultiLevelSubdomain +--- PASS: TestExtractBaseDomain_MultiLevelSubdomain (0.00s) +=== RUN TestMatchesZoneFilter_EmptyFilter +--- PASS: TestMatchesZoneFilter_EmptyFilter (0.00s) +=== RUN TestMatchesZoneFilter_EmptyZonesInList +--- PASS: TestMatchesZoneFilter_EmptyZonesInList (0.00s) +=== RUN TestMatchesZoneFilter_ExactMatch +--- PASS: TestMatchesZoneFilter_ExactMatch (0.00s) +=== RUN TestMatchesZoneFilter_ExactMatchOnly +--- PASS: TestMatchesZoneFilter_ExactMatchOnly (0.00s) +=== RUN TestMatchesZoneFilter_WildcardMatch +--- PASS: TestMatchesZoneFilter_WildcardMatch (0.00s) +=== RUN TestMatchesZoneFilter_MultipleZones +--- PASS: TestMatchesZoneFilter_MultipleZones (0.00s) +=== RUN TestMatchesZoneFilter_MultipleZonesWithWildcard +--- PASS: TestMatchesZoneFilter_MultipleZonesWithWildcard (0.00s) +=== RUN TestMatchesZoneFilter_WhitespaceTrimming_Detailed +--- PASS: TestMatchesZoneFilter_WhitespaceTrimming_Detailed (0.00s) +=== RUN TestMatchesZoneFilter_DeepSubdomain +--- PASS: TestMatchesZoneFilter_DeepSubdomain (0.00s) +=== RUN TestGetCredentialForDomain_NoEncryptionKey +--- PASS: TestGetCredentialForDomain_NoEncryptionKey (0.00s) +=== RUN TestGetCredentialForDomain_MultiCredential_NoMatch +--- PASS: TestGetCredentialForDomain_MultiCredential_NoMatch (0.00s) +=== RUN TestGetCredentialForDomain_MultiCredential_DisabledSkipped +--- PASS: TestGetCredentialForDomain_MultiCredential_DisabledSkipped (0.00s) +=== RUN TestGetCredentialForDomain_MultiCredential_CatchAllMatch +--- PASS: TestGetCredentialForDomain_MultiCredential_CatchAllMatch (0.00s) +=== RUN TestComputeEffectiveFlags_DB_SecurityConfigWAFDisabled + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.045ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.039ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestComputeEffectiveFlags_DB_SecurityConfigWAFDisabled (0.01s) +=== RUN TestComputeEffectiveFlags_DB_RateLimitFromBooleanField + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.058ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.085ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.074ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestComputeEffectiveFlags_DB_RateLimitFromBooleanField (0.00s) +=== RUN TestComputeEffectiveFlags_DB_CrowdSecModeFromSecurityConfig + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.079ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.072ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.068ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestComputeEffectiveFlags_DB_CrowdSecModeFromSecurityConfig (0.01s) +=== RUN TestComputeEffectiveFlags_DB_LegacyCerberusKey + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.121ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.084ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.094ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" AND `settings`.`id` = 1 ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestComputeEffectiveFlags_DB_LegacyCerberusKey (0.00s) +=== RUN TestApplyConfig_SingleCredential_BackwardCompatibility + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.036ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 record not found +[0.102ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.086ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.067ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.062ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 record not found +[0.092ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.906ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.875ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:572 no such table: caddy_configs +[0.970ms] [rows:0] INSERT INTO `caddy_configs` (`config_hash`,`applied_at`,`success`,`error_msg`) VALUES ("5e816e57eb11055dffe0c15e31a606002ac4a0e55d5cffaf5a0d83b81d660105","2026-01-10 02:16:58.944",true,"") RETURNING `id` +--- PASS: TestApplyConfig_SingleCredential_BackwardCompatibility (0.03s) +=== RUN TestApplyConfig_MultiCredential_ExactMatch + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.064ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:598 record not found +[0.108ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.095ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.072ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.072ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:285 record not found +[0.087ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[1.132ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.930ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc + +2026/01/10 02:16:58 /projects/Charon/backend/internal/caddy/manager.go:572 no such table: caddy_configs +[2.050ms] [rows:0] INSERT INTO `caddy_configs` (`config_hash`,`applied_at`,`success`,`error_msg`) VALUES ("84844f433ace3d57bfa2bcb2f12ca1d13fe94265f9e4652ad1d94ab8627c9bca","2026-01-10 02:16:58.979",true,"") RETURNING `id` +--- PASS: TestApplyConfig_MultiCredential_ExactMatch (0.04s) +=== RUN TestApplyConfig_MultiCredential_WildcardMatch + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.380ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 record not found +[0.129ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.074ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 record not found +[0.108ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[1.125ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.346ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:572 no such table: caddy_configs +[0.904ms] [rows:0] INSERT INTO `caddy_configs` (`config_hash`,`applied_at`,`success`,`error_msg`) VALUES ("3b0f21b3fd87815befff8bee0f98a4e2a859adf7aecc02d7b8d0a627a477187e","2026-01-10 02:16:59.011",true,"") RETURNING `id` +--- PASS: TestApplyConfig_MultiCredential_WildcardMatch (0.03s) +=== RUN TestApplyConfig_MultiCredential_CatchAll + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.037ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 record not found +[0.096ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.059ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.087ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.072ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 record not found +[0.075ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.964ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[2.521ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:572 no such table: caddy_configs +[1.072ms] [rows:0] INSERT INTO `caddy_configs` (`config_hash`,`applied_at`,`success`,`error_msg`) VALUES ("03b090b404ed34a6054d2a728c08991e385b3587215219e1371b9d8944ce3099","2026-01-10 02:16:59.049",true,"") RETURNING `id` +--- PASS: TestApplyConfig_MultiCredential_CatchAll (0.04s) +=== RUN TestExtractBaseDomain +=== RUN TestExtractBaseDomain/wildcard_domain +=== RUN TestExtractBaseDomain/normal_domain +=== RUN TestExtractBaseDomain/multiple_domains +=== RUN TestExtractBaseDomain/empty +=== RUN TestExtractBaseDomain/with_spaces +--- PASS: TestExtractBaseDomain (0.00s) + --- PASS: TestExtractBaseDomain/wildcard_domain (0.00s) + --- PASS: TestExtractBaseDomain/normal_domain (0.00s) + --- PASS: TestExtractBaseDomain/multiple_domains (0.00s) + --- PASS: TestExtractBaseDomain/empty (0.00s) + --- PASS: TestExtractBaseDomain/with_spaces (0.00s) +=== RUN TestMatchesZoneFilter +=== RUN TestMatchesZoneFilter/exact_match +=== RUN TestMatchesZoneFilter/exact_match_(not_exact_only) +=== RUN TestMatchesZoneFilter/wildcard_match +=== RUN TestMatchesZoneFilter/wildcard_no_match_(exact_only) +=== RUN TestMatchesZoneFilter/wildcard_base_domain_match +=== RUN TestMatchesZoneFilter/no_match +=== RUN TestMatchesZoneFilter/comma-separated_zones +=== RUN TestMatchesZoneFilter/empty_filter +--- PASS: TestMatchesZoneFilter (0.00s) + --- PASS: TestMatchesZoneFilter/exact_match (0.00s) + --- PASS: TestMatchesZoneFilter/exact_match_(not_exact_only) (0.00s) + --- PASS: TestMatchesZoneFilter/wildcard_match (0.00s) + --- PASS: TestMatchesZoneFilter/wildcard_no_match_(exact_only) (0.00s) + --- PASS: TestMatchesZoneFilter/wildcard_base_domain_match (0.00s) + --- PASS: TestMatchesZoneFilter/no_match (0.00s) + --- PASS: TestMatchesZoneFilter/comma-separated_zones (0.00s) + --- PASS: TestMatchesZoneFilter/empty_filter (0.00s) +=== RUN TestManager_GetCredentialForDomain_NoMatch +--- PASS: TestManager_GetCredentialForDomain_NoMatch (0.00s) +=== RUN TestManager_GetCredentialForDomain_NoEncryptionKey +--- PASS: TestManager_GetCredentialForDomain_NoEncryptionKey (0.00s) +=== RUN TestManager_GetCredentialForDomain_DecryptionFailure +--- PASS: TestManager_GetCredentialForDomain_DecryptionFailure (0.00s) +=== RUN TestManager_GetCredentialForDomain_InvalidJSON +--- PASS: TestManager_GetCredentialForDomain_InvalidJSON (0.01s) +=== RUN TestManager_GetCredentialForDomain_SkipsDisabledCredentials +--- PASS: TestManager_GetCredentialForDomain_SkipsDisabledCredentials (0.00s) +=== RUN TestManager_GetCredentialForDomain_MultiCredential_DecryptionFailure +--- PASS: TestManager_GetCredentialForDomain_MultiCredential_DecryptionFailure (0.00s) +=== RUN TestManager_GetCredentialForDomain_MultiCredential_InvalidJSON +--- PASS: TestManager_GetCredentialForDomain_MultiCredential_InvalidJSON (0.00s) +=== RUN TestExtractBaseDomain_EmptyAfterSplit +--- PASS: TestExtractBaseDomain_EmptyAfterSplit (0.00s) +=== RUN TestMatchesZoneFilter_WhitespaceInFilter +--- PASS: TestMatchesZoneFilter_WhitespaceInFilter (0.00s) +=== RUN TestMatchesZoneFilter_CaseInsensitive +--- PASS: TestMatchesZoneFilter_CaseInsensitive (0.00s) +=== RUN TestManagerApplyConfig_DNSProviders_NoKey_SkipsDecryption + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.053ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.048ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.045ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.039ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.038ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestManagerApplyConfig_DNSProviders_NoKey_SkipsDecryption (0.03s) +=== RUN TestManagerApplyConfig_DNSProviders_UsesFallbackEnvKeys + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.078ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.057ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.055ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.051ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.050ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestManagerApplyConfig_DNSProviders_UsesFallbackEnvKeys (0.03s) +=== RUN TestManagerApplyConfig_DNSProviders_SkipsDecryptOrJSONFailures + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.074ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.089ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.071ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.086ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.086ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestManagerApplyConfig_DNSProviders_SkipsDecryptOrJSONFailures (0.03s) +=== RUN TestManager_ApplyConfig_SSLProvider_Auto + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.057ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.781ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.054ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.048ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.043ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.023ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.729ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[2.131ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_SSLProvider_Auto (0.03s) +=== RUN TestManager_ApplyConfig_SSLProvider_LetsEncryptStaging + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.074ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.286ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.084ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.064ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.059ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.030ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[1.013ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.887ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_SSLProvider_LetsEncryptStaging (0.03s) +=== RUN TestManager_ApplyConfig_SSLProvider_LetsEncryptProd + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.230ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.807ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.117ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.181ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.116ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.044ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[2.410ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.124ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_SSLProvider_LetsEncryptProd (0.04s) +=== RUN TestManager_ApplyConfig_SSLProvider_ZeroSSL + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.063ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.212ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.075ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.074ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.260ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.041ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[1.164ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[4.163ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_SSLProvider_ZeroSSL (0.03s) +=== RUN TestManager_ApplyConfig_SSLProvider_Empty + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.072ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.069ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[2.277ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.090ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.079ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.087ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.043ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[1.310ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.091ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_SSLProvider_Empty (0.04s) +=== RUN TestManager_ApplyConfig_SSLProvider_EmptyWithNoStaging + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.048ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.772ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.085ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.060ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.091ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.027ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.838ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.890ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_SSLProvider_EmptyWithNoStaging (0.03s) +=== RUN TestManager_ApplyConfig_SSLProvider_Unknown + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.089ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.455ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.081ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.109ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.089ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.148ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[1.627ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.903ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_SSLProvider_Unknown (0.03s) +=== RUN TestManager_ApplyConfig + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.068ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.067ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.071ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.068ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.064ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.036ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[2.232ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.888ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig (0.03s) +=== RUN TestManager_ApplyConfig_Failure + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.046ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.072ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[2.970ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.145ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.073ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.071ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.125ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[1.062ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.021ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_Failure (0.03s) +=== RUN TestManager_Ping +--- PASS: TestManager_Ping (0.00s) +=== RUN TestManager_GetCurrentConfig +--- PASS: TestManager_GetCurrentConfig (0.00s) +=== RUN TestManager_RotateSnapshots + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.077ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.146ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.042ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.088ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.146ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.380ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.064ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.854ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.975ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_RotateSnapshots (0.03s) +=== RUN TestManager_Rollback_Success + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.049ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.074ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.865ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.070ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.078ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.094ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.046ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[1.293ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:16:59 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.878ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.098ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.097ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.058ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.094ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.093ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.080ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.043ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.044ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[0.039ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_Rollback_Success (1.14s) +=== RUN TestManager_ApplyConfig_DBError + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:84 sql: database is closed +[0.073ms] [rows:0] SELECT * FROM `proxy_hosts` +--- PASS: TestManager_ApplyConfig_DBError (0.03s) +=== RUN TestManager_ApplyConfig_ValidationError + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.089ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.080ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[4.193ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.126ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.084ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.089ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.031ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[0.977ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[2.278ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_ApplyConfig_ValidationError (0.04s) +=== RUN TestManager_Rollback_Failure + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.064ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.065ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.903ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.091ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.065ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.060ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:285 no such table: security_configs +[0.044ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:295 no such table: security_rule_sets +[3.050ms] [rows:0] SELECT * FROM `security_rule_sets` + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:302 no such table: security_decisions +[1.947ms] [rows:0] SELECT * FROM `security_decisions` ORDER BY created_at desc +--- PASS: TestManager_Rollback_Failure (0.04s) +=== RUN TestComputeEffectiveFlags_DefaultsNoDB +--- PASS: TestComputeEffectiveFlags_DefaultsNoDB (0.00s) +=== RUN TestComputeEffectiveFlags_DB_CerberusDisabled + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.646ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.085ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" AND `settings`.`id` = 1 ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestComputeEffectiveFlags_DB_CerberusDisabled (0.00s) +=== RUN TestComputeEffectiveFlags_DB_CrowdSecExternal + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.790ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.115ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.082ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.068ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestComputeEffectiveFlags_DB_CrowdSecExternal (0.01s) +=== RUN TestComputeEffectiveFlags_DB_CrowdSecUnknown + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.991ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.114ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.072ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.051ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestComputeEffectiveFlags_DB_CrowdSecUnknown (0.00s) +=== RUN TestComputeEffectiveFlags_DB_CrowdSecLocal + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[2.212ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.272ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.081ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.093ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestComputeEffectiveFlags_DB_CrowdSecLocal (0.00s) +=== RUN TestComputeEffectiveFlags_DB_ACLTrueAndFalse + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[1.074ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.084ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.075ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:598 no such table: security_configs +[0.054ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.101ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.081ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestComputeEffectiveFlags_DB_ACLTrueAndFalse (0.01s) +=== RUN TestComputeEffectiveFlags_DB_WAFMonitor + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.178ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.121ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.098ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestComputeEffectiveFlags_DB_WAFMonitor (0.01s) +=== RUN TestManager_ApplyConfig_WAFMonitor + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:239 record not found +[0.056ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.acme_email" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:246 record not found +[0.055ms] [rows:0] SELECT * FROM `settings` WHERE key = "caddy.ssl_provider" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:627 record not found +[0.054ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:629 record not found +[0.080ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:00 /projects/Charon/backend/internal/caddy/manager.go:634 record not found +[0.061ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.acl.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestManager_ApplyConfig_WAFMonitor (0.04s) +=== RUN TestNormalizeAdvancedConfig_MapWithNestedHandles +--- PASS: TestNormalizeAdvancedConfig_MapWithNestedHandles (0.00s) +=== RUN TestNormalizeAdvancedConfig_ArrayTopLevel +--- PASS: TestNormalizeAdvancedConfig_ArrayTopLevel (0.00s) +=== RUN TestNormalizeAdvancedConfig_DefaultPrimitives +--- PASS: TestNormalizeAdvancedConfig_DefaultPrimitives (0.00s) +=== RUN TestNormalizeAdvancedConfig_CoerceNonStandardTypes +--- PASS: TestNormalizeAdvancedConfig_CoerceNonStandardTypes (0.00s) +=== RUN TestNormalizeAdvancedConfig_JSONRoundtrip +--- PASS: TestNormalizeAdvancedConfig_JSONRoundtrip (0.00s) +=== RUN TestNormalizeAdvancedConfig_TopLevelHeaders +--- PASS: TestNormalizeAdvancedConfig_TopLevelHeaders (0.00s) +=== RUN TestNormalizeAdvancedConfig_HeadersAlreadyArray +--- PASS: TestNormalizeAdvancedConfig_HeadersAlreadyArray (0.00s) +=== RUN TestNormalizeAdvancedConfig_MapWithTopLevelHandle +--- PASS: TestNormalizeAdvancedConfig_MapWithTopLevelHandle (0.00s) +=== RUN TestReverseProxyHandler_PlexAndOthers +--- PASS: TestReverseProxyHandler_PlexAndOthers (0.00s) +=== RUN TestReverseProxyHandler_WebSocketHeaders +--- PASS: TestReverseProxyHandler_WebSocketHeaders (0.00s) +=== RUN TestReverseProxyHandler_StandardProxyHeadersAlwaysSet +--- PASS: TestReverseProxyHandler_StandardProxyHeadersAlwaysSet (0.00s) +=== RUN TestReverseProxyHandler_ApplicationSpecificHeaders +--- PASS: TestReverseProxyHandler_ApplicationSpecificHeaders (0.00s) +=== RUN TestReverseProxyHandler_WebSocketWithApplication +--- PASS: TestReverseProxyHandler_WebSocketWithApplication (0.00s) +=== RUN TestReverseProxyHandler_FeatureFlagDisabled +--- PASS: TestReverseProxyHandler_FeatureFlagDisabled (0.00s) +=== RUN TestReverseProxyHandler_XForwardedForNotDuplicated +--- PASS: TestReverseProxyHandler_XForwardedForNotDuplicated (0.00s) +=== RUN TestReverseProxyHandler_TrustedProxiesConfiguration +--- PASS: TestReverseProxyHandler_TrustedProxiesConfiguration (0.00s) +=== RUN TestHandlers +--- PASS: TestHandlers (0.00s) +=== RUN TestReverseProxyHandler_NoWebSocket +--- PASS: TestReverseProxyHandler_NoWebSocket (0.00s) +=== RUN TestReverseProxyHandler_WithWebSocket +--- PASS: TestReverseProxyHandler_WithWebSocket (0.00s) +=== RUN TestReverseProxyHandler_StandardHeaders +--- PASS: TestReverseProxyHandler_StandardHeaders (0.00s) +=== RUN TestReverseProxyHandler_Plex +--- PASS: TestReverseProxyHandler_Plex (0.00s) +=== RUN TestReverseProxyHandler_PlexWithoutStandardHeaders +--- PASS: TestReverseProxyHandler_PlexWithoutStandardHeaders (0.00s) +=== RUN TestReverseProxyHandler_Jellyfin +--- PASS: TestReverseProxyHandler_Jellyfin (0.00s) +=== RUN TestReverseProxyHandler_JellyfinWithoutStandardHeaders +--- PASS: TestReverseProxyHandler_JellyfinWithoutStandardHeaders (0.00s) +=== RUN TestReverseProxyHandler_Emby +--- PASS: TestReverseProxyHandler_Emby (0.00s) +=== RUN TestReverseProxyHandler_HomeAssistant +--- PASS: TestReverseProxyHandler_HomeAssistant (0.00s) +=== RUN TestReverseProxyHandler_Nextcloud +--- PASS: TestReverseProxyHandler_Nextcloud (0.00s) +=== RUN TestReverseProxyHandler_Vaultwarden +--- PASS: TestReverseProxyHandler_Vaultwarden (0.00s) +=== RUN TestReverseProxyHandler_UnknownApplication +--- PASS: TestReverseProxyHandler_UnknownApplication (0.00s) +=== RUN TestReverseProxyHandler_NoHeaders +--- PASS: TestReverseProxyHandler_NoHeaders (0.00s) +=== RUN TestHeaderHandler_EmptyHeaders +--- PASS: TestHeaderHandler_EmptyHeaders (0.00s) +=== RUN TestHeaderHandler_MultipleHeaders +--- PASS: TestHeaderHandler_MultipleHeaders (0.00s) +=== RUN TestValidate_NilConfig +--- PASS: TestValidate_NilConfig (0.00s) +=== RUN TestValidateHandler_MissingHandlerField +--- PASS: TestValidateHandler_MissingHandlerField (0.00s) +=== RUN TestValidateHandler_UnknownHandlerAllowed +--- PASS: TestValidateHandler_UnknownHandlerAllowed (0.00s) +=== RUN TestValidateHandler_FileServerAndStaticResponseAllowed +--- PASS: TestValidateHandler_FileServerAndStaticResponseAllowed (0.00s) +=== RUN TestValidateRoute_InvalidHandler +--- PASS: TestValidateRoute_InvalidHandler (0.00s) +=== RUN TestValidateListenAddr_InvalidHostName +--- PASS: TestValidateListenAddr_InvalidHostName (0.00s) +=== RUN TestValidateListenAddr_InvalidPortNonNumeric +--- PASS: TestValidateListenAddr_InvalidPortNonNumeric (0.00s) +=== RUN TestValidate_MarshalError +--- PASS: TestValidate_MarshalError (0.00s) +=== RUN TestValidate_EmptyConfig +--- PASS: TestValidate_EmptyConfig (0.00s) +=== RUN TestValidate_ValidConfig +--- PASS: TestValidate_ValidConfig (0.00s) +=== RUN TestValidate_DuplicateHosts +--- PASS: TestValidate_DuplicateHosts (0.00s) +=== RUN TestValidate_NoListenAddresses +--- PASS: TestValidate_NoListenAddresses (0.00s) +=== RUN TestValidate_InvalidPort +--- PASS: TestValidate_InvalidPort (0.00s) +=== RUN TestValidate_NoHandlers +--- PASS: TestValidate_NoHandlers (0.00s) +=== RUN TestValidateListenAddr +=== RUN TestValidateListenAddr/Valid +=== RUN TestValidateListenAddr/ValidIP +=== RUN TestValidateListenAddr/ValidTCP +=== RUN TestValidateListenAddr/ValidUDP +=== RUN TestValidateListenAddr/InvalidFormat +=== RUN TestValidateListenAddr/InvalidPort +=== RUN TestValidateListenAddr/InvalidPortNegative +=== RUN TestValidateListenAddr/InvalidIP +--- PASS: TestValidateListenAddr (0.00s) + --- PASS: TestValidateListenAddr/Valid (0.00s) + --- PASS: TestValidateListenAddr/ValidIP (0.00s) + --- PASS: TestValidateListenAddr/ValidTCP (0.00s) + --- PASS: TestValidateListenAddr/ValidUDP (0.00s) + --- PASS: TestValidateListenAddr/InvalidFormat (0.00s) + --- PASS: TestValidateListenAddr/InvalidPort (0.00s) + --- PASS: TestValidateListenAddr/InvalidPortNegative (0.00s) + --- PASS: TestValidateListenAddr/InvalidIP (0.00s) +=== RUN TestValidateReverseProxy +=== RUN TestValidateReverseProxy/Valid +=== RUN TestValidateReverseProxy/MissingUpstreams +=== RUN TestValidateReverseProxy/EmptyUpstreams +=== RUN TestValidateReverseProxy/MissingDial +=== RUN TestValidateReverseProxy/InvalidDial +--- PASS: TestValidateReverseProxy (0.00s) + --- PASS: TestValidateReverseProxy/Valid (0.00s) + --- PASS: TestValidateReverseProxy/MissingUpstreams (0.00s) + --- PASS: TestValidateReverseProxy/EmptyUpstreams (0.00s) + --- PASS: TestValidateReverseProxy/MissingDial (0.00s) + --- PASS: TestValidateReverseProxy/InvalidDial (0.00s) +PASS +coverage: 98.7% of statements +ok github.com/Wikid82/charon/backend/internal/caddy (cached) coverage: 98.7% of statements +=== RUN TestIsEnabled_ConfigTrue +--- PASS: TestIsEnabled_ConfigTrue (0.00s) +=== RUN TestIsEnabled_WAFModeEnabled +--- PASS: TestIsEnabled_WAFModeEnabled (0.00s) +=== RUN TestIsEnabled_ACLModeEnabled +--- PASS: TestIsEnabled_ACLModeEnabled (0.00s) +=== RUN TestIsEnabled_RateLimitModeEnabled +--- PASS: TestIsEnabled_RateLimitModeEnabled (0.00s) +=== RUN TestIsEnabled_CrowdSecModeLocal +--- PASS: TestIsEnabled_CrowdSecModeLocal (0.00s) +=== RUN TestIsEnabled_DBSetting_FeatureFlag +--- PASS: TestIsEnabled_DBSetting_FeatureFlag (0.01s) +=== RUN TestIsEnabled_DBSetting_LegacyKey + +2026/01/10 02:17:04 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.095ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestIsEnabled_DBSetting_LegacyKey (0.01s) +=== RUN TestIsEnabled_DBSetting_FeatureFlagTakesPrecedence +--- PASS: TestIsEnabled_DBSetting_FeatureFlagTakesPrecedence (0.00s) +=== RUN TestIsEnabled_DBSettingCaseInsensitive +--- PASS: TestIsEnabled_DBSettingCaseInsensitive (0.00s) +=== RUN TestIsEnabled_DBSettingFalse +--- PASS: TestIsEnabled_DBSettingFalse (0.00s) +=== RUN TestIsEnabled_DefaultTrue +--- PASS: TestIsEnabled_DefaultTrue (0.00s) +=== RUN TestMiddleware_WAFEnabledTracksMetrics +--- PASS: TestMiddleware_WAFEnabledTracksMetrics (0.01s) +=== RUN TestMiddleware_ACLBlocksClientIP + +2026/01/10 02:17:04 /projects/Charon/backend/internal/services/security_notification_service.go:32 no such table: notification_configs +[3.914ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestMiddleware_ACLBlocksClientIP (0.01s) +=== RUN TestMiddleware_ACLAllowsClientIP +--- PASS: TestMiddleware_ACLAllowsClientIP (0.01s) +=== RUN TestMiddleware_NotEnabledSkips + +2026/01/10 02:17:04 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.062ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 + +2026/01/10 02:17:04 /projects/Charon/backend/internal/cerberus/cerberus.go:61 record not found +[0.052ms] [rows:0] SELECT * FROM `settings` WHERE key = "security.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestMiddleware_NotEnabledSkips (0.01s) +=== RUN TestMiddleware_WAFPassesWithNoPayload +--- PASS: TestMiddleware_WAFPassesWithNoPayload (0.01s) +=== RUN TestMiddleware_WAFMonitorLogsButDoesNotBlock +--- PASS: TestMiddleware_WAFMonitorLogsButDoesNotBlock (0.02s) +=== RUN TestMiddleware_ACLDisabledDoesNotBlock +--- PASS: TestMiddleware_ACLDisabledDoesNotBlock (0.01s) +=== RUN TestCerberus_IsEnabled_ConfigTrue +--- PASS: TestCerberus_IsEnabled_ConfigTrue (0.00s) +=== RUN TestCerberus_IsEnabled_DBSetting +--- PASS: TestCerberus_IsEnabled_DBSetting (0.00s) +=== RUN TestCerberus_IsEnabled_Disabled + cerberus_test.go:68: cfg: {CrowdSecMode: CrowdSecAPIURL: CrowdSecAPIKey: CrowdSecConfigDir: WAFMode: RateLimitMode: ACLMode: CerberusEnabled:false} + cerberus_test.go:69: IsEnabled() -> false +--- PASS: TestCerberus_IsEnabled_Disabled (0.00s) +=== RUN TestCerberus_IsEnabled_CrowdSecLocal +--- PASS: TestCerberus_IsEnabled_CrowdSecLocal (0.00s) +=== RUN TestCerberus_IsEnabled_WAFEnabled +--- PASS: TestCerberus_IsEnabled_WAFEnabled (0.00s) +=== RUN TestCerberus_IsEnabled_RateLimitEnabled +--- PASS: TestCerberus_IsEnabled_RateLimitEnabled (0.00s) +=== RUN TestCerberus_IsEnabled_ACLEnabled +--- PASS: TestCerberus_IsEnabled_ACLEnabled (0.00s) +=== RUN TestCerberus_IsEnabled_LegacySetting + +2026/01/10 02:17:04 /projects/Charon/backend/internal/cerberus/cerberus.go:57 record not found +[0.149ms] [rows:0] SELECT * FROM `settings` WHERE key = "feature.cerberus.enabled" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestCerberus_IsEnabled_LegacySetting (0.00s) +=== RUN TestCerberus_Middleware_Disabled +--- PASS: TestCerberus_Middleware_Disabled (0.00s) +=== RUN TestCerberus_Middleware_WAFEnabled +--- PASS: TestCerberus_Middleware_WAFEnabled (0.01s) +=== RUN TestCerberus_Middleware_ACLEnabled_NoAccessLists +--- PASS: TestCerberus_Middleware_ACLEnabled_NoAccessLists (0.01s) +=== RUN TestCerberus_Middleware_ACLEnabled_DisabledList +--- PASS: TestCerberus_Middleware_ACLEnabled_DisabledList (0.01s) +=== RUN TestCerberus_Middleware_ACLEnabled_Blocked + +2026/01/10 02:17:04 /projects/Charon/backend/internal/services/security_notification_service.go:32 no such table: notification_configs +[3.089ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestCerberus_Middleware_ACLEnabled_Blocked (0.01s) +=== RUN TestCerberus_Middleware_CrowdSecLocal +--- PASS: TestCerberus_Middleware_CrowdSecLocal (0.01s) +PASS +coverage: 100.0% of statements +ok github.com/Wikid82/charon/backend/internal/cerberus (cached) coverage: 100.0% of statements +=== RUN TestLoad +--- PASS: TestLoad (0.00s) +=== RUN TestLoad_Defaults +--- PASS: TestLoad_Defaults (0.00s) +=== RUN TestLoad_CharonPrefersOverCPM +--- PASS: TestLoad_CharonPrefersOverCPM (0.00s) +=== RUN TestLoad_Error +--- PASS: TestLoad_Error (0.00s) +=== RUN TestGetEnvAny +--- PASS: TestGetEnvAny (0.00s) +=== RUN TestLoad_SecurityConfig +--- PASS: TestLoad_SecurityConfig (0.00s) +=== RUN TestLoad_DatabasePathError +--- PASS: TestLoad_DatabasePathError (0.00s) +=== RUN TestLoad_ACMEStaging +--- PASS: TestLoad_ACMEStaging (0.00s) +=== RUN TestLoad_DebugMode +--- PASS: TestLoad_DebugMode (0.00s) +PASS +coverage: 100.0% of statements +ok github.com/Wikid82/charon/backend/internal/config (cached) coverage: 100.0% of statements +=== RUN TestConsoleEnrollSuccess +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.126ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:06Z" level=info msg="starting crowdsec console enrollment" agent=agent-one config= correlation_id=ed782206-e7ca-420b-b94f-8fc8e147de23 force=false tenant=tenant-a +time="2026-01-10T02:17:06Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=agent-one correlation_id=ed782206-e7ca-420b-b94f-8fc8e147de23 tenant=tenant-a +--- PASS: TestConsoleEnrollSuccess (0.01s) +=== RUN TestConsoleEnrollFailureRedactsSecret +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.098ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:06Z" level=info msg="starting crowdsec console enrollment" agent=agent config= correlation_id=0892af32-2308-4d12-9c9d-773e6b81216c force=false tenant=tenant +time="2026-01-10T02:17:06Z" level=warning msg="crowdsec console enrollment failed" correlation_id=0892af32-2308-4d12-9c9d-773e6b81216c error="bad key " output="invalid " tenant=tenant +--- PASS: TestConsoleEnrollFailureRedactsSecret (0.01s) +=== RUN TestConsoleEnrollIdempotentWhenAlreadyEnrolled +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.114ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:06Z" level=info msg="starting crowdsec console enrollment" agent=agent config= correlation_id=7deabff7-eca2-4211-884e-f699e7f87a70 force=false tenant=tenant +time="2026-01-10T02:17:06Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=agent correlation_id=7deabff7-eca2-4211-884e-f699e7f87a70 tenant=tenant +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" +time="2026-01-10T02:17:06Z" level=info msg="console enrollment skipped: already enrolled or pending acceptance - use force=true to re-enroll" agent_name=agent status=pending_acceptance tenant=tenant +--- PASS: TestConsoleEnrollIdempotentWhenAlreadyEnrolled (0.01s) +=== RUN TestConsoleEnrollBlockedWhenInProgress +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" +--- PASS: TestConsoleEnrollBlockedWhenInProgress (0.01s) +=== RUN TestConsoleEnrollNormalizesFullCommand +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.125ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:06Z" level=info msg="starting crowdsec console enrollment" agent=agent config= correlation_id=4caeb337-bfc6-4fd1-ad6a-b2bae08fb89c force=false tenant=tenant +time="2026-01-10T02:17:06Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=agent correlation_id=4caeb337-bfc6-4fd1-ad6a-b2bae08fb89c tenant=tenant +--- PASS: TestConsoleEnrollNormalizesFullCommand (0.01s) +=== RUN TestConsoleEnrollRejectsUnsafeInput +--- PASS: TestConsoleEnrollRejectsUnsafeInput (0.01s) +=== RUN TestConsoleEnrollPassesTenantAsTags +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.105ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:06Z" level=info msg="starting crowdsec console enrollment" agent=agent-one config= correlation_id=74dd0030-7778-437b-b72d-b780578102b4 force=false tenant=some-tenant-id +time="2026-01-10T02:17:06Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=agent-one correlation_id=74dd0030-7778-437b-b72d-b780578102b4 tenant=some-tenant-id +--- PASS: TestConsoleEnrollPassesTenantAsTags (0.01s) +=== RUN TestConsoleEnrollNoTenantOmitsTags +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.092ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:06Z" level=info msg="starting crowdsec console enrollment" agent=agent-one config= correlation_id=55b45045-c01d-493d-be66-9f6abd028810 force=false tenant= +time="2026-01-10T02:17:06Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=agent-one correlation_id=55b45045-c01d-493d-be66-9f6abd028810 tenant= +--- PASS: TestConsoleEnrollNoTenantOmitsTags (0.01s) +=== RUN TestConsoleEnrollPassesForceAsOverwrite +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.116ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:06Z" level=info msg="starting crowdsec console enrollment" agent=agent-one config= correlation_id=4e911619-d08f-4515-8175-9568e2ab1387 force=true tenant= +time="2026-01-10T02:17:06Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=agent-one correlation_id=4e911619-d08f-4515-8175-9568e2ab1387 tenant= +--- PASS: TestConsoleEnrollPassesForceAsOverwrite (0.01s) +=== RUN TestConsoleEnrollNoForceOmitsOverwrite +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.104ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:06Z" level=info msg="starting crowdsec console enrollment" agent=agent-one config= correlation_id=7a445029-c21f-4ac6-b265-2eccc0e6b9c0 force=false tenant= +time="2026-01-10T02:17:06Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=agent-one correlation_id=7a445029-c21f-4ac6-b265-2eccc0e6b9c0 tenant= +--- PASS: TestConsoleEnrollNoForceOmitsOverwrite (0.01s) +=== RUN TestConsoleEnrollWithTenantAndForce +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.079ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:06Z" level=info msg="starting crowdsec console enrollment" agent=agent-one config= correlation_id=915fc0c4-36f4-4008-aef1-658e49da6714 force=true tenant=my-tenant +time="2026-01-10T02:17:06Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=agent-one correlation_id=915fc0c4-36f4-4008-aef1-658e49da6714 tenant=my-tenant +--- PASS: TestConsoleEnrollWithTenantAndForce (0.01s) +=== RUN TestSecureCommandExecutorExecuteWithEnv +=== RUN TestSecureCommandExecutorExecuteWithEnv/executes_command_successfully +=== RUN TestSecureCommandExecutorExecuteWithEnv/passes_environment_variables +=== RUN TestSecureCommandExecutorExecuteWithEnv/handles_empty_env_map +=== RUN TestSecureCommandExecutorExecuteWithEnv/handles_command_failure +=== RUN TestSecureCommandExecutorExecuteWithEnv/handles_context_timeout +--- PASS: TestSecureCommandExecutorExecuteWithEnv (0.01s) + --- PASS: TestSecureCommandExecutorExecuteWithEnv/executes_command_successfully (0.00s) + --- PASS: TestSecureCommandExecutorExecuteWithEnv/passes_environment_variables (0.00s) + --- PASS: TestSecureCommandExecutorExecuteWithEnv/handles_empty_env_map (0.00s) + --- PASS: TestSecureCommandExecutorExecuteWithEnv/handles_command_failure (0.00s) + --- PASS: TestSecureCommandExecutorExecuteWithEnv/handles_context_timeout (0.00s) +=== RUN TestFormatEnv +=== RUN TestFormatEnv/formats_single_env_var +=== RUN TestFormatEnv/formats_multiple_env_vars +=== RUN TestFormatEnv/handles_empty_map +=== RUN TestFormatEnv/handles_nil_map +=== RUN TestFormatEnv/handles_special_characters +--- PASS: TestFormatEnv (0.00s) + --- PASS: TestFormatEnv/formats_single_env_var (0.00s) + --- PASS: TestFormatEnv/formats_multiple_env_vars (0.00s) + --- PASS: TestFormatEnv/handles_empty_map (0.00s) + --- PASS: TestFormatEnv/handles_nil_map (0.00s) + --- PASS: TestFormatEnv/handles_special_characters (0.00s) +=== RUN TestConsoleEnrollmentStatus +=== RUN TestConsoleEnrollmentStatus/returns_not_enrolled_for_new_service + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.077ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +=== RUN TestConsoleEnrollmentStatus/returns_pending_acceptance_status_after_enrollment +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.097ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:06Z" level=info msg="starting crowdsec console enrollment" agent=test-agent config= correlation_id=52fce0c6-2ff8-4cc4-afc7-a8db06d702f5 force=false tenant= +time="2026-01-10T02:17:06Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=test-agent correlation_id=52fce0c6-2ff8-4cc4-afc7-a8db06d702f5 tenant= +=== RUN TestConsoleEnrollmentStatus/returns_failed_status_after_failed_enrollment +time="2026-01-10T02:17:06Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:06 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.116ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:06Z" level=info msg="starting crowdsec console enrollment" agent=test-agent config= correlation_id=5a7b3017-86c9-4975-8525-afe1029750c1 force=false tenant= +time="2026-01-10T02:17:06Z" level=warning msg="crowdsec console enrollment failed" correlation_id=5a7b3017-86c9-4975-8525-afe1029750c1 error="enroll failed" output=error tenant= +--- PASS: TestConsoleEnrollmentStatus (0.02s) + --- PASS: TestConsoleEnrollmentStatus/returns_not_enrolled_for_new_service (0.00s) + --- PASS: TestConsoleEnrollmentStatus/returns_pending_acceptance_status_after_enrollment (0.00s) + --- PASS: TestConsoleEnrollmentStatus/returns_failed_status_after_failed_enrollment (0.01s) +=== RUN TestDeriveKey +=== RUN TestDeriveKey/derives_consistent_key +=== RUN TestDeriveKey/derives_different_keys_for_different_secrets +=== RUN TestDeriveKey/uses_default_for_empty_secret +--- PASS: TestDeriveKey (0.00s) + --- PASS: TestDeriveKey/derives_consistent_key (0.00s) + --- PASS: TestDeriveKey/derives_different_keys_for_different_secrets (0.00s) + --- PASS: TestDeriveKey/uses_default_for_empty_secret (0.00s) +=== RUN TestNormalizeEnrollmentKey +=== RUN TestNormalizeEnrollmentKey/valid_raw_key +=== RUN TestNormalizeEnrollmentKey/full_command_with_sudo +=== RUN TestNormalizeEnrollmentKey/full_command_without_sudo +=== RUN TestNormalizeEnrollmentKey/key_with_whitespace +=== RUN TestNormalizeEnrollmentKey/empty_key +=== RUN TestNormalizeEnrollmentKey/only_whitespace +=== RUN TestNormalizeEnrollmentKey/invalid_format +=== RUN TestNormalizeEnrollmentKey/injection_attempt +--- PASS: TestNormalizeEnrollmentKey (0.00s) + --- PASS: TestNormalizeEnrollmentKey/valid_raw_key (0.00s) + --- PASS: TestNormalizeEnrollmentKey/full_command_with_sudo (0.00s) + --- PASS: TestNormalizeEnrollmentKey/full_command_without_sudo (0.00s) + --- PASS: TestNormalizeEnrollmentKey/key_with_whitespace (0.00s) + --- PASS: TestNormalizeEnrollmentKey/empty_key (0.00s) + --- PASS: TestNormalizeEnrollmentKey/only_whitespace (0.00s) + --- PASS: TestNormalizeEnrollmentKey/invalid_format (0.00s) + --- PASS: TestNormalizeEnrollmentKey/injection_attempt (0.00s) +=== RUN TestRedactSecret +=== RUN TestRedactSecret/redacts_secret_from_message +=== RUN TestRedactSecret/handles_empty_secret +=== RUN TestRedactSecret/handles_secret_not_in_message +=== RUN TestRedactSecret/redacts_multiple_occurrences +--- PASS: TestRedactSecret (0.00s) + --- PASS: TestRedactSecret/redacts_secret_from_message (0.00s) + --- PASS: TestRedactSecret/handles_empty_secret (0.00s) + --- PASS: TestRedactSecret/handles_secret_not_in_message (0.00s) + --- PASS: TestRedactSecret/redacts_multiple_occurrences (0.00s) +=== RUN TestExtractCscliErrorMessage +=== RUN TestExtractCscliErrorMessage/msg_format_with_quotes +=== RUN TestExtractCscliErrorMessage/ERRO_format_with_timestamp +=== RUN TestExtractCscliErrorMessage/plain_error_message +=== RUN TestExtractCscliErrorMessage/multiline_with_error_in_middle +=== RUN TestExtractCscliErrorMessage/empty_output +=== RUN TestExtractCscliErrorMessage/whitespace_only +=== RUN TestExtractCscliErrorMessage/no_recognizable_pattern_-_returns_first_line +=== RUN TestExtractCscliErrorMessage/failed_keyword_detection +=== RUN TestExtractCscliErrorMessage/invalid_keyword_detection +=== RUN TestExtractCscliErrorMessage/complex_cscli_output_with_msg +--- PASS: TestExtractCscliErrorMessage (0.00s) + --- PASS: TestExtractCscliErrorMessage/msg_format_with_quotes (0.00s) + --- PASS: TestExtractCscliErrorMessage/ERRO_format_with_timestamp (0.00s) + --- PASS: TestExtractCscliErrorMessage/plain_error_message (0.00s) + --- PASS: TestExtractCscliErrorMessage/multiline_with_error_in_middle (0.00s) + --- PASS: TestExtractCscliErrorMessage/empty_output (0.00s) + --- PASS: TestExtractCscliErrorMessage/whitespace_only (0.00s) + --- PASS: TestExtractCscliErrorMessage/no_recognizable_pattern_-_returns_first_line (0.00s) + --- PASS: TestExtractCscliErrorMessage/failed_keyword_detection (0.00s) + --- PASS: TestExtractCscliErrorMessage/invalid_keyword_detection (0.00s) + --- PASS: TestExtractCscliErrorMessage/complex_cscli_output_with_msg (0.00s) +=== RUN TestEncryptDecrypt +=== RUN TestEncryptDecrypt/encrypts_and_decrypts_successfully +=== RUN TestEncryptDecrypt/handles_empty_string +=== RUN TestEncryptDecrypt/different_encryptions_produce_different_ciphertext +--- PASS: TestEncryptDecrypt (0.00s) + --- PASS: TestEncryptDecrypt/encrypts_and_decrypts_successfully (0.00s) + --- PASS: TestEncryptDecrypt/handles_empty_string (0.00s) + --- PASS: TestEncryptDecrypt/different_encryptions_produce_different_ciphertext (0.00s) +=== RUN TestCheckLAPIAvailable_Retries +--- PASS: TestCheckLAPIAvailable_Retries (4.00s) +=== RUN TestCheckLAPIAvailable_RetriesExhausted +--- PASS: TestCheckLAPIAvailable_RetriesExhausted (4.01s) +=== RUN TestCheckLAPIAvailable_FirstAttemptSuccess +--- PASS: TestCheckLAPIAvailable_FirstAttemptSuccess (0.01s) +=== RUN TestEnroll_RequiresLAPI +--- PASS: TestEnroll_RequiresLAPI (4.01s) +=== RUN TestConsoleEnrollService_ClearEnrollment +time="2026-01-10T02:17:18Z" level=info msg="clearing console enrollment state" previous_status=enrolled +--- PASS: TestConsoleEnrollService_ClearEnrollment (0.00s) +=== RUN TestConsoleEnrollService_ClearEnrollment_NoRecord + +2026/01/10 02:17:18 /projects/Charon/backend/internal/crowdsec/console_enroll.go:355 record not found +[0.073ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +--- PASS: TestConsoleEnrollService_ClearEnrollment_NoRecord (0.00s) +=== RUN TestConsoleEnrollService_ClearEnrollment_NilDB +--- PASS: TestConsoleEnrollService_ClearEnrollment_NilDB (0.00s) +=== RUN TestConsoleEnrollService_ClearEnrollment_ThenReenroll +time="2026-01-10T02:17:18Z" level=info msg="registering with crowdsec capi" + +2026/01/10 02:17:18 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.087ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:18Z" level=info msg="starting crowdsec console enrollment" agent=agent-one config= correlation_id=a1a86ed2-7fe3-4d42-a98c-fa439d219941 force=false tenant= +time="2026-01-10T02:17:18Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=agent-one correlation_id=a1a86ed2-7fe3-4d42-a98c-fa439d219941 tenant= +time="2026-01-10T02:17:18Z" level=info msg="clearing console enrollment state" previous_status=pending_acceptance + +2026/01/10 02:17:18 /projects/Charon/backend/internal/crowdsec/console_enroll.go:327 record not found +[0.057ms] [rows:0] SELECT * FROM `crowdsec_console_enrollments` ORDER BY `crowdsec_console_enrollments`.`id` LIMIT 1 +time="2026-01-10T02:17:18Z" level=info msg="registering with crowdsec capi" +time="2026-01-10T02:17:18Z" level=info msg="starting crowdsec console enrollment" agent=agent-two config= correlation_id=c2001970-5a34-40d1-9454-a2ac1d48c5b8 force=false tenant= +time="2026-01-10T02:17:18Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=agent-two correlation_id=c2001970-5a34-40d1-9454-a2ac1d48c5b8 tenant= +--- PASS: TestConsoleEnrollService_ClearEnrollment_ThenReenroll (0.01s) +=== RUN TestConsoleEnrollService_LogsWhenSkipped +time="2026-01-10T02:17:18Z" level=info msg="registering with crowdsec capi" +time="2026-01-10T02:17:18Z" level=info msg="console enrollment skipped: already enrolled or pending acceptance - use force=true to re-enroll" agent_name=test-agent status=enrolled tenant=test-tenant +--- PASS: TestConsoleEnrollService_LogsWhenSkipped (0.00s) +=== RUN TestConsoleEnrollService_LogsWhenSkipped_PendingAcceptance +time="2026-01-10T02:17:18Z" level=info msg="registering with crowdsec capi" +time="2026-01-10T02:17:18Z" level=info msg="console enrollment skipped: already enrolled or pending acceptance - use force=true to re-enroll" agent_name=test-agent status=pending_acceptance tenant=test-tenant +--- PASS: TestConsoleEnrollService_LogsWhenSkipped_PendingAcceptance (0.00s) +=== RUN TestConsoleEnrollService_ForceOverridesSkip +time="2026-01-10T02:17:18Z" level=info msg="registering with crowdsec capi" +time="2026-01-10T02:17:18Z" level=info msg="starting crowdsec console enrollment" agent=new-agent config= correlation_id=b0b9632b-d724-4339-a04b-171797fd2b39 force=true tenant= +time="2026-01-10T02:17:18Z" level=info msg="crowdsec console enrollment request sent - pending acceptance on crowdsec.net" agent=new-agent correlation_id=b0b9632b-d724-4339-a04b-171797fd2b39 tenant= +--- PASS: TestConsoleEnrollService_ForceOverridesSkip (0.00s) +=== RUN TestEnroll_InvalidAgentNameCharacters +--- PASS: TestEnroll_InvalidAgentNameCharacters (0.00s) +=== RUN TestEnroll_InvalidTenantNameCharacters +--- PASS: TestEnroll_InvalidTenantNameCharacters (0.00s) +=== RUN TestEnsureCAPIRegistered_StandardLayoutExists +--- PASS: TestEnsureCAPIRegistered_StandardLayoutExists (0.00s) +=== RUN TestEnsureCAPIRegistered_RegisterError +time="2026-01-10T02:17:18Z" level=info msg="registering with crowdsec capi" +--- PASS: TestEnsureCAPIRegistered_RegisterError (0.00s) +=== RUN TestFindConfigPath_StandardLayout +--- PASS: TestFindConfigPath_StandardLayout (0.00s) +=== RUN TestFindConfigPath_RootLayout +--- PASS: TestFindConfigPath_RootLayout (0.00s) +=== RUN TestFindConfigPath_NeitherExists +--- PASS: TestFindConfigPath_NeitherExists (0.00s) +=== RUN TestStatusFromModel_NilModel +--- PASS: TestStatusFromModel_NilModel (0.00s) +=== RUN TestNormalizeEnrollmentKey_InvalidCharacters +--- PASS: TestNormalizeEnrollmentKey_InvalidCharacters (0.00s) +=== RUN TestNormalizeEnrollmentKey_TooShort +--- PASS: TestNormalizeEnrollmentKey_TooShort (0.00s) +=== RUN TestNormalizeEnrollmentKey_NonMatchingFormat +--- PASS: TestNormalizeEnrollmentKey_NonMatchingFormat (0.00s) +=== RUN TestApplyWithOpenFileHandles +time="2026-01-10T02:17:18Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestApplyWithOpenFileHandles3946802142/001/test/preset/bundle.tgz cache_key=test/preset-1768011438 meta_path=/tmp/TestApplyWithOpenFileHandles3946802142/001/test/preset/metadata.json preview_path=/tmp/TestApplyWithOpenFileHandles3946802142/001/test/preset/preview.yaml slug=test/preset +time="2026-01-10T02:17:18Z" level=info msg="successfully loaded cached preset metadata" archive_path=/tmp/TestApplyWithOpenFileHandles3946802142/001/test/preset/bundle.tgz cache_key=test/preset-1768011438 slug=test/preset +--- PASS: TestApplyWithOpenFileHandles (0.02s) +=== RUN TestBackupPathOnlySetAfterSuccessfulBackup +=== RUN TestBackupPathOnlySetAfterSuccessfulBackup/backup_path_not_set_when_cache_missing +time="2026-01-10T02:17:18Z" level=warning msg="failed to load cached preset metadata" error="cache miss" slug=nonexistent/preset +time="2026-01-10T02:17:18Z" level=info msg="attempting to repull preset after cache load failure" error="load cache for nonexistent/preset: cache miss" slug=nonexistent/preset +time="2026-01-10T02:17:18Z" level=warning msg="hub index fetch failed, trying mirror" attempt=1 error="https://hub-data.crowdsec.net/api/index.json (status 403)" hub_index="https://hub-data.crowdsec.net/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=true hub_index="https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json" +time="2026-01-10T02:17:19Z" level=warning msg="cache refresh failed; rolled back backup" backup_path=/tmp/TestBackupPathOnlySetAfterSuccessfulBackupbackup_path_not_set_w4106602953/002/crowdsec.backup.20260110-021718 error="load cache for nonexistent/preset: cache miss: refresh cache: preset not found in hub" slug=nonexistent/preset +=== RUN TestBackupPathOnlySetAfterSuccessfulBackup/backup_path_set_only_after_successful_backup +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestBackupPathOnlySetAfterSuccessfulBackupbackup_path_set_only_1030315152/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 meta_path=/tmp/TestBackupPathOnlySetAfterSuccessfulBackupbackup_path_set_only_1030315152/001/test/preset/metadata.json preview_path=/tmp/TestBackupPathOnlySetAfterSuccessfulBackupbackup_path_set_only_1030315152/001/test/preset/preview.yaml slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="successfully loaded cached preset metadata" archive_path=/tmp/TestBackupPathOnlySetAfterSuccessfulBackupbackup_path_set_only_1030315152/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 slug=test/preset +--- PASS: TestBackupPathOnlySetAfterSuccessfulBackup (1.24s) + --- PASS: TestBackupPathOnlySetAfterSuccessfulBackup/backup_path_not_set_when_cache_missing (1.24s) + --- PASS: TestBackupPathOnlySetAfterSuccessfulBackup/backup_path_set_only_after_successful_backup (0.01s) +=== RUN TestHubCacheStoreLoadAndExpire +=== PAUSE TestHubCacheStoreLoadAndExpire +=== RUN TestHubCacheRejectsBadSlug +=== PAUSE TestHubCacheRejectsBadSlug +=== RUN TestHubCacheListAndEvict +=== PAUSE TestHubCacheListAndEvict +=== RUN TestHubCacheTouchUpdatesTTL +=== PAUSE TestHubCacheTouchUpdatesTTL +=== RUN TestHubCachePreviewExistsAndSize +=== PAUSE TestHubCachePreviewExistsAndSize +=== RUN TestHubCacheExistsHonorsTTL +=== PAUSE TestHubCacheExistsHonorsTTL +=== RUN TestSanitizeSlugCases +=== PAUSE TestSanitizeSlugCases +=== RUN TestNewHubCacheRequiresBaseDir +=== PAUSE TestNewHubCacheRequiresBaseDir +=== RUN TestHubCacheTouchMissing +=== PAUSE TestHubCacheTouchMissing +=== RUN TestHubCacheTouchInvalidSlug +=== PAUSE TestHubCacheTouchInvalidSlug +=== RUN TestHubCacheStoreContextCanceled +=== PAUSE TestHubCacheStoreContextCanceled +=== RUN TestHubCacheLoadInvalidSlug +=== PAUSE TestHubCacheLoadInvalidSlug +=== RUN TestHubCacheExistsContextCanceled +=== PAUSE TestHubCacheExistsContextCanceled +=== RUN TestHubCacheListSkipsExpired +=== PAUSE TestHubCacheListSkipsExpired +=== RUN TestHubCacheEvictInvalidSlug +=== PAUSE TestHubCacheEvictInvalidSlug +=== RUN TestHubCacheListContextCanceled +=== PAUSE TestHubCacheListContextCanceled +=== RUN TestHubCacheTTL +=== PAUSE TestHubCacheTTL +=== RUN TestPullThenApplyFlow + hub_pull_apply_test.go:90: Step 1: Pulling preset +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://test.example.com/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://test.example.com/test.tgz" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://test.example.com/test.yaml" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="storing preset in cache" archive_size=158 etag=etag123 hub_endpoint="http://test.example.com/test.tgz" preview_size=24 slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestPullThenApplyFlow2203214519/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 meta_path=/tmp/TestPullThenApplyFlow2203214519/001/test/preset/metadata.json preview_path=/tmp/TestPullThenApplyFlow2203214519/001/test/preset/preview.yaml slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="preset successfully cached" archive_path=/tmp/TestPullThenApplyFlow2203214519/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 preview_path=/tmp/TestPullThenApplyFlow2203214519/001/test/preset/preview.yaml slug=test/preset + hub_pull_apply_test.go:110: Step 2: Verifying cache can be loaded + hub_pull_apply_test.go:117: Step 3: Applying preset from cache +time="2026-01-10T02:17:19Z" level=info msg="successfully loaded cached preset metadata" archive_path=/tmp/TestPullThenApplyFlow2203214519/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 slug=test/preset +--- PASS: TestPullThenApplyFlow (0.01s) +=== RUN TestApplyRepullsOnCacheMissAfterCSCLIFailure +time="2026-01-10T02:17:19Z" level=warning msg="failed to load cached preset metadata" error="cache miss" slug=test/preset +time="2026-01-10T02:17:19Z" level=warning msg="cscli install failed; attempting cache fallback" error="install failed" slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="attempting to repull preset after cache load failure" error="load cache for test/preset: cache miss" slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://test.example.com/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://test.example.com/test/preset.tgz" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://test.example.com/test/preset.yaml" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="storing preset in cache" archive_size=110 etag=e1 hub_endpoint="http://test.example.com/test/preset.tgz" preview_size=7 slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestApplyRepullsOnCacheMissAfterCSCLIFailure1830341465/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 meta_path=/tmp/TestApplyRepullsOnCacheMissAfterCSCLIFailure1830341465/001/test/preset/metadata.json preview_path=/tmp/TestApplyRepullsOnCacheMissAfterCSCLIFailure1830341465/001/test/preset/preview.yaml slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="preset successfully cached" archive_path=/tmp/TestApplyRepullsOnCacheMissAfterCSCLIFailure1830341465/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 preview_path=/tmp/TestApplyRepullsOnCacheMissAfterCSCLIFailure1830341465/001/test/preset/preview.yaml slug=test/preset +--- PASS: TestApplyRepullsOnCacheMissAfterCSCLIFailure (0.01s) +=== RUN TestApplyRepullsOnCacheExpired +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestApplyRepullsOnCacheExpired748670146/001/expired/preset/bundle.tgz cache_key=expired/preset-1768011439 meta_path=/tmp/TestApplyRepullsOnCacheExpired748670146/001/expired/preset/metadata.json preview_path=/tmp/TestApplyRepullsOnCacheExpired748670146/001/expired/preset/preview.yaml slug=expired/preset +time="2026-01-10T02:17:19Z" level=warning msg="failed to load cached preset metadata" error="cache expired" slug=expired/preset +time="2026-01-10T02:17:19Z" level=info msg="attempting to repull preset after cache load failure" error="load cache for expired/preset: cache expired" slug=expired/preset +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://test.example.com/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://test.example.com/expired/preset.tgz" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://test.example.com/expired/preset.yaml" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="storing preset in cache" archive_size=112 etag=e2 hub_endpoint="http://test.example.com/expired/preset.tgz" preview_size=11 slug=expired/preset +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestApplyRepullsOnCacheExpired748670146/001/expired/preset/bundle.tgz cache_key=expired/preset-1768011439 meta_path=/tmp/TestApplyRepullsOnCacheExpired748670146/001/expired/preset/metadata.json preview_path=/tmp/TestApplyRepullsOnCacheExpired748670146/001/expired/preset/preview.yaml slug=expired/preset +time="2026-01-10T02:17:19Z" level=info msg="preset successfully cached" archive_path=/tmp/TestApplyRepullsOnCacheExpired748670146/001/expired/preset/bundle.tgz cache_key=expired/preset-1768011439 preview_path=/tmp/TestApplyRepullsOnCacheExpired748670146/001/expired/preset/preview.yaml slug=expired/preset +--- PASS: TestApplyRepullsOnCacheExpired (0.02s) +=== RUN TestPullAcceptsNamespacedIndexEntry +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://test.example.com/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://test.example.com/crowdsecurity/bot-mitigation-essentials.tgz" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://test.example.com/crowdsecurity/bot-mitigation-essentials.yaml" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="storing preset in cache" archive_size=114 etag=etag-bme hub_endpoint="http://test.example.com/crowdsecurity/bot-mitigation-essentials.tgz" preview_size=18 slug=bot-mitigation-essentials +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestPullAcceptsNamespacedIndexEntry586165543/001/bot-mitigation-essentials/bundle.tgz cache_key=bot-mitigation-essentials-1768011439 meta_path=/tmp/TestPullAcceptsNamespacedIndexEntry586165543/001/bot-mitigation-essentials/metadata.json preview_path=/tmp/TestPullAcceptsNamespacedIndexEntry586165543/001/bot-mitigation-essentials/preview.yaml slug=bot-mitigation-essentials +time="2026-01-10T02:17:19Z" level=info msg="preset successfully cached" archive_path=/tmp/TestPullAcceptsNamespacedIndexEntry586165543/001/bot-mitigation-essentials/bundle.tgz cache_key=bot-mitigation-essentials-1768011439 preview_path=/tmp/TestPullAcceptsNamespacedIndexEntry586165543/001/bot-mitigation-essentials/preview.yaml slug=bot-mitigation-essentials +--- PASS: TestPullAcceptsNamespacedIndexEntry (0.00s) +=== RUN TestHubFallbackToMirrorOnForbidden +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=1 error="http://primary.example.com/api/index.json (status 403)" hub_index="http://primary.example.com/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=true hub_index="http://mirror.example.com/api/index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub fetch failed, attempting fallback" attempt=1 endpoint="http://primary.example.com/fallback/preset.tgz" error="http://primary.example.com/fallback/preset.tgz (status 403)" +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://mirror.example.com/fallback/preset.tgz" fallback_used=true +time="2026-01-10T02:17:19Z" level=warning msg="hub fetch failed, attempting fallback" attempt=1 endpoint="http://primary.example.com/fallback/preset.yaml" error="http://primary.example.com/fallback/preset.yaml (status 403)" +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://mirror.example.com/fallback/preset.yaml" fallback_used=true +time="2026-01-10T02:17:19Z" level=info msg="storing preset in cache" archive_size=104 etag=etag-mirror hub_endpoint="http://mirror.example.com/fallback/preset.tgz" preview_size=14 slug=fallback/preset +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubFallbackToMirrorOnForbidden3143303761/001/fallback/preset/bundle.tgz cache_key=fallback/preset-1768011439 meta_path=/tmp/TestHubFallbackToMirrorOnForbidden3143303761/001/fallback/preset/metadata.json preview_path=/tmp/TestHubFallbackToMirrorOnForbidden3143303761/001/fallback/preset/preview.yaml slug=fallback/preset +time="2026-01-10T02:17:19Z" level=info msg="preset successfully cached" archive_path=/tmp/TestHubFallbackToMirrorOnForbidden3143303761/001/fallback/preset/bundle.tgz cache_key=fallback/preset-1768011439 preview_path=/tmp/TestHubFallbackToMirrorOnForbidden3143303761/001/fallback/preset/preview.yaml slug=fallback/preset +--- PASS: TestHubFallbackToMirrorOnForbidden (0.01s) +=== RUN TestApplyWithoutPullFails +time="2026-01-10T02:17:19Z" level=warning msg="failed to load cached preset metadata" error="cache miss" slug=nonexistent/preset +time="2026-01-10T02:17:19Z" level=info msg="attempting to repull preset after cache load failure" error="load cache for nonexistent/preset: cache miss" slug=nonexistent/preset +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=1 error="http://test.example.com/api/index.json (status 500)" hub_index="http://test.example.com/api/index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=2 error="https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json (status 500)" hub_index="https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=3 error="https://raw.githubusercontent.com/crowdsecurity/hub/master/api/index.json (status 500)" hub_index="https://raw.githubusercontent.com/crowdsecurity/hub/master/api/index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=4 error="https://hub-data.crowdsec.net/api/index.json (status 500)" hub_index="https://hub-data.crowdsec.net/api/index.json" +time="2026-01-10T02:17:19Z" level=warning msg="cache refresh failed; rolled back backup" backup_path=/tmp/TestApplyWithoutPullFails1841293971/002.backup.20260110-021719 error="load cache for nonexistent/preset: cache miss: refresh cache: fetch hub index: http://test.example.com/api/index.json: http://test.example.com/api/index.json (status 500)\nhttps://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json: https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json (status 500)\nhttps://raw.githubusercontent.com/crowdsecurity/hub/master/api/index.json: https://raw.githubusercontent.com/crowdsecurity/hub/master/api/index.json (status 500)\nhttps://hub-data.crowdsec.net/api/index.json: https://hub-data.crowdsec.net/api/index.json (status 500)" slug=nonexistent/preset +--- PASS: TestApplyWithoutPullFails (0.00s) +=== RUN TestCacheExpiration +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestCacheExpiration2058278143/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 meta_path=/tmp/TestCacheExpiration2058278143/001/test/preset/metadata.json preview_path=/tmp/TestCacheExpiration2058278143/001/test/preset/preview.yaml slug=test/preset +--- PASS: TestCacheExpiration (0.01s) +=== RUN TestCacheListAfterPull +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://test.example.com/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://test.example.com/preset1.tgz" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://test.example.com/preset1.yaml" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="storing preset in cache" archive_size=105 etag=e1 hub_endpoint="http://test.example.com/preset1.tgz" preview_size=8 slug=preset1 +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestCacheListAfterPull3507341671/001/preset1/bundle.tgz cache_key=preset1-1768011439 meta_path=/tmp/TestCacheListAfterPull3507341671/001/preset1/metadata.json preview_path=/tmp/TestCacheListAfterPull3507341671/001/preset1/preview.yaml slug=preset1 +time="2026-01-10T02:17:19Z" level=info msg="preset successfully cached" archive_path=/tmp/TestCacheListAfterPull3507341671/001/preset1/bundle.tgz cache_key=preset1-1768011439 preview_path=/tmp/TestCacheListAfterPull3507341671/001/preset1/preview.yaml slug=preset1 +--- PASS: TestCacheListAfterPull (0.01s) +=== RUN TestApplyReadsArchiveBeforeBackup +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestApplyReadsArchiveBeforeBackup1310230569/001/crowdsec/hub_cache/test/preset/bundle.tgz cache_key=test/preset-1768011439 meta_path=/tmp/TestApplyReadsArchiveBeforeBackup1310230569/001/crowdsec/hub_cache/test/preset/metadata.json preview_path=/tmp/TestApplyReadsArchiveBeforeBackup1310230569/001/crowdsec/hub_cache/test/preset/preview.yaml slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="successfully loaded cached preset metadata" archive_path=/tmp/TestApplyReadsArchiveBeforeBackup1310230569/001/crowdsec/hub_cache/test/preset/bundle.tgz cache_key=test/preset-1768011439 slug=test/preset +--- PASS: TestApplyReadsArchiveBeforeBackup (0.01s) +=== RUN TestFetchIndexParsesRawIndexFormat +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://example.com/api/index.json" +--- PASS: TestFetchIndexParsesRawIndexFormat (0.00s) +=== RUN TestFetchIndexPrefersCSCLI +=== PAUSE TestFetchIndexPrefersCSCLI +=== RUN TestFetchIndexFallbackHTTP +=== PAUSE TestFetchIndexFallbackHTTP +=== RUN TestFetchIndexHTTPRejectsRedirect +=== PAUSE TestFetchIndexHTTPRejectsRedirect +=== RUN TestFetchIndexHTTPRejectsHTML +=== PAUSE TestFetchIndexHTTPRejectsHTML +=== RUN TestFetchIndexHTTPFallsBackToDefaultHub +=== PAUSE TestFetchIndexHTTPFallsBackToDefaultHub +=== RUN TestFetchIndexFallsBackToMirrorOnForbidden +=== PAUSE TestFetchIndexFallsBackToMirrorOnForbidden +=== RUN TestPullCachesPreview +=== PAUSE TestPullCachesPreview +=== RUN TestApplyUsesCacheWhenCSCLIFails +=== PAUSE TestApplyUsesCacheWhenCSCLIFails +=== RUN TestApplyRollsBackOnBadArchive +=== PAUSE TestApplyRollsBackOnBadArchive +=== RUN TestApplyUsesCacheWhenCscliMissing +=== PAUSE TestApplyUsesCacheWhenCscliMissing +=== RUN TestPullReturnsCachedPreviewWithoutNetwork +=== PAUSE TestPullReturnsCachedPreviewWithoutNetwork +=== RUN TestPullEvictsExpiredCacheAndRefreshes +=== PAUSE TestPullEvictsExpiredCacheAndRefreshes +=== RUN TestPullFallsBackToArchivePreview +=== PAUSE TestPullFallsBackToArchivePreview +=== RUN TestPullFallsBackToMirrorArchiveOnForbidden +=== PAUSE TestPullFallsBackToMirrorArchiveOnForbidden +=== RUN TestFetchWithLimitRejectsLargePayload +=== PAUSE TestFetchWithLimitRejectsLargePayload +=== RUN TestExtractTarGzRejectsSymlink +=== PAUSE TestExtractTarGzRejectsSymlink +=== RUN TestExtractTarGzRejectsAbsolutePath +=== PAUSE TestExtractTarGzRejectsAbsolutePath +=== RUN TestFetchIndexHTTPError +=== PAUSE TestFetchIndexHTTPError +=== RUN TestPullValidatesSlugAndMissingPreset +=== PAUSE TestPullValidatesSlugAndMissingPreset +=== RUN TestFetchPreviewRequiresURL +=== PAUSE TestFetchPreviewRequiresURL +=== RUN TestFetchWithLimitRequiresClient +=== PAUSE TestFetchWithLimitRequiresClient +=== RUN TestRunCSCLIRejectsUnsafeSlug +=== PAUSE TestRunCSCLIRejectsUnsafeSlug +=== RUN TestApplyUsesCSCLISuccess +=== PAUSE TestApplyUsesCSCLISuccess +=== RUN TestFetchIndexCSCLIParseError +=== PAUSE TestFetchIndexCSCLIParseError +=== RUN TestFetchWithLimitStatusError +=== PAUSE TestFetchWithLimitStatusError +=== RUN TestApplyRollsBackWhenCacheMissing +=== PAUSE TestApplyRollsBackWhenCacheMissing +=== RUN TestNormalizeHubBaseURL +=== PAUSE TestNormalizeHubBaseURL +=== RUN TestBuildIndexURL +=== PAUSE TestBuildIndexURL +=== RUN TestUniqueStrings +=== PAUSE TestUniqueStrings +=== RUN TestFirstNonEmpty +=== PAUSE TestFirstNonEmpty +=== RUN TestCleanShellArg +=== PAUSE TestCleanShellArg +=== RUN TestHasCSCLI +=== PAUSE TestHasCSCLI +=== RUN TestFindPreviewFileFromArchive +=== PAUSE TestFindPreviewFileFromArchive +=== RUN TestApplyWithCopyBasedBackup +=== PAUSE TestApplyWithCopyBasedBackup +=== RUN TestBackupExistingHandlesDeviceBusy +=== PAUSE TestBackupExistingHandlesDeviceBusy +=== RUN TestCopyFile +=== PAUSE TestCopyFile +=== RUN TestCopyDir +=== PAUSE TestCopyDir +=== RUN TestFetchIndexHTTPAcceptsTextPlain +=== PAUSE TestFetchIndexHTTPAcceptsTextPlain +=== RUN TestValidateHubURL_ValidHTTPSProduction +=== PAUSE TestValidateHubURL_ValidHTTPSProduction +=== RUN TestValidateHubURL_InvalidSchemes +=== PAUSE TestValidateHubURL_InvalidSchemes +=== RUN TestValidateHubURL_LocalhostExceptions +=== PAUSE TestValidateHubURL_LocalhostExceptions +=== RUN TestValidateHubURL_UnknownDomainRejection +=== PAUSE TestValidateHubURL_UnknownDomainRejection +=== RUN TestValidateHubURL_HTTPRejectedForProduction +=== PAUSE TestValidateHubURL_HTTPRejectedForProduction +=== RUN TestBuildResourceURLs +=== PAUSE TestBuildResourceURLs +=== RUN TestParseRawIndex +=== PAUSE TestParseRawIndex +=== RUN TestFetchIndexHTTPFromURL_HTMLDetection +=== PAUSE TestFetchIndexHTTPFromURL_HTMLDetection +=== RUN TestHubService_Apply_ArchiveReadBeforeBackup +=== PAUSE TestHubService_Apply_ArchiveReadBeforeBackup +=== RUN TestHubService_Apply_CacheRefresh +=== PAUSE TestHubService_Apply_CacheRefresh +=== RUN TestHubService_Apply_RollbackOnExtractionFailure +=== PAUSE TestHubService_Apply_RollbackOnExtractionFailure +=== RUN TestCopyDirAndCopyFile +=== PAUSE TestCopyDirAndCopyFile +=== RUN TestEmptyDir +=== PAUSE TestEmptyDir +=== RUN TestExtractTarGz +=== PAUSE TestExtractTarGz +=== RUN TestBackupExisting +=== PAUSE TestBackupExisting +=== RUN TestRollback +=== PAUSE TestRollback +=== RUN TestHubHTTPErrorError +=== PAUSE TestHubHTTPErrorError +=== RUN TestHubHTTPErrorUnwrap +=== PAUSE TestHubHTTPErrorUnwrap +=== RUN TestHubHTTPErrorCanFallback +=== PAUSE TestHubHTTPErrorCanFallback +=== RUN TestValidateHubURL_EdgeCases +=== PAUSE TestValidateHubURL_EdgeCases +=== RUN TestNewHubService_DefaultTimeouts +=== PAUSE TestNewHubService_DefaultTimeouts +=== RUN TestNewHubService_EnvVarTimeouts_Valid +--- PASS: TestNewHubService_EnvVarTimeouts_Valid (0.00s) +=== RUN TestNewHubService_EnvVarTimeouts_Invalid +--- PASS: TestNewHubService_EnvVarTimeouts_Invalid (0.00s) +=== RUN TestNewHubService_EnvVarTimeouts_Negative +--- PASS: TestNewHubService_EnvVarTimeouts_Negative (0.00s) +=== RUN TestNewHubService_EnvVarTimeouts_Whitespace +--- PASS: TestNewHubService_EnvVarTimeouts_Whitespace (0.00s) +=== RUN TestNewHubService_CustomHubBaseURL +--- PASS: TestNewHubService_CustomHubBaseURL (0.00s) +=== RUN TestNewHubService_CustomMirrorBaseURL +--- PASS: TestNewHubService_CustomMirrorBaseURL (0.00s) +=== RUN TestBackupExisting_CopyFallback_Success +=== PAUSE TestBackupExisting_CopyFallback_Success +=== RUN TestBackupExisting_RenameSuccess +=== PAUSE TestBackupExisting_RenameSuccess +=== RUN TestBackupExisting_EmptyDirectory +=== PAUSE TestBackupExisting_EmptyDirectory +=== RUN TestBackupExisting_PreservesPermissions +=== PAUSE TestBackupExisting_PreservesPermissions +=== RUN TestExtractTarGz_NestedPathTraversal +=== PAUSE TestExtractTarGz_NestedPathTraversal +=== RUN TestExtractTarGz_AbsolutePathWithDots +=== PAUSE TestExtractTarGz_AbsolutePathWithDots +=== RUN TestExtractTarGz_EmptyArchive +=== PAUSE TestExtractTarGz_EmptyArchive +=== RUN TestExtractTarGz_InvalidTarAfterGzip +=== PAUSE TestExtractTarGz_InvalidTarAfterGzip +=== RUN TestExtractTarGz_LargeNestedStructure +=== PAUSE TestExtractTarGz_LargeNestedStructure +=== RUN TestExtractTarGz_SpecialCharactersInFilenames +=== PAUSE TestExtractTarGz_SpecialCharactersInFilenames +=== RUN TestExtractTarGz_DirectoriesWithoutFiles +=== PAUSE TestExtractTarGz_DirectoriesWithoutFiles +=== RUN TestExtractTarGz_SkipsSpecialFileTypes +=== PAUSE TestExtractTarGz_SkipsSpecialFileTypes +=== RUN TestAsString_Nil +=== PAUSE TestAsString_Nil +=== RUN TestAsString_String +=== PAUSE TestAsString_String +=== RUN TestAsString_Int +=== PAUSE TestAsString_Int +=== RUN TestAsString_Float +=== PAUSE TestAsString_Float +=== RUN TestAsString_Bool +=== PAUSE TestAsString_Bool +=== RUN TestAsString_Struct +=== PAUSE TestAsString_Struct +=== RUN TestAsString_EmptyString +=== PAUSE TestAsString_EmptyString +=== RUN TestFetchIndexHTTPFromURL_ParseRawIndexFallback +=== PAUSE TestFetchIndexHTTPFromURL_ParseRawIndexFallback +=== RUN TestFetchIndexHTTPFromURL_EmptyJSONArray +=== PAUSE TestFetchIndexHTTPFromURL_EmptyJSONArray +=== RUN TestFetchIndexHTTPFromURL_InvalidJSON +=== PAUSE TestFetchIndexHTTPFromURL_InvalidJSON +=== RUN TestIsGzip_ValidGzip +=== PAUSE TestIsGzip_ValidGzip +=== RUN TestIsGzip_NotGzip +=== PAUSE TestIsGzip_NotGzip +=== RUN TestIsGzip_TooShort +=== PAUSE TestIsGzip_TooShort +=== RUN TestPeekFirstYAML_FindsYAML +=== PAUSE TestPeekFirstYAML_FindsYAML +=== RUN TestPeekFirstYAML_NoYAMLFiles +=== PAUSE TestPeekFirstYAML_NoYAMLFiles +=== RUN TestPeekFirstYAML_InvalidArchive +=== PAUSE TestPeekFirstYAML_InvalidArchive +=== RUN TestFindIndexEntry_ExactMatch +=== PAUSE TestFindIndexEntry_ExactMatch +=== RUN TestFindIndexEntry_ShortName +=== PAUSE TestFindIndexEntry_ShortName +=== RUN TestFindIndexEntry_AmbiguousShortName +=== PAUSE TestFindIndexEntry_AmbiguousShortName +=== RUN TestFindIndexEntry_NotFound +=== PAUSE TestFindIndexEntry_NotFound +=== RUN TestFindIndexEntry_EmptySlug +=== PAUSE TestFindIndexEntry_EmptySlug +=== RUN TestListCuratedPresetsReturnsCopy +=== PAUSE TestListCuratedPresetsReturnsCopy +=== RUN TestFindPreset +=== PAUSE TestFindPreset +=== RUN TestFindPresetCaseVariants +=== PAUSE TestFindPresetCaseVariants +=== RUN TestListCuratedPresetsReturnsDifferentCopy +=== PAUSE TestListCuratedPresetsReturnsDifferentCopy +=== RUN TestCheckLAPIHealth_Healthy +--- PASS: TestCheckLAPIHealth_Healthy (0.00s) +=== RUN TestCheckLAPIHealth_Unhealthy +--- PASS: TestCheckLAPIHealth_Unhealthy (0.00s) +=== RUN TestCheckLAPIHealth_Unreachable +--- PASS: TestCheckLAPIHealth_Unreachable (0.00s) +=== RUN TestCheckLAPIHealth_FallbackToDecisions +--- PASS: TestCheckLAPIHealth_FallbackToDecisions (0.00s) +=== RUN TestCheckLAPIHealth_DefaultURL +--- PASS: TestCheckLAPIHealth_DefaultURL (0.00s) +=== RUN TestGetBouncerAPIKey_FromEnv +--- PASS: TestGetBouncerAPIKey_FromEnv (0.00s) +=== RUN TestGetBouncerAPIKey_Empty +--- PASS: TestGetBouncerAPIKey_Empty (0.00s) +=== RUN TestGetBouncerAPIKey_Fallback +--- PASS: TestGetBouncerAPIKey_Fallback (0.00s) +=== RUN TestEnsureBouncerRegistered_UsesEnvKey +--- PASS: TestEnsureBouncerRegistered_UsesEnvKey (0.00s) +=== RUN TestEnsureBouncerRegistered_NoEnvNoCSCLI +--- PASS: TestEnsureBouncerRegistered_NoEnvNoCSCLI (0.00s) +=== RUN TestEnsureBouncerRegistered_ReturnsExistingBouncerKey +--- PASS: TestEnsureBouncerRegistered_ReturnsExistingBouncerKey (0.00s) +=== RUN TestEnsureBouncerRegistered_RegistersNewWhenNoneExists +--- PASS: TestEnsureBouncerRegistered_RegistersNewWhenNoneExists (0.01s) +=== RUN TestGetLAPIVersion_JSON +--- PASS: TestGetLAPIVersion_JSON (0.00s) +=== RUN TestGetLAPIVersion_PlainText +--- PASS: TestGetLAPIVersion_PlainText (0.00s) +=== RUN TestValidateLAPIURL +=== RUN TestValidateLAPIURL/valid_localhost_with_port +=== RUN TestValidateLAPIURL/valid_127.0.0.1 +=== RUN TestValidateLAPIURL/external_URL_blocked +=== RUN TestValidateLAPIURL/HTTPS_localhost +=== RUN TestValidateLAPIURL/invalid_scheme +=== RUN TestValidateLAPIURL/no_scheme +=== RUN TestValidateLAPIURL/empty_URL_allowed_(defaults_to_localhost) +=== RUN TestValidateLAPIURL/IPv6_localhost +=== RUN TestValidateLAPIURL/private_IP_192.168.x.x_blocked_(security) +=== RUN TestValidateLAPIURL/private_IP_10.x.x.x_blocked_(security) +=== RUN TestValidateLAPIURL/missing_hostname +--- PASS: TestValidateLAPIURL (0.00s) + --- PASS: TestValidateLAPIURL/valid_localhost_with_port (0.00s) + --- PASS: TestValidateLAPIURL/valid_127.0.0.1 (0.00s) + --- PASS: TestValidateLAPIURL/external_URL_blocked (0.00s) + --- PASS: TestValidateLAPIURL/HTTPS_localhost (0.00s) + --- PASS: TestValidateLAPIURL/invalid_scheme (0.00s) + --- PASS: TestValidateLAPIURL/no_scheme (0.00s) + --- PASS: TestValidateLAPIURL/empty_URL_allowed_(defaults_to_localhost) (0.00s) + --- PASS: TestValidateLAPIURL/IPv6_localhost (0.00s) + --- PASS: TestValidateLAPIURL/private_IP_192.168.x.x_blocked_(security) (0.00s) + --- PASS: TestValidateLAPIURL/private_IP_10.x.x.x_blocked_(security) (0.00s) + --- PASS: TestValidateLAPIURL/missing_hostname (0.00s) +=== RUN TestEnsureBouncerRegistered_InvalidURL +=== RUN TestEnsureBouncerRegistered_InvalidURL/external_URL_rejected +=== RUN TestEnsureBouncerRegistered_InvalidURL/invalid_scheme_rejected +--- PASS: TestEnsureBouncerRegistered_InvalidURL (0.00s) + --- PASS: TestEnsureBouncerRegistered_InvalidURL/external_URL_rejected (0.00s) + --- PASS: TestEnsureBouncerRegistered_InvalidURL/invalid_scheme_rejected (0.00s) +=== CONT TestHubCacheStoreLoadAndExpire +=== CONT TestPullEvictsExpiredCacheAndRefreshes +=== CONT TestValidateHubURL_UnknownDomainRejection +=== RUN TestValidateHubURL_UnknownDomainRejection/https://evil.com/index.json +=== CONT TestValidateHubURL_ValidHTTPSProduction +=== RUN TestValidateHubURL_ValidHTTPSProduction/https://hub-data.crowdsec.net/api/index.json +=== PAUSE TestValidateHubURL_ValidHTTPSProduction/https://hub-data.crowdsec.net/api/index.json +=== RUN TestValidateHubURL_ValidHTTPSProduction/https://hub.crowdsec.net/api/index.json +=== PAUSE TestValidateHubURL_ValidHTTPSProduction/https://hub.crowdsec.net/api/index.json +=== RUN TestValidateHubURL_ValidHTTPSProduction/https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json +=== PAUSE TestValidateHubURL_ValidHTTPSProduction/https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json +=== CONT TestFetchIndexHTTPAcceptsTextPlain +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="https://hub-data.crowdsec.net/api/index.json" +=== PAUSE TestValidateHubURL_UnknownDomainRejection/https://evil.com/index.json +=== RUN TestValidateHubURL_UnknownDomainRejection/https://attacker.net/hub/index.json +--- PASS: TestFetchIndexHTTPAcceptsTextPlain (0.00s) +=== PAUSE TestValidateHubURL_UnknownDomainRejection/https://attacker.net/hub/index.json +=== RUN TestValidateHubURL_UnknownDomainRejection/https://hub.evil.com/index.json +=== CONT TestCopyDir +=== PAUSE TestValidateHubURL_UnknownDomainRejection/https://hub.evil.com/index.json +=== CONT TestBackupExistingHandlesDeviceBusy +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubCacheStoreLoadAndExpire3016545954/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestHubCacheStoreLoadAndExpire3016545954/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestHubCacheStoreLoadAndExpire3016545954/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +--- PASS: TestBackupExistingHandlesDeviceBusy (0.00s) +=== CONT TestApplyWithCopyBasedBackup +--- PASS: TestHubCacheStoreLoadAndExpire (0.01s) +=== CONT TestCopyFile +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestPullEvictsExpiredCacheAndRefreshes3657415151/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011437 meta_path=/tmp/TestPullEvictsExpiredCacheAndRefreshes3657415151/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestPullEvictsExpiredCacheAndRefreshes3657415151/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://example.com/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://example.com/demo.tgz" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://example.com/demo.yaml" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="storing preset in cache" archive_size=99 etag=etag2 hub_endpoint="http://example.com/demo.tgz" preview_size=13 slug=crowdsecurity/demo +--- PASS: TestCopyFile (0.00s) +=== CONT TestFindPreviewFileFromArchive +=== RUN TestFindPreviewFileFromArchive/finds_yaml_in_archive +=== PAUSE TestFindPreviewFileFromArchive/finds_yaml_in_archive +=== RUN TestFindPreviewFileFromArchive/returns_empty_for_no_yaml +=== PAUSE TestFindPreviewFileFromArchive/returns_empty_for_no_yaml +=== RUN TestFindPreviewFileFromArchive/returns_empty_for_invalid_archive +=== PAUSE TestFindPreviewFileFromArchive/returns_empty_for_invalid_archive +=== CONT TestHasCSCLI +=== RUN TestHasCSCLI/cscli_available +=== PAUSE TestHasCSCLI/cscli_available +=== RUN TestHasCSCLI/cscli_not_found +=== PAUSE TestHasCSCLI/cscli_not_found +=== CONT TestCleanShellArg +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestPullEvictsExpiredCacheAndRefreshes3657415151/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011440 meta_path=/tmp/TestPullEvictsExpiredCacheAndRefreshes3657415151/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestPullEvictsExpiredCacheAndRefreshes3657415151/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="preset successfully cached" archive_path=/tmp/TestPullEvictsExpiredCacheAndRefreshes3657415151/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011440 preview_path=/tmp/TestPullEvictsExpiredCacheAndRefreshes3657415151/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +=== RUN TestCleanShellArg/clean_slug +=== PAUSE TestCleanShellArg/clean_slug +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestApplyWithCopyBasedBackup1615199222/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 meta_path=/tmp/TestApplyWithCopyBasedBackup1615199222/001/test/preset/metadata.json preview_path=/tmp/TestApplyWithCopyBasedBackup1615199222/001/test/preset/preview.yaml slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="successfully loaded cached preset metadata" archive_path=/tmp/TestApplyWithCopyBasedBackup1615199222/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 slug=test/preset +=== CONT TestBuildIndexURL +=== RUN TestBuildIndexURL/empty_base_uses_default +=== PAUSE TestBuildIndexURL/empty_base_uses_default +=== RUN TestBuildIndexURL/standard_base_appends_path +=== PAUSE TestBuildIndexURL/standard_base_appends_path +=== RUN TestBuildIndexURL/trailing_slash_removed +=== PAUSE TestBuildIndexURL/trailing_slash_removed +=== RUN TestBuildIndexURL/direct_json_url_unchanged +=== PAUSE TestBuildIndexURL/direct_json_url_unchanged +=== RUN TestBuildIndexURL/case_insensitive_json +=== PAUSE TestBuildIndexURL/case_insensitive_json +=== CONT TestNormalizeHubBaseURL +=== RUN TestNormalizeHubBaseURL/empty_uses_default +=== PAUSE TestNormalizeHubBaseURL/empty_uses_default +=== RUN TestNormalizeHubBaseURL/whitespace_uses_default +=== PAUSE TestNormalizeHubBaseURL/whitespace_uses_default +=== RUN TestNormalizeHubBaseURL/removes_trailing_slash +=== PAUSE TestNormalizeHubBaseURL/removes_trailing_slash +=== RUN TestNormalizeHubBaseURL/removes_multiple_trailing_slashes +=== PAUSE TestNormalizeHubBaseURL/removes_multiple_trailing_slashes +=== RUN TestNormalizeHubBaseURL/trims_spaces +=== PAUSE TestNormalizeHubBaseURL/trims_spaces +=== RUN TestNormalizeHubBaseURL/no_slash_unchanged +--- PASS: TestPullEvictsExpiredCacheAndRefreshes (0.02s) +--- PASS: TestCopyDir (0.01s) +=== RUN TestCleanShellArg/with_dash +--- PASS: TestApplyWithCopyBasedBackup (0.01s) +=== CONT TestApplyRollsBackWhenCacheMissing +time="2026-01-10T02:17:19Z" level=error msg="cache unavailable for apply" slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=warning msg="cache refresh failed; rolled back backup" backup_path=/tmp/TestApplyRollsBackWhenCacheMissing965748357/001/crowdsec.backup.20260110-021719 error="cache unavailable for manual apply" slug=crowdsecurity/demo +=== PAUSE TestCleanShellArg/with_dash +=== RUN TestCleanShellArg/with_underscore +=== PAUSE TestCleanShellArg/with_underscore +=== RUN TestCleanShellArg/with_dot +=== PAUSE TestNormalizeHubBaseURL/no_slash_unchanged +=== PAUSE TestCleanShellArg/with_dot +=== RUN TestCleanShellArg/path_traversal +=== CONT TestFirstNonEmpty +=== RUN TestFirstNonEmpty/first_non-empty +=== CONT TestFetchWithLimitStatusError +--- PASS: TestApplyRollsBackWhenCacheMissing (0.00s) +=== CONT TestUniqueStrings +=== CONT TestFetchIndexCSCLIParseError +=== RUN TestUniqueStrings/empty_slice +=== PAUSE TestUniqueStrings/empty_slice +=== RUN TestUniqueStrings/no_duplicates +=== PAUSE TestUniqueStrings/no_duplicates +=== RUN TestUniqueStrings/with_duplicates +=== PAUSE TestUniqueStrings/with_duplicates +=== RUN TestUniqueStrings/all_duplicates +=== PAUSE TestUniqueStrings/all_duplicates +=== RUN TestUniqueStrings/preserves_order +=== PAUSE TestUniqueStrings/preserves_order +=== CONT TestRunCSCLIRejectsUnsafeSlug +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=1 error="http://hub.example/api/index.json (status 500)" hub_index="http://hub.example/api/index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=2 error="https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json (status 500)" hub_index="https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=3 error="https://raw.githubusercontent.com/crowdsecurity/hub/master/api/index.json (status 500)" hub_index="https://raw.githubusercontent.com/crowdsecurity/hub/master/api/index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=4 error="https://hub-data.crowdsec.net/api/index.json (status 500)" hub_index="https://hub-data.crowdsec.net/api/index.json" +=== CONT TestApplyUsesCSCLISuccess +=== CONT TestFetchPreviewRequiresURL +=== CONT TestFetchWithLimitRequiresClient +=== CONT TestPullValidatesSlugAndMissingPreset +=== PAUSE TestCleanShellArg/path_traversal +=== RUN TestCleanShellArg/absolute_path +=== PAUSE TestCleanShellArg/absolute_path +=== RUN TestCleanShellArg/backslash_converted +=== PAUSE TestCleanShellArg/backslash_converted +=== RUN TestCleanShellArg/colon_not_allowed +=== PAUSE TestFirstNonEmpty/first_non-empty +=== PAUSE TestCleanShellArg/colon_not_allowed +=== RUN TestFirstNonEmpty/all_empty +=== RUN TestCleanShellArg/semicolon +=== PAUSE TestFirstNonEmpty/all_empty +=== PAUSE TestCleanShellArg/semicolon +=== RUN TestFirstNonEmpty/first_is_non-empty +=== PAUSE TestFirstNonEmpty/first_is_non-empty +=== RUN TestCleanShellArg/pipe +=== RUN TestFirstNonEmpty/whitespace_treated_as_empty +=== PAUSE TestFirstNonEmpty/whitespace_treated_as_empty +=== PAUSE TestCleanShellArg/pipe +=== RUN TestFirstNonEmpty/whitespace_with_content +=== PAUSE TestFirstNonEmpty/whitespace_with_content +=== RUN TestCleanShellArg/ampersand +=== RUN TestFirstNonEmpty/empty_slice +=== PAUSE TestCleanShellArg/ampersand +--- PASS: TestFetchWithLimitStatusError (0.00s) +=== PAUSE TestFirstNonEmpty/empty_slice +=== RUN TestFirstNonEmpty/tabs_and_newlines +=== RUN TestCleanShellArg/backtick +=== PAUSE TestCleanShellArg/backtick +--- PASS: TestRunCSCLIRejectsUnsafeSlug (0.00s) +=== PAUSE TestFirstNonEmpty/tabs_and_newlines +=== RUN TestCleanShellArg/dollar +--- PASS: TestFetchIndexCSCLIParseError (0.00s) +--- PASS: TestFetchPreviewRequiresURL (0.00s) +--- PASS: TestFetchWithLimitRequiresClient (0.00s) +=== CONT TestFetchIndexHTTPError +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=1 error="https://hub-data.crowdsec.net/api/index.json (status 503)" hub_index="https://hub-data.crowdsec.net/api/index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=2 error="https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json (status 503)" hub_index="https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=3 error="https://raw.githubusercontent.com/crowdsecurity/hub/master/api/index.json (status 503)" hub_index="https://raw.githubusercontent.com/crowdsecurity/hub/master/api/index.json" +--- PASS: TestFetchIndexHTTPError (0.00s) +=== PAUSE TestCleanShellArg/dollar +=== CONT TestExtractTarGzRejectsSymlink +=== RUN TestCleanShellArg/parenthesis +=== PAUSE TestCleanShellArg/parenthesis +=== CONT TestExtractTarGzRejectsAbsolutePath +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestApplyUsesCSCLISuccess2135085979/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestApplyUsesCSCLISuccess2135085979/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestApplyUsesCSCLISuccess2135085979/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://hub.example/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="successfully loaded cached preset metadata" archive_path=/tmp/TestApplyUsesCSCLISuccess2135085979/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 slug=crowdsecurity/demo +--- PASS: TestPullValidatesSlugAndMissingPreset (0.01s) +=== CONT TestPullFallsBackToMirrorArchiveOnForbidden +--- PASS: TestApplyUsesCSCLISuccess (0.01s) +=== CONT TestFetchWithLimitRejectsLargePayload +--- PASS: TestExtractTarGzRejectsSymlink (0.01s) +=== CONT TestHubCacheTTL +=== RUN TestHubCacheTTL/returns_configured_TTL +=== PAUSE TestHubCacheTTL/returns_configured_TTL +=== RUN TestHubCacheTTL/returns_minute_TTL +=== PAUSE TestHubCacheTTL/returns_minute_TTL +--- PASS: TestExtractTarGzRejectsAbsolutePath (0.01s) +=== CONT TestPullReturnsCachedPreviewWithoutNetwork +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="https://primary.example/api/index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub fetch failed, attempting fallback" attempt=1 endpoint="https://primary.example/crowdsecurity/demo.tgz" error="https://primary.example/crowdsecurity/demo.tgz (status 403)" +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="https://raw.githubusercontent.com/crowdsecurity/hub/master/crowdsecurity/demo.tgz" fallback_used=true +time="2026-01-10T02:17:19Z" level=warning msg="hub fetch failed, attempting fallback" attempt=1 endpoint="https://primary.example/crowdsecurity/demo.yaml" error="https://primary.example/crowdsecurity/demo.yaml (status 403)" +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="https://raw.githubusercontent.com/crowdsecurity/hub/master/crowdsecurity/demo.yaml" fallback_used=true +time="2026-01-10T02:17:19Z" level=info msg="storing preset in cache" archive_size=105 etag=etag1 hub_endpoint="https://raw.githubusercontent.com/crowdsecurity/hub/master/crowdsecurity/demo.tgz" preview_size=14 slug=crowdsecurity/demo +=== RUN TestHubCacheTTL/returns_zero_TTL_if_configured +=== PAUSE TestHubCacheTTL/returns_zero_TTL_if_configured +=== CONT TestApplyUsesCacheWhenCscliMissing +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestPullFallsBackToMirrorArchiveOnForbidden11765555/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestPullFallsBackToMirrorArchiveOnForbidden11765555/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestPullFallsBackToMirrorArchiveOnForbidden11765555/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="preset successfully cached" archive_path=/tmp/TestPullFallsBackToMirrorArchiveOnForbidden11765555/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 preview_path=/tmp/TestPullFallsBackToMirrorArchiveOnForbidden11765555/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +--- PASS: TestPullFallsBackToMirrorArchiveOnForbidden (0.01s) +=== CONT TestApplyRollsBackOnBadArchive +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestPullReturnsCachedPreviewWithoutNetwork3052954076/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestPullReturnsCachedPreviewWithoutNetwork3052954076/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestPullReturnsCachedPreviewWithoutNetwork3052954076/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +--- PASS: TestPullReturnsCachedPreviewWithoutNetwork (0.01s) +=== CONT TestPullFallsBackToArchivePreview +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestApplyRollsBackOnBadArchive1855248391/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestApplyRollsBackOnBadArchive1855248391/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestApplyRollsBackOnBadArchive1855248391/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://example.com/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="successfully loaded cached preset metadata" archive_path=/tmp/TestApplyRollsBackOnBadArchive1855248391/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://example.com/demo.tgz" fallback_used=false +time="2026-01-10T02:17:19Z" level=warning msg="hub fetch failed, attempting fallback" attempt=1 endpoint="http://example.com/demo.yaml" error="http://example.com/demo.yaml (status 500)" +time="2026-01-10T02:17:19Z" level=warning msg="failed to download preview, falling back to archive inspection" error="preview fetch failed (last endpoint http://example.com/crowdsecurity/demo.yaml): http://example.com/demo.yaml: http://example.com/demo.yaml (status 500)\nhttp://example.com/crowdsecurity/demo.yaml: http://example.com/crowdsecurity/demo.yaml (status 404)" slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestApplyUsesCacheWhenCscliMissing3901280632/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestApplyUsesCacheWhenCscliMissing3901280632/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestApplyUsesCacheWhenCscliMissing3901280632/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="successfully loaded cached preset metadata" archive_path=/tmp/TestApplyUsesCacheWhenCscliMissing3901280632/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 slug=crowdsecurity/demo +--- PASS: TestApplyRollsBackOnBadArchive (0.02s) +=== CONT TestPullCachesPreview +time="2026-01-10T02:17:19Z" level=info msg="storing preset in cache" archive_size=116 etag=etag1 hub_endpoint="http://example.com/demo.tgz" preview_size=11 slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestPullFallsBackToArchivePreview2937992452/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestPullFallsBackToArchivePreview2937992452/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestPullFallsBackToArchivePreview2937992452/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="preset successfully cached" archive_path=/tmp/TestPullFallsBackToArchivePreview2937992452/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 preview_path=/tmp/TestPullFallsBackToArchivePreview2937992452/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +--- PASS: TestApplyUsesCacheWhenCscliMissing (0.02s) +--- PASS: TestPullFallsBackToArchivePreview (0.02s) +=== CONT TestApplyUsesCacheWhenCSCLIFails +=== CONT TestFetchIndexFallsBackToMirrorOnForbidden +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=1 error="https://hub-data.crowdsec.net/api/index.json (status 403)" hub_index="https://hub-data.crowdsec.net/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=true hub_index="https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json" +--- PASS: TestFetchIndexFallsBackToMirrorOnForbidden (0.00s) +=== CONT TestFetchIndexHTTPRejectsHTML +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=1 error="https://hub-data.crowdsec.net/api/index.json (status 200): hub index responded with HTML; install cscli or set HUB_BASE_URL to a JSON hub endpoint" hub_index="https://hub-data.crowdsec.net/api/index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=2 error="https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json (status 200): hub index responded with HTML; install cscli or set HUB_BASE_URL to a JSON hub endpoint" hub_index="https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json" +time="2026-01-10T02:17:19Z" level=warning msg="hub index fetch failed, trying mirror" attempt=3 error="https://raw.githubusercontent.com/crowdsecurity/hub/master/api/index.json (status 200): hub index responded with HTML; install cscli or set HUB_BASE_URL to a JSON hub endpoint" hub_index="https://raw.githubusercontent.com/crowdsecurity/hub/master/api/index.json" +--- PASS: TestFetchIndexHTTPRejectsHTML (0.00s) +=== CONT TestFetchIndexHTTPRejectsRedirect +--- PASS: TestFetchIndexHTTPRejectsRedirect (0.00s) +=== CONT TestFetchIndexHTTPFallsBackToDefaultHub +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="https://hub.crowdsec.net/api/index.json" +--- PASS: TestFetchIndexHTTPFallsBackToDefaultHub (0.00s) +=== CONT TestFetchIndexFallbackHTTP +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://example.com/api/index.json" +--- PASS: TestFetchIndexFallbackHTTP (0.00s) +=== CONT TestSanitizeSlugCases +--- PASS: TestSanitizeSlugCases (0.00s) +=== CONT TestFetchIndexPrefersCSCLI +--- PASS: TestFetchIndexPrefersCSCLI (0.00s) +=== CONT TestHubCacheListContextCanceled +--- PASS: TestHubCacheListContextCanceled (0.00s) +=== CONT TestExtractTarGz_LargeNestedStructure +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestApplyUsesCacheWhenCSCLIFails3365830785/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestApplyUsesCacheWhenCSCLIFails3365830785/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestApplyUsesCacheWhenCSCLIFails3365830785/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://example.com/api/index.json" +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://example.com/demo.tgz" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://example.com/demo.yaml" fallback_used=false +time="2026-01-10T02:17:19Z" level=info msg="successfully loaded cached preset metadata" archive_path=/tmp/TestApplyUsesCacheWhenCSCLIFails3365830785/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=warning msg="cscli install failed; attempting cache fallback" error="install failed" slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="storing preset in cache" archive_size=106 etag=etag1 hub_endpoint="http://example.com/demo.tgz" preview_size=12 slug=crowdsecurity/demo +--- PASS: TestApplyUsesCacheWhenCSCLIFails (0.01s) +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestPullCachesPreview1283738611/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestPullCachesPreview1283738611/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestPullCachesPreview1283738611/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="preset successfully cached" archive_path=/tmp/TestPullCachesPreview1283738611/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 preview_path=/tmp/TestPullCachesPreview1283738611/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +=== CONT TestFindPresetCaseVariants +=== RUN TestFindPresetCaseVariants/exact_match +=== PAUSE TestFindPresetCaseVariants/exact_match +=== RUN TestFindPresetCaseVariants/another_preset +=== PAUSE TestFindPresetCaseVariants/another_preset +=== RUN TestFindPresetCaseVariants/case_sensitive_miss +=== PAUSE TestFindPresetCaseVariants/case_sensitive_miss +=== RUN TestFindPresetCaseVariants/partial_match_miss +=== PAUSE TestFindPresetCaseVariants/partial_match_miss +=== RUN TestFindPresetCaseVariants/empty_slug +=== PAUSE TestFindPresetCaseVariants/empty_slug +=== CONT TestFindPreset +--- PASS: TestFindPreset (0.00s) +=== CONT TestListCuratedPresetsReturnsCopy +--- PASS: TestListCuratedPresetsReturnsCopy (0.00s) +=== CONT TestFindIndexEntry_EmptySlug +--- PASS: TestFindIndexEntry_EmptySlug (0.00s) +=== CONT TestFindIndexEntry_NotFound +--- PASS: TestPullCachesPreview (0.02s) +--- PASS: TestFindIndexEntry_NotFound (0.00s) +=== CONT TestListCuratedPresetsReturnsDifferentCopy +--- PASS: TestListCuratedPresetsReturnsDifferentCopy (0.00s) +=== CONT TestFindIndexEntry_ShortName +--- PASS: TestFindIndexEntry_ShortName (0.00s) +=== CONT TestFindIndexEntry_AmbiguousShortName +--- PASS: TestFindIndexEntry_AmbiguousShortName (0.00s) +=== CONT TestPeekFirstYAML_InvalidArchive +=== CONT TestPeekFirstYAML_NoYAMLFiles +--- PASS: TestPeekFirstYAML_InvalidArchive (0.00s) +=== CONT TestFindIndexEntry_ExactMatch +--- PASS: TestFindIndexEntry_ExactMatch (0.00s) +=== CONT TestPeekFirstYAML_FindsYAML +--- PASS: TestExtractTarGz_LargeNestedStructure (0.02s) +=== CONT TestIsGzip_NotGzip +--- PASS: TestIsGzip_NotGzip (0.00s) +=== CONT TestIsGzip_ValidGzip +--- PASS: TestPeekFirstYAML_NoYAMLFiles (0.01s) +=== CONT TestFetchIndexHTTPFromURL_InvalidJSON +--- PASS: TestFetchIndexHTTPFromURL_InvalidJSON (0.00s) +=== CONT TestFetchIndexHTTPFromURL_EmptyJSONArray +--- PASS: TestFetchIndexHTTPFromURL_EmptyJSONArray (0.00s) +=== CONT TestFetchIndexHTTPFromURL_ParseRawIndexFallback +--- PASS: TestFetchIndexHTTPFromURL_ParseRawIndexFallback (0.00s) +=== CONT TestAsString_EmptyString +--- PASS: TestAsString_EmptyString (0.00s) +=== CONT TestAsString_Struct +--- PASS: TestAsString_Struct (0.00s) +=== CONT TestAsString_Bool +--- PASS: TestPeekFirstYAML_FindsYAML (0.01s) +=== CONT TestAsString_Float +--- PASS: TestAsString_Float (0.00s) +=== CONT TestIsGzip_TooShort +--- PASS: TestIsGzip_TooShort (0.00s) +=== CONT TestAsString_String +--- PASS: TestAsString_Bool (0.00s) +=== CONT TestAsString_Nil +--- PASS: TestAsString_Nil (0.00s) +=== CONT TestAsString_Int +--- PASS: TestAsString_Int (0.00s) +=== CONT TestExtractTarGz_SkipsSpecialFileTypes +--- PASS: TestIsGzip_ValidGzip (0.01s) +=== CONT TestExtractTarGz_DirectoriesWithoutFiles +--- PASS: TestAsString_String (0.00s) +=== CONT TestRollback +=== RUN TestRollback/rollback_with_backup +=== PAUSE TestRollback/rollback_with_backup +=== RUN TestRollback/rollback_with_empty_backup_path +=== PAUSE TestRollback/rollback_with_empty_backup_path +=== RUN TestRollback/rollback_with_non-existent_backup +=== PAUSE TestRollback/rollback_with_non-existent_backup +=== CONT TestExtractTarGz_SpecialCharactersInFilenames +--- PASS: TestExtractTarGz_DirectoriesWithoutFiles (0.01s) +=== CONT TestExtractTarGz_EmptyArchive +--- PASS: TestExtractTarGz_SkipsSpecialFileTypes (0.01s) +=== CONT TestExtractTarGz_InvalidTarAfterGzip +--- PASS: TestExtractTarGz_SpecialCharactersInFilenames (0.01s) +=== CONT TestExtractTarGz_NestedPathTraversal +--- PASS: TestExtractTarGz_EmptyArchive (0.01s) +=== CONT TestBackupExisting_PreservesPermissions +--- PASS: TestBackupExisting_PreservesPermissions (0.00s) +=== CONT TestExtractTarGz_AbsolutePathWithDots +--- PASS: TestExtractTarGz_NestedPathTraversal (0.01s) +=== CONT TestBackupExisting_EmptyDirectory +--- PASS: TestExtractTarGz_InvalidTarAfterGzip (0.01s) +=== CONT TestBackupExisting_RenameSuccess +--- PASS: TestBackupExisting_RenameSuccess (0.00s) +=== CONT TestHubHTTPErrorUnwrap +=== RUN TestHubHTTPErrorUnwrap/unwrap_returns_inner_error +=== PAUSE TestHubHTTPErrorUnwrap/unwrap_returns_inner_error +=== RUN TestHubHTTPErrorUnwrap/unwrap_returns_nil_when_no_inner +=== PAUSE TestHubHTTPErrorUnwrap/unwrap_returns_nil_when_no_inner +=== RUN TestHubHTTPErrorUnwrap/errors.Is_works_through_Unwrap +=== PAUSE TestHubHTTPErrorUnwrap/errors.Is_works_through_Unwrap +=== CONT TestNewHubService_DefaultTimeouts +--- PASS: TestBackupExisting_EmptyDirectory (0.00s) +=== CONT TestBackupExisting_CopyFallback_Success +--- PASS: TestBackupExisting_CopyFallback_Success (0.00s) +=== CONT TestValidateHubURL_EdgeCases +=== RUN TestValidateHubURL_EdgeCases/Missing_hostname +=== PAUSE TestValidateHubURL_EdgeCases/Missing_hostname +=== RUN TestValidateHubURL_EdgeCases/Invalid_URL_format_-_unsupported_scheme_caught +=== PAUSE TestValidateHubURL_EdgeCases/Invalid_URL_format_-_unsupported_scheme_caught +=== RUN TestValidateHubURL_EdgeCases/FTP_scheme_rejected +=== PAUSE TestValidateHubURL_EdgeCases/FTP_scheme_rejected +=== RUN TestValidateHubURL_EdgeCases/File_scheme_rejected +=== PAUSE TestValidateHubURL_EdgeCases/File_scheme_rejected +=== RUN TestValidateHubURL_EdgeCases/Test_domain_allowed +=== PAUSE TestValidateHubURL_EdgeCases/Test_domain_allowed +=== RUN TestValidateHubURL_EdgeCases/Example.com_allowed_for_testing +=== PAUSE TestValidateHubURL_EdgeCases/Example.com_allowed_for_testing +=== RUN TestValidateHubURL_EdgeCases/.local_domain_allowed +=== PAUSE TestValidateHubURL_EdgeCases/.local_domain_allowed +=== RUN TestValidateHubURL_EdgeCases/Subdomain_of_example.com_allowed +=== PAUSE TestValidateHubURL_EdgeCases/Subdomain_of_example.com_allowed +=== RUN TestValidateHubURL_EdgeCases/IPv6_loopback_allowed +=== PAUSE TestValidateHubURL_EdgeCases/IPv6_loopback_allowed +=== RUN TestValidateHubURL_EdgeCases/Unknown_production_domain_rejected +=== PAUSE TestValidateHubURL_EdgeCases/Unknown_production_domain_rejected +=== CONT TestHubHTTPErrorCanFallback +=== RUN TestHubHTTPErrorCanFallback/returns_true_when_fallback_is_true +=== PAUSE TestHubHTTPErrorCanFallback/returns_true_when_fallback_is_true +=== RUN TestHubHTTPErrorCanFallback/returns_false_when_fallback_is_false +=== PAUSE TestHubHTTPErrorCanFallback/returns_false_when_fallback_is_false +--- PASS: TestNewHubService_DefaultTimeouts (0.00s) +=== CONT TestHubCacheTouchMissing +--- PASS: TestHubCacheTouchMissing (0.00s) +=== CONT TestHubCacheEvictInvalidSlug +--- PASS: TestHubCacheEvictInvalidSlug (0.00s) +=== CONT TestHubCacheListSkipsExpired +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubCacheListSkipsExpired1527189804/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1704110400 meta_path=/tmp/TestHubCacheListSkipsExpired1527189804/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestHubCacheListSkipsExpired1527189804/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +=== CONT TestHubCacheExistsContextCanceled +--- PASS: TestHubCacheExistsContextCanceled (0.00s) +--- PASS: TestHubCacheListSkipsExpired (0.00s) +=== CONT TestHubCacheTouchInvalidSlug +--- PASS: TestExtractTarGz_AbsolutePathWithDots (0.01s) +--- PASS: TestHubCacheTouchInvalidSlug (0.00s) +=== CONT TestHubCacheLoadInvalidSlug +=== CONT TestHubCacheStoreContextCanceled +--- PASS: TestHubCacheStoreContextCanceled (0.00s) +=== CONT TestHubCacheListAndEvict +--- PASS: TestHubCacheLoadInvalidSlug (0.00s) +=== CONT TestHubCachePreviewExistsAndSize +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubCacheListAndEvict1933366209/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestHubCacheListAndEvict1933366209/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestHubCacheListAndEvict1933366209/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubCacheListAndEvict1933366209/001/crowdsecurity/other/bundle.tgz cache_key=crowdsecurity/other-1768011439 meta_path=/tmp/TestHubCacheListAndEvict1933366209/001/crowdsecurity/other/metadata.json preview_path=/tmp/TestHubCacheListAndEvict1933366209/001/crowdsecurity/other/preview.yaml slug=crowdsecurity/other +=== CONT TestHubCacheExistsHonorsTTL +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubCacheExistsHonorsTTL4074854815/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestHubCacheExistsHonorsTTL4074854815/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestHubCacheExistsHonorsTTL4074854815/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +--- PASS: TestHubCacheExistsHonorsTTL (0.00s) +=== CONT TestNewHubCacheRequiresBaseDir +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubCachePreviewExistsAndSize2948237553/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestHubCachePreviewExistsAndSize2948237553/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestHubCachePreviewExistsAndSize2948237553/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +--- PASS: TestNewHubCacheRequiresBaseDir (0.00s) +=== CONT TestHubService_Apply_CacheRefresh +--- PASS: TestHubCachePreviewExistsAndSize (0.01s) +=== CONT TestBackupExisting +=== RUN TestBackupExisting/handles_non-existent_directory +=== PAUSE TestBackupExisting/handles_non-existent_directory +=== RUN TestBackupExisting/creates_backup_of_existing_directory +=== PAUSE TestBackupExisting/creates_backup_of_existing_directory +=== RUN TestBackupExisting/backup_contents_match_original +=== PAUSE TestBackupExisting/backup_contents_match_original +=== CONT TestExtractTarGz +=== RUN TestExtractTarGz/extracts_valid_archive +=== PAUSE TestExtractTarGz/extracts_valid_archive +=== RUN TestExtractTarGz/rejects_path_traversal +=== PAUSE TestExtractTarGz/rejects_path_traversal +=== RUN TestExtractTarGz/rejects_symlinks +=== PAUSE TestExtractTarGz/rejects_symlinks +=== RUN TestExtractTarGz/handles_corrupted_gzip +=== PAUSE TestExtractTarGz/handles_corrupted_gzip +=== RUN TestExtractTarGz/handles_context_cancellation +=== PAUSE TestExtractTarGz/handles_context_cancellation +=== RUN TestExtractTarGz/creates_nested_directories +=== PAUSE TestExtractTarGz/creates_nested_directories +=== CONT TestHubHTTPErrorError +=== RUN TestHubHTTPErrorError/error_with_inner_error +=== PAUSE TestHubHTTPErrorError/error_with_inner_error +=== RUN TestHubHTTPErrorError/error_without_inner_error +=== PAUSE TestHubHTTPErrorError/error_without_inner_error +=== CONT TestEmptyDir +=== RUN TestEmptyDir/empties_directory_with_files +=== PAUSE TestEmptyDir/empties_directory_with_files +=== RUN TestEmptyDir/empties_directory_with_subdirectories +=== PAUSE TestEmptyDir/empties_directory_with_subdirectories +=== RUN TestEmptyDir/handles_non-existent_directory +=== PAUSE TestEmptyDir/handles_non-existent_directory +=== RUN TestEmptyDir/handles_empty_directory +=== PAUSE TestEmptyDir/handles_empty_directory +=== CONT TestCopyDirAndCopyFile +=== RUN TestCopyDirAndCopyFile/copyFile_success +=== CONT TestHubService_Apply_RollbackOnExtractionFailure +--- PASS: TestHubCacheListAndEvict (0.01s) +=== PAUSE TestCopyDirAndCopyFile/copyFile_success +=== RUN TestCopyDirAndCopyFile/copyFile_preserves_permissions +=== PAUSE TestCopyDirAndCopyFile/copyFile_preserves_permissions +=== RUN TestCopyDirAndCopyFile/copyDir_with_nested_structure +=== PAUSE TestCopyDirAndCopyFile/copyDir_with_nested_structure +=== RUN TestCopyDirAndCopyFile/copyDir_fails_on_non-directory_source +=== PAUSE TestCopyDirAndCopyFile/copyDir_fails_on_non-directory_source +=== CONT TestHubCacheRejectsBadSlug +--- PASS: TestHubCacheRejectsBadSlug (0.00s) +=== CONT TestHubCacheTouchUpdatesTTL +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubCacheTouchUpdatesTTL3154057090/001/crowdsecurity/demo/bundle.tgz cache_key=crowdsecurity/demo-1768011439 meta_path=/tmp/TestHubCacheTouchUpdatesTTL3154057090/001/crowdsecurity/demo/metadata.json preview_path=/tmp/TestHubCacheTouchUpdatesTTL3154057090/001/crowdsecurity/demo/preview.yaml slug=crowdsecurity/demo +--- PASS: TestHubCacheTouchUpdatesTTL (0.00s) +=== CONT TestHubService_Apply_ArchiveReadBeforeBackup +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubService_Apply_CacheRefresh936131160/001/test/preset/bundle.tgz cache_key=test/preset-1768011434 meta_path=/tmp/TestHubService_Apply_CacheRefresh936131160/001/test/preset/metadata.json preview_path=/tmp/TestHubService_Apply_CacheRefresh936131160/001/test/preset/preview.yaml slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubService_Apply_ArchiveReadBeforeBackup2498877560/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 meta_path=/tmp/TestHubService_Apply_ArchiveReadBeforeBackup2498877560/001/test/preset/metadata.json preview_path=/tmp/TestHubService_Apply_ArchiveReadBeforeBackup2498877560/001/test/preset/preview.yaml slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="successfully loaded cached preset metadata" archive_path=/tmp/TestHubService_Apply_ArchiveReadBeforeBackup2498877560/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 slug=test/preset +--- PASS: TestHubService_Apply_ArchiveReadBeforeBackup (0.01s) +=== CONT TestFetchIndexHTTPFromURL_HTMLDetection +--- PASS: TestFetchIndexHTTPFromURL_HTMLDetection (0.00s) +=== CONT TestParseRawIndex +=== RUN TestParseRawIndex/parses_valid_raw_index +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubService_Apply_RollbackOnExtractionFailure1106849730/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 meta_path=/tmp/TestHubService_Apply_RollbackOnExtractionFailure1106849730/001/test/preset/metadata.json preview_path=/tmp/TestHubService_Apply_RollbackOnExtractionFailure1106849730/001/test/preset/preview.yaml slug=test/preset +=== PAUSE TestParseRawIndex/parses_valid_raw_index +=== RUN TestParseRawIndex/returns_error_on_invalid_JSON +=== PAUSE TestParseRawIndex/returns_error_on_invalid_JSON +=== RUN TestParseRawIndex/returns_error_on_empty_index +=== PAUSE TestParseRawIndex/returns_error_on_empty_index +=== CONT TestValidateHubURL_HTTPRejectedForProduction +=== RUN TestValidateHubURL_HTTPRejectedForProduction/http://hub-data.crowdsec.net/api/index.json +=== PAUSE TestValidateHubURL_HTTPRejectedForProduction/http://hub-data.crowdsec.net/api/index.json +=== RUN TestValidateHubURL_HTTPRejectedForProduction/http://hub.crowdsec.net/api/index.json +=== PAUSE TestValidateHubURL_HTTPRejectedForProduction/http://hub.crowdsec.net/api/index.json +=== RUN TestValidateHubURL_HTTPRejectedForProduction/http://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json +=== PAUSE TestValidateHubURL_HTTPRejectedForProduction/http://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json +=== CONT TestBuildResourceURLs +=== RUN TestBuildResourceURLs/with_explicit_URL +=== PAUSE TestBuildResourceURLs/with_explicit_URL +=== RUN TestBuildResourceURLs/without_explicit_URL +=== PAUSE TestBuildResourceURLs/without_explicit_URL +=== RUN TestBuildResourceURLs/removes_duplicates +=== PAUSE TestBuildResourceURLs/removes_duplicates +=== RUN TestBuildResourceURLs/handles_empty_bases +=== PAUSE TestBuildResourceURLs/handles_empty_bases +=== CONT TestValidateHubURL_LocalhostExceptions +=== RUN TestValidateHubURL_LocalhostExceptions/http://localhost:8080/index.json +=== PAUSE TestValidateHubURL_LocalhostExceptions/http://localhost:8080/index.json +=== RUN TestValidateHubURL_LocalhostExceptions/http://127.0.0.1:8080/index.json +=== PAUSE TestValidateHubURL_LocalhostExceptions/http://127.0.0.1:8080/index.json +=== RUN TestValidateHubURL_LocalhostExceptions/http://[::1]:8080/index.json +=== PAUSE TestValidateHubURL_LocalhostExceptions/http://[::1]:8080/index.json +=== RUN TestValidateHubURL_LocalhostExceptions/http://test.hub/api/index.json +=== PAUSE TestValidateHubURL_LocalhostExceptions/http://test.hub/api/index.json +=== RUN TestValidateHubURL_LocalhostExceptions/http://example.com/api/index.json +=== PAUSE TestValidateHubURL_LocalhostExceptions/http://example.com/api/index.json +=== RUN TestValidateHubURL_LocalhostExceptions/http://test.example.com/api/index.json +=== PAUSE TestValidateHubURL_LocalhostExceptions/http://test.example.com/api/index.json +time="2026-01-10T02:17:19Z" level=warning msg="failed to load cached preset metadata" error="cache expired" slug=test/preset +=== RUN TestValidateHubURL_LocalhostExceptions/http://server.local/api/index.json +=== PAUSE TestValidateHubURL_LocalhostExceptions/http://server.local/api/index.json +=== CONT TestValidateHubURL_InvalidSchemes +=== RUN TestValidateHubURL_InvalidSchemes/ftp://hub.crowdsec.net/index.json +=== PAUSE TestValidateHubURL_InvalidSchemes/ftp://hub.crowdsec.net/index.json +time="2026-01-10T02:17:19Z" level=info msg="attempting to repull preset after cache load failure" error="load cache for test/preset: cache expired" slug=test/preset +=== RUN TestValidateHubURL_InvalidSchemes/file:///etc/passwd +=== PAUSE TestValidateHubURL_InvalidSchemes/file:///etc/passwd +=== RUN TestValidateHubURL_InvalidSchemes/gopher://attacker.com +=== PAUSE TestValidateHubURL_InvalidSchemes/gopher://attacker.com +=== RUN TestValidateHubURL_InvalidSchemes/data:text/html, +=== PAUSE TestValidateHubURL_InvalidSchemes/data:text/html, +=== CONT TestValidateHubURL_UnknownDomainRejection/https://attacker.net/hub/index.json +=== CONT TestValidateHubURL_ValidHTTPSProduction/https://hub.crowdsec.net/api/index.json +=== CONT TestValidateHubURL_ValidHTTPSProduction/https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json +=== CONT TestValidateHubURL_UnknownDomainRejection/https://hub.evil.com/index.json +time="2026-01-10T02:17:19Z" level=info msg="hub index fetched" fallback_used=false hub_index="http://test.hub/api/index.json" +=== CONT TestValidateHubURL_UnknownDomainRejection/https://evil.com/index.json +time="2026-01-10T02:17:19Z" level=info msg="hub fetch succeeded" endpoint="http://test.hub/preset.tgz" fallback_used=false +=== CONT TestValidateHubURL_ValidHTTPSProduction/https://hub-data.crowdsec.net/api/index.json +--- PASS: TestValidateHubURL_ValidHTTPSProduction (0.00s) + --- PASS: TestValidateHubURL_ValidHTTPSProduction/https://hub.crowdsec.net/api/index.json (0.00s) + --- PASS: TestValidateHubURL_ValidHTTPSProduction/https://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json (0.00s) + --- PASS: TestValidateHubURL_ValidHTTPSProduction/https://hub-data.crowdsec.net/api/index.json (0.00s) +--- PASS: TestValidateHubURL_UnknownDomainRejection (0.00s) + --- PASS: TestValidateHubURL_UnknownDomainRejection/https://attacker.net/hub/index.json (0.00s) + --- PASS: TestValidateHubURL_UnknownDomainRejection/https://hub.evil.com/index.json (0.00s) + --- PASS: TestValidateHubURL_UnknownDomainRejection/https://evil.com/index.json (0.00s) +=== CONT TestFindPreviewFileFromArchive/finds_yaml_in_archive +time="2026-01-10T02:17:19Z" level=warning msg="failed to download preview, falling back to archive inspection" error="preview fetch failed (last endpoint http://test.hub/test/preset.yaml): http://test.hub/test/preset.yaml: http://test.hub/test/preset.yaml (status 404)" slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="successfully loaded cached preset metadata" archive_path=/tmp/TestHubService_Apply_RollbackOnExtractionFailure1106849730/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 slug=test/preset +--- PASS: TestHubService_Apply_RollbackOnExtractionFailure (0.02s) +=== CONT TestFindPreviewFileFromArchive/returns_empty_for_no_yaml +time="2026-01-10T02:17:19Z" level=info msg="storing preset in cache" archive_size=102 etag=etag2 hub_endpoint="http://test.hub/preset.tgz" preview_size=3 slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="preset successfully stored in cache" archive_path=/tmp/TestHubService_Apply_CacheRefresh936131160/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 meta_path=/tmp/TestHubService_Apply_CacheRefresh936131160/001/test/preset/metadata.json preview_path=/tmp/TestHubService_Apply_CacheRefresh936131160/001/test/preset/preview.yaml slug=test/preset +time="2026-01-10T02:17:19Z" level=info msg="preset successfully cached" archive_path=/tmp/TestHubService_Apply_CacheRefresh936131160/001/test/preset/bundle.tgz cache_key=test/preset-1768011439 preview_path=/tmp/TestHubService_Apply_CacheRefresh936131160/001/test/preset/preview.yaml slug=test/preset +=== CONT TestFindPreviewFileFromArchive/returns_empty_for_invalid_archive +=== CONT TestHasCSCLI/cscli_available +--- PASS: TestHubService_Apply_CacheRefresh (0.04s) +=== CONT TestBuildIndexURL/empty_base_uses_default +=== CONT TestBuildIndexURL/case_insensitive_json +=== CONT TestBuildIndexURL/direct_json_url_unchanged +=== CONT TestBuildIndexURL/trailing_slash_removed +=== CONT TestHasCSCLI/cscli_not_found +=== CONT TestNormalizeHubBaseURL/empty_uses_default +=== CONT TestBuildIndexURL/standard_base_appends_path +--- PASS: TestBuildIndexURL (0.00s) + --- PASS: TestBuildIndexURL/empty_base_uses_default (0.00s) + --- PASS: TestBuildIndexURL/case_insensitive_json (0.00s) + --- PASS: TestBuildIndexURL/direct_json_url_unchanged (0.00s) + --- PASS: TestBuildIndexURL/trailing_slash_removed (0.00s) + --- PASS: TestBuildIndexURL/standard_base_appends_path (0.00s) +--- PASS: TestHasCSCLI (0.00s) + --- PASS: TestHasCSCLI/cscli_available (0.00s) + --- PASS: TestHasCSCLI/cscli_not_found (0.00s) +=== CONT TestUniqueStrings/preserves_order +=== CONT TestUniqueStrings/all_duplicates +=== CONT TestUniqueStrings/no_duplicates +=== CONT TestUniqueStrings/empty_slice +=== CONT TestUniqueStrings/with_duplicates +=== CONT TestNormalizeHubBaseURL/whitespace_uses_default +=== CONT TestNormalizeHubBaseURL/no_slash_unchanged +=== CONT TestNormalizeHubBaseURL/trims_spaces +=== CONT TestNormalizeHubBaseURL/removes_multiple_trailing_slashes +=== CONT TestFirstNonEmpty/whitespace_with_content +=== CONT TestFirstNonEmpty/tabs_and_newlines +=== CONT TestNormalizeHubBaseURL/removes_trailing_slash +=== CONT TestFirstNonEmpty/empty_slice +--- PASS: TestUniqueStrings (0.00s) + --- PASS: TestUniqueStrings/preserves_order (0.00s) + --- PASS: TestUniqueStrings/all_duplicates (0.00s) + --- PASS: TestUniqueStrings/empty_slice (0.00s) + --- PASS: TestUniqueStrings/with_duplicates (0.00s) + --- PASS: TestUniqueStrings/no_duplicates (0.00s) +--- PASS: TestNormalizeHubBaseURL (0.00s) + --- PASS: TestNormalizeHubBaseURL/empty_uses_default (0.00s) + --- PASS: TestNormalizeHubBaseURL/whitespace_uses_default (0.00s) + --- PASS: TestNormalizeHubBaseURL/no_slash_unchanged (0.00s) + --- PASS: TestNormalizeHubBaseURL/trims_spaces (0.00s) + --- PASS: TestNormalizeHubBaseURL/removes_multiple_trailing_slashes (0.00s) + --- PASS: TestNormalizeHubBaseURL/removes_trailing_slash (0.00s) +=== CONT TestFirstNonEmpty/whitespace_treated_as_empty +=== CONT TestFirstNonEmpty/first_is_non-empty +=== CONT TestFirstNonEmpty/all_empty +=== CONT TestFirstNonEmpty/first_non-empty +--- PASS: TestFirstNonEmpty (0.00s) + --- PASS: TestFirstNonEmpty/whitespace_with_content (0.00s) + --- PASS: TestFirstNonEmpty/tabs_and_newlines (0.00s) + --- PASS: TestFirstNonEmpty/empty_slice (0.00s) + --- PASS: TestFirstNonEmpty/first_is_non-empty (0.00s) + --- PASS: TestFirstNonEmpty/whitespace_treated_as_empty (0.00s) + --- PASS: TestFirstNonEmpty/all_empty (0.00s) + --- PASS: TestFirstNonEmpty/first_non-empty (0.00s) +=== CONT TestCleanShellArg/clean_slug +=== CONT TestCleanShellArg/parenthesis +=== CONT TestCleanShellArg/backslash_converted +=== CONT TestCleanShellArg/dollar +=== CONT TestCleanShellArg/backtick +=== CONT TestCleanShellArg/pipe +=== CONT TestCleanShellArg/ampersand +=== CONT TestCleanShellArg/colon_not_allowed +=== CONT TestCleanShellArg/with_dot +=== CONT TestCleanShellArg/semicolon +=== CONT TestCleanShellArg/absolute_path +=== CONT TestCleanShellArg/with_dash +=== CONT TestCleanShellArg/path_traversal +=== CONT TestHubCacheTTL/returns_zero_TTL_if_configured +=== CONT TestCleanShellArg/with_underscore +=== CONT TestHubCacheTTL/returns_minute_TTL +=== CONT TestHubCacheTTL/returns_configured_TTL +=== CONT TestFindPresetCaseVariants/another_preset +--- PASS: TestCleanShellArg (0.01s) + --- PASS: TestCleanShellArg/parenthesis (0.00s) + --- PASS: TestCleanShellArg/clean_slug (0.00s) + --- PASS: TestCleanShellArg/backslash_converted (0.00s) + --- PASS: TestCleanShellArg/backtick (0.00s) + --- PASS: TestCleanShellArg/dollar (0.00s) + --- PASS: TestCleanShellArg/pipe (0.00s) + --- PASS: TestCleanShellArg/colon_not_allowed (0.00s) + --- PASS: TestCleanShellArg/ampersand (0.00s) + --- PASS: TestCleanShellArg/semicolon (0.00s) + --- PASS: TestCleanShellArg/with_dash (0.00s) + --- PASS: TestCleanShellArg/absolute_path (0.00s) + --- PASS: TestCleanShellArg/with_dot (0.00s) + --- PASS: TestCleanShellArg/with_underscore (0.00s) + --- PASS: TestCleanShellArg/path_traversal (0.00s) +=== CONT TestFindPresetCaseVariants/empty_slug +=== CONT TestFindPresetCaseVariants/case_sensitive_miss +=== CONT TestFindPresetCaseVariants/partial_match_miss +=== CONT TestFindPresetCaseVariants/exact_match +=== CONT TestRollback/rollback_with_backup +=== CONT TestRollback/rollback_with_empty_backup_path +--- PASS: TestFindPresetCaseVariants (0.00s) + --- PASS: TestFindPresetCaseVariants/another_preset (0.00s) + --- PASS: TestFindPresetCaseVariants/empty_slug (0.00s) + --- PASS: TestFindPresetCaseVariants/case_sensitive_miss (0.00s) + --- PASS: TestFindPresetCaseVariants/partial_match_miss (0.00s) + --- PASS: TestFindPresetCaseVariants/exact_match (0.00s) +=== CONT TestValidateHubURL_EdgeCases/Missing_hostname +--- PASS: TestFindPreviewFileFromArchive (0.00s) + --- PASS: TestFindPreviewFileFromArchive/finds_yaml_in_archive (0.01s) + --- PASS: TestFindPreviewFileFromArchive/returns_empty_for_invalid_archive (0.00s) + --- PASS: TestFindPreviewFileFromArchive/returns_empty_for_no_yaml (0.02s) +=== CONT TestHubHTTPErrorUnwrap/unwrap_returns_nil_when_no_inner +=== CONT TestValidateHubURL_EdgeCases/IPv6_loopback_allowed +=== CONT TestValidateHubURL_EdgeCases/Subdomain_of_example.com_allowed +--- PASS: TestHubCacheTTL (0.01s) + --- PASS: TestHubCacheTTL/returns_zero_TTL_if_configured (0.00s) + --- PASS: TestHubCacheTTL/returns_minute_TTL (0.00s) + --- PASS: TestHubCacheTTL/returns_configured_TTL (0.00s) +=== CONT TestRollback/rollback_with_non-existent_backup +=== CONT TestValidateHubURL_EdgeCases/Unknown_production_domain_rejected +=== CONT TestValidateHubURL_EdgeCases/.local_domain_allowed +=== CONT TestValidateHubURL_EdgeCases/Example.com_allowed_for_testing +=== CONT TestValidateHubURL_EdgeCases/File_scheme_rejected +=== CONT TestValidateHubURL_EdgeCases/Test_domain_allowed +=== CONT TestValidateHubURL_EdgeCases/Invalid_URL_format_-_unsupported_scheme_caught +--- PASS: TestRollback (0.00s) + --- PASS: TestRollback/rollback_with_empty_backup_path (0.00s) + --- PASS: TestRollback/rollback_with_backup (0.00s) + --- PASS: TestRollback/rollback_with_non-existent_backup (0.00s) +=== CONT TestValidateHubURL_EdgeCases/FTP_scheme_rejected +=== CONT TestHubHTTPErrorCanFallback/returns_false_when_fallback_is_false +=== CONT TestHubHTTPErrorCanFallback/returns_true_when_fallback_is_true +=== CONT TestHubHTTPErrorUnwrap/errors.Is_works_through_Unwrap +=== CONT TestHubHTTPErrorUnwrap/unwrap_returns_inner_error +--- PASS: TestHubHTTPErrorUnwrap (0.00s) + --- PASS: TestHubHTTPErrorUnwrap/unwrap_returns_nil_when_no_inner (0.00s) + --- PASS: TestHubHTTPErrorUnwrap/errors.Is_works_through_Unwrap (0.00s) + --- PASS: TestHubHTTPErrorUnwrap/unwrap_returns_inner_error (0.00s) +=== CONT TestBackupExisting/backup_contents_match_original +--- PASS: TestHubHTTPErrorCanFallback (0.00s) + --- PASS: TestHubHTTPErrorCanFallback/returns_false_when_fallback_is_false (0.00s) + --- PASS: TestHubHTTPErrorCanFallback/returns_true_when_fallback_is_true (0.00s) +=== CONT TestBackupExisting/creates_backup_of_existing_directory +=== CONT TestBackupExisting/handles_non-existent_directory +=== CONT TestExtractTarGz/extracts_valid_archive +=== CONT TestExtractTarGz/handles_context_cancellation +=== CONT TestExtractTarGz/creates_nested_directories +=== CONT TestExtractTarGz/handles_corrupted_gzip +=== CONT TestExtractTarGz/rejects_symlinks +=== CONT TestExtractTarGz/rejects_path_traversal +--- PASS: TestBackupExisting (0.00s) + --- PASS: TestBackupExisting/backup_contents_match_original (0.00s) + --- PASS: TestBackupExisting/creates_backup_of_existing_directory (0.00s) + --- PASS: TestBackupExisting/handles_non-existent_directory (0.00s) +--- PASS: TestValidateHubURL_EdgeCases (0.00s) + --- PASS: TestValidateHubURL_EdgeCases/IPv6_loopback_allowed (0.00s) + --- PASS: TestValidateHubURL_EdgeCases/Missing_hostname (0.00s) + --- PASS: TestValidateHubURL_EdgeCases/Unknown_production_domain_rejected (0.00s) + --- PASS: TestValidateHubURL_EdgeCases/.local_domain_allowed (0.00s) + --- PASS: TestValidateHubURL_EdgeCases/Example.com_allowed_for_testing (0.00s) + --- PASS: TestValidateHubURL_EdgeCases/File_scheme_rejected (0.00s) + --- PASS: TestValidateHubURL_EdgeCases/Test_domain_allowed (0.00s) + --- PASS: TestValidateHubURL_EdgeCases/Invalid_URL_format_-_unsupported_scheme_caught (0.00s) + --- PASS: TestValidateHubURL_EdgeCases/FTP_scheme_rejected (0.00s) + --- PASS: TestValidateHubURL_EdgeCases/Subdomain_of_example.com_allowed (0.00s) +=== CONT TestHubHTTPErrorError/error_without_inner_error +=== CONT TestHubHTTPErrorError/error_with_inner_error +=== CONT TestEmptyDir/handles_non-existent_directory +--- PASS: TestHubHTTPErrorError (0.00s) + --- PASS: TestHubHTTPErrorError/error_without_inner_error (0.00s) + --- PASS: TestHubHTTPErrorError/error_with_inner_error (0.00s) +=== CONT TestEmptyDir/handles_empty_directory +=== CONT TestEmptyDir/empties_directory_with_subdirectories +=== CONT TestEmptyDir/empties_directory_with_files +=== CONT TestCopyDirAndCopyFile/copyFile_preserves_permissions +--- PASS: TestEmptyDir (0.00s) + --- PASS: TestEmptyDir/handles_non-existent_directory (0.00s) + --- PASS: TestEmptyDir/handles_empty_directory (0.00s) + --- PASS: TestEmptyDir/empties_directory_with_files (0.00s) + --- PASS: TestEmptyDir/empties_directory_with_subdirectories (0.01s) +=== CONT TestCopyDirAndCopyFile/copyDir_fails_on_non-directory_source +=== CONT TestCopyDirAndCopyFile/copyDir_with_nested_structure +=== CONT TestParseRawIndex/parses_valid_raw_index +--- PASS: TestExtractTarGz (0.00s) + --- PASS: TestExtractTarGz/extracts_valid_archive (0.01s) + --- PASS: TestExtractTarGz/handles_corrupted_gzip (0.00s) + --- PASS: TestExtractTarGz/creates_nested_directories (0.01s) + --- PASS: TestExtractTarGz/rejects_symlinks (0.01s) + --- PASS: TestExtractTarGz/handles_context_cancellation (0.03s) + --- PASS: TestExtractTarGz/rejects_path_traversal (0.02s) +=== CONT TestCopyDirAndCopyFile/copyFile_success +=== CONT TestParseRawIndex/returns_error_on_empty_index +=== CONT TestParseRawIndex/returns_error_on_invalid_JSON +=== CONT TestValidateHubURL_HTTPRejectedForProduction/http://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json +--- PASS: TestParseRawIndex (0.00s) + --- PASS: TestParseRawIndex/parses_valid_raw_index (0.00s) + --- PASS: TestParseRawIndex/returns_error_on_empty_index (0.00s) + --- PASS: TestParseRawIndex/returns_error_on_invalid_JSON (0.00s) +=== CONT TestValidateHubURL_HTTPRejectedForProduction/http://hub.crowdsec.net/api/index.json +=== CONT TestValidateHubURL_HTTPRejectedForProduction/http://hub-data.crowdsec.net/api/index.json +--- PASS: TestValidateHubURL_HTTPRejectedForProduction (0.00s) + --- PASS: TestValidateHubURL_HTTPRejectedForProduction/http://raw.githubusercontent.com/crowdsecurity/hub/master/.index.json (0.00s) + --- PASS: TestValidateHubURL_HTTPRejectedForProduction/http://hub.crowdsec.net/api/index.json (0.00s) + --- PASS: TestValidateHubURL_HTTPRejectedForProduction/http://hub-data.crowdsec.net/api/index.json (0.00s) +=== CONT TestBuildResourceURLs/removes_duplicates +=== CONT TestBuildResourceURLs/handles_empty_bases +=== CONT TestBuildResourceURLs/without_explicit_URL +=== CONT TestBuildResourceURLs/with_explicit_URL +=== CONT TestValidateHubURL_LocalhostExceptions/http://127.0.0.1:8080/index.json +=== CONT TestValidateHubURL_LocalhostExceptions/http://server.local/api/index.json +--- PASS: TestBuildResourceURLs (0.00s) + --- PASS: TestBuildResourceURLs/removes_duplicates (0.00s) + --- PASS: TestBuildResourceURLs/handles_empty_bases (0.00s) + --- PASS: TestBuildResourceURLs/without_explicit_URL (0.00s) + --- PASS: TestBuildResourceURLs/with_explicit_URL (0.00s) +=== CONT TestValidateHubURL_LocalhostExceptions/http://test.example.com/api/index.json +=== CONT TestValidateHubURL_LocalhostExceptions/http://example.com/api/index.json +=== CONT TestValidateHubURL_LocalhostExceptions/http://[::1]:8080/index.json +=== CONT TestValidateHubURL_LocalhostExceptions/http://test.hub/api/index.json +=== CONT TestValidateHubURL_LocalhostExceptions/http://localhost:8080/index.json +--- PASS: TestValidateHubURL_LocalhostExceptions (0.00s) + --- PASS: TestValidateHubURL_LocalhostExceptions/http://127.0.0.1:8080/index.json (0.00s) + --- PASS: TestValidateHubURL_LocalhostExceptions/http://server.local/api/index.json (0.00s) + --- PASS: TestValidateHubURL_LocalhostExceptions/http://test.example.com/api/index.json (0.00s) + --- PASS: TestValidateHubURL_LocalhostExceptions/http://example.com/api/index.json (0.00s) + --- PASS: TestValidateHubURL_LocalhostExceptions/http://[::1]:8080/index.json (0.00s) + --- PASS: TestValidateHubURL_LocalhostExceptions/http://test.hub/api/index.json (0.00s) + --- PASS: TestValidateHubURL_LocalhostExceptions/http://localhost:8080/index.json (0.00s) +=== CONT TestValidateHubURL_InvalidSchemes/ftp://hub.crowdsec.net/index.json +=== CONT TestValidateHubURL_InvalidSchemes/gopher://attacker.com +=== CONT TestValidateHubURL_InvalidSchemes/file:///etc/passwd +=== CONT TestValidateHubURL_InvalidSchemes/data:text/html, +--- PASS: TestValidateHubURL_InvalidSchemes (0.00s) + --- PASS: TestValidateHubURL_InvalidSchemes/ftp://hub.crowdsec.net/index.json (0.00s) + --- PASS: TestValidateHubURL_InvalidSchemes/gopher://attacker.com (0.00s) + --- PASS: TestValidateHubURL_InvalidSchemes/file:///etc/passwd (0.00s) + --- PASS: TestValidateHubURL_InvalidSchemes/data:text/html, (0.00s) +--- PASS: TestCopyDirAndCopyFile (0.00s) + --- PASS: TestCopyDirAndCopyFile/copyDir_fails_on_non-directory_source (0.00s) + --- PASS: TestCopyDirAndCopyFile/copyFile_preserves_permissions (0.01s) + --- PASS: TestCopyDirAndCopyFile/copyFile_success (0.01s) + --- PASS: TestCopyDirAndCopyFile/copyDir_with_nested_structure (0.01s) +--- PASS: TestFetchWithLimitRejectsLargePayload (0.77s) +PASS +coverage: 85.8% of statements +ok github.com/Wikid82/charon/backend/internal/crowdsec (cached) coverage: 85.8% of statements +=== RUN TestNewEncryptionService_ValidKey +--- PASS: TestNewEncryptionService_ValidKey (0.00s) +=== RUN TestNewEncryptionService_InvalidBase64 +--- PASS: TestNewEncryptionService_InvalidBase64 (0.00s) +=== RUN TestNewEncryptionService_WrongKeyLength +=== RUN TestNewEncryptionService_WrongKeyLength/16_bytes +=== RUN TestNewEncryptionService_WrongKeyLength/24_bytes +=== RUN TestNewEncryptionService_WrongKeyLength/31_bytes +=== RUN TestNewEncryptionService_WrongKeyLength/33_bytes +=== RUN TestNewEncryptionService_WrongKeyLength/0_bytes +--- PASS: TestNewEncryptionService_WrongKeyLength (0.00s) + --- PASS: TestNewEncryptionService_WrongKeyLength/16_bytes (0.00s) + --- PASS: TestNewEncryptionService_WrongKeyLength/24_bytes (0.00s) + --- PASS: TestNewEncryptionService_WrongKeyLength/31_bytes (0.00s) + --- PASS: TestNewEncryptionService_WrongKeyLength/33_bytes (0.00s) + --- PASS: TestNewEncryptionService_WrongKeyLength/0_bytes (0.00s) +=== RUN TestEncryptDecrypt_RoundTrip +=== RUN TestEncryptDecrypt_RoundTrip/simple_text +=== RUN TestEncryptDecrypt_RoundTrip/with_special_chars +=== RUN TestEncryptDecrypt_RoundTrip/json_data +=== RUN TestEncryptDecrypt_RoundTrip/unicode +=== RUN TestEncryptDecrypt_RoundTrip/long_text +--- PASS: TestEncryptDecrypt_RoundTrip (0.00s) + --- PASS: TestEncryptDecrypt_RoundTrip/simple_text (0.00s) + --- PASS: TestEncryptDecrypt_RoundTrip/with_special_chars (0.00s) + --- PASS: TestEncryptDecrypt_RoundTrip/json_data (0.00s) + --- PASS: TestEncryptDecrypt_RoundTrip/unicode (0.00s) + --- PASS: TestEncryptDecrypt_RoundTrip/long_text (0.00s) +=== RUN TestEncrypt_EmptyPlaintext +--- PASS: TestEncrypt_EmptyPlaintext (0.00s) +=== RUN TestDecrypt_InvalidCiphertext +=== RUN TestDecrypt_InvalidCiphertext/invalid_base64 +=== RUN TestDecrypt_InvalidCiphertext/too_short +=== RUN TestDecrypt_InvalidCiphertext/empty_string +--- PASS: TestDecrypt_InvalidCiphertext (0.00s) + --- PASS: TestDecrypt_InvalidCiphertext/invalid_base64 (0.00s) + --- PASS: TestDecrypt_InvalidCiphertext/too_short (0.00s) + --- PASS: TestDecrypt_InvalidCiphertext/empty_string (0.00s) +=== RUN TestDecrypt_TamperedCiphertext +--- PASS: TestDecrypt_TamperedCiphertext (0.00s) +=== RUN TestEncrypt_DifferentNonces +--- PASS: TestEncrypt_DifferentNonces (0.00s) +=== RUN TestDecrypt_WrongKey +--- PASS: TestDecrypt_WrongKey (0.00s) +=== RUN TestEncrypt_NilPlaintext +--- PASS: TestEncrypt_NilPlaintext (0.00s) +=== RUN TestDecrypt_ExactNonceSize +--- PASS: TestDecrypt_ExactNonceSize (0.00s) +=== RUN TestDecrypt_OneByteLessThanNonce +--- PASS: TestDecrypt_OneByteLessThanNonce (0.00s) +=== RUN TestEncryptDecrypt_BinaryData +--- PASS: TestEncryptDecrypt_BinaryData (0.00s) +=== RUN TestEncryptDecrypt_LargePlaintext +--- PASS: TestEncryptDecrypt_LargePlaintext (0.13s) +=== RUN TestDecrypt_CorruptedNonce +--- PASS: TestDecrypt_CorruptedNonce (0.00s) +=== RUN TestDecrypt_TruncatedCiphertext +--- PASS: TestDecrypt_TruncatedCiphertext (0.00s) +=== RUN TestDecrypt_AppendedData +--- PASS: TestDecrypt_AppendedData (0.00s) +=== RUN TestEncryptionService_ConcurrentAccess +--- PASS: TestEncryptionService_ConcurrentAccess (0.01s) +=== RUN TestDecrypt_AllZerosCiphertext +--- PASS: TestDecrypt_AllZerosCiphertext (0.00s) +=== RUN TestDecrypt_RandomGarbageCiphertext +--- PASS: TestDecrypt_RandomGarbageCiphertext (0.00s) +=== RUN TestNewEncryptionService_EmptyKey +--- PASS: TestNewEncryptionService_EmptyKey (0.00s) +=== RUN TestNewEncryptionService_WhitespaceKey +--- PASS: TestNewEncryptionService_WhitespaceKey (0.00s) +=== RUN TestEncrypt_CipherCreationError +--- PASS: TestEncrypt_CipherCreationError (0.00s) +=== RUN TestEncrypt_GCMCreationError +--- PASS: TestEncrypt_GCMCreationError (0.00s) +=== RUN TestEncrypt_NonceGenerationError +--- PASS: TestEncrypt_NonceGenerationError (0.00s) +=== RUN TestDecrypt_CipherCreationError +--- PASS: TestDecrypt_CipherCreationError (0.00s) +=== RUN TestDecrypt_GCMCreationError +--- PASS: TestDecrypt_GCMCreationError (0.00s) +=== RUN TestNewRotationService +=== RUN TestNewRotationService/successful_initialization_with_current_key_only +=== RUN TestNewRotationService/successful_initialization_with_next_key +=== RUN TestNewRotationService/successful_initialization_with_legacy_keys +=== RUN TestNewRotationService/fails_without_current_key +=== RUN TestNewRotationService/handles_invalid_next_key_gracefully +--- PASS: TestNewRotationService (0.01s) + --- PASS: TestNewRotationService/successful_initialization_with_current_key_only (0.00s) + --- PASS: TestNewRotationService/successful_initialization_with_next_key (0.00s) + --- PASS: TestNewRotationService/successful_initialization_with_legacy_keys (0.00s) + --- PASS: TestNewRotationService/fails_without_current_key (0.00s) + --- PASS: TestNewRotationService/handles_invalid_next_key_gracefully (0.00s) +=== RUN TestEncryptWithCurrentKey +=== RUN TestEncryptWithCurrentKey/encrypts_with_current_key_when_no_next_key +=== RUN TestEncryptWithCurrentKey/encrypts_with_next_key_when_configured +--- PASS: TestEncryptWithCurrentKey (0.01s) + --- PASS: TestEncryptWithCurrentKey/encrypts_with_current_key_when_no_next_key (0.00s) + --- PASS: TestEncryptWithCurrentKey/encrypts_with_next_key_when_configured (0.00s) +=== RUN TestDecryptWithVersion +=== RUN TestDecryptWithVersion/decrypts_with_correct_version +=== RUN TestDecryptWithVersion/falls_back_to_other_versions_on_failure + rotation_service_test.go:155: Version fallback edge case - functionality verified in integration test +=== RUN TestDecryptWithVersion/fails_when_no_keys_can_decrypt +--- PASS: TestDecryptWithVersion (0.01s) + --- PASS: TestDecryptWithVersion/decrypts_with_correct_version (0.00s) + --- SKIP: TestDecryptWithVersion/falls_back_to_other_versions_on_failure (0.00s) + --- PASS: TestDecryptWithVersion/fails_when_no_keys_can_decrypt (0.00s) +=== RUN TestRotateAllCredentials +=== RUN TestRotateAllCredentials/successfully_rotates_all_providers +=== RUN TestRotateAllCredentials/fails_when_next_key_not_configured +=== RUN TestRotateAllCredentials/handles_partial_failures +Failed to rotate provider 1 (Corrupted): failed to decrypt credentials: failed to decrypt with version 1 or any fallback version +--- PASS: TestRotateAllCredentials (0.02s) + --- PASS: TestRotateAllCredentials/successfully_rotates_all_providers (0.01s) + --- PASS: TestRotateAllCredentials/fails_when_next_key_not_configured (0.00s) + --- PASS: TestRotateAllCredentials/handles_partial_failures (0.01s) +=== RUN TestGetStatus +=== RUN TestGetStatus/returns_correct_status_with_no_providers +=== RUN TestGetStatus/returns_correct_status_with_next_key_configured +=== RUN TestGetStatus/returns_correct_status_with_legacy_keys +=== RUN TestGetStatus/counts_providers_by_version +--- PASS: TestGetStatus (0.00s) + --- PASS: TestGetStatus/returns_correct_status_with_no_providers (0.00s) + --- PASS: TestGetStatus/returns_correct_status_with_next_key_configured (0.00s) + --- PASS: TestGetStatus/returns_correct_status_with_legacy_keys (0.00s) + --- PASS: TestGetStatus/counts_providers_by_version (0.00s) +=== RUN TestValidateKeyConfiguration +=== RUN TestValidateKeyConfiguration/validates_current_key_successfully +=== RUN TestValidateKeyConfiguration/validates_next_key_successfully +=== RUN TestValidateKeyConfiguration/validates_legacy_keys_successfully +--- PASS: TestValidateKeyConfiguration (0.00s) + --- PASS: TestValidateKeyConfiguration/validates_current_key_successfully (0.00s) + --- PASS: TestValidateKeyConfiguration/validates_next_key_successfully (0.00s) + --- PASS: TestValidateKeyConfiguration/validates_legacy_keys_successfully (0.00s) +=== RUN TestGenerateNewKey +=== RUN TestGenerateNewKey/generates_valid_base64_key +=== RUN TestGenerateNewKey/generates_unique_keys +--- PASS: TestGenerateNewKey (0.00s) + --- PASS: TestGenerateNewKey/generates_valid_base64_key (0.00s) + --- PASS: TestGenerateNewKey/generates_unique_keys (0.00s) +=== RUN TestRotationServiceConcurrency +--- PASS: TestRotationServiceConcurrency (0.01s) +=== RUN TestRotationServiceZeroDowntime +=== RUN TestRotationServiceZeroDowntime/step_1:_initial_setup_with_current_key +=== RUN TestRotationServiceZeroDowntime/step_2:_configure_next_key_and_rotate +=== RUN TestRotationServiceZeroDowntime/step_3:_promote_next_to_current +Warning: credential decrypted with version 1 but was tagged as version 2 +--- PASS: TestRotationServiceZeroDowntime (0.00s) + --- PASS: TestRotationServiceZeroDowntime/step_1:_initial_setup_with_current_key (0.00s) + --- PASS: TestRotationServiceZeroDowntime/step_2:_configure_next_key_and_rotate (0.00s) + --- PASS: TestRotationServiceZeroDowntime/step_3:_promote_next_to_current (0.00s) +PASS +coverage: 86.9% of statements +ok github.com/Wikid82/charon/backend/internal/crypto (cached) coverage: 86.9% of statements +=== RUN TestConnect +=== PAUSE TestConnect +=== RUN TestConnect_Error +=== PAUSE TestConnect_Error +=== RUN TestConnect_WALMode +=== PAUSE TestConnect_WALMode +=== RUN TestConnect_InvalidDSN +=== PAUSE TestConnect_InvalidDSN +=== RUN TestConnect_IntegrityCheckCorrupted +=== PAUSE TestConnect_IntegrityCheckCorrupted +=== RUN TestConnect_PRAGMAVerification +=== PAUSE TestConnect_PRAGMAVerification +=== RUN TestConnect_CorruptedDatabase_FullIntegrationScenario +=== PAUSE TestConnect_CorruptedDatabase_FullIntegrationScenario +=== RUN TestIsCorruptionError +=== PAUSE TestIsCorruptionError +=== RUN TestLogCorruptionError +=== PAUSE TestLogCorruptionError +=== RUN TestCheckIntegrity +=== PAUSE TestCheckIntegrity +=== RUN TestLogCorruptionError_EmptyContext +=== PAUSE TestLogCorruptionError_EmptyContext +=== RUN TestCheckIntegrity_ActualCorruption +=== PAUSE TestCheckIntegrity_ActualCorruption +=== RUN TestCheckIntegrity_PRAGMAError +=== PAUSE TestCheckIntegrity_PRAGMAError +=== CONT TestConnect +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=memory +time="2026-01-10T02:17:08Z" level=info msg="SQLite database integrity check passed" +=== CONT TestIsCorruptionError +=== RUN TestIsCorruptionError/nil_error +=== CONT TestConnect_IntegrityCheckCorrupted +=== RUN TestIsCorruptionError/generic_error +=== RUN TestIsCorruptionError/database_disk_image_is_malformed +=== RUN TestIsCorruptionError/malformed_in_message +=== RUN TestIsCorruptionError/corrupt_database +=== CONT TestCheckIntegrity +=== RUN TestCheckIntegrity/healthy_database_returns_ok +=== RUN TestIsCorruptionError/disk_I/O_error +=== RUN TestIsCorruptionError/file_is_not_a_database +=== RUN TestIsCorruptionError/file_is_encrypted_or_is_not_a_database +=== RUN TestIsCorruptionError/database_or_disk_is_full +=== RUN TestIsCorruptionError/case_insensitive_-_MALFORMED_uppercase +=== RUN TestIsCorruptionError/wrapped_error_with_corruption +=== RUN TestIsCorruptionError/network_error_-_not_corruption +=== RUN TestIsCorruptionError/record_not_found_-_not_corruption +=== RUN TestIsCorruptionError/constraint_violation_-_not_corruption +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=memory +--- PASS: TestIsCorruptionError (0.00s) + --- PASS: TestIsCorruptionError/nil_error (0.00s) + --- PASS: TestIsCorruptionError/generic_error (0.00s) + --- PASS: TestIsCorruptionError/database_disk_image_is_malformed (0.00s) + --- PASS: TestIsCorruptionError/malformed_in_message (0.00s) + --- PASS: TestIsCorruptionError/corrupt_database (0.00s) + --- PASS: TestIsCorruptionError/disk_I/O_error (0.00s) + --- PASS: TestIsCorruptionError/file_is_not_a_database (0.00s) + --- PASS: TestIsCorruptionError/file_is_encrypted_or_is_not_a_database (0.00s) + --- PASS: TestIsCorruptionError/database_or_disk_is_full (0.00s) + --- PASS: TestIsCorruptionError/case_insensitive_-_MALFORMED_uppercase (0.00s) + --- PASS: TestIsCorruptionError/wrapped_error_with_corruption (0.00s) + --- PASS: TestIsCorruptionError/network_error_-_not_corruption (0.00s) + --- PASS: TestIsCorruptionError/record_not_found_-_not_corruption (0.00s) + --- PASS: TestIsCorruptionError/constraint_violation_-_not_corruption (0.00s) +=== CONT TestCheckIntegrity_ActualCorruption +time="2026-01-10T02:17:08Z" level=info msg="SQLite database integrity check passed" +=== RUN TestCheckIntegrity/file-based_database_passes_check +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:17:08Z" level=info msg="SQLite database integrity check passed" +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:17:08Z" level=info msg="SQLite database integrity check passed" +--- PASS: TestConnect (0.02s) +=== CONT TestLogCorruptionError_EmptyContext +time="2026-01-10T02:17:08Z" level=error msg="SQLite database corruption detected" error="database disk image is malformed" error_type=database_corruption +--- PASS: TestLogCorruptionError_EmptyContext (0.00s) +=== CONT TestLogCorruptionError +=== RUN TestLogCorruptionError/nil_error_does_not_panic +=== RUN TestLogCorruptionError/logs_with_context +time="2026-01-10T02:17:08Z" level=error msg="SQLite database corruption detected" error="database disk image is malformed" error_type=database_corruption monitor_id=test-uuid operation=GetMonitorHistory table=uptime_heartbeats +=== RUN TestLogCorruptionError/logs_without_context +time="2026-01-10T02:17:08Z" level=error msg="SQLite database corruption detected" error="database corrupt" error_type=database_corruption +--- PASS: TestLogCorruptionError (0.00s) + --- PASS: TestLogCorruptionError/nil_error_does_not_panic (0.00s) + --- PASS: TestLogCorruptionError/logs_with_context (0.00s) + --- PASS: TestLogCorruptionError/logs_without_context (0.00s) +=== CONT TestCheckIntegrity_PRAGMAError +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:17:08Z" level=info msg="SQLite database integrity check passed" +time="2026-01-10T02:17:08Z" level=info msg="SQLite database integrity check passed" +--- PASS: TestCheckIntegrity (0.02s) + --- PASS: TestCheckIntegrity/healthy_database_returns_ok (0.00s) + --- PASS: TestCheckIntegrity/file-based_database_passes_check (0.02s) +=== CONT TestConnect_InvalidDSN +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +--- PASS: TestConnect_InvalidDSN (0.00s) +=== CONT TestConnect_PRAGMAVerification +time="2026-01-10T02:17:08Z" level=info msg="SQLite database integrity check passed" + +2026/01/10 02:17:08 /projects/Charon/backend/internal/database/errors.go:63 sql: database is closed +[0.039ms] [rows:-] PRAGMA quick_check +--- PASS: TestCheckIntegrity_PRAGMAError (0.01s) +=== CONT TestConnect_CorruptedDatabase_FullIntegrationScenario +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal + +2026/01/10 02:17:08 /projects/Charon/backend/internal/database/database.go:57 database disk image is malformed +[0.207ms] [rows:1] PRAGMA quick_check +time="2026-01-10T02:17:08Z" level=warning msg="Failed to run SQLite integrity check on startup" error="database disk image is malformed" + +2026/01/10 02:17:08 /projects/Charon/backend/internal/database/database.go:57 database disk image is malformed +[0.224ms] [rows:1] PRAGMA quick_check +time="2026-01-10T02:17:08Z" level=warning msg="Failed to run SQLite integrity check on startup" error="database disk image is malformed" + +2026/01/10 02:17:08 /projects/Charon/backend/internal/database/errors.go:63 database disk image is malformed +[0.090ms] [rows:1] PRAGMA quick_check +--- PASS: TestConnect_IntegrityCheckCorrupted (0.03s) +=== CONT TestConnect_Error +--- PASS: TestCheckIntegrity_ActualCorruption (0.03s) +=== CONT TestConnect_WALMode +--- PASS: TestConnect_Error (0.00s) +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:17:08Z" level=info msg="SQLite database integrity check passed" +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal +time="2026-01-10T02:17:08Z" level=info msg="SQLite database integrity check passed" +--- PASS: TestConnect_PRAGMAVerification (0.01s) +time="2026-01-10T02:17:08Z" level=info msg="SQLite database integrity check passed" +--- PASS: TestConnect_WALMode (0.01s) +time="2026-01-10T02:17:08Z" level=info msg="SQLite database connected with WAL mode enabled" journal_mode=wal + +2026/01/10 02:17:08 /projects/Charon/backend/internal/database/database.go:57 database disk image is malformed +[0.098ms] [rows:1] PRAGMA quick_check +time="2026-01-10T02:17:08Z" level=warning msg="Failed to run SQLite integrity check on startup" error="database disk image is malformed" + +2026/01/10 02:17:08 /projects/Charon/backend/internal/database/database_test.go:169 database disk image is malformed +[0.076ms] [rows:0] SELECT COUNT(*) FROM users +--- PASS: TestConnect_CorruptedDatabase_FullIntegrationScenario (0.02s) +PASS +coverage: 91.3% of statements +ok github.com/Wikid82/charon/backend/internal/database (cached) coverage: 91.3% of statements +=== RUN TestNewBroadcastHook +--- PASS: TestNewBroadcastHook (0.00s) +=== RUN TestBroadcastHook_Levels +--- PASS: TestBroadcastHook_Levels (0.00s) +=== RUN TestBroadcastHook_Subscribe +--- PASS: TestBroadcastHook_Subscribe (0.00s) +=== RUN TestBroadcastHook_Unsubscribe +--- PASS: TestBroadcastHook_Unsubscribe (0.00s) +=== RUN TestInit +--- PASS: TestInit (0.00s) +=== RUN TestLog +--- PASS: TestLog (0.00s) +=== RUN TestWithFields +--- PASS: TestWithFields (0.00s) +=== RUN TestBroadcastHook_Fire +--- PASS: TestBroadcastHook_Fire (0.00s) +=== RUN TestGetBroadcastHook +--- PASS: TestGetBroadcastHook (0.00s) +PASS +coverage: 85.7% of statements +ok github.com/Wikid82/charon/backend/internal/logger (cached) coverage: 85.7% of statements +=== RUN TestMetrics_Register +=== PAUSE TestMetrics_Register +=== RUN TestMetrics_Increment +=== PAUSE TestMetrics_Increment +=== RUN TestRecordURLValidation +=== PAUSE TestRecordURLValidation +=== RUN TestRecordSSRFBlock +=== PAUSE TestRecordSSRFBlock +=== RUN TestRecordURLTestDuration +=== PAUSE TestRecordURLTestDuration +=== RUN TestMetricsLabels +=== PAUSE TestMetricsLabels +=== RUN TestMetricsRegistration +=== PAUSE TestMetricsRegistration +=== CONT TestMetrics_Register +=== CONT TestMetricsRegistration +=== CONT TestRecordURLTestDuration +=== CONT TestRecordSSRFBlock +=== RUN TestRecordSSRFBlock/Private_IP_block +=== NAME TestRecordURLTestDuration + security_metrics_test.go:88: Successfully recorded histogram observations +--- PASS: TestRecordURLTestDuration (0.00s) +=== CONT TestMetrics_Increment +=== PAUSE TestRecordSSRFBlock/Private_IP_block +--- PASS: TestMetrics_Increment (0.00s) +=== CONT TestMetricsLabels +=== RUN TestRecordSSRFBlock/Loopback_block +--- PASS: TestMetricsRegistration (0.00s) +=== CONT TestRecordURLValidation +--- PASS: TestMetricsLabels (0.00s) +=== RUN TestRecordURLValidation/Allowed_validation +--- PASS: TestMetrics_Register (0.00s) +=== PAUSE TestRecordURLValidation/Allowed_validation +=== PAUSE TestRecordSSRFBlock/Loopback_block +=== RUN TestRecordURLValidation/Blocked_private_IP +=== PAUSE TestRecordURLValidation/Blocked_private_IP +=== RUN TestRecordSSRFBlock/Link-local_block +=== RUN TestRecordURLValidation/DNS_failure +=== PAUSE TestRecordSSRFBlock/Link-local_block +=== PAUSE TestRecordURLValidation/DNS_failure +=== RUN TestRecordSSRFBlock/Metadata_endpoint_block +=== RUN TestRecordURLValidation/Invalid_format +=== PAUSE TestRecordURLValidation/Invalid_format +=== PAUSE TestRecordSSRFBlock/Metadata_endpoint_block +=== CONT TestRecordURLValidation/DNS_failure +=== CONT TestRecordSSRFBlock/Link-local_block +=== CONT TestRecordSSRFBlock/Loopback_block +=== CONT TestRecordSSRFBlock/Metadata_endpoint_block +=== CONT TestRecordSSRFBlock/Private_IP_block +=== CONT TestRecordURLValidation/Blocked_private_IP +=== CONT TestRecordURLValidation/Invalid_format +=== CONT TestRecordURLValidation/Allowed_validation +--- PASS: TestRecordSSRFBlock (0.00s) + --- PASS: TestRecordSSRFBlock/Metadata_endpoint_block (0.00s) + --- PASS: TestRecordSSRFBlock/Link-local_block (0.00s) + --- PASS: TestRecordSSRFBlock/Loopback_block (0.00s) + --- PASS: TestRecordSSRFBlock/Private_IP_block (0.00s) +--- PASS: TestRecordURLValidation (0.00s) + --- PASS: TestRecordURLValidation/DNS_failure (0.00s) + --- PASS: TestRecordURLValidation/Blocked_private_IP (0.00s) + --- PASS: TestRecordURLValidation/Allowed_validation (0.00s) + --- PASS: TestRecordURLValidation/Invalid_format (0.00s) +PASS +coverage: 100.0% of statements +ok github.com/Wikid82/charon/backend/internal/metrics (cached) coverage: 100.0% of statements +=== RUN TestDNSProvider_TableName +--- PASS: TestDNSProvider_TableName (0.00s) +=== RUN TestDNSProvider_Fields +--- PASS: TestDNSProvider_Fields (0.00s) +=== RUN TestDNSProvider_CredentialsEncrypted_NotSerialized +--- PASS: TestDNSProvider_CredentialsEncrypted_NotSerialized (0.00s) +=== RUN TestDomain_BeforeCreate +--- PASS: TestDomain_BeforeCreate (0.01s) +=== RUN TestNotificationTemplate_BeforeCreate +--- PASS: TestNotificationTemplate_BeforeCreate (0.01s) +=== RUN TestUptimeHost_BeforeCreate +--- PASS: TestUptimeHost_BeforeCreate (0.02s) +=== RUN TestUptimeNotificationEvent_BeforeCreate +--- PASS: TestUptimeNotificationEvent_BeforeCreate (0.01s) +=== RUN TestNotification_BeforeCreate +--- PASS: TestNotification_BeforeCreate (0.00s) +=== RUN TestNotificationConfig_BeforeCreate +--- PASS: TestNotificationConfig_BeforeCreate (0.00s) +=== RUN TestSecurityHeaderProfile_Create +--- PASS: TestSecurityHeaderProfile_Create (0.01s) +=== RUN TestSecurityHeaderProfile_JSONSerialization +--- PASS: TestSecurityHeaderProfile_JSONSerialization (0.00s) +=== RUN TestCSPDirective_JSONSerialization +--- PASS: TestCSPDirective_JSONSerialization (0.00s) +=== RUN TestPermissionsPolicyItem_JSONSerialization +--- PASS: TestPermissionsPolicyItem_JSONSerialization (0.00s) +=== RUN TestSecurityHeaderProfile_Defaults +--- PASS: TestSecurityHeaderProfile_Defaults (0.00s) +=== RUN TestSecurityHeaderProfile_UniqueUUID + +2026/01/10 02:17:11 /projects/Charon/backend/internal/models/security_header_profile_test.go:155 UNIQUE constraint failed: security_header_profiles.uuid +[0.409ms] [rows:0] INSERT INTO `security_header_profiles` (`uuid`,`name`,`hsts_enabled`,`hsts_max_age`,`hsts_include_subdomains`,`hsts_preload`,`csp_enabled`,`csp_directives`,`csp_report_only`,`csp_report_uri`,`x_frame_options`,`x_content_type_options`,`referrer_policy`,`permissions_policy`,`cross_origin_opener_policy`,`cross_origin_resource_policy`,`cross_origin_embedder_policy`,`xss_protection`,`cache_control_no_store`,`security_score`,`is_preset`,`preset_type`,`description`,`created_at`,`updated_at`) VALUES ("12b9b39c-6355-4693-9d69-e600f6161ac0","Profile 2",true,31536000,true,false,false,"",false,"","DENY",true,"strict-origin-when-cross-origin","","same-origin","same-origin","",true,false,0,false,"","","2026-01-10 02:17:11.033","2026-01-10 02:17:11.033") RETURNING `id` +--- PASS: TestSecurityHeaderProfile_UniqueUUID (0.00s) +=== RUN TestSecurityHeaderProfile_CSPDirectivesStorage +--- PASS: TestSecurityHeaderProfile_CSPDirectivesStorage (0.01s) +=== RUN TestSecurityHeaderProfile_PermissionsPolicyStorage +--- PASS: TestSecurityHeaderProfile_PermissionsPolicyStorage (0.00s) +=== RUN TestSecurityHeaderProfile_PresetFields +--- PASS: TestSecurityHeaderProfile_PresetFields (0.00s) +=== RUN TestUser_SetPassword +--- PASS: TestUser_SetPassword (3.06s) +=== RUN TestUser_CheckPassword +--- PASS: TestUser_CheckPassword (2.39s) +=== RUN TestUser_HasPendingInvite +=== RUN TestUser_HasPendingInvite/no_invite_token +=== RUN TestUser_HasPendingInvite/expired_invite +=== RUN TestUser_HasPendingInvite/valid_pending_invite +=== RUN TestUser_HasPendingInvite/already_accepted_invite +--- PASS: TestUser_HasPendingInvite (0.00s) + --- PASS: TestUser_HasPendingInvite/no_invite_token (0.00s) + --- PASS: TestUser_HasPendingInvite/expired_invite (0.00s) + --- PASS: TestUser_HasPendingInvite/valid_pending_invite (0.00s) + --- PASS: TestUser_HasPendingInvite/already_accepted_invite (0.00s) +=== RUN TestUser_CanAccessHost_AllowAll +--- PASS: TestUser_CanAccessHost_AllowAll (0.00s) +=== RUN TestUser_CanAccessHost_DenyAll +--- PASS: TestUser_CanAccessHost_DenyAll (0.00s) +=== RUN TestUser_CanAccessHost_AdminBypass +--- PASS: TestUser_CanAccessHost_AdminBypass (0.00s) +=== RUN TestUser_CanAccessHost_DefaultBehavior +--- PASS: TestUser_CanAccessHost_DefaultBehavior (0.00s) +=== RUN TestUser_CanAccessHost_EmptyPermittedHosts +=== RUN TestUser_CanAccessHost_EmptyPermittedHosts/allow_all_with_no_exceptions_allows_all +=== RUN TestUser_CanAccessHost_EmptyPermittedHosts/deny_all_with_no_exceptions_denies_all +--- PASS: TestUser_CanAccessHost_EmptyPermittedHosts (0.00s) + --- PASS: TestUser_CanAccessHost_EmptyPermittedHosts/allow_all_with_no_exceptions_allows_all (0.00s) + --- PASS: TestUser_CanAccessHost_EmptyPermittedHosts/deny_all_with_no_exceptions_denies_all (0.00s) +=== RUN TestPermissionMode_Constants +--- PASS: TestPermissionMode_Constants (0.00s) +=== RUN TestDNSProviderCredential_TableName +--- PASS: TestDNSProviderCredential_TableName (0.00s) +=== RUN TestDNSProviderCredential_Struct +--- PASS: TestDNSProviderCredential_Struct (0.00s) +=== RUN TestNotificationProvider_BeforeCreate +--- PASS: TestNotificationProvider_BeforeCreate (0.00s) +=== RUN TestUptimeMonitor_BeforeCreate +--- PASS: TestUptimeMonitor_BeforeCreate (0.02s) +PASS +coverage: 96.4% of statements +ok github.com/Wikid82/charon/backend/internal/models (cached) coverage: 96.4% of statements +=== RUN TestNewInternalServiceHTTPClient +=== PAUSE TestNewInternalServiceHTTPClient +=== RUN TestNewInternalServiceHTTPClient_TransportConfiguration +=== PAUSE TestNewInternalServiceHTTPClient_TransportConfiguration +=== RUN TestNewInternalServiceHTTPClient_RedirectsDisabled +=== PAUSE TestNewInternalServiceHTTPClient_RedirectsDisabled +=== RUN TestNewInternalServiceHTTPClient_CheckRedirectReturnsErrUseLastResponse +=== PAUSE TestNewInternalServiceHTTPClient_CheckRedirectReturnsErrUseLastResponse +=== RUN TestNewInternalServiceHTTPClient_ActualRequest +=== PAUSE TestNewInternalServiceHTTPClient_ActualRequest +=== RUN TestNewInternalServiceHTTPClient_TimeoutEnforced +=== PAUSE TestNewInternalServiceHTTPClient_TimeoutEnforced +=== RUN TestNewInternalServiceHTTPClient_MultipleClients +=== PAUSE TestNewInternalServiceHTTPClient_MultipleClients +=== RUN TestNewInternalServiceHTTPClient_ProxyIgnored +=== PAUSE TestNewInternalServiceHTTPClient_ProxyIgnored +=== RUN TestNewInternalServiceHTTPClient_PostRequest +=== PAUSE TestNewInternalServiceHTTPClient_PostRequest +=== RUN TestIsPrivateIP +=== PAUSE TestIsPrivateIP +=== RUN TestIsPrivateIP_NilIP +=== PAUSE TestIsPrivateIP_NilIP +=== RUN TestSafeDialer_BlocksPrivateIPs +=== PAUSE TestSafeDialer_BlocksPrivateIPs +=== RUN TestSafeDialer_AllowsLocalhost +=== PAUSE TestSafeDialer_AllowsLocalhost +=== RUN TestSafeDialer_AllowedDomains +=== PAUSE TestSafeDialer_AllowedDomains +=== RUN TestNewSafeHTTPClient_DefaultOptions +=== PAUSE TestNewSafeHTTPClient_DefaultOptions +=== RUN TestNewSafeHTTPClient_WithTimeout +=== PAUSE TestNewSafeHTTPClient_WithTimeout +=== RUN TestNewSafeHTTPClient_WithAllowLocalhost +=== PAUSE TestNewSafeHTTPClient_WithAllowLocalhost +=== RUN TestNewSafeHTTPClient_BlocksSSRF +=== PAUSE TestNewSafeHTTPClient_BlocksSSRF +=== RUN TestNewSafeHTTPClient_WithMaxRedirects +=== PAUSE TestNewSafeHTTPClient_WithMaxRedirects +=== RUN TestNewSafeHTTPClient_WithAllowedDomains +=== PAUSE TestNewSafeHTTPClient_WithAllowedDomains +=== RUN TestClientOptions_Defaults +=== PAUSE TestClientOptions_Defaults +=== RUN TestWithDialTimeout +=== PAUSE TestWithDialTimeout +=== RUN TestSafeDialer_InvalidAddress +=== PAUSE TestSafeDialer_InvalidAddress +=== RUN TestSafeDialer_LoopbackIPv6 +=== PAUSE TestSafeDialer_LoopbackIPv6 +=== RUN TestValidateRedirectTarget_EmptyHostname +=== PAUSE TestValidateRedirectTarget_EmptyHostname +=== RUN TestValidateRedirectTarget_Localhost +=== PAUSE TestValidateRedirectTarget_Localhost +=== RUN TestValidateRedirectTarget_127 +=== PAUSE TestValidateRedirectTarget_127 +=== RUN TestValidateRedirectTarget_IPv6Loopback +=== PAUSE TestValidateRedirectTarget_IPv6Loopback +=== RUN TestNewSafeHTTPClient_NoRedirectsByDefault +=== PAUSE TestNewSafeHTTPClient_NoRedirectsByDefault +=== RUN TestIsPrivateIP_IPv4MappedIPv6 +=== PAUSE TestIsPrivateIP_IPv4MappedIPv6 +=== RUN TestIsPrivateIP_Multicast +=== PAUSE TestIsPrivateIP_Multicast +=== RUN TestIsPrivateIP_Unspecified +=== PAUSE TestIsPrivateIP_Unspecified +=== RUN TestValidateRedirectTarget_DNSFailure +=== PAUSE TestValidateRedirectTarget_DNSFailure +=== RUN TestValidateRedirectTarget_PrivateIPInRedirect +=== PAUSE TestValidateRedirectTarget_PrivateIPInRedirect +=== RUN TestSafeDialer_AllIPsPrivate +=== PAUSE TestSafeDialer_AllIPsPrivate +=== RUN TestNewSafeHTTPClient_RedirectToPrivateIP +=== PAUSE TestNewSafeHTTPClient_RedirectToPrivateIP +=== RUN TestSafeDialer_DNSResolutionFailure +=== PAUSE TestSafeDialer_DNSResolutionFailure +=== RUN TestSafeDialer_NoIPsReturned +=== PAUSE TestSafeDialer_NoIPsReturned +=== RUN TestNewSafeHTTPClient_TooManyRedirects +=== PAUSE TestNewSafeHTTPClient_TooManyRedirects +=== RUN TestValidateRedirectTarget_AllowedLocalhost +=== PAUSE TestValidateRedirectTarget_AllowedLocalhost +=== RUN TestNewSafeHTTPClient_MetadataEndpoint +=== PAUSE TestNewSafeHTTPClient_MetadataEndpoint +=== RUN TestSafeDialer_IPv4MappedIPv6 +=== PAUSE TestSafeDialer_IPv4MappedIPv6 +=== RUN TestClientOptions_AllFunctionalOptions +=== PAUSE TestClientOptions_AllFunctionalOptions +=== RUN TestSafeDialer_ContextCancelled +=== PAUSE TestSafeDialer_ContextCancelled +=== RUN TestNewSafeHTTPClient_RedirectValidation +=== PAUSE TestNewSafeHTTPClient_RedirectValidation +=== CONT TestNewInternalServiceHTTPClient_RedirectsDisabled +=== CONT TestSafeDialer_LoopbackIPv6 +=== CONT TestSafeDialer_NoIPsReturned +=== CONT TestSafeDialer_BlocksPrivateIPs +=== RUN TestSafeDialer_BlocksPrivateIPs/blocks_10.x.x.x +=== PAUSE TestSafeDialer_BlocksPrivateIPs/blocks_10.x.x.x +=== RUN TestSafeDialer_BlocksPrivateIPs/blocks_172.16.x.x +=== PAUSE TestSafeDialer_BlocksPrivateIPs/blocks_172.16.x.x +=== RUN TestSafeDialer_BlocksPrivateIPs/blocks_192.168.x.x +=== PAUSE TestSafeDialer_BlocksPrivateIPs/blocks_192.168.x.x +=== RUN TestSafeDialer_BlocksPrivateIPs/blocks_127.0.0.1 +=== PAUSE TestSafeDialer_BlocksPrivateIPs/blocks_127.0.0.1 +=== RUN TestSafeDialer_BlocksPrivateIPs/blocks_localhost +=== PAUSE TestSafeDialer_BlocksPrivateIPs/blocks_localhost +=== CONT TestNewSafeHTTPClient_RedirectValidation +--- PASS: TestSafeDialer_LoopbackIPv6 (0.00s) +=== CONT TestClientOptions_AllFunctionalOptions +--- PASS: TestNewInternalServiceHTTPClient_RedirectsDisabled (0.00s) +--- PASS: TestClientOptions_AllFunctionalOptions (0.00s) +=== CONT TestSafeDialer_IPv4MappedIPv6 +--- PASS: TestSafeDialer_IPv4MappedIPv6 (0.00s) +=== CONT TestNewSafeHTTPClient_MetadataEndpoint +=== CONT TestSafeDialer_ContextCancelled +--- PASS: TestSafeDialer_ContextCancelled (0.00s) +=== CONT TestNewSafeHTTPClient_TooManyRedirects +--- PASS: TestNewSafeHTTPClient_MetadataEndpoint (0.00s) +=== CONT TestValidateRedirectTarget_AllowedLocalhost +=== RUN TestValidateRedirectTarget_AllowedLocalhost/http://localhost/path +=== PAUSE TestValidateRedirectTarget_AllowedLocalhost/http://localhost/path +=== RUN TestValidateRedirectTarget_AllowedLocalhost/http://127.0.0.1/path +=== PAUSE TestValidateRedirectTarget_AllowedLocalhost/http://127.0.0.1/path +=== RUN TestValidateRedirectTarget_AllowedLocalhost/http://[::1]/path +=== PAUSE TestValidateRedirectTarget_AllowedLocalhost/http://[::1]/path +=== CONT TestSafeDialer_DNSResolutionFailure +--- PASS: TestSafeDialer_NoIPsReturned (0.00s) +=== CONT TestNewSafeHTTPClient_RedirectToPrivateIP +--- PASS: TestSafeDialer_DNSResolutionFailure (0.00s) +=== CONT TestSafeDialer_AllIPsPrivate +=== RUN TestSafeDialer_AllIPsPrivate/10.0.0.1:80 +=== PAUSE TestSafeDialer_AllIPsPrivate/10.0.0.1:80 +=== RUN TestSafeDialer_AllIPsPrivate/172.16.0.1:443 +=== PAUSE TestSafeDialer_AllIPsPrivate/172.16.0.1:443 +=== RUN TestSafeDialer_AllIPsPrivate/192.168.0.1:8080 +=== PAUSE TestSafeDialer_AllIPsPrivate/192.168.0.1:8080 +=== RUN TestSafeDialer_AllIPsPrivate/169.254.169.254:80 +=== PAUSE TestSafeDialer_AllIPsPrivate/169.254.169.254:80 +=== CONT TestWithDialTimeout +--- PASS: TestWithDialTimeout (0.00s) +=== CONT TestClientOptions_Defaults +--- PASS: TestClientOptions_Defaults (0.00s) +=== CONT TestSafeDialer_InvalidAddress +--- PASS: TestSafeDialer_InvalidAddress (0.00s) +=== CONT TestNewSafeHTTPClient_WithMaxRedirects +--- PASS: TestNewSafeHTTPClient_RedirectToPrivateIP (0.00s) +=== CONT TestNewSafeHTTPClient_WithAllowedDomains +--- PASS: TestNewSafeHTTPClient_WithAllowedDomains (0.00s) +=== CONT TestNewSafeHTTPClient_WithAllowLocalhost +--- PASS: TestNewSafeHTTPClient_RedirectValidation (0.01s) +=== CONT TestNewSafeHTTPClient_BlocksSSRF +=== RUN TestNewSafeHTTPClient_BlocksSSRF/http://127.0.0.1/ +=== PAUSE TestNewSafeHTTPClient_BlocksSSRF/http://127.0.0.1/ +=== RUN TestNewSafeHTTPClient_BlocksSSRF/http://10.0.0.1/ +=== PAUSE TestNewSafeHTTPClient_BlocksSSRF/http://10.0.0.1/ +=== RUN TestNewSafeHTTPClient_BlocksSSRF/http://172.16.0.1/ +=== PAUSE TestNewSafeHTTPClient_BlocksSSRF/http://172.16.0.1/ +=== RUN TestNewSafeHTTPClient_BlocksSSRF/http://192.168.1.1/ +--- PASS: TestNewSafeHTTPClient_WithAllowLocalhost (0.00s) +--- PASS: TestNewSafeHTTPClient_WithMaxRedirects (0.00s) +=== PAUSE TestNewSafeHTTPClient_BlocksSSRF/http://192.168.1.1/ +--- PASS: TestNewSafeHTTPClient_TooManyRedirects (0.01s) +=== CONT TestSafeDialer_AllowedDomains +=== CONT TestNewSafeHTTPClient_WithTimeout +=== CONT TestSafeDialer_AllowsLocalhost +--- PASS: TestNewSafeHTTPClient_WithTimeout (0.00s) +=== CONT TestSafeDialer_BlocksPrivateIPs/blocks_172.16.x.x +=== CONT TestSafeDialer_BlocksPrivateIPs/blocks_localhost +=== RUN TestNewSafeHTTPClient_BlocksSSRF/http://localhost/ +=== PAUSE TestNewSafeHTTPClient_BlocksSSRF/http://localhost/ +--- PASS: TestSafeDialer_AllowsLocalhost (0.00s) +=== CONT TestSafeDialer_BlocksPrivateIPs/blocks_127.0.0.1 +=== CONT TestIsPrivateIP_NilIP +--- PASS: TestIsPrivateIP_NilIP (0.00s) +=== CONT TestSafeDialer_BlocksPrivateIPs/blocks_10.x.x.x +=== CONT TestSafeDialer_BlocksPrivateIPs/blocks_192.168.x.x +=== CONT TestNewInternalServiceHTTPClient_PostRequest +=== CONT TestNewInternalServiceHTTPClient_ProxyIgnored +=== CONT TestNewSafeHTTPClient_DefaultOptions +--- PASS: TestNewSafeHTTPClient_DefaultOptions (0.00s) +=== CONT TestIsPrivateIP +=== RUN TestIsPrivateIP/10.0.0.0/8_start +=== PAUSE TestIsPrivateIP/10.0.0.0/8_start +--- PASS: TestSafeDialer_BlocksPrivateIPs (0.00s) + --- PASS: TestSafeDialer_BlocksPrivateIPs/blocks_172.16.x.x (0.00s) + --- PASS: TestSafeDialer_BlocksPrivateIPs/blocks_localhost (0.00s) + --- PASS: TestSafeDialer_BlocksPrivateIPs/blocks_127.0.0.1 (0.00s) + --- PASS: TestSafeDialer_BlocksPrivateIPs/blocks_10.x.x.x (0.00s) + --- PASS: TestSafeDialer_BlocksPrivateIPs/blocks_192.168.x.x (0.00s) +=== RUN TestIsPrivateIP/10.0.0.0/8_middle +=== PAUSE TestIsPrivateIP/10.0.0.0/8_middle +=== RUN TestIsPrivateIP/172.16.0.0/12_start +=== PAUSE TestIsPrivateIP/172.16.0.0/12_start +=== RUN TestIsPrivateIP/172.16.0.0/12_end +=== PAUSE TestIsPrivateIP/172.16.0.0/12_end +=== RUN TestIsPrivateIP/192.168.0.0/16_start +=== PAUSE TestIsPrivateIP/192.168.0.0/16_start +=== RUN TestIsPrivateIP/192.168.0.0/16_end +=== PAUSE TestIsPrivateIP/192.168.0.0/16_end +=== RUN TestIsPrivateIP/169.254.0.0/16_start +=== PAUSE TestIsPrivateIP/169.254.0.0/16_start +=== RUN TestIsPrivateIP/169.254.0.0/16_end +=== PAUSE TestIsPrivateIP/169.254.0.0/16_end +=== RUN TestIsPrivateIP/127.0.0.0/8_localhost +=== PAUSE TestIsPrivateIP/127.0.0.0/8_localhost +--- PASS: TestNewInternalServiceHTTPClient_ProxyIgnored (0.00s) +=== RUN TestIsPrivateIP/127.0.0.0/8_other +=== CONT TestNewInternalServiceHTTPClient_TimeoutEnforced +=== PAUSE TestIsPrivateIP/127.0.0.0/8_other +=== RUN TestIsPrivateIP/127.0.0.0/8_end +=== PAUSE TestIsPrivateIP/127.0.0.0/8_end +=== RUN TestIsPrivateIP/0.0.0.0/8 +=== PAUSE TestIsPrivateIP/0.0.0.0/8 +=== RUN TestIsPrivateIP/240.0.0.0/4_reserved +=== PAUSE TestIsPrivateIP/240.0.0.0/4_reserved +=== RUN TestIsPrivateIP/255.255.255.255_broadcast +=== PAUSE TestIsPrivateIP/255.255.255.255_broadcast +=== RUN TestIsPrivateIP/IPv6_loopback +=== PAUSE TestIsPrivateIP/IPv6_loopback +=== RUN TestIsPrivateIP/fc00::/7_unique_local +=== PAUSE TestIsPrivateIP/fc00::/7_unique_local +=== RUN TestIsPrivateIP/fd00::/8_unique_local +=== PAUSE TestIsPrivateIP/fd00::/8_unique_local +=== RUN TestIsPrivateIP/fe80::/10_link-local +=== PAUSE TestIsPrivateIP/fe80::/10_link-local +=== RUN TestIsPrivateIP/Public_IPv4_1 +=== PAUSE TestIsPrivateIP/Public_IPv4_1 +=== RUN TestIsPrivateIP/Public_IPv4_2 +=== PAUSE TestIsPrivateIP/Public_IPv4_2 +=== RUN TestIsPrivateIP/Public_IPv4_3 +=== PAUSE TestIsPrivateIP/Public_IPv4_3 +=== RUN TestIsPrivateIP/Public_IPv6 +=== PAUSE TestIsPrivateIP/Public_IPv6 +=== RUN TestIsPrivateIP/Just_outside_172.16 +=== PAUSE TestIsPrivateIP/Just_outside_172.16 +=== RUN TestIsPrivateIP/Just_outside_172.31 +=== PAUSE TestIsPrivateIP/Just_outside_172.31 +=== RUN TestIsPrivateIP/Just_outside_192.168 +=== PAUSE TestIsPrivateIP/Just_outside_192.168 +=== CONT TestNewInternalServiceHTTPClient_ActualRequest +--- PASS: TestNewInternalServiceHTTPClient_PostRequest (0.00s) +=== CONT TestNewInternalServiceHTTPClient_CheckRedirectReturnsErrUseLastResponse +=== CONT TestNewInternalServiceHTTPClient_MultipleClients +--- PASS: TestNewInternalServiceHTTPClient_CheckRedirectReturnsErrUseLastResponse (0.00s) +=== CONT TestNewInternalServiceHTTPClient +--- PASS: TestNewInternalServiceHTTPClient_MultipleClients (0.00s) +--- PASS: TestNewInternalServiceHTTPClient_ActualRequest (0.00s) +=== RUN TestNewInternalServiceHTTPClient/with_1_second_timeout +=== CONT TestNewInternalServiceHTTPClient_TransportConfiguration +=== PAUSE TestNewInternalServiceHTTPClient/with_1_second_timeout +--- PASS: TestNewInternalServiceHTTPClient_TransportConfiguration (0.00s) +=== RUN TestNewInternalServiceHTTPClient/with_5_second_timeout +=== CONT TestIsPrivateIP_IPv4MappedIPv6 +=== PAUSE TestNewInternalServiceHTTPClient/with_5_second_timeout +=== RUN TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_private +=== RUN TestNewInternalServiceHTTPClient/with_30_second_timeout +=== PAUSE TestNewInternalServiceHTTPClient/with_30_second_timeout +=== PAUSE TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_private +=== RUN TestNewInternalServiceHTTPClient/with_100ms_timeout +=== PAUSE TestNewInternalServiceHTTPClient/with_100ms_timeout +=== RUN TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_public +=== PAUSE TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_public +=== RUN TestNewInternalServiceHTTPClient/with_zero_timeout +=== RUN TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_loopback +=== PAUSE TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_loopback +=== CONT TestValidateRedirectTarget_DNSFailure +=== PAUSE TestNewInternalServiceHTTPClient/with_zero_timeout +=== CONT TestValidateRedirectTarget_PrivateIPInRedirect +=== RUN TestValidateRedirectTarget_PrivateIPInRedirect/http://10.0.0.1/path +=== PAUSE TestValidateRedirectTarget_PrivateIPInRedirect/http://10.0.0.1/path +=== RUN TestValidateRedirectTarget_PrivateIPInRedirect/http://172.16.0.1/path +=== PAUSE TestValidateRedirectTarget_PrivateIPInRedirect/http://172.16.0.1/path +=== RUN TestValidateRedirectTarget_PrivateIPInRedirect/http://192.168.1.1/path +=== PAUSE TestValidateRedirectTarget_PrivateIPInRedirect/http://192.168.1.1/path +=== RUN TestValidateRedirectTarget_PrivateIPInRedirect/http://169.254.169.254/latest/meta-data/ +=== PAUSE TestValidateRedirectTarget_PrivateIPInRedirect/http://169.254.169.254/latest/meta-data/ +=== CONT TestIsPrivateIP_Unspecified +=== RUN TestIsPrivateIP_Unspecified/IPv4_unspecified +=== PAUSE TestIsPrivateIP_Unspecified/IPv4_unspecified +=== RUN TestIsPrivateIP_Unspecified/IPv6_unspecified +=== PAUSE TestIsPrivateIP_Unspecified/IPv6_unspecified +=== CONT TestIsPrivateIP_Multicast +=== RUN TestIsPrivateIP_Multicast/IPv4_multicast +=== PAUSE TestIsPrivateIP_Multicast/IPv4_multicast +=== RUN TestIsPrivateIP_Multicast/IPv6_multicast +=== PAUSE TestIsPrivateIP_Multicast/IPv6_multicast +=== CONT TestNewSafeHTTPClient_NoRedirectsByDefault +--- PASS: TestValidateRedirectTarget_DNSFailure (0.00s) +=== CONT TestValidateRedirectTarget_IPv6Loopback +--- PASS: TestValidateRedirectTarget_IPv6Loopback (0.00s) +=== CONT TestValidateRedirectTarget_127 +--- PASS: TestValidateRedirectTarget_127 (0.00s) +=== CONT TestValidateRedirectTarget_EmptyHostname +--- PASS: TestValidateRedirectTarget_EmptyHostname (0.00s) +=== CONT TestValidateRedirectTarget_Localhost +--- PASS: TestValidateRedirectTarget_Localhost (0.00s) +=== CONT TestValidateRedirectTarget_AllowedLocalhost/http://127.0.0.1/path +=== CONT TestValidateRedirectTarget_AllowedLocalhost/http://[::1]/path +=== CONT TestValidateRedirectTarget_AllowedLocalhost/http://localhost/path +--- PASS: TestValidateRedirectTarget_AllowedLocalhost (0.00s) + --- PASS: TestValidateRedirectTarget_AllowedLocalhost/http://127.0.0.1/path (0.00s) + --- PASS: TestValidateRedirectTarget_AllowedLocalhost/http://[::1]/path (0.00s) + --- PASS: TestValidateRedirectTarget_AllowedLocalhost/http://localhost/path (0.00s) +=== CONT TestSafeDialer_AllIPsPrivate/10.0.0.1:80 +=== CONT TestSafeDialer_AllIPsPrivate/169.254.169.254:80 +--- PASS: TestNewSafeHTTPClient_NoRedirectsByDefault (0.00s) +=== CONT TestSafeDialer_AllIPsPrivate/192.168.0.1:8080 +=== CONT TestSafeDialer_AllIPsPrivate/172.16.0.1:443 +=== CONT TestNewSafeHTTPClient_BlocksSSRF/http://192.168.1.1/ +=== CONT TestNewSafeHTTPClient_BlocksSSRF/http://127.0.0.1/ +--- PASS: TestSafeDialer_AllIPsPrivate (0.00s) + --- PASS: TestSafeDialer_AllIPsPrivate/10.0.0.1:80 (0.00s) + --- PASS: TestSafeDialer_AllIPsPrivate/169.254.169.254:80 (0.00s) + --- PASS: TestSafeDialer_AllIPsPrivate/192.168.0.1:8080 (0.00s) + --- PASS: TestSafeDialer_AllIPsPrivate/172.16.0.1:443 (0.00s) +=== CONT TestNewSafeHTTPClient_BlocksSSRF/http://10.0.0.1/ +=== CONT TestNewSafeHTTPClient_BlocksSSRF/http://localhost/ +=== CONT TestNewSafeHTTPClient_BlocksSSRF/http://172.16.0.1/ +=== CONT TestIsPrivateIP/10.0.0.0/8_start +=== CONT TestIsPrivateIP/Just_outside_192.168 +=== CONT TestIsPrivateIP/Just_outside_172.16 +=== CONT TestIsPrivateIP/240.0.0.0/4_reserved +--- PASS: TestNewSafeHTTPClient_BlocksSSRF (0.00s) + --- PASS: TestNewSafeHTTPClient_BlocksSSRF/http://127.0.0.1/ (0.00s) + --- PASS: TestNewSafeHTTPClient_BlocksSSRF/http://192.168.1.1/ (0.00s) + --- PASS: TestNewSafeHTTPClient_BlocksSSRF/http://10.0.0.1/ (0.00s) + --- PASS: TestNewSafeHTTPClient_BlocksSSRF/http://localhost/ (0.00s) + --- PASS: TestNewSafeHTTPClient_BlocksSSRF/http://172.16.0.1/ (0.00s) +=== CONT TestIsPrivateIP/Just_outside_172.31 +=== CONT TestIsPrivateIP/Public_IPv6 +=== CONT TestIsPrivateIP/Public_IPv4_2 +=== CONT TestIsPrivateIP/Public_IPv4_3 +=== CONT TestIsPrivateIP/fe80::/10_link-local +=== CONT TestIsPrivateIP/fd00::/8_unique_local +=== CONT TestIsPrivateIP/fc00::/7_unique_local +=== CONT TestIsPrivateIP/Public_IPv4_1 +=== CONT TestIsPrivateIP/IPv6_loopback +=== CONT TestIsPrivateIP/255.255.255.255_broadcast +=== CONT TestIsPrivateIP/169.254.0.0/16_start +=== CONT TestIsPrivateIP/127.0.0.0/8_end +=== CONT TestIsPrivateIP/127.0.0.0/8_localhost +=== CONT TestIsPrivateIP/0.0.0.0/8 +=== CONT TestIsPrivateIP/169.254.0.0/16_end +=== CONT TestIsPrivateIP/127.0.0.0/8_other +=== CONT TestIsPrivateIP/192.168.0.0/16_end +=== CONT TestIsPrivateIP/172.16.0.0/12_start +=== CONT TestIsPrivateIP/192.168.0.0/16_start +=== CONT TestIsPrivateIP/172.16.0.0/12_end +=== CONT TestIsPrivateIP/10.0.0.0/8_middle +=== CONT TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_loopback +=== CONT TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_public +=== CONT TestNewInternalServiceHTTPClient/with_1_second_timeout +=== CONT TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_private +=== CONT TestNewInternalServiceHTTPClient/with_zero_timeout +=== CONT TestNewInternalServiceHTTPClient/with_100ms_timeout +=== CONT TestNewInternalServiceHTTPClient/with_30_second_timeout +--- PASS: TestIsPrivateIP (0.00s) + --- PASS: TestIsPrivateIP/10.0.0.0/8_start (0.00s) + --- PASS: TestIsPrivateIP/Just_outside_172.16 (0.00s) + --- PASS: TestIsPrivateIP/240.0.0.0/4_reserved (0.00s) + --- PASS: TestIsPrivateIP/Just_outside_172.31 (0.00s) + --- PASS: TestIsPrivateIP/Just_outside_192.168 (0.00s) + --- PASS: TestIsPrivateIP/Public_IPv6 (0.00s) + --- PASS: TestIsPrivateIP/Public_IPv4_2 (0.00s) + --- PASS: TestIsPrivateIP/Public_IPv4_3 (0.00s) + --- PASS: TestIsPrivateIP/fe80::/10_link-local (0.00s) + --- PASS: TestIsPrivateIP/fd00::/8_unique_local (0.00s) + --- PASS: TestIsPrivateIP/fc00::/7_unique_local (0.00s) + --- PASS: TestIsPrivateIP/IPv6_loopback (0.00s) + --- PASS: TestIsPrivateIP/255.255.255.255_broadcast (0.00s) + --- PASS: TestIsPrivateIP/Public_IPv4_1 (0.00s) + --- PASS: TestIsPrivateIP/169.254.0.0/16_start (0.00s) + --- PASS: TestIsPrivateIP/127.0.0.0/8_localhost (0.00s) + --- PASS: TestIsPrivateIP/169.254.0.0/16_end (0.00s) + --- PASS: TestIsPrivateIP/0.0.0.0/8 (0.00s) + --- PASS: TestIsPrivateIP/192.168.0.0/16_start (0.00s) + --- PASS: TestIsPrivateIP/192.168.0.0/16_end (0.00s) + --- PASS: TestIsPrivateIP/127.0.0.0/8_end (0.00s) + --- PASS: TestIsPrivateIP/172.16.0.0/12_start (0.00s) + --- PASS: TestIsPrivateIP/10.0.0.0/8_middle (0.00s) + --- PASS: TestIsPrivateIP/172.16.0.0/12_end (0.00s) + --- PASS: TestIsPrivateIP/127.0.0.0/8_other (0.00s) +=== CONT TestValidateRedirectTarget_PrivateIPInRedirect/http://192.168.1.1/path +--- PASS: TestIsPrivateIP_IPv4MappedIPv6 (0.00s) + --- PASS: TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_loopback (0.00s) + --- PASS: TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_public (0.00s) + --- PASS: TestIsPrivateIP_IPv4MappedIPv6/IPv4-mapped_private (0.00s) +=== CONT TestNewInternalServiceHTTPClient/with_5_second_timeout +--- PASS: TestNewInternalServiceHTTPClient (0.00s) + --- PASS: TestNewInternalServiceHTTPClient/with_1_second_timeout (0.00s) + --- PASS: TestNewInternalServiceHTTPClient/with_zero_timeout (0.00s) + --- PASS: TestNewInternalServiceHTTPClient/with_30_second_timeout (0.00s) + --- PASS: TestNewInternalServiceHTTPClient/with_100ms_timeout (0.00s) + --- PASS: TestNewInternalServiceHTTPClient/with_5_second_timeout (0.00s) +=== CONT TestValidateRedirectTarget_PrivateIPInRedirect/http://10.0.0.1/path +=== CONT TestIsPrivateIP_Unspecified/IPv4_unspecified +=== CONT TestIsPrivateIP_Unspecified/IPv6_unspecified +=== CONT TestIsPrivateIP_Multicast/IPv6_multicast +=== CONT TestValidateRedirectTarget_PrivateIPInRedirect/http://172.16.0.1/path +--- PASS: TestIsPrivateIP_Unspecified (0.00s) + --- PASS: TestIsPrivateIP_Unspecified/IPv4_unspecified (0.00s) + --- PASS: TestIsPrivateIP_Unspecified/IPv6_unspecified (0.00s) +=== CONT TestIsPrivateIP_Multicast/IPv4_multicast +=== CONT TestValidateRedirectTarget_PrivateIPInRedirect/http://169.254.169.254/latest/meta-data/ +--- PASS: TestValidateRedirectTarget_PrivateIPInRedirect (0.00s) + --- PASS: TestValidateRedirectTarget_PrivateIPInRedirect/http://192.168.1.1/path (0.00s) + --- PASS: TestValidateRedirectTarget_PrivateIPInRedirect/http://10.0.0.1/path (0.00s) + --- PASS: TestValidateRedirectTarget_PrivateIPInRedirect/http://172.16.0.1/path (0.00s) + --- PASS: TestValidateRedirectTarget_PrivateIPInRedirect/http://169.254.169.254/latest/meta-data/ (0.00s) +--- PASS: TestIsPrivateIP_Multicast (0.00s) + --- PASS: TestIsPrivateIP_Multicast/IPv6_multicast (0.00s) + --- PASS: TestIsPrivateIP_Multicast/IPv4_multicast (0.00s) +=== NAME TestSafeDialer_AllowedDomains + safeclient_test.go:171: Got expected error type for allowed domain: *fmt.wrapError: DNS resolution failed for app.crowdsec.net: lookup app.crowdsec.net: i/o timeout +--- PASS: TestSafeDialer_AllowedDomains (0.10s) +--- PASS: TestNewInternalServiceHTTPClient_TimeoutEnforced (0.50s) +PASS +coverage: 81.3% of statements +ok github.com/Wikid82/charon/backend/internal/network (cached) coverage: 81.3% of statements +=== RUN TestAuditEvent_JSONSerialization +=== PAUSE TestAuditEvent_JSONSerialization +=== RUN TestAuditLogger_LogURLValidation +=== PAUSE TestAuditLogger_LogURLValidation +=== RUN TestAuditLogger_LogURLTest +=== PAUSE TestAuditLogger_LogURLTest +=== RUN TestAuditLogger_LogSSRFBlock +=== PAUSE TestAuditLogger_LogSSRFBlock +=== RUN TestGlobalAuditLogger +=== PAUSE TestGlobalAuditLogger +=== RUN TestAuditEvent_RequiredFields +=== PAUSE TestAuditEvent_RequiredFields +=== RUN TestAuditLogger_TimestampFormat +=== PAUSE TestAuditLogger_TimestampFormat +=== RUN TestParseExactHostnameAllowlist +--- PASS: TestParseExactHostnameAllowlist (0.00s) +=== RUN TestValidateInternalServiceBaseURL +=== RUN TestValidateInternalServiceBaseURL/OK_http_localhost_explicit_port +=== RUN TestValidateInternalServiceBaseURL/OK_http_localhost_path_normalized +=== RUN TestValidateInternalServiceBaseURL/OK_https_localhost_default_port +=== RUN TestValidateInternalServiceBaseURL/OK_ipv6_loopback_explicit_port +=== RUN TestValidateInternalServiceBaseURL/Reject_userinfo +=== RUN TestValidateInternalServiceBaseURL/Reject_unsupported_scheme +=== RUN TestValidateInternalServiceBaseURL/Reject_missing_hostname +=== RUN TestValidateInternalServiceBaseURL/Reject_hostname_not_allowed +=== RUN TestValidateInternalServiceBaseURL/Reject_unexpected_port_when_omitted +=== RUN TestValidateInternalServiceBaseURL/Reject_invalid_port +=== RUN TestValidateInternalServiceBaseURL/Reject_out-of-range_port +--- PASS: TestValidateInternalServiceBaseURL (0.00s) + --- PASS: TestValidateInternalServiceBaseURL/OK_http_localhost_explicit_port (0.00s) + --- PASS: TestValidateInternalServiceBaseURL/OK_http_localhost_path_normalized (0.00s) + --- PASS: TestValidateInternalServiceBaseURL/OK_https_localhost_default_port (0.00s) + --- PASS: TestValidateInternalServiceBaseURL/OK_ipv6_loopback_explicit_port (0.00s) + --- PASS: TestValidateInternalServiceBaseURL/Reject_userinfo (0.00s) + --- PASS: TestValidateInternalServiceBaseURL/Reject_unsupported_scheme (0.00s) + --- PASS: TestValidateInternalServiceBaseURL/Reject_missing_hostname (0.00s) + --- PASS: TestValidateInternalServiceBaseURL/Reject_hostname_not_allowed (0.00s) + --- PASS: TestValidateInternalServiceBaseURL/Reject_unexpected_port_when_omitted (0.00s) + --- PASS: TestValidateInternalServiceBaseURL/Reject_invalid_port (0.00s) + --- PASS: TestValidateInternalServiceBaseURL/Reject_out-of-range_port (0.00s) +=== RUN TestInternalServiceHostAllowlist +=== RUN TestInternalServiceHostAllowlist/DefaultLocalhostOnly +=== RUN TestInternalServiceHostAllowlist/WithAdditionalHosts +=== RUN TestInternalServiceHostAllowlist/WithEmptyAndWhitespaceEntries +=== RUN TestInternalServiceHostAllowlist/WithInvalidEntries +--- PASS: TestInternalServiceHostAllowlist (0.00s) + --- PASS: TestInternalServiceHostAllowlist/DefaultLocalhostOnly (0.00s) + --- PASS: TestInternalServiceHostAllowlist/WithAdditionalHosts (0.00s) + --- PASS: TestInternalServiceHostAllowlist/WithEmptyAndWhitespaceEntries (0.00s) + --- PASS: TestInternalServiceHostAllowlist/WithInvalidEntries (0.00s) +=== RUN TestWithMaxRedirects +=== RUN TestWithMaxRedirects/Zero_redirects +=== RUN TestWithMaxRedirects/Five_redirects +=== RUN TestWithMaxRedirects/Ten_redirects +--- PASS: TestWithMaxRedirects (0.00s) + --- PASS: TestWithMaxRedirects/Zero_redirects (0.00s) + --- PASS: TestWithMaxRedirects/Five_redirects (0.00s) + --- PASS: TestWithMaxRedirects/Ten_redirects (0.00s) +=== RUN TestValidateInternalServiceBaseURL_AdditionalCases +=== RUN TestValidateInternalServiceBaseURL_AdditionalCases/HTTPSWithDefaultPort +=== RUN TestValidateInternalServiceBaseURL_AdditionalCases/HTTPWithDefaultPort +=== RUN TestValidateInternalServiceBaseURL_AdditionalCases/PortMismatchWithDefaultHTTPS +=== RUN TestValidateInternalServiceBaseURL_AdditionalCases/PortMismatchWithDefaultHTTP +=== RUN TestValidateInternalServiceBaseURL_AdditionalCases/InvalidPortNumber +=== RUN TestValidateInternalServiceBaseURL_AdditionalCases/NegativePort +=== RUN TestValidateInternalServiceBaseURL_AdditionalCases/HostNotInAllowlist +=== RUN TestValidateInternalServiceBaseURL_AdditionalCases/EmptyAllowlist +=== RUN TestValidateInternalServiceBaseURL_AdditionalCases/CaseInsensitiveHostMatching +=== RUN TestValidateInternalServiceBaseURL_AdditionalCases/AllowedHostDifferentCase +--- PASS: TestValidateInternalServiceBaseURL_AdditionalCases (0.00s) + --- PASS: TestValidateInternalServiceBaseURL_AdditionalCases/HTTPSWithDefaultPort (0.00s) + --- PASS: TestValidateInternalServiceBaseURL_AdditionalCases/HTTPWithDefaultPort (0.00s) + --- PASS: TestValidateInternalServiceBaseURL_AdditionalCases/PortMismatchWithDefaultHTTPS (0.00s) + --- PASS: TestValidateInternalServiceBaseURL_AdditionalCases/PortMismatchWithDefaultHTTP (0.00s) + --- PASS: TestValidateInternalServiceBaseURL_AdditionalCases/InvalidPortNumber (0.00s) + --- PASS: TestValidateInternalServiceBaseURL_AdditionalCases/NegativePort (0.00s) + --- PASS: TestValidateInternalServiceBaseURL_AdditionalCases/HostNotInAllowlist (0.00s) + --- PASS: TestValidateInternalServiceBaseURL_AdditionalCases/EmptyAllowlist (0.00s) + --- PASS: TestValidateInternalServiceBaseURL_AdditionalCases/CaseInsensitiveHostMatching (0.00s) + --- PASS: TestValidateInternalServiceBaseURL_AdditionalCases/AllowedHostDifferentCase (0.00s) +=== RUN TestSanitizeIPForError_AdditionalCases +=== RUN TestSanitizeIPForError_AdditionalCases/InvalidIPString +=== RUN TestSanitizeIPForError_AdditionalCases/EmptyString +=== RUN TestSanitizeIPForError_AdditionalCases/IPv4Malformed +=== RUN TestSanitizeIPForError_AdditionalCases/IPv6SingleSegment +=== RUN TestSanitizeIPForError_AdditionalCases/IPv6MultipleSegments +=== RUN TestSanitizeIPForError_AdditionalCases/IPv6Compressed +=== RUN TestSanitizeIPForError_AdditionalCases/IPv4ThreeOctets +=== RUN TestSanitizeIPForError_AdditionalCases/IPv4FiveOctets +--- PASS: TestSanitizeIPForError_AdditionalCases (0.00s) + --- PASS: TestSanitizeIPForError_AdditionalCases/InvalidIPString (0.00s) + --- PASS: TestSanitizeIPForError_AdditionalCases/EmptyString (0.00s) + --- PASS: TestSanitizeIPForError_AdditionalCases/IPv4Malformed (0.00s) + --- PASS: TestSanitizeIPForError_AdditionalCases/IPv6SingleSegment (0.00s) + --- PASS: TestSanitizeIPForError_AdditionalCases/IPv6MultipleSegments (0.00s) + --- PASS: TestSanitizeIPForError_AdditionalCases/IPv6Compressed (0.00s) + --- PASS: TestSanitizeIPForError_AdditionalCases/IPv4ThreeOctets (0.00s) + --- PASS: TestSanitizeIPForError_AdditionalCases/IPv4FiveOctets (0.00s) +=== RUN TestValidateExternalURL_BasicValidation +=== PAUSE TestValidateExternalURL_BasicValidation +=== RUN TestValidateExternalURL_LocalhostHandling +=== PAUSE TestValidateExternalURL_LocalhostHandling +=== RUN TestValidateExternalURL_PrivateIPBlocking +=== PAUSE TestValidateExternalURL_PrivateIPBlocking +=== RUN TestValidateExternalURL_Options +=== PAUSE TestValidateExternalURL_Options +=== RUN TestIsPrivateIP +=== PAUSE TestIsPrivateIP +=== RUN TestValidateExternalURL_RealWorldURLs +=== PAUSE TestValidateExternalURL_RealWorldURLs +=== RUN TestValidateExternalURL_MultipleOptions +=== PAUSE TestValidateExternalURL_MultipleOptions +=== RUN TestValidateExternalURL_CustomTimeout +=== PAUSE TestValidateExternalURL_CustomTimeout +=== RUN TestValidateExternalURL_DNSTimeout +=== PAUSE TestValidateExternalURL_DNSTimeout +=== RUN TestValidateExternalURL_MultipleIPsAllPrivate +=== PAUSE TestValidateExternalURL_MultipleIPsAllPrivate +=== RUN TestValidateExternalURL_CloudMetadataDetection +=== PAUSE TestValidateExternalURL_CloudMetadataDetection +=== RUN TestIsPrivateIP_IPv6Comprehensive +=== PAUSE TestIsPrivateIP_IPv6Comprehensive +=== RUN TestIPv4MappedIPv6Detection +=== PAUSE TestIPv4MappedIPv6Detection +=== RUN TestValidateExternalURL_IPv4MappedIPv6Blocking +=== PAUSE TestValidateExternalURL_IPv4MappedIPv6Blocking +=== RUN TestValidateExternalURL_HostnameValidation +=== PAUSE TestValidateExternalURL_HostnameValidation +=== RUN TestValidateExternalURL_PortValidation +=== PAUSE TestValidateExternalURL_PortValidation +=== RUN TestSanitizeIPForError +=== PAUSE TestSanitizeIPForError +=== RUN TestParsePort +=== PAUSE TestParsePort +=== RUN TestValidateExternalURL_EdgeCases +=== PAUSE TestValidateExternalURL_EdgeCases +=== RUN TestIsIPv4MappedIPv6_EdgeCases +=== PAUSE TestIsIPv4MappedIPv6_EdgeCases +=== CONT TestAuditEvent_JSONSerialization +=== CONT TestValidateExternalURL_EdgeCases +=== RUN TestValidateExternalURL_EdgeCases/Port_with_non-numeric_characters +=== CONT TestIsIPv4MappedIPv6_EdgeCases +=== RUN TestIsIPv4MappedIPv6_EdgeCases/Standard_mapped +=== PAUSE TestIsIPv4MappedIPv6_EdgeCases/Standard_mapped +=== RUN TestIsIPv4MappedIPv6_EdgeCases/Mapped_public_IP +=== PAUSE TestValidateExternalURL_EdgeCases/Port_with_non-numeric_characters +=== RUN TestValidateExternalURL_EdgeCases/Maximum_valid_port +=== PAUSE TestIsIPv4MappedIPv6_EdgeCases/Mapped_public_IP +=== PAUSE TestValidateExternalURL_EdgeCases/Maximum_valid_port +=== RUN TestIsIPv4MappedIPv6_EdgeCases/Pure_IPv6_2001:db8 +=== PAUSE TestIsIPv4MappedIPv6_EdgeCases/Pure_IPv6_2001:db8 +=== RUN TestIsIPv4MappedIPv6_EdgeCases/IPv6_loopback +--- PASS: TestAuditEvent_JSONSerialization (0.00s) +=== RUN TestValidateExternalURL_EdgeCases/Port_1_(privileged_but_not_blocked_with_AllowLocalhost) +=== CONT TestParsePort +=== CONT TestValidateExternalURL_Options +=== PAUSE TestValidateExternalURL_EdgeCases/Port_1_(privileged_but_not_blocked_with_AllowLocalhost) +=== RUN TestValidateExternalURL_Options/WithTimeout +=== RUN TestValidateExternalURL_EdgeCases/Port_1023_(edge_of_privileged_range) +=== PAUSE TestValidateExternalURL_Options/WithTimeout +=== PAUSE TestValidateExternalURL_EdgeCases/Port_1023_(edge_of_privileged_range) +=== RUN TestValidateExternalURL_EdgeCases/Port_1024_(first_non-privileged) +=== RUN TestParsePort/Valid_port_80 +=== RUN TestValidateExternalURL_Options/Multiple_options +=== PAUSE TestParsePort/Valid_port_80 +=== PAUSE TestIsIPv4MappedIPv6_EdgeCases/IPv6_loopback +=== PAUSE TestValidateExternalURL_EdgeCases/Port_1024_(first_non-privileged) +=== RUN TestIsIPv4MappedIPv6_EdgeCases/All_zeros_except_prefix +=== RUN TestValidateExternalURL_EdgeCases/URL_with_username_only +=== PAUSE TestIsIPv4MappedIPv6_EdgeCases/All_zeros_except_prefix +=== PAUSE TestValidateExternalURL_Options/Multiple_options +=== RUN TestIsIPv4MappedIPv6_EdgeCases/All_ones +=== PAUSE TestValidateExternalURL_EdgeCases/URL_with_username_only +=== RUN TestParsePort/Valid_port_443 +=== PAUSE TestIsIPv4MappedIPv6_EdgeCases/All_ones +=== CONT TestSanitizeIPForError +=== RUN TestSanitizeIPForError/Private_IPv4_192.168 +=== PAUSE TestParsePort/Valid_port_443 +=== RUN TestValidateExternalURL_EdgeCases/Hostname_with_single_dot +=== CONT TestValidateExternalURL_HostnameValidation +=== PAUSE TestValidateExternalURL_EdgeCases/Hostname_with_single_dot +=== PAUSE TestSanitizeIPForError/Private_IPv4_192.168 +=== RUN TestParsePort/Valid_port_8080 +=== RUN TestValidateExternalURL_HostnameValidation/Extremely_long_hostname_(254_chars) +=== RUN TestValidateExternalURL_EdgeCases/Triple_dots_in_hostname +=== RUN TestSanitizeIPForError/Private_IPv4_10.x +=== PAUSE TestParsePort/Valid_port_8080 +=== PAUSE TestValidateExternalURL_EdgeCases/Triple_dots_in_hostname +=== RUN TestParsePort/Valid_port_65535 +=== PAUSE TestSanitizeIPForError/Private_IPv4_10.x +=== PAUSE TestValidateExternalURL_HostnameValidation/Extremely_long_hostname_(254_chars) +=== RUN TestValidateExternalURL_EdgeCases/Hostname_at_252_chars_(just_under_limit) +=== RUN TestValidateExternalURL_HostnameValidation/Hostname_with_double_dots +=== RUN TestSanitizeIPForError/Private_IPv4_172.16 +=== PAUSE TestValidateExternalURL_EdgeCases/Hostname_at_252_chars_(just_under_limit) +=== PAUSE TestValidateExternalURL_HostnameValidation/Hostname_with_double_dots +=== CONT TestValidateExternalURL_IPv4MappedIPv6Blocking + url_validator_test.go:707: DNS resolution of IPv4-mapped IPv6 not testable without custom DNS server +=== PAUSE TestSanitizeIPForError/Private_IPv4_172.16 +=== PAUSE TestParsePort/Valid_port_65535 +=== RUN TestParsePort/Empty_port +=== RUN TestSanitizeIPForError/Loopback_IPv4 +=== PAUSE TestParsePort/Empty_port +=== RUN TestParsePort/Non-numeric_port +=== PAUSE TestSanitizeIPForError/Loopback_IPv4 +=== PAUSE TestParsePort/Non-numeric_port +--- SKIP: TestValidateExternalURL_IPv4MappedIPv6Blocking (0.00s) +=== CONT TestIPv4MappedIPv6Detection +=== RUN TestIPv4MappedIPv6Detection/IPv4-mapped_loopback +=== RUN TestParsePort/Negative_port +=== PAUSE TestIPv4MappedIPv6Detection/IPv4-mapped_loopback +=== RUN TestValidateExternalURL_HostnameValidation/Hostname_with_double_dots_mid +=== PAUSE TestParsePort/Negative_port +=== RUN TestSanitizeIPForError/Metadata_IPv4 +=== PAUSE TestValidateExternalURL_HostnameValidation/Hostname_with_double_dots_mid +=== PAUSE TestSanitizeIPForError/Metadata_IPv4 +=== CONT TestValidateExternalURL_PortValidation +=== RUN TestValidateExternalURL_PortValidation/Port_80_(standard_HTTP)_-_should_allow +=== RUN TestIPv4MappedIPv6Detection/IPv4-mapped_private_10.x +=== RUN TestParsePort/Port_zero +=== PAUSE TestValidateExternalURL_PortValidation/Port_80_(standard_HTTP)_-_should_allow +=== RUN TestSanitizeIPForError/IPv6_link-local +=== PAUSE TestParsePort/Port_zero +=== RUN TestValidateExternalURL_PortValidation/Port_443_(standard_HTTPS)_-_should_allow +=== CONT TestValidateExternalURL_CloudMetadataDetection +=== PAUSE TestValidateExternalURL_PortValidation/Port_443_(standard_HTTPS)_-_should_allow +=== RUN TestValidateExternalURL_CloudMetadataDetection/AWS_metadata_service +=== PAUSE TestSanitizeIPForError/IPv6_link-local +=== PAUSE TestValidateExternalURL_CloudMetadataDetection/AWS_metadata_service +=== RUN TestSanitizeIPForError/IPv6_unique_local +=== PAUSE TestSanitizeIPForError/IPv6_unique_local +=== RUN TestValidateExternalURL_CloudMetadataDetection/AWS_metadata_IPv6 +=== RUN TestValidateExternalURL_PortValidation/Port_22_(SSH)_-_should_block +=== PAUSE TestValidateExternalURL_PortValidation/Port_22_(SSH)_-_should_block +=== PAUSE TestValidateExternalURL_CloudMetadataDetection/AWS_metadata_IPv6 +=== RUN TestValidateExternalURL_PortValidation/Port_25_(SMTP)_-_should_block +=== RUN TestValidateExternalURL_CloudMetadataDetection/GCP_metadata_service +=== RUN TestSanitizeIPForError/Invalid_IP +=== PAUSE TestValidateExternalURL_CloudMetadataDetection/GCP_metadata_service +=== RUN TestValidateExternalURL_CloudMetadataDetection/Azure_metadata_service +=== PAUSE TestValidateExternalURL_PortValidation/Port_25_(SMTP)_-_should_block +=== RUN TestValidateExternalURL_PortValidation/Port_3306_(MySQL)_-_should_block_if_<_1024 +=== PAUSE TestValidateExternalURL_CloudMetadataDetection/Azure_metadata_service +=== CONT TestValidateExternalURL_MultipleIPsAllPrivate +=== PAUSE TestSanitizeIPForError/Invalid_IP +=== PAUSE TestValidateExternalURL_PortValidation/Port_3306_(MySQL)_-_should_block_if_<_1024 +=== RUN TestValidateExternalURL_MultipleIPsAllPrivate/IP_10.0.0.1 +=== RUN TestValidateExternalURL_PortValidation/Port_8080_(non-privileged)_-_should_allow +=== CONT TestValidateExternalURL_DNSTimeout +=== PAUSE TestValidateExternalURL_PortValidation/Port_8080_(non-privileged)_-_should_allow +=== RUN TestValidateExternalURL_PortValidation/Port_22_with_AllowLocalhost_-_should_allow +=== PAUSE TestValidateExternalURL_PortValidation/Port_22_with_AllowLocalhost_-_should_allow +=== RUN TestValidateExternalURL_PortValidation/Port_0_-_should_block +=== PAUSE TestValidateExternalURL_PortValidation/Port_0_-_should_block +=== RUN TestValidateExternalURL_PortValidation/Port_65536_-_should_block +=== PAUSE TestValidateExternalURL_PortValidation/Port_65536_-_should_block +=== CONT TestIsPrivateIP_IPv6Comprehensive +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback_expanded +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback_expanded +=== NAME TestValidateExternalURL_DNSTimeout + url_validator_test.go:527: Got acceptable error: connection to private ip addresses is blocked for security (detected IPv4-mapped IPv6: 10.255.255.1) +--- PASS: TestValidateExternalURL_DNSTimeout (0.00s) +=== PAUSE TestValidateExternalURL_MultipleIPsAllPrivate/IP_10.0.0.1 +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_start +=== RUN TestValidateExternalURL_MultipleIPsAllPrivate/IP_172.16.0.1 +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_start +=== CONT TestValidateExternalURL_MultipleOptions +=== PAUSE TestValidateExternalURL_MultipleIPsAllPrivate/IP_172.16.0.1 +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_mid +=== RUN TestValidateExternalURL_MultipleIPsAllPrivate/IP_192.168.1.1 +=== PAUSE TestValidateExternalURL_MultipleIPsAllPrivate/IP_192.168.1.1 +=== RUN TestValidateExternalURL_MultipleOptions/All_options_enabled +=== CONT TestValidateExternalURL_CustomTimeout +=== PAUSE TestValidateExternalURL_MultipleOptions/All_options_enabled +=== RUN TestValidateExternalURL_MultipleOptions/Custom_timeout_with_HTTPS +=== PAUSE TestValidateExternalURL_MultipleOptions/Custom_timeout_with_HTTPS +=== RUN TestValidateExternalURL_CustomTimeout/Very_short_timeout +=== PAUSE TestValidateExternalURL_CustomTimeout/Very_short_timeout +=== RUN TestValidateExternalURL_CustomTimeout/Standard_timeout +=== RUN TestValidateExternalURL_MultipleOptions/HTTP_without_AllowHTTP_fails +=== PAUSE TestIPv4MappedIPv6Detection/IPv4-mapped_private_10.x +=== PAUSE TestValidateExternalURL_CustomTimeout/Standard_timeout +=== RUN TestValidateExternalURL_CustomTimeout/Long_timeout +=== RUN TestIPv4MappedIPv6Detection/IPv4-mapped_private_192.168 +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_mid +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_end +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_end +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fc00 +=== PAUSE TestValidateExternalURL_MultipleOptions/HTTP_without_AllowHTTP_fails +=== PAUSE TestValidateExternalURL_CustomTimeout/Long_timeout +=== PAUSE TestIPv4MappedIPv6Detection/IPv4-mapped_private_192.168 +=== RUN TestValidateExternalURL_MultipleOptions/Localhost_without_AllowLocalhost_fails +=== RUN TestIPv4MappedIPv6Detection/IPv4-mapped_metadata +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fc00 +=== PAUSE TestValidateExternalURL_MultipleOptions/Localhost_without_AllowLocalhost_fails +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fd00 +=== CONT TestValidateExternalURL_RealWorldURLs +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fd00 +=== CONT TestValidateExternalURL_PrivateIPBlocking +=== PAUSE TestIPv4MappedIPv6Detection/IPv4-mapped_metadata +=== RUN TestIPv4MappedIPv6Detection/IPv4-mapped_public +=== RUN TestValidateExternalURL_PrivateIPBlocking/Private_IP_10.x.x.x +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fd12 +=== PAUSE TestIPv4MappedIPv6Detection/IPv4-mapped_public +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fd12 +=== RUN TestIPv4MappedIPv6Detection/Regular_IPv6_loopback +=== RUN TestValidateExternalURL_RealWorldURLs/Slack_webhook_format +=== PAUSE TestIPv4MappedIPv6Detection/Regular_IPv6_loopback +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fdff +=== RUN TestIPv4MappedIPv6Detection/Regular_IPv6_link-local +=== PAUSE TestValidateExternalURL_RealWorldURLs/Slack_webhook_format +=== RUN TestValidateExternalURL_RealWorldURLs/Discord_webhook_format +=== PAUSE TestIPv4MappedIPv6Detection/Regular_IPv6_link-local +=== PAUSE TestValidateExternalURL_PrivateIPBlocking/Private_IP_10.x.x.x +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fdff +=== PAUSE TestValidateExternalURL_RealWorldURLs/Discord_webhook_format +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_Google_DNS +=== RUN TestValidateExternalURL_RealWorldURLs/Generic_API_endpoint +=== RUN TestValidateExternalURL_PrivateIPBlocking/Private_IP_192.168.x.x +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_Google_DNS +=== PAUSE TestValidateExternalURL_RealWorldURLs/Generic_API_endpoint +=== PAUSE TestValidateExternalURL_PrivateIPBlocking/Private_IP_192.168.x.x +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_Cloudflare_DNS +=== RUN TestIPv4MappedIPv6Detection/Regular_IPv6_public +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_Cloudflare_DNS +=== RUN TestValidateExternalURL_RealWorldURLs/Localhost_for_testing +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_documentation_range +=== PAUSE TestIPv4MappedIPv6Detection/Regular_IPv6_public +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_documentation_range +=== CONT TestValidateExternalURL_LocalhostHandling +=== PAUSE TestValidateExternalURL_RealWorldURLs/Localhost_for_testing +=== RUN TestValidateExternalURL_LocalhostHandling/Localhost_without_AllowLocalhost +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_public +=== CONT TestIsPrivateIP +=== PAUSE TestValidateExternalURL_LocalhostHandling/Localhost_without_AllowLocalhost +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_public +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_loopback +=== RUN TestIsPrivateIP/10.0.0.0 +=== RUN TestValidateExternalURL_LocalhostHandling/Localhost_with_AllowLocalhost +=== PAUSE TestValidateExternalURL_LocalhostHandling/Localhost_with_AllowLocalhost +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_loopback +=== PAUSE TestIsPrivateIP/10.0.0.0 +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_private +=== RUN TestValidateExternalURL_LocalhostHandling/127.0.0.1_with_AllowLocalhost_and_AllowHTTP +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_private +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_unspecified +=== PAUSE TestValidateExternalURL_LocalhostHandling/127.0.0.1_with_AllowLocalhost_and_AllowHTTP +=== RUN TestIsPrivateIP/10.255.255.255 +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_unspecified +=== RUN TestValidateExternalURL_PrivateIPBlocking/Private_IP_172.16.x.x +=== PAUSE TestValidateExternalURL_PrivateIPBlocking/Private_IP_172.16.x.x +=== PAUSE TestIsPrivateIP/10.255.255.255 +=== RUN TestValidateExternalURL_LocalhostHandling/IPv6_loopback_with_AllowLocalhost +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_multicast +=== PAUSE TestIsPrivateIP_IPv6Comprehensive/IPv6_multicast +=== PAUSE TestValidateExternalURL_LocalhostHandling/IPv6_loopback_with_AllowLocalhost +=== RUN TestValidateExternalURL_PrivateIPBlocking/AWS_Metadata_IP +=== RUN TestIsPrivateIP/172.16.0.0 +=== PAUSE TestIsPrivateIP/172.16.0.0 +=== CONT TestValidateExternalURL_BasicValidation +=== RUN TestValidateExternalURL_BasicValidation/Valid_HTTPS_URL +=== CONT TestAuditEvent_RequiredFields +--- PASS: TestAuditEvent_RequiredFields (0.00s) +=== CONT TestAuditLogger_TimestampFormat +2026/01/10 02:17:13 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:13Z","action":"test","host":"test.com","request_id":"","result":"","resolved_ips":null,"blocked_reason":"","user_id":"","source_ip":""} +--- PASS: TestAuditLogger_TimestampFormat (0.00s) +=== CONT TestGlobalAuditLogger +2026/01/10 02:17:13 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:13Z","action":"url_connectivity_test","host":"test.com","request_id":"req-global","result":"allowed","resolved_ips":null,"blocked_reason":"","user_id":"user-global","source_ip":"192.0.2.10"} +2026/01/10 02:17:13 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:13Z","action":"ssrf_block","host":"blocked.local","request_id":"","result":"blocked","resolved_ips":["127.0.0.1"],"blocked_reason":"loopback","user_id":"user-global","source_ip":"198.51.100.10"} +--- PASS: TestGlobalAuditLogger (0.00s) +=== PAUSE TestValidateExternalURL_PrivateIPBlocking/AWS_Metadata_IP +=== RUN TestIsPrivateIP/172.31.255.255 +=== PAUSE TestIsPrivateIP/172.31.255.255 +=== PAUSE TestValidateExternalURL_BasicValidation/Valid_HTTPS_URL +=== CONT TestAuditLogger_LogURLTest +2026/01/10 02:17:13 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:13Z","action":"url_connectivity_test","host":"example.com","request_id":"req-789","result":"allowed","resolved_ips":null,"blocked_reason":"","user_id":"user456","source_ip":"192.0.2.1"} +--- PASS: TestAuditLogger_LogURLTest (0.00s) +=== RUN TestValidateExternalURL_PrivateIPBlocking/Loopback_without_AllowLocalhost +=== RUN TestIsPrivateIP/192.168.0.0 +=== RUN TestValidateExternalURL_BasicValidation/HTTP_without_AllowHTTP_option +=== PAUSE TestValidateExternalURL_BasicValidation/HTTP_without_AllowHTTP_option +=== CONT TestAuditLogger_LogURLValidation +2026/01/10 02:17:13 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:13Z","action":"url_test","host":"malicious.com","request_id":"req-456","result":"blocked","resolved_ips":["169.254.169.254"],"blocked_reason":"metadata_endpoint","user_id":"attacker","source_ip":"198.51.100.1"} +2026/01/10 02:17:13 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:13Z","action":"test","host":"test.com","request_id":"","result":"","resolved_ips":null,"blocked_reason":"","user_id":"","source_ip":""} +--- PASS: TestAuditLogger_LogURLValidation (0.00s) +=== PAUSE TestValidateExternalURL_PrivateIPBlocking/Loopback_without_AllowLocalhost +=== PAUSE TestIsPrivateIP/192.168.0.0 +=== RUN TestIsPrivateIP/192.168.255.255 +=== PAUSE TestIsPrivateIP/192.168.255.255 +=== RUN TestValidateExternalURL_BasicValidation/HTTP_with_AllowHTTP_option +=== CONT TestAuditLogger_LogSSRFBlock +2026/01/10 02:17:13 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:13Z","action":"ssrf_block","host":"internal.local","request_id":"","result":"blocked","resolved_ips":["10.0.0.1","192.168.1.1"],"blocked_reason":"private_ip","user_id":"user123","source_ip":"203.0.113.5"} +--- PASS: TestAuditLogger_LogSSRFBlock (0.00s) +=== CONT TestValidateExternalURL_Options/WithTimeout +=== CONT TestIsIPv4MappedIPv6_EdgeCases/Mapped_public_IP +=== CONT TestValidateExternalURL_Options/Multiple_options +=== CONT TestIsIPv4MappedIPv6_EdgeCases/All_zeros_except_prefix +=== RUN TestIsPrivateIP/127.0.0.1 +=== PAUSE TestIsPrivateIP/127.0.0.1 +=== PAUSE TestValidateExternalURL_BasicValidation/HTTP_with_AllowHTTP_option +=== RUN TestValidateExternalURL_BasicValidation/Empty_URL +--- PASS: TestValidateExternalURL_Options (0.00s) + --- PASS: TestValidateExternalURL_Options/WithTimeout (0.00s) + --- PASS: TestValidateExternalURL_Options/Multiple_options (0.00s) +=== CONT TestIsIPv4MappedIPv6_EdgeCases/All_ones +=== CONT TestIsIPv4MappedIPv6_EdgeCases/Pure_IPv6_2001:db8 +=== RUN TestIsPrivateIP/127.0.0.2 +=== PAUSE TestValidateExternalURL_BasicValidation/Empty_URL +=== CONT TestIsIPv4MappedIPv6_EdgeCases/Standard_mapped +=== PAUSE TestIsPrivateIP/127.0.0.2 +=== RUN TestValidateExternalURL_BasicValidation/Missing_scheme +=== RUN TestIsPrivateIP/IPv6_loopback +=== CONT TestValidateExternalURL_EdgeCases/Port_with_non-numeric_characters +=== PAUSE TestValidateExternalURL_BasicValidation/Missing_scheme +=== RUN TestValidateExternalURL_BasicValidation/Just_scheme +=== PAUSE TestIsPrivateIP/IPv6_loopback +=== PAUSE TestValidateExternalURL_BasicValidation/Just_scheme +=== CONT TestIsIPv4MappedIPv6_EdgeCases/IPv6_loopback +=== RUN TestIsPrivateIP/169.254.1.1 +=== PAUSE TestIsPrivateIP/169.254.1.1 +=== CONT TestValidateExternalURL_EdgeCases/Hostname_at_252_chars_(just_under_limit) +=== RUN TestIsPrivateIP/AWS_metadata +=== PAUSE TestIsPrivateIP/AWS_metadata +=== RUN TestValidateExternalURL_BasicValidation/FTP_protocol +=== RUN TestIsPrivateIP/0.0.0.0 +=== PAUSE TestValidateExternalURL_BasicValidation/FTP_protocol +--- PASS: TestIsIPv4MappedIPv6_EdgeCases (0.00s) + --- PASS: TestIsIPv4MappedIPv6_EdgeCases/Mapped_public_IP (0.00s) + --- PASS: TestIsIPv4MappedIPv6_EdgeCases/All_zeros_except_prefix (0.00s) + --- PASS: TestIsIPv4MappedIPv6_EdgeCases/All_ones (0.00s) + --- PASS: TestIsIPv4MappedIPv6_EdgeCases/Pure_IPv6_2001:db8 (0.00s) + --- PASS: TestIsIPv4MappedIPv6_EdgeCases/Standard_mapped (0.00s) + --- PASS: TestIsIPv4MappedIPv6_EdgeCases/IPv6_loopback (0.00s) +=== RUN TestValidateExternalURL_BasicValidation/File_protocol +=== PAUSE TestValidateExternalURL_BasicValidation/File_protocol +=== PAUSE TestIsPrivateIP/0.0.0.0 +=== RUN TestValidateExternalURL_BasicValidation/Gopher_protocol +=== CONT TestValidateExternalURL_EdgeCases/Hostname_with_single_dot +=== PAUSE TestValidateExternalURL_BasicValidation/Gopher_protocol +=== RUN TestIsPrivateIP/255.255.255.255 +=== RUN TestValidateExternalURL_BasicValidation/Data_URL +=== PAUSE TestIsPrivateIP/255.255.255.255 +=== PAUSE TestValidateExternalURL_BasicValidation/Data_URL +=== RUN TestValidateExternalURL_BasicValidation/URL_with_credentials +=== RUN TestIsPrivateIP/240.0.0.1 +=== PAUSE TestValidateExternalURL_BasicValidation/URL_with_credentials +=== PAUSE TestIsPrivateIP/240.0.0.1 +=== RUN TestValidateExternalURL_BasicValidation/Valid_with_port +=== RUN TestIsPrivateIP/IPv6_unique_local +=== PAUSE TestValidateExternalURL_BasicValidation/Valid_with_port +=== PAUSE TestIsPrivateIP/IPv6_unique_local +=== RUN TestValidateExternalURL_BasicValidation/Valid_with_path +=== RUN TestIsPrivateIP/IPv6_link-local +=== PAUSE TestValidateExternalURL_BasicValidation/Valid_with_path +=== RUN TestValidateExternalURL_BasicValidation/Valid_with_query +=== PAUSE TestIsPrivateIP/IPv6_link-local +=== RUN TestIsPrivateIP/Google_DNS +=== PAUSE TestValidateExternalURL_BasicValidation/Valid_with_query +=== CONT TestValidateExternalURL_EdgeCases/URL_with_username_only +=== PAUSE TestIsPrivateIP/Google_DNS +=== RUN TestIsPrivateIP/Cloudflare_DNS +=== CONT TestValidateExternalURL_EdgeCases/Port_1024_(first_non-privileged) +=== PAUSE TestIsPrivateIP/Cloudflare_DNS +=== RUN TestIsPrivateIP/Public_IPv6 +=== PAUSE TestIsPrivateIP/Public_IPv6 +=== CONT TestValidateExternalURL_EdgeCases/Triple_dots_in_hostname +=== CONT TestValidateExternalURL_EdgeCases/Port_1_(privileged_but_not_blocked_with_AllowLocalhost) +=== CONT TestValidateExternalURL_EdgeCases/Maximum_valid_port +=== CONT TestValidateExternalURL_EdgeCases/Port_1023_(edge_of_privileged_range) +=== CONT TestValidateExternalURL_HostnameValidation/Hostname_with_double_dots_mid +=== CONT TestValidateExternalURL_HostnameValidation/Extremely_long_hostname_(254_chars) +=== CONT TestParsePort/Valid_port_80 +=== CONT TestParsePort/Negative_port +=== CONT TestParsePort/Port_zero +=== CONT TestParsePort/Non-numeric_port +=== CONT TestParsePort/Empty_port +=== CONT TestParsePort/Valid_port_8080 +=== CONT TestParsePort/Valid_port_65535 +=== CONT TestParsePort/Valid_port_443 +=== CONT TestValidateExternalURL_CloudMetadataDetection/Azure_metadata_service + url_validator_test.go:600: Correctly blocked http://169.254.169.254/metadata/instance with error: connection to private ip addresses is blocked for security (detected IPv4-mapped IPv6: 169.254.169.254) +=== CONT TestValidateExternalURL_CloudMetadataDetection/GCP_metadata_service +=== CONT TestValidateExternalURL_CloudMetadataDetection/AWS_metadata_IPv6 +--- PASS: TestParsePort (0.00s) + --- PASS: TestParsePort/Valid_port_80 (0.00s) + --- PASS: TestParsePort/Negative_port (0.00s) + --- PASS: TestParsePort/Port_zero (0.00s) + --- PASS: TestParsePort/Non-numeric_port (0.00s) + --- PASS: TestParsePort/Valid_port_8080 (0.00s) + --- PASS: TestParsePort/Valid_port_65535 (0.00s) + --- PASS: TestParsePort/Valid_port_443 (0.00s) + --- PASS: TestParsePort/Empty_port (0.00s) +=== NAME TestValidateExternalURL_CloudMetadataDetection/AWS_metadata_IPv6 + url_validator_test.go:600: Correctly blocked http://[fd00:ec2::254]/latest/meta-data/ with error: connection to private ip addresses is blocked for security (detected: fd00::) +=== CONT TestValidateExternalURL_CloudMetadataDetection/AWS_metadata_service + url_validator_test.go:600: Correctly blocked http://169.254.169.254/latest/meta-data/ with error: connection to private ip addresses is blocked for security (detected IPv4-mapped IPv6: 169.254.169.254) +=== CONT TestSanitizeIPForError/Private_IPv4_192.168 +=== CONT TestValidateExternalURL_PortValidation/Port_80_(standard_HTTP)_-_should_allow +=== NAME TestValidateExternalURL_CloudMetadataDetection/GCP_metadata_service + url_validator_test.go:600: Correctly blocked http://metadata.google.internal/computeMetadata/v1/ with error: dns resolution failed for metadata.google.internal: lookup metadata.google.internal on 127.0.0.53:53: no such host +=== CONT TestValidateExternalURL_PortValidation/Port_0_-_should_block +=== CONT TestValidateExternalURL_PortValidation/Port_22_with_AllowLocalhost_-_should_allow +=== CONT TestValidateExternalURL_PortValidation/Port_65536_-_should_block +--- PASS: TestValidateExternalURL_CloudMetadataDetection (0.00s) + --- PASS: TestValidateExternalURL_CloudMetadataDetection/Azure_metadata_service (0.00s) + --- PASS: TestValidateExternalURL_CloudMetadataDetection/AWS_metadata_IPv6 (0.00s) + --- PASS: TestValidateExternalURL_CloudMetadataDetection/AWS_metadata_service (0.00s) + --- PASS: TestValidateExternalURL_CloudMetadataDetection/GCP_metadata_service (0.00s) +=== CONT TestValidateExternalURL_PortValidation/Port_8080_(non-privileged)_-_should_allow +=== CONT TestValidateExternalURL_PortValidation/Port_3306_(MySQL)_-_should_block_if_<_1024 +=== CONT TestValidateExternalURL_PortValidation/Port_22_(SSH)_-_should_block +=== CONT TestValidateExternalURL_PortValidation/Port_443_(standard_HTTPS)_-_should_allow +=== CONT TestSanitizeIPForError/Loopback_IPv4 +--- PASS: TestValidateExternalURL_EdgeCases (0.00s) + --- PASS: TestValidateExternalURL_EdgeCases/Port_with_non-numeric_characters (0.00s) + --- PASS: TestValidateExternalURL_EdgeCases/URL_with_username_only (0.00s) + --- PASS: TestValidateExternalURL_EdgeCases/Hostname_at_252_chars_(just_under_limit) (0.00s) + --- PASS: TestValidateExternalURL_EdgeCases/Triple_dots_in_hostname (0.00s) + --- PASS: TestValidateExternalURL_EdgeCases/Port_1_(privileged_but_not_blocked_with_AllowLocalhost) (0.00s) + --- PASS: TestValidateExternalURL_EdgeCases/Port_1023_(edge_of_privileged_range) (0.00s) + --- PASS: TestValidateExternalURL_EdgeCases/Hostname_with_single_dot (0.00s) + --- PASS: TestValidateExternalURL_EdgeCases/Maximum_valid_port (0.01s) + --- PASS: TestValidateExternalURL_EdgeCases/Port_1024_(first_non-privileged) (0.01s) +=== CONT TestValidateExternalURL_PortValidation/Port_25_(SMTP)_-_should_block +=== CONT TestSanitizeIPForError/Invalid_IP +=== CONT TestSanitizeIPForError/IPv6_unique_local +=== CONT TestSanitizeIPForError/IPv6_link-local +=== CONT TestSanitizeIPForError/Private_IPv4_172.16 +=== CONT TestSanitizeIPForError/Private_IPv4_10.x +=== CONT TestSanitizeIPForError/Metadata_IPv4 +=== CONT TestValidateExternalURL_MultipleIPsAllPrivate/IP_192.168.1.1 +=== CONT TestValidateExternalURL_MultipleIPsAllPrivate/IP_172.16.0.1 +=== CONT TestValidateExternalURL_MultipleIPsAllPrivate/IP_10.0.0.1 +=== CONT TestValidateExternalURL_CustomTimeout/Very_short_timeout +--- PASS: TestSanitizeIPForError (0.00s) + --- PASS: TestSanitizeIPForError/Private_IPv4_192.168 (0.00s) + --- PASS: TestSanitizeIPForError/Loopback_IPv4 (0.00s) + --- PASS: TestSanitizeIPForError/IPv6_unique_local (0.00s) + --- PASS: TestSanitizeIPForError/IPv6_link-local (0.00s) + --- PASS: TestSanitizeIPForError/Invalid_IP (0.00s) + --- PASS: TestSanitizeIPForError/Private_IPv4_172.16 (0.00s) + --- PASS: TestSanitizeIPForError/Metadata_IPv4 (0.00s) + --- PASS: TestSanitizeIPForError/Private_IPv4_10.x (0.00s) +=== NAME TestValidateExternalURL_CustomTimeout/Very_short_timeout + url_validator_test.go:499: Warning: timeout may not be strictly enforced (elapsed: 91.891µs, timeout: 1ns) + url_validator_test.go:504: URL: https://example.com, Timeout: 1ns, Elapsed: 91.891µs, Error: dns resolution failed for example.com: lookup example.com: i/o timeout +=== CONT TestValidateExternalURL_HostnameValidation/Hostname_with_double_dots +--- PASS: TestValidateExternalURL_HostnameValidation (0.00s) + --- PASS: TestValidateExternalURL_HostnameValidation/Hostname_with_double_dots_mid (0.00s) + --- PASS: TestValidateExternalURL_HostnameValidation/Extremely_long_hostname_(254_chars) (0.00s) + --- PASS: TestValidateExternalURL_HostnameValidation/Hostname_with_double_dots (0.00s) +--- PASS: TestValidateExternalURL_MultipleIPsAllPrivate (0.00s) + --- PASS: TestValidateExternalURL_MultipleIPsAllPrivate/IP_192.168.1.1 (0.00s) + --- PASS: TestValidateExternalURL_MultipleIPsAllPrivate/IP_172.16.0.1 (0.00s) + --- PASS: TestValidateExternalURL_MultipleIPsAllPrivate/IP_10.0.0.1 (0.00s) +=== CONT TestValidateExternalURL_CustomTimeout/Long_timeout +=== CONT TestValidateExternalURL_CustomTimeout/Standard_timeout +=== CONT TestValidateExternalURL_MultipleOptions/All_options_enabled +=== CONT TestValidateExternalURL_MultipleOptions/Localhost_without_AllowLocalhost_fails +=== CONT TestValidateExternalURL_MultipleOptions/HTTP_without_AllowHTTP_fails +=== CONT TestValidateExternalURL_MultipleOptions/Custom_timeout_with_HTTPS +=== CONT TestIPv4MappedIPv6Detection/IPv4-mapped_loopback +=== CONT TestIPv4MappedIPv6Detection/Regular_IPv6_link-local +=== CONT TestIPv4MappedIPv6Detection/Regular_IPv6_public +=== CONT TestIPv4MappedIPv6Detection/Regular_IPv6_loopback +--- PASS: TestValidateExternalURL_PortValidation (0.00s) + --- PASS: TestValidateExternalURL_PortValidation/Port_0_-_should_block (0.00s) + --- PASS: TestValidateExternalURL_PortValidation/Port_22_with_AllowLocalhost_-_should_allow (0.00s) + --- PASS: TestValidateExternalURL_PortValidation/Port_65536_-_should_block (0.00s) + --- PASS: TestValidateExternalURL_PortValidation/Port_80_(standard_HTTP)_-_should_allow (0.01s) + --- PASS: TestValidateExternalURL_PortValidation/Port_22_(SSH)_-_should_block (0.00s) + --- PASS: TestValidateExternalURL_PortValidation/Port_8080_(non-privileged)_-_should_allow (0.00s) + --- PASS: TestValidateExternalURL_PortValidation/Port_25_(SMTP)_-_should_block (0.00s) + --- PASS: TestValidateExternalURL_PortValidation/Port_3306_(MySQL)_-_should_block_if_<_1024 (0.00s) + --- PASS: TestValidateExternalURL_PortValidation/Port_443_(standard_HTTPS)_-_should_allow (0.00s) +=== CONT TestIPv4MappedIPv6Detection/IPv4-mapped_public +=== CONT TestIPv4MappedIPv6Detection/IPv4-mapped_private_10.x +=== CONT TestIPv4MappedIPv6Detection/IPv4-mapped_private_192.168 +--- PASS: TestValidateExternalURL_MultipleOptions (0.00s) + --- PASS: TestValidateExternalURL_MultipleOptions/All_options_enabled (0.00s) + --- PASS: TestValidateExternalURL_MultipleOptions/Localhost_without_AllowLocalhost_fails (0.00s) + --- PASS: TestValidateExternalURL_MultipleOptions/HTTP_without_AllowHTTP_fails (0.00s) + --- PASS: TestValidateExternalURL_MultipleOptions/Custom_timeout_with_HTTPS (0.00s) +=== CONT TestIPv4MappedIPv6Detection/IPv4-mapped_metadata +=== CONT TestValidateExternalURL_RealWorldURLs/Discord_webhook_format +=== CONT TestValidateExternalURL_RealWorldURLs/Generic_API_endpoint +--- PASS: TestIPv4MappedIPv6Detection (0.00s) + --- PASS: TestIPv4MappedIPv6Detection/IPv4-mapped_loopback (0.00s) + --- PASS: TestIPv4MappedIPv6Detection/Regular_IPv6_link-local (0.00s) + --- PASS: TestIPv4MappedIPv6Detection/Regular_IPv6_public (0.00s) + --- PASS: TestIPv4MappedIPv6Detection/Regular_IPv6_loopback (0.00s) + --- PASS: TestIPv4MappedIPv6Detection/IPv4-mapped_public (0.00s) + --- PASS: TestIPv4MappedIPv6Detection/IPv4-mapped_private_10.x (0.00s) + --- PASS: TestIPv4MappedIPv6Detection/IPv4-mapped_private_192.168 (0.00s) + --- PASS: TestIPv4MappedIPv6Detection/IPv4-mapped_metadata (0.00s) +=== CONT TestValidateExternalURL_RealWorldURLs/Localhost_for_testing +=== CONT TestValidateExternalURL_RealWorldURLs/Slack_webhook_format +=== NAME TestValidateExternalURL_CustomTimeout/Standard_timeout + url_validator_test.go:504: URL: https://api.github.com, Timeout: 3s, Elapsed: 11.316478ms, Error: +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback +=== CONT TestValidateExternalURL_LocalhostHandling/Localhost_without_AllowLocalhost +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_multicast +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_unspecified +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_private +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_loopback +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_public +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_Cloudflare_DNS +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fdff +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_Google_DNS +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fd12 +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fd00 +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_documentation_range +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fc00 +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_mid +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_start +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback_expanded +=== CONT TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_end +=== CONT TestValidateExternalURL_LocalhostHandling/IPv6_loopback_with_AllowLocalhost +=== CONT TestValidateExternalURL_LocalhostHandling/Localhost_with_AllowLocalhost +=== CONT TestValidateExternalURL_LocalhostHandling/127.0.0.1_with_AllowLocalhost_and_AllowHTTP +--- PASS: TestIsPrivateIP_IPv6Comprehensive (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_multicast (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_unspecified (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_loopback (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_private (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_Cloudflare_DNS (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fdff (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv4-mapped_public (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_Google_DNS (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fd12 (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fd00 (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_documentation_range (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fc00 (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_mid (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_start (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback_expanded (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_end (0.00s) +=== CONT TestValidateExternalURL_PrivateIPBlocking/Private_IP_192.168.x.x +=== CONT TestValidateExternalURL_PrivateIPBlocking/AWS_Metadata_IP +=== CONT TestValidateExternalURL_PrivateIPBlocking/Private_IP_10.x.x.x +=== CONT TestValidateExternalURL_PrivateIPBlocking/Loopback_without_AllowLocalhost +=== CONT TestValidateExternalURL_PrivateIPBlocking/Private_IP_172.16.x.x +=== CONT TestValidateExternalURL_BasicValidation/HTTP_without_AllowHTTP_option +=== CONT TestValidateExternalURL_BasicValidation/Valid_with_query +=== CONT TestValidateExternalURL_BasicValidation/Valid_with_port +--- PASS: TestValidateExternalURL_LocalhostHandling (0.00s) + --- PASS: TestValidateExternalURL_LocalhostHandling/Localhost_without_AllowLocalhost (0.00s) + --- PASS: TestValidateExternalURL_LocalhostHandling/IPv6_loopback_with_AllowLocalhost (0.00s) + --- PASS: TestValidateExternalURL_LocalhostHandling/Localhost_with_AllowLocalhost (0.00s) + --- PASS: TestValidateExternalURL_LocalhostHandling/127.0.0.1_with_AllowLocalhost_and_AllowHTTP (0.00s) +--- PASS: TestValidateExternalURL_PrivateIPBlocking (0.00s) + --- PASS: TestValidateExternalURL_PrivateIPBlocking/Private_IP_192.168.x.x (0.00s) + --- PASS: TestValidateExternalURL_PrivateIPBlocking/AWS_Metadata_IP (0.00s) + --- PASS: TestValidateExternalURL_PrivateIPBlocking/Private_IP_10.x.x.x (0.00s) + --- PASS: TestValidateExternalURL_PrivateIPBlocking/Private_IP_172.16.x.x (0.00s) + --- PASS: TestValidateExternalURL_PrivateIPBlocking/Loopback_without_AllowLocalhost (0.00s) +=== CONT TestValidateExternalURL_BasicValidation/URL_with_credentials +=== CONT TestValidateExternalURL_BasicValidation/Valid_with_path +--- PASS: TestValidateExternalURL_RealWorldURLs (0.00s) + --- PASS: TestValidateExternalURL_RealWorldURLs/Generic_API_endpoint (0.01s) + --- PASS: TestValidateExternalURL_RealWorldURLs/Localhost_for_testing (0.00s) + --- PASS: TestValidateExternalURL_RealWorldURLs/Discord_webhook_format (0.01s) + --- PASS: TestValidateExternalURL_RealWorldURLs/Slack_webhook_format (0.01s) +=== NAME TestValidateExternalURL_BasicValidation/Valid_with_query + url_validator_test.go:133: Note: DNS resolution failed for https://api.example.com/webhook?token=abc123 (expected in test environment) +=== NAME TestValidateExternalURL_BasicValidation/Valid_with_path + url_validator_test.go:133: Note: DNS resolution failed for https://api.example.com/path/to/webhook (expected in test environment) +=== CONT TestValidateExternalURL_BasicValidation/File_protocol +=== NAME TestValidateExternalURL_BasicValidation/Valid_with_port + url_validator_test.go:133: Note: DNS resolution failed for https://api.example.com:8080/webhook (expected in test environment) +=== CONT TestValidateExternalURL_BasicValidation/FTP_protocol +=== CONT TestValidateExternalURL_BasicValidation/Just_scheme +=== CONT TestValidateExternalURL_BasicValidation/Missing_scheme +=== CONT TestValidateExternalURL_BasicValidation/Empty_URL +=== CONT TestValidateExternalURL_BasicValidation/HTTP_with_AllowHTTP_option +=== CONT TestValidateExternalURL_BasicValidation/Gopher_protocol +=== CONT TestValidateExternalURL_BasicValidation/Valid_HTTPS_URL +=== CONT TestValidateExternalURL_BasicValidation/Data_URL +=== CONT TestIsPrivateIP/10.0.0.0 +=== CONT TestIsPrivateIP/Public_IPv6 +=== CONT TestIsPrivateIP/Cloudflare_DNS +=== CONT TestIsPrivateIP/Google_DNS +=== CONT TestIsPrivateIP/IPv6_link-local +=== CONT TestIsPrivateIP/IPv6_unique_local +=== CONT TestIsPrivateIP/240.0.0.1 +=== CONT TestIsPrivateIP/255.255.255.255 +=== CONT TestIsPrivateIP/0.0.0.0 +=== CONT TestIsPrivateIP/169.254.1.1 +=== CONT TestIsPrivateIP/AWS_metadata +=== CONT TestIsPrivateIP/127.0.0.2 +=== CONT TestIsPrivateIP/127.0.0.1 +=== CONT TestIsPrivateIP/IPv6_loopback +=== CONT TestIsPrivateIP/192.168.0.0 +=== CONT TestIsPrivateIP/172.31.255.255 +=== CONT TestIsPrivateIP/192.168.255.255 +=== CONT TestIsPrivateIP/10.255.255.255 +=== CONT TestIsPrivateIP/172.16.0.0 +--- PASS: TestIsPrivateIP (0.01s) + --- PASS: TestIsPrivateIP/10.0.0.0 (0.00s) + --- PASS: TestIsPrivateIP/Public_IPv6 (0.00s) + --- PASS: TestIsPrivateIP/Cloudflare_DNS (0.00s) + --- PASS: TestIsPrivateIP/Google_DNS (0.00s) + --- PASS: TestIsPrivateIP/IPv6_link-local (0.00s) + --- PASS: TestIsPrivateIP/IPv6_unique_local (0.00s) + --- PASS: TestIsPrivateIP/240.0.0.1 (0.00s) + --- PASS: TestIsPrivateIP/255.255.255.255 (0.00s) + --- PASS: TestIsPrivateIP/0.0.0.0 (0.00s) + --- PASS: TestIsPrivateIP/169.254.1.1 (0.00s) + --- PASS: TestIsPrivateIP/AWS_metadata (0.00s) + --- PASS: TestIsPrivateIP/127.0.0.2 (0.00s) + --- PASS: TestIsPrivateIP/127.0.0.1 (0.00s) + --- PASS: TestIsPrivateIP/IPv6_loopback (0.00s) + --- PASS: TestIsPrivateIP/192.168.0.0 (0.00s) + --- PASS: TestIsPrivateIP/172.31.255.255 (0.00s) + --- PASS: TestIsPrivateIP/192.168.255.255 (0.00s) + --- PASS: TestIsPrivateIP/10.255.255.255 (0.00s) + --- PASS: TestIsPrivateIP/172.16.0.0 (0.00s) +=== NAME TestValidateExternalURL_BasicValidation/Valid_HTTPS_URL + url_validator_test.go:133: Note: DNS resolution failed for https://api.example.com/webhook (expected in test environment) +=== NAME TestValidateExternalURL_BasicValidation/HTTP_with_AllowHTTP_option + url_validator_test.go:133: Note: DNS resolution failed for http://api.example.com/webhook (expected in test environment) +--- PASS: TestValidateExternalURL_BasicValidation (0.00s) + --- PASS: TestValidateExternalURL_BasicValidation/HTTP_without_AllowHTTP_option (0.00s) + --- PASS: TestValidateExternalURL_BasicValidation/URL_with_credentials (0.00s) + --- PASS: TestValidateExternalURL_BasicValidation/Valid_with_query (0.03s) + --- PASS: TestValidateExternalURL_BasicValidation/Valid_with_path (0.02s) + --- PASS: TestValidateExternalURL_BasicValidation/File_protocol (0.00s) + --- PASS: TestValidateExternalURL_BasicValidation/Valid_with_port (0.03s) + --- PASS: TestValidateExternalURL_BasicValidation/FTP_protocol (0.00s) + --- PASS: TestValidateExternalURL_BasicValidation/Just_scheme (0.00s) + --- PASS: TestValidateExternalURL_BasicValidation/Missing_scheme (0.00s) + --- PASS: TestValidateExternalURL_BasicValidation/Empty_URL (0.00s) + --- PASS: TestValidateExternalURL_BasicValidation/Gopher_protocol (0.00s) + --- PASS: TestValidateExternalURL_BasicValidation/Data_URL (0.00s) + --- PASS: TestValidateExternalURL_BasicValidation/Valid_HTTPS_URL (0.00s) + --- PASS: TestValidateExternalURL_BasicValidation/HTTP_with_AllowHTTP_option (0.00s) +=== NAME TestValidateExternalURL_CustomTimeout/Long_timeout + url_validator_test.go:504: URL: https://slow-dns-server.example, Timeout: 30s, Elapsed: 98.181784ms, Error: dns resolution failed for slow-dns-server.example: lookup slow-dns-server.example on 127.0.0.53:53: no such host +--- PASS: TestValidateExternalURL_CustomTimeout (0.00s) + --- PASS: TestValidateExternalURL_CustomTimeout/Very_short_timeout (0.00s) + --- PASS: TestValidateExternalURL_CustomTimeout/Standard_timeout (0.01s) + --- PASS: TestValidateExternalURL_CustomTimeout/Long_timeout (0.10s) +PASS +coverage: 95.7% of statements +ok github.com/Wikid82/charon/backend/internal/security (cached) coverage: 95.7% of statements +=== RUN TestNewRouter +[GIN] 2026/01/10 - 02:17:15 | 200 | 16.260065ms | | GET "/" +[GIN] 2026/01/10 - 02:17:15 | 404 | 168.03µs | | GET "/api/this-route-does-not-exist" +--- PASS: TestNewRouter (0.02s) +PASS +coverage: 93.3% of statements +ok github.com/Wikid82/charon/backend/internal/server (cached) coverage: 93.3% of statements +=== RUN TestAccessListService_Create +=== RUN TestAccessListService_Create/create_whitelist_with_valid_IP_rules +=== RUN TestAccessListService_Create/create_geo_whitelist_with_valid_country_codes +=== RUN TestAccessListService_Create/create_local_network_only_ACL +=== RUN TestAccessListService_Create/fail_with_empty_name +=== RUN TestAccessListService_Create/fail_with_invalid_type +=== RUN TestAccessListService_Create/fail_with_invalid_IP_address +=== RUN TestAccessListService_Create/fail_geo-blocking_without_country_codes +=== RUN TestAccessListService_Create/fail_with_invalid_country_code +--- PASS: TestAccessListService_Create (0.03s) + --- PASS: TestAccessListService_Create/create_whitelist_with_valid_IP_rules (0.00s) + --- PASS: TestAccessListService_Create/create_geo_whitelist_with_valid_country_codes (0.00s) + --- PASS: TestAccessListService_Create/create_local_network_only_ACL (0.00s) + --- PASS: TestAccessListService_Create/fail_with_empty_name (0.00s) + --- PASS: TestAccessListService_Create/fail_with_invalid_type (0.00s) + --- PASS: TestAccessListService_Create/fail_with_invalid_IP_address (0.00s) + --- PASS: TestAccessListService_Create/fail_geo-blocking_without_country_codes (0.00s) + --- PASS: TestAccessListService_Create/fail_with_invalid_country_code (0.00s) +=== RUN TestAccessListService_GetByID +=== RUN TestAccessListService_GetByID/get_existing_ACL +=== RUN TestAccessListService_GetByID/get_non-existent_ACL + +2026/01/10 02:17:38 /projects/Charon/backend/internal/services/access_list_service.go:105 record not found +[0.089ms] [rows:0] SELECT * FROM `access_lists` WHERE `access_lists`.`id` = 99999 ORDER BY `access_lists`.`id` LIMIT 1 +--- PASS: TestAccessListService_GetByID (0.02s) + --- PASS: TestAccessListService_GetByID/get_existing_ACL (0.00s) + --- PASS: TestAccessListService_GetByID/get_non-existent_ACL (0.00s) +=== RUN TestAccessListService_GetByUUID +=== RUN TestAccessListService_GetByUUID/get_existing_ACL_by_UUID +=== RUN TestAccessListService_GetByUUID/get_non-existent_ACL_by_UUID + +2026/01/10 02:17:38 /projects/Charon/backend/internal/services/access_list_service.go:117 record not found +[0.099ms] [rows:0] SELECT * FROM `access_lists` WHERE uuid = "non-existent-uuid" ORDER BY `access_lists`.`id` LIMIT 1 +--- PASS: TestAccessListService_GetByUUID (0.02s) + --- PASS: TestAccessListService_GetByUUID/get_existing_ACL_by_UUID (0.00s) + --- PASS: TestAccessListService_GetByUUID/get_non-existent_ACL_by_UUID (0.00s) +=== RUN TestAccessListService_List +=== RUN TestAccessListService_List/list_all_ACLs +--- PASS: TestAccessListService_List (0.02s) + --- PASS: TestAccessListService_List/list_all_ACLs (0.00s) +=== RUN TestAccessListService_Update +=== RUN TestAccessListService_Update/update_successfully +=== RUN TestAccessListService_Update/fail_update_on_non-existent_ACL + +2026/01/10 02:17:38 /projects/Charon/backend/internal/services/access_list_service.go:105 record not found +[0.093ms] [rows:0] SELECT * FROM `access_lists` WHERE `access_lists`.`id` = 99999 ORDER BY `access_lists`.`id` LIMIT 1 +=== RUN TestAccessListService_Update/fail_update_with_invalid_data +--- PASS: TestAccessListService_Update (0.03s) + --- PASS: TestAccessListService_Update/update_successfully (0.00s) + --- PASS: TestAccessListService_Update/fail_update_on_non-existent_ACL (0.00s) + --- PASS: TestAccessListService_Update/fail_update_with_invalid_data (0.00s) +=== RUN TestAccessListService_Delete +=== RUN TestAccessListService_Delete/delete_successfully + +2026/01/10 02:17:38 /projects/Charon/backend/internal/services/access_list_service.go:105 record not found +[0.226ms] [rows:0] SELECT * FROM `access_lists` WHERE `access_lists`.`id` = 1 ORDER BY `access_lists`.`id` LIMIT 1 +=== RUN TestAccessListService_Delete/fail_delete_non-existent_ACL +=== RUN TestAccessListService_Delete/fail_delete_ACL_in_use +--- PASS: TestAccessListService_Delete (0.03s) + --- PASS: TestAccessListService_Delete/delete_successfully (0.00s) + --- PASS: TestAccessListService_Delete/fail_delete_non-existent_ACL (0.00s) + --- PASS: TestAccessListService_Delete/fail_delete_ACL_in_use (0.00s) +=== RUN TestAccessListService_TestIP +=== RUN TestAccessListService_TestIP/whitelist_allows_matching_IP +=== RUN TestAccessListService_TestIP/whitelist_blocks_non-matching_IP +=== RUN TestAccessListService_TestIP/blacklist_blocks_matching_IP +=== RUN TestAccessListService_TestIP/blacklist_allows_non-matching_IP +=== RUN TestAccessListService_TestIP/local_network_only_allows_RFC1918 +=== RUN TestAccessListService_TestIP/disabled_ACL_allows_all +=== RUN TestAccessListService_TestIP/fail_with_invalid_IP +--- PASS: TestAccessListService_TestIP (0.03s) + --- PASS: TestAccessListService_TestIP/whitelist_allows_matching_IP (0.00s) + --- PASS: TestAccessListService_TestIP/whitelist_blocks_non-matching_IP (0.00s) + --- PASS: TestAccessListService_TestIP/blacklist_blocks_matching_IP (0.00s) + --- PASS: TestAccessListService_TestIP/blacklist_allows_non-matching_IP (0.00s) + --- PASS: TestAccessListService_TestIP/local_network_only_allows_RFC1918 (0.00s) + --- PASS: TestAccessListService_TestIP/disabled_ACL_allows_all (0.00s) + --- PASS: TestAccessListService_TestIP/fail_with_invalid_IP (0.00s) +=== RUN TestAccessListService_GetTemplates +--- PASS: TestAccessListService_GetTemplates (0.02s) +=== RUN TestAccessListService_Validation +=== RUN TestAccessListService_Validation/validate_CIDR_formats +=== RUN TestAccessListService_Validation/validate_country_codes +=== RUN TestAccessListService_Validation/validate_types +--- PASS: TestAccessListService_Validation (0.02s) + --- PASS: TestAccessListService_Validation/validate_CIDR_formats (0.00s) + --- PASS: TestAccessListService_Validation/validate_country_codes (0.00s) + --- PASS: TestAccessListService_Validation/validate_types (0.00s) +=== RUN TestIPMatchesCIDR_Helper +=== RUN TestIPMatchesCIDR_Helper/IPv4_in_subnet +=== RUN TestIPMatchesCIDR_Helper/IPv4_not_in_subnet +=== RUN TestIPMatchesCIDR_Helper/IPv4_single_IP_match +=== RUN TestIPMatchesCIDR_Helper/IPv4_single_IP_no_match +=== RUN TestIPMatchesCIDR_Helper/IPv6_in_subnet +=== RUN TestIPMatchesCIDR_Helper/IPv6_not_in_subnet +=== RUN TestIPMatchesCIDR_Helper/Invalid_CIDR +--- PASS: TestIPMatchesCIDR_Helper (0.02s) + --- PASS: TestIPMatchesCIDR_Helper/IPv4_in_subnet (0.00s) + --- PASS: TestIPMatchesCIDR_Helper/IPv4_not_in_subnet (0.00s) + --- PASS: TestIPMatchesCIDR_Helper/IPv4_single_IP_match (0.00s) + --- PASS: TestIPMatchesCIDR_Helper/IPv4_single_IP_no_match (0.00s) + --- PASS: TestIPMatchesCIDR_Helper/IPv6_in_subnet (0.00s) + --- PASS: TestIPMatchesCIDR_Helper/IPv6_not_in_subnet (0.00s) + --- PASS: TestIPMatchesCIDR_Helper/Invalid_CIDR (0.00s) +=== RUN TestIsPrivateIP_Helper +=== RUN TestIsPrivateIP_Helper/Private_10.x.x.x +=== RUN TestIsPrivateIP_Helper/Private_172.16.x.x +=== RUN TestIsPrivateIP_Helper/Private_192.168.x.x +=== RUN TestIsPrivateIP_Helper/Private_127.0.0.1 +=== RUN TestIsPrivateIP_Helper/Private_::1 +=== RUN TestIsPrivateIP_Helper/Private_fc00::/7 +=== RUN TestIsPrivateIP_Helper/Public_8.8.8.8 +=== RUN TestIsPrivateIP_Helper/Public_1.1.1.1 +=== RUN TestIsPrivateIP_Helper/Public_IPv6 +--- PASS: TestIsPrivateIP_Helper (0.02s) + --- PASS: TestIsPrivateIP_Helper/Private_10.x.x.x (0.00s) + --- PASS: TestIsPrivateIP_Helper/Private_172.16.x.x (0.00s) + --- PASS: TestIsPrivateIP_Helper/Private_192.168.x.x (0.00s) + --- PASS: TestIsPrivateIP_Helper/Private_127.0.0.1 (0.00s) + --- PASS: TestIsPrivateIP_Helper/Private_::1 (0.00s) + --- PASS: TestIsPrivateIP_Helper/Private_fc00::/7 (0.00s) + --- PASS: TestIsPrivateIP_Helper/Public_8.8.8.8 (0.00s) + --- PASS: TestIsPrivateIP_Helper/Public_1.1.1.1 (0.00s) + --- PASS: TestIsPrivateIP_Helper/Public_IPv6 (0.00s) +=== RUN TestAccessListService_ListFunction +--- PASS: TestAccessListService_ListFunction (0.02s) +=== RUN TestAccessListService_SetGeoIPService +--- PASS: TestAccessListService_SetGeoIPService (0.01s) +=== RUN TestAccessListService_GeoACL_NoGeoIPService +=== RUN TestAccessListService_GeoACL_NoGeoIPService/geo_whitelist_without_GeoIP_service_allows_traffic +=== RUN TestAccessListService_GeoACL_NoGeoIPService/geo_blacklist_without_GeoIP_service_allows_traffic +--- PASS: TestAccessListService_GeoACL_NoGeoIPService (0.02s) + --- PASS: TestAccessListService_GeoACL_NoGeoIPService/geo_whitelist_without_GeoIP_service_allows_traffic (0.00s) + --- PASS: TestAccessListService_GeoACL_NoGeoIPService/geo_blacklist_without_GeoIP_service_allows_traffic (0.00s) +=== RUN TestAccessListService_ParseCountryCodes +=== RUN TestAccessListService_ParseCountryCodes/parse_single_code +=== RUN TestAccessListService_ParseCountryCodes/parse_multiple_codes +=== RUN TestAccessListService_ParseCountryCodes/parse_with_spaces +=== RUN TestAccessListService_ParseCountryCodes/parse_with_lowercase +=== RUN TestAccessListService_ParseCountryCodes/parse_empty_string +=== RUN TestAccessListService_ParseCountryCodes/parse_with_empty_entries +--- PASS: TestAccessListService_ParseCountryCodes (0.02s) + --- PASS: TestAccessListService_ParseCountryCodes/parse_single_code (0.00s) + --- PASS: TestAccessListService_ParseCountryCodes/parse_multiple_codes (0.00s) + --- PASS: TestAccessListService_ParseCountryCodes/parse_with_spaces (0.00s) + --- PASS: TestAccessListService_ParseCountryCodes/parse_with_lowercase (0.00s) + --- PASS: TestAccessListService_ParseCountryCodes/parse_empty_string (0.00s) + --- PASS: TestAccessListService_ParseCountryCodes/parse_with_empty_entries (0.00s) +=== RUN TestAuthService_Register +--- PASS: TestAuthService_Register (1.62s) +=== RUN TestAuthService_Login +--- PASS: TestAuthService_Login (5.20s) +=== RUN TestAuthService_ChangePassword + +2026/01/10 02:17:49 /projects/Charon/backend/internal/services/auth_service.go:113 record not found +[0.178ms] [rows:0] SELECT * FROM `users` WHERE `users`.`id` = 999 ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestAuthService_ChangePassword (4.53s) +=== RUN TestAuthService_ValidateToken +--- PASS: TestAuthService_ValidateToken (1.59s) +=== RUN TestAuthService_GetUserByID + +2026/01/10 02:17:52 /projects/Charon/backend/internal/services/auth_service.go:147 record not found +[0.064ms] [rows:0] SELECT * FROM `users` WHERE `users`.`id` = 999 ORDER BY `users`.`id` LIMIT 1 +--- PASS: TestAuthService_GetUserByID (0.81s) +=== RUN TestBackupService_GetAvailableSpace +=== PAUSE TestBackupService_GetAvailableSpace +=== RUN TestBackupService_CreateAndList +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestBackupService_CreateAndList (0.00s) +=== RUN TestBackupService_Restore_ZipSlip +--- PASS: TestBackupService_Restore_ZipSlip (0.00s) +=== RUN TestBackupService_PathTraversal +--- PASS: TestBackupService_PathTraversal (0.00s) +=== RUN TestBackupService_RunScheduledBackup +time="2026-01-10T02:17:52Z" level=info msg="Starting scheduled backup" +time="2026-01-10T02:17:52Z" level=warning msg="Warning: could not backup caddy dir" error="lstat /tmp/TestBackupService_RunScheduledBackup1537072281/001/data/caddy: no such file or directory" +time="2026-01-10T02:17:52Z" level=info msg="Scheduled backup created" backup=backup_2026-01-10_02-17-52.zip +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestBackupService_RunScheduledBackup (0.00s) +=== RUN TestBackupService_CreateBackup_Errors +=== RUN TestBackupService_CreateBackup_Errors/missing_database_file +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +=== RUN TestBackupService_CreateBackup_Errors/cannot_create_backup_directory +--- PASS: TestBackupService_CreateBackup_Errors (0.00s) + --- PASS: TestBackupService_CreateBackup_Errors/missing_database_file (0.00s) + --- PASS: TestBackupService_CreateBackup_Errors/cannot_create_backup_directory (0.00s) +=== RUN TestBackupService_RestoreBackup_Errors +=== RUN TestBackupService_RestoreBackup_Errors/non-existent_backup +=== RUN TestBackupService_RestoreBackup_Errors/invalid_zip_file +--- PASS: TestBackupService_RestoreBackup_Errors (0.00s) + --- PASS: TestBackupService_RestoreBackup_Errors/non-existent_backup (0.00s) + --- PASS: TestBackupService_RestoreBackup_Errors/invalid_zip_file (0.00s) +=== RUN TestBackupService_ListBackups_EmptyDir +--- PASS: TestBackupService_ListBackups_EmptyDir (0.00s) +=== RUN TestBackupService_ListBackups_MissingDir +--- PASS: TestBackupService_ListBackups_MissingDir (0.00s) +=== RUN TestBackupService_CleanupOldBackups +=== RUN TestBackupService_CleanupOldBackups/deletes_backups_exceeding_retention +=== RUN TestBackupService_CleanupOldBackups/keeps_all_when_under_retention +=== RUN TestBackupService_CleanupOldBackups/minimum_retention_of_1 +=== RUN TestBackupService_CleanupOldBackups/empty_backup_directory +--- PASS: TestBackupService_CleanupOldBackups (0.00s) + --- PASS: TestBackupService_CleanupOldBackups/deletes_backups_exceeding_retention (0.00s) + --- PASS: TestBackupService_CleanupOldBackups/keeps_all_when_under_retention (0.00s) + --- PASS: TestBackupService_CleanupOldBackups/minimum_retention_of_1 (0.00s) + --- PASS: TestBackupService_CleanupOldBackups/empty_backup_directory (0.00s) +=== RUN TestBackupService_GetLastBackupTime +=== RUN TestBackupService_GetLastBackupTime/returns_latest_backup_time +time="2026-01-10T02:17:52Z" level=warning msg="Warning: could not backup caddy dir" error="lstat /tmp/TestBackupService_GetLastBackupTimereturns_latest_backup_time1266614275/001/data/caddy: no such file or directory" +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +=== RUN TestBackupService_GetLastBackupTime/returns_zero_time_when_no_backups +--- PASS: TestBackupService_GetLastBackupTime (0.00s) + --- PASS: TestBackupService_GetLastBackupTime/returns_latest_backup_time (0.00s) + --- PASS: TestBackupService_GetLastBackupTime/returns_zero_time_when_no_backups (0.00s) +=== RUN TestDefaultBackupRetention +--- PASS: TestDefaultBackupRetention (0.00s) +=== RUN TestNewBackupService_BackupDirCreationError +time="2026-01-10T02:17:52Z" level=error msg="Failed to create backup directory" error="mkdir /tmp/TestNewBackupService_BackupDirCreationError2121673763/001/data/backups: not a directory" +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestNewBackupService_BackupDirCreationError (0.00s) +=== RUN TestNewBackupService_CronScheduleError +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestNewBackupService_CronScheduleError (0.00s) +=== RUN TestRunScheduledBackup_CreateBackupFails +time="2026-01-10T02:17:52Z" level=info msg="Starting scheduled backup" +time="2026-01-10T02:17:52Z" level=error msg="Scheduled backup failed" error="database file not found: /tmp/TestRunScheduledBackup_CreateBackupFails1566695776/001/data/charon.db" +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestRunScheduledBackup_CreateBackupFails (0.00s) +=== RUN TestRunScheduledBackup_CleanupFails +time="2026-01-10T02:17:52Z" level=warning msg="Warning: could not backup caddy dir" error="lstat /tmp/TestRunScheduledBackup_CleanupFails1518931621/001/data/caddy: no such file or directory" +time="2026-01-10T02:17:52Z" level=info msg="Starting scheduled backup" +time="2026-01-10T02:17:52Z" level=warning msg="Warning: could not backup caddy dir" error="lstat /tmp/TestRunScheduledBackup_CleanupFails1518931621/001/data/caddy: no such file or directory" +time="2026-01-10T02:17:52Z" level=info msg="Scheduled backup created" backup=backup_2026-01-10_02-17-52.zip +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestRunScheduledBackup_CleanupFails (0.01s) +=== RUN TestGetLastBackupTime_ListBackupsError +--- PASS: TestGetLastBackupTime_ListBackupsError (0.00s) +=== RUN TestRunScheduledBackup_CleanupDeletesZero +time="2026-01-10T02:17:52Z" level=info msg="Starting scheduled backup" +time="2026-01-10T02:17:52Z" level=warning msg="Warning: could not backup caddy dir" error="lstat /tmp/TestRunScheduledBackup_CleanupDeletesZero3348750762/001/data/caddy: no such file or directory" +time="2026-01-10T02:17:52Z" level=info msg="Scheduled backup created" backup=backup_2026-01-10_02-17-52.zip +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestRunScheduledBackup_CleanupDeletesZero (0.00s) +=== RUN TestCleanupOldBackups_PartialFailure +--- PASS: TestCleanupOldBackups_PartialFailure (0.00s) +=== RUN TestCreateBackup_CaddyDirMissing +time="2026-01-10T02:17:52Z" level=warning msg="Warning: could not backup caddy dir" error="lstat /tmp/TestCreateBackup_CaddyDirMissing2609396373/001/data/caddy: no such file or directory" +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestCreateBackup_CaddyDirMissing (0.00s) +=== RUN TestCreateBackup_CaddyDirUnreadable +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestCreateBackup_CaddyDirUnreadable (0.01s) +=== RUN TestBackupService_addToZip_FileNotFound +--- PASS: TestBackupService_addToZip_FileNotFound (0.00s) +=== RUN TestBackupService_addToZip_FileOpenError + backup_service_test.go:619: Skipping test that requires non-root user for permission testing +--- SKIP: TestBackupService_addToZip_FileOpenError (0.00s) +=== RUN TestBackupService_Start +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler started" +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestBackupService_Start (0.00s) +=== RUN TestRunScheduledBackup_CleanupSucceedsWithDeletions +time="2026-01-10T02:17:52Z" level=info msg="Starting scheduled backup" +time="2026-01-10T02:17:52Z" level=warning msg="Warning: could not backup caddy dir" error="lstat /tmp/TestRunScheduledBackup_CleanupSucceedsWithDeletions438938866/001/data/caddy: no such file or directory" +time="2026-01-10T02:17:52Z" level=info msg="Scheduled backup created" backup=backup_2026-01-10_02-17-52.zip +time="2026-01-10T02:17:52Z" level=info msg="Cleaned up old backups" deleted_count=4 +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestRunScheduledBackup_CleanupSucceedsWithDeletions (0.00s) +=== RUN TestCleanupOldBackups_ListBackupsError +--- PASS: TestCleanupOldBackups_ListBackupsError (0.00s) +=== RUN TestListBackups_EntryInfoError +--- PASS: TestListBackups_EntryInfoError (0.00s) +=== RUN TestRestoreBackup_PathTraversal_FirstCheck +--- PASS: TestRestoreBackup_PathTraversal_FirstCheck (0.00s) +=== RUN TestRestoreBackup_PathTraversal_SecondCheck +--- PASS: TestRestoreBackup_PathTraversal_SecondCheck (0.00s) +=== RUN TestDeleteBackup_PathTraversal_SecondCheck +--- PASS: TestDeleteBackup_PathTraversal_SecondCheck (0.00s) +=== RUN TestGetBackupPath_PathTraversal_SecondCheck +--- PASS: TestGetBackupPath_PathTraversal_SecondCheck (0.00s) +=== RUN TestUnzip_DirectoryCreation +--- PASS: TestUnzip_DirectoryCreation (0.00s) +=== RUN TestUnzip_OpenFileError + backup_service_test.go:847: Skipping test that requires non-root user +--- SKIP: TestUnzip_OpenFileError (0.00s) +=== RUN TestUnzip_FileOpenInZipError +--- PASS: TestUnzip_FileOpenInZipError (0.00s) +=== RUN TestAddDirToZip_WalkError +--- PASS: TestAddDirToZip_WalkError (0.00s) +=== RUN TestAddDirToZip_SkipsDirectories +--- PASS: TestAddDirToZip_SkipsDirectories (0.00s) +=== RUN TestGetAvailableSpace_Success +--- PASS: TestGetAvailableSpace_Success (0.00s) +=== RUN TestGetAvailableSpace_NonExistentDir +--- PASS: TestGetAvailableSpace_NonExistentDir (0.00s) +=== RUN TestUnzip_CopyError + backup_service_test.go:991: Skipping test that requires non-root user +--- SKIP: TestUnzip_CopyError (0.00s) +=== RUN TestCreateBackup_ZipWriterCloseError +time="2026-01-10T02:17:52Z" level=warning msg="Warning: could not backup caddy dir" error="lstat /tmp/TestCreateBackup_ZipWriterCloseError1638110052/001/data/caddy: no such file or directory" +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestCreateBackup_ZipWriterCloseError (0.00s) +=== RUN TestAddToZip_CreateError +--- PASS: TestAddToZip_CreateError (0.00s) +=== RUN TestListBackups_IgnoresNonZipFiles +--- PASS: TestListBackups_IgnoresNonZipFiles (0.00s) +=== RUN TestRestoreBackup_CreatesNestedDirectories +--- PASS: TestRestoreBackup_CreatesNestedDirectories (0.00s) +=== RUN TestBackupService_FullCycle +time="2026-01-10T02:17:52Z" level=info msg="Backup service cron scheduler stopped" +--- PASS: TestBackupService_FullCycle (0.00s) +=== RUN TestNewCertificateService +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestNewCertificateService3827131621/001/certificates +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestNewCertificateService (0.12s) +=== RUN TestCertificateService_GetCertificateInfo +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/cert-test2326179515/certificates + +2026/01/10 02:17:52 /projects/Charon/backend/internal/services/certificate_service.go:123 record not found +[0.183ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE domains = "example.com" ORDER BY `ssl_certificates`.`id` LIMIT 1 + +2026/01/10 02:17:52 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[5.836ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: disk sync complete" count=1 +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/cert-test2326179515/certificates + +2026/01/10 02:17:52 /projects/Charon/backend/internal/services/certificate_service.go:123 record not found +[0.086ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE domains = "expired.com" ORDER BY `ssl_certificates`.`id` LIMIT 1 + +2026/01/10 02:17:52 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[0.045ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: disk sync complete" count=2 +--- PASS: TestCertificateService_GetCertificateInfo (0.22s) +=== RUN TestCertificateService_UploadAndDelete +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_UploadAndDelete2793602283/001/certificates +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/tmp/TestCertificateService_UploadAndDelete2793602283/001/certificates +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: disk sync complete" count=1 +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_UploadAndDelete2793602283/001/certificates +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/tmp/TestCertificateService_UploadAndDelete2793602283/001/certificates +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestCertificateService_UploadAndDelete (0.13s) +=== RUN TestCertificateService_Persistence +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_Persistence2684953317/001/certificates + +2026/01/10 02:17:52 /projects/Charon/backend/internal/services/certificate_service.go:123 record not found +[0.182ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE domains = "persist.example.com" ORDER BY `ssl_certificates`.`id` LIMIT 1 +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: disk sync complete" count=1 +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: deleting ACME cert file" path=/tmp/TestCertificateService_Persistence2684953317/001/certificates/acme-v02.api.letsencrypt.org-directory/persist.example.com/persist.example.com.crt +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_Persistence2684953317/001/certificates +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: disk sync complete" count=0 + +2026/01/10 02:17:52 /projects/Charon/backend/internal/services/certificate_service_test.go:289 record not found +[0.101ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE (domains = "persist.example.com" AND provider = "letsencrypt") AND `ssl_certificates`.`id` = 1 ORDER BY `ssl_certificates`.`id` LIMIT 1 +--- PASS: TestCertificateService_Persistence (0.11s) +=== RUN TestCertificateService_UploadCertificate_Errors +=== RUN TestCertificateService_UploadCertificate_Errors/invalid_PEM_format +=== RUN TestCertificateService_UploadCertificate_Errors/empty_certificate +=== RUN TestCertificateService_UploadCertificate_Errors/certificate_without_key_allowed +=== RUN TestCertificateService_UploadCertificate_Errors/valid_certificate_with_name +=== RUN TestCertificateService_UploadCertificate_Errors/expired_certificate_can_be_uploaded +--- PASS: TestCertificateService_UploadCertificate_Errors (0.28s) + --- PASS: TestCertificateService_UploadCertificate_Errors/invalid_PEM_format (0.00s) + --- PASS: TestCertificateService_UploadCertificate_Errors/empty_certificate (0.00s) + --- PASS: TestCertificateService_UploadCertificate_Errors/certificate_without_key_allowed (0.03s) + --- PASS: TestCertificateService_UploadCertificate_Errors/valid_certificate_with_name (0.10s) + --- PASS: TestCertificateService_UploadCertificate_Errors/expired_certificate_can_be_uploaded (0.13s) +=== RUN TestCertificateService_ListCertificates_EdgeCases +=== RUN TestCertificateService_ListCertificates_EdgeCases/empty_certificates_directory +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_ListCertificates_EdgeCasesempty_certific1470065107/001/certificates +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/tmp/TestCertificateService_ListCertificates_EdgeCasesempty_certific1470065107/001/certificates + +2026/01/10 02:17:52 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[4.390ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: disk sync complete" count=0 +=== RUN TestCertificateService_ListCertificates_EdgeCases/certificates_directory_does_not_exist +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_ListCertificates_EdgeCasescertificates_d2286856882/001/does-not-exist/certificates +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/tmp/TestCertificateService_ListCertificates_EdgeCasescertificates_d2286856882/001/does-not-exist/certificates + +2026/01/10 02:17:52 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[5.546ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: disk sync complete" count=0 +=== RUN TestCertificateService_ListCertificates_EdgeCases/invalid_certificate_files_are_skipped +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_ListCertificates_EdgeCasesinvalid_certif3126530968/001/certificates + +2026/01/10 02:17:52 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[5.902ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:52Z" level=info msg="CertificateService: disk sync complete" count=0 +=== RUN TestCertificateService_ListCertificates_EdgeCases/multiple_certificates_from_different_providers +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_ListCertificates_EdgeCasesmultiple_certi1140893787/001/certificates + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:123 record not found +[0.147ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE domains = "le.example.com" ORDER BY `ssl_certificates`.`id` LIMIT 1 + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[5.423ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: disk sync complete" count=2 +--- PASS: TestCertificateService_ListCertificates_EdgeCases (0.24s) + --- PASS: TestCertificateService_ListCertificates_EdgeCases/empty_certificates_directory (0.01s) + --- PASS: TestCertificateService_ListCertificates_EdgeCases/certificates_directory_does_not_exist (0.01s) + --- PASS: TestCertificateService_ListCertificates_EdgeCases/invalid_certificate_files_are_skipped (0.01s) + --- PASS: TestCertificateService_ListCertificates_EdgeCases/multiple_certificates_from_different_providers (0.22s) +=== RUN TestCertificateService_DeleteCertificate_Errors +=== RUN TestCertificateService_DeleteCertificate_Errors/delete_non-existent_certificate + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:410 record not found +[0.098ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE `ssl_certificates`.`id` = 99999 ORDER BY `ssl_certificates`.`id` LIMIT 1 +=== RUN TestCertificateService_DeleteCertificate_Errors/delete_certificate_in_use_returns_ErrCertInUse +=== RUN TestCertificateService_DeleteCertificate_Errors/delete_certificate_when_file_already_removed + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service_test.go:513 record not found +[0.091ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE id = 2 ORDER BY `ssl_certificates`.`id` LIMIT 1 +--- PASS: TestCertificateService_DeleteCertificate_Errors (0.24s) + --- PASS: TestCertificateService_DeleteCertificate_Errors/delete_non-existent_certificate (0.00s) + --- PASS: TestCertificateService_DeleteCertificate_Errors/delete_certificate_in_use_returns_ErrCertInUse (0.05s) + --- PASS: TestCertificateService_DeleteCertificate_Errors/delete_certificate_when_file_already_removed (0.17s) +=== RUN TestCertificateService_StagingCertificates +=== RUN TestCertificateService_StagingCertificates/staging_certificate_detected_by_path +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_StagingCertificatesstaging_certificate_d1544785683/001/certificates + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:123 record not found +[0.212ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE domains = "staging.example.com" ORDER BY `ssl_certificates`.`id` LIMIT 1 + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[5.219ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: disk sync complete" count=1 +=== RUN TestCertificateService_StagingCertificates/production_cert_preferred_over_staging +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_StagingCertificatesproduction_cert_prefe1619917906/001/certificates + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:123 record not found +[0.232ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE domains = "both.example.com" ORDER BY `ssl_certificates`.`id` LIMIT 1 + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[5.727ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: disk sync complete" count=1 +=== RUN TestCertificateService_StagingCertificates/upgrade_from_staging_to_production +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_StagingCertificatesupgrade_from_staging_3224756401/001/certificates + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:123 record not found +[0.162ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE domains = "upgrade.example.com" ORDER BY `ssl_certificates`.`id` LIMIT 1 + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[5.372ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: disk sync complete" count=1 +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_StagingCertificatesupgrade_from_staging_3224756401/001/certificates + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[0.018ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: disk sync complete" count=1 +--- PASS: TestCertificateService_StagingCertificates (0.33s) + --- PASS: TestCertificateService_StagingCertificates/staging_certificate_detected_by_path (0.06s) + --- PASS: TestCertificateService_StagingCertificates/production_cert_preferred_over_staging (0.06s) + --- PASS: TestCertificateService_StagingCertificates/upgrade_from_staging_to_production (0.21s) +=== RUN TestCertificateService_ExpiringStatus +=== RUN TestCertificateService_ExpiringStatus/certificate_expiring_within_30_days +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_ExpiringStatuscertificate_expiring_withi2710914074/001/certificates + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:123 record not found +[0.202ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE domains = "expiring.example.com" ORDER BY `ssl_certificates`.`id` LIMIT 1 + +2026/01/10 02:17:53 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[4.068ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:53Z" level=info msg="CertificateService: disk sync complete" count=1 +=== RUN TestCertificateService_ExpiringStatus/certificate_valid_for_more_than_30_days +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_ExpiringStatuscertificate_valid_for_more2839624725/001/certificates + +2026/01/10 02:17:54 /projects/Charon/backend/internal/services/certificate_service.go:123 record not found +[0.220ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE domains = "valid-long.example.com" ORDER BY `ssl_certificates`.`id` LIMIT 1 + +2026/01/10 02:17:54 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[6.358ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: disk sync complete" count=1 +=== RUN TestCertificateService_ExpiringStatus/staging_cert_always_untrusted_even_if_expiring +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_ExpiringStatusstaging_cert_always_untrus4054643254/001/certificates + +2026/01/10 02:17:54 /projects/Charon/backend/internal/services/certificate_service.go:123 record not found +[0.169ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE domains = "staging-expiring.example.com" ORDER BY `ssl_certificates`.`id` LIMIT 1 + +2026/01/10 02:17:54 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[4.582ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: disk sync complete" count=1 +--- PASS: TestCertificateService_ExpiringStatus (0.33s) + --- PASS: TestCertificateService_ExpiringStatus/certificate_expiring_within_30_days (0.03s) + --- PASS: TestCertificateService_ExpiringStatus/certificate_valid_for_more_than_30_days (0.25s) + --- PASS: TestCertificateService_ExpiringStatus/staging_cert_always_untrusted_even_if_expiring (0.05s) +=== RUN TestCertificateService_StaleCertCleanup +=== RUN TestCertificateService_StaleCertCleanup/stale_DB_entries_removed_when_file_deleted +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_StaleCertCleanupstale_DB_entries_removed2164192469/001/certificates + +2026/01/10 02:17:54 /projects/Charon/backend/internal/services/certificate_service.go:123 record not found +[0.158ms] [rows:0] SELECT * FROM `ssl_certificates` WHERE domains = "stale.example.com" ORDER BY `ssl_certificates`.`id` LIMIT 1 + +2026/01/10 02:17:54 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[5.359ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: disk sync complete" count=1 +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_StaleCertCleanupstale_DB_entries_removed2164192469/001/certificates +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: removed stale DB cert" domain=stale.example.com + +2026/01/10 02:17:54 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[0.028ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: disk sync complete" count=0 +--- PASS: TestCertificateService_StaleCertCleanup (0.10s) + --- PASS: TestCertificateService_StaleCertCleanup/stale_DB_entries_removed_when_file_deleted (0.10s) +=== RUN TestCertificateService_CertificateWithSANs +=== RUN TestCertificateService_CertificateWithSANs/certificate_with_SANs_uses_joined_domains +--- PASS: TestCertificateService_CertificateWithSANs (0.05s) + --- PASS: TestCertificateService_CertificateWithSANs/certificate_with_SANs_uses_joined_domains (0.05s) +=== RUN TestCertificateService_IsCertificateInUse +=== RUN TestCertificateService_IsCertificateInUse/certificate_not_in_use +=== RUN TestCertificateService_IsCertificateInUse/certificate_used_by_one_proxy_host +=== RUN TestCertificateService_IsCertificateInUse/certificate_used_by_multiple_proxy_hosts +=== RUN TestCertificateService_IsCertificateInUse/non-existent_certificate +=== RUN TestCertificateService_IsCertificateInUse/certificate_freed_after_proxy_host_deletion +--- PASS: TestCertificateService_IsCertificateInUse (0.57s) + --- PASS: TestCertificateService_IsCertificateInUse/certificate_not_in_use (0.07s) + --- PASS: TestCertificateService_IsCertificateInUse/certificate_used_by_one_proxy_host (0.15s) + --- PASS: TestCertificateService_IsCertificateInUse/certificate_used_by_multiple_proxy_hosts (0.18s) + --- PASS: TestCertificateService_IsCertificateInUse/non-existent_certificate (0.00s) + --- PASS: TestCertificateService_IsCertificateInUse/certificate_freed_after_proxy_host_deletion (0.16s) +=== RUN TestCertificateService_CacheBehavior +=== RUN TestCertificateService_CacheBehavior/cache_returns_consistent_results +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_CacheBehaviorcache_returns_consistent_re3010371691/001/certificates +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/tmp/TestCertificateService_CacheBehaviorcache_returns_consistent_re3010371691/001/certificates + +2026/01/10 02:17:54 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[5.504ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:54Z" level=info msg="CertificateService: disk sync complete" count=1 +=== RUN TestCertificateService_CacheBehavior/invalidate_cache_forces_resync +time="2026-01-10T02:17:55Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_CacheBehaviorinvalidate_cache_forces_res1082538232/001/certificates +time="2026-01-10T02:17:55Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/tmp/TestCertificateService_CacheBehaviorinvalidate_cache_forces_res1082538232/001/certificates + +2026/01/10 02:17:55 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[4.735ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:55Z" level=info msg="CertificateService: disk sync complete" count=1 +time="2026-01-10T02:17:55Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_CacheBehaviorinvalidate_cache_forces_res1082538232/001/certificates +time="2026-01-10T02:17:55Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/tmp/TestCertificateService_CacheBehaviorinvalidate_cache_forces_res1082538232/001/certificates + +2026/01/10 02:17:55 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[0.021ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:55Z" level=info msg="CertificateService: disk sync complete" count=2 +=== RUN TestCertificateService_CacheBehavior/refreshCacheFromDB_used_when_directory_nonexistent +time="2026-01-10T02:17:55Z" level=info msg="CertificateService: scanning cert directory" certRoot=/tmp/TestCertificateService_CacheBehaviorrefreshCacheFromDB_used_whe4116796523/001/nonexistent/certificates +time="2026-01-10T02:17:55Z" level=info msg="CertificateService: cert directory does not exist" certRoot=/tmp/TestCertificateService_CacheBehaviorrefreshCacheFromDB_used_whe4116796523/001/nonexistent/certificates + +2026/01/10 02:17:55 /projects/Charon/backend/internal/services/certificate_service.go:232 no such table: proxy_hosts +[4.656ms] [rows:0] SELECT * FROM `proxy_hosts` +time="2026-01-10T02:17:55Z" level=info msg="CertificateService: disk sync complete" count=1 +--- PASS: TestCertificateService_CacheBehavior (0.32s) + --- PASS: TestCertificateService_CacheBehavior/cache_returns_consistent_results (0.14s) + --- PASS: TestCertificateService_CacheBehavior/invalidate_cache_forces_resync (0.18s) + --- PASS: TestCertificateService_CacheBehavior/refreshCacheFromDB_used_when_directory_nonexistent (0.01s) +=== RUN TestCoverageBoost_ErrorPaths +=== RUN TestCoverageBoost_ErrorPaths/ProxyHostService_GetByUUID_Error +=== RUN TestCoverageBoost_ErrorPaths/ProxyHostService_List_WithValidDB +=== RUN TestCoverageBoost_ErrorPaths/RemoteServerService_GetByUUID_Error +=== RUN TestCoverageBoost_ErrorPaths/RemoteServerService_List_WithValidDB +=== RUN TestCoverageBoost_ErrorPaths/SecurityService_Get_NotFound +=== RUN TestCoverageBoost_ErrorPaths/SecurityService_ListRuleSets_EmptyDB +=== RUN TestCoverageBoost_ErrorPaths/SecurityService_DeleteRuleSet_NotFound +=== RUN TestCoverageBoost_ErrorPaths/SecurityService_VerifyBreakGlass_MissingConfig +=== RUN TestCoverageBoost_ErrorPaths/SecurityService_GenerateBreakGlassToken_Success +=== RUN TestCoverageBoost_ErrorPaths/NotificationService_ListTemplates_EmptyDB +=== RUN TestCoverageBoost_ErrorPaths/NotificationService_GetTemplate_NotFound +--- PASS: TestCoverageBoost_ErrorPaths (0.74s) + --- PASS: TestCoverageBoost_ErrorPaths/ProxyHostService_GetByUUID_Error (0.00s) + --- PASS: TestCoverageBoost_ErrorPaths/ProxyHostService_List_WithValidDB (0.00s) + --- PASS: TestCoverageBoost_ErrorPaths/RemoteServerService_GetByUUID_Error (0.00s) + --- PASS: TestCoverageBoost_ErrorPaths/RemoteServerService_List_WithValidDB (0.00s) + --- PASS: TestCoverageBoost_ErrorPaths/SecurityService_Get_NotFound (0.00s) + --- PASS: TestCoverageBoost_ErrorPaths/SecurityService_ListRuleSets_EmptyDB (0.00s) + --- PASS: TestCoverageBoost_ErrorPaths/SecurityService_DeleteRuleSet_NotFound (0.00s) + --- PASS: TestCoverageBoost_ErrorPaths/SecurityService_VerifyBreakGlass_MissingConfig (0.00s) + --- PASS: TestCoverageBoost_ErrorPaths/SecurityService_GenerateBreakGlassToken_Success (0.72s) + --- PASS: TestCoverageBoost_ErrorPaths/NotificationService_ListTemplates_EmptyDB (0.00s) + --- PASS: TestCoverageBoost_ErrorPaths/NotificationService_GetTemplate_NotFound (0.00s) +=== RUN TestCoverageBoost_SecurityService_AdditionalPaths +=== RUN TestCoverageBoost_SecurityService_AdditionalPaths/Upsert_Create +=== RUN TestCoverageBoost_SecurityService_AdditionalPaths/UpsertRuleSet_Create +--- PASS: TestCoverageBoost_SecurityService_AdditionalPaths (0.01s) + --- PASS: TestCoverageBoost_SecurityService_AdditionalPaths/Upsert_Create (0.00s) + --- PASS: TestCoverageBoost_SecurityService_AdditionalPaths/UpsertRuleSet_Create (0.00s) +=== RUN TestCoverageBoost_MinInt +=== RUN TestCoverageBoost_MinInt/minInt_FirstSmaller +=== RUN TestCoverageBoost_MinInt/minInt_SecondSmaller +=== RUN TestCoverageBoost_MinInt/minInt_Equal +--- PASS: TestCoverageBoost_MinInt (0.00s) + --- PASS: TestCoverageBoost_MinInt/minInt_FirstSmaller (0.00s) + --- PASS: TestCoverageBoost_MinInt/minInt_SecondSmaller (0.00s) + --- PASS: TestCoverageBoost_MinInt/minInt_Equal (0.00s) +=== RUN TestCoverageBoost_MailService_ErrorPaths +=== RUN TestCoverageBoost_MailService_ErrorPaths/GetSMTPConfig_EmptyDB +=== RUN TestCoverageBoost_MailService_ErrorPaths/IsConfigured_NoConfig +=== RUN TestCoverageBoost_MailService_ErrorPaths/TestConnection_NoConfig +=== RUN TestCoverageBoost_MailService_ErrorPaths/SendEmail_NoConfig +--- PASS: TestCoverageBoost_MailService_ErrorPaths (0.00s) + --- PASS: TestCoverageBoost_MailService_ErrorPaths/GetSMTPConfig_EmptyDB (0.00s) + --- PASS: TestCoverageBoost_MailService_ErrorPaths/IsConfigured_NoConfig (0.00s) + --- PASS: TestCoverageBoost_MailService_ErrorPaths/TestConnection_NoConfig (0.00s) + --- PASS: TestCoverageBoost_MailService_ErrorPaths/SendEmail_NoConfig (0.00s) +=== RUN TestCoverageBoost_AccessListService_Paths +=== RUN TestCoverageBoost_AccessListService_Paths/GetByID_NotFound +=== RUN TestCoverageBoost_AccessListService_Paths/GetByUUID_NotFound +=== RUN TestCoverageBoost_AccessListService_Paths/List_EmptyDB +--- PASS: TestCoverageBoost_AccessListService_Paths (0.00s) + --- PASS: TestCoverageBoost_AccessListService_Paths/GetByID_NotFound (0.00s) + --- PASS: TestCoverageBoost_AccessListService_Paths/GetByUUID_NotFound (0.00s) + --- PASS: TestCoverageBoost_AccessListService_Paths/List_EmptyDB (0.00s) +=== RUN TestCoverageBoost_HelperFunctions +=== RUN TestCoverageBoost_HelperFunctions/extractPort_HTTP +=== RUN TestCoverageBoost_HelperFunctions/extractPort_HTTPS +=== RUN TestCoverageBoost_HelperFunctions/extractPort_Invalid +=== RUN TestCoverageBoost_HelperFunctions/hasHeader_Found +=== RUN TestCoverageBoost_HelperFunctions/hasHeader_NotFound +=== RUN TestCoverageBoost_HelperFunctions/hasHeader_EmptyMap +=== RUN TestCoverageBoost_HelperFunctions/isPrivateIP_PrivateRanges +=== RUN TestCoverageBoost_HelperFunctions/isPrivateIP_PublicIP +--- PASS: TestCoverageBoost_HelperFunctions (0.00s) + --- PASS: TestCoverageBoost_HelperFunctions/extractPort_HTTP (0.00s) + --- PASS: TestCoverageBoost_HelperFunctions/extractPort_HTTPS (0.00s) + --- PASS: TestCoverageBoost_HelperFunctions/extractPort_Invalid (0.00s) + --- PASS: TestCoverageBoost_HelperFunctions/hasHeader_Found (0.00s) + --- PASS: TestCoverageBoost_HelperFunctions/hasHeader_NotFound (0.00s) + --- PASS: TestCoverageBoost_HelperFunctions/hasHeader_EmptyMap (0.00s) + --- PASS: TestCoverageBoost_HelperFunctions/isPrivateIP_PrivateRanges (0.00s) + --- PASS: TestCoverageBoost_HelperFunctions/isPrivateIP_PublicIP (0.00s) +=== RUN TestCoverageBoost_ProxyHostService_DB +=== RUN TestCoverageBoost_ProxyHostService_DB/DB_ReturnsValidDB +--- PASS: TestCoverageBoost_ProxyHostService_DB (0.00s) + --- PASS: TestCoverageBoost_ProxyHostService_DB/DB_ReturnsValidDB (0.00s) +=== RUN TestCoverageBoost_DNSProviderService_SupportedTypes +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +=== RUN TestCoverageBoost_DNSProviderService_SupportedTypes/GetSupportedProviderTypes +=== RUN TestCoverageBoost_DNSProviderService_SupportedTypes/GetProviderCredentialFields_ValidProvider +=== RUN TestCoverageBoost_DNSProviderService_SupportedTypes/GetProviderCredentialFields_InvalidProvider +--- PASS: TestCoverageBoost_DNSProviderService_SupportedTypes (0.00s) + --- PASS: TestCoverageBoost_DNSProviderService_SupportedTypes/GetSupportedProviderTypes (0.00s) + --- PASS: TestCoverageBoost_DNSProviderService_SupportedTypes/GetProviderCredentialFields_ValidProvider (0.00s) + --- PASS: TestCoverageBoost_DNSProviderService_SupportedTypes/GetProviderCredentialFields_InvalidProvider (0.00s) +=== RUN TestCoverageBoost_SecurityService_Close +=== RUN TestCoverageBoost_SecurityService_Close/Close_Success +=== RUN TestCoverageBoost_SecurityService_Close/Flush_Success +--- PASS: TestCoverageBoost_SecurityService_Close (0.01s) + --- PASS: TestCoverageBoost_SecurityService_Close/Close_Success (0.00s) + --- PASS: TestCoverageBoost_SecurityService_Close/Flush_Success (0.01s) +=== RUN TestCoverageBoost_BackupService_GetAvailableSpace + coverage_boost_test.go:387: BackupService requires full config.Config, tested elsewhere +--- SKIP: TestCoverageBoost_BackupService_GetAvailableSpace (0.00s) +=== RUN TestCoverageBoost_CertificateService_ListCertificates + coverage_boost_test.go:393: Certificate models tested in certificate_service_test.go +--- SKIP: TestCoverageBoost_CertificateService_ListCertificates (0.00s) +=== RUN TestCoverageBoost_MailService_SendSSL +=== RUN TestCoverageBoost_MailService_SendSSL/SendEmail_SSL_InvalidHost +=== RUN TestCoverageBoost_MailService_SendSSL/SendEmail_STARTTLS_InvalidHost +--- PASS: TestCoverageBoost_MailService_SendSSL (0.01s) + --- PASS: TestCoverageBoost_MailService_SendSSL/SendEmail_SSL_InvalidHost (0.01s) + --- PASS: TestCoverageBoost_MailService_SendSSL/SendEmail_STARTTLS_InvalidHost (0.00s) +=== RUN TestCoverageBoost_CredentialService_ErrorPaths + coverage_boost_test.go:456: CredentialService requires crypto.EncryptionService, tested elsewhere +--- SKIP: TestCoverageBoost_CredentialService_ErrorPaths (0.00s) +=== RUN TestCoverageBoost_GeoIPService_ErrorPaths +=== RUN TestCoverageBoost_GeoIPService_ErrorPaths/NewGeoIPService_InvalidPath +--- PASS: TestCoverageBoost_GeoIPService_ErrorPaths (0.00s) + --- PASS: TestCoverageBoost_GeoIPService_ErrorPaths/NewGeoIPService_InvalidPath (0.00s) +=== RUN TestCoverageBoost_DockerService_ErrorPaths + coverage_boost_test.go:470: Docker service tests require specific setup, tested in docker_service_test.go +--- SKIP: TestCoverageBoost_DockerService_ErrorPaths (0.00s) +=== RUN TestCoverageBoost_UptimeService_FlushNotifications +=== RUN TestCoverageBoost_UptimeService_FlushNotifications/FlushPendingNotifications +--- PASS: TestCoverageBoost_UptimeService_FlushNotifications (0.02s) + --- PASS: TestCoverageBoost_UptimeService_FlushNotifications/FlushPendingNotifications (0.00s) +=== RUN TestCoverageBoost_LogService_NewLogService + coverage_boost_test.go:493: LogService requires full config, tested in log_service_test.go +--- SKIP: TestCoverageBoost_LogService_NewLogService (0.00s) +=== RUN TestCoverageBoost_UpdateService_ClearCache +=== RUN TestCoverageBoost_UpdateService_ClearCache/ClearCache +=== RUN TestCoverageBoost_UpdateService_ClearCache/SetCurrentVersion +--- PASS: TestCoverageBoost_UpdateService_ClearCache (0.00s) + --- PASS: TestCoverageBoost_UpdateService_ClearCache/ClearCache (0.00s) + --- PASS: TestCoverageBoost_UpdateService_ClearCache/SetCurrentVersion (0.00s) +=== RUN TestCoverageBoost_NotificationService_Providers +=== RUN TestCoverageBoost_NotificationService_Providers/ListProviders_EmptyDB +=== RUN TestCoverageBoost_NotificationService_Providers/CreateProvider +=== RUN TestCoverageBoost_NotificationService_Providers/UpdateProvider +=== RUN TestCoverageBoost_NotificationService_Providers/DeleteProvider +--- PASS: TestCoverageBoost_NotificationService_Providers (0.01s) + --- PASS: TestCoverageBoost_NotificationService_Providers/ListProviders_EmptyDB (0.00s) + --- PASS: TestCoverageBoost_NotificationService_Providers/CreateProvider (0.00s) + --- PASS: TestCoverageBoost_NotificationService_Providers/UpdateProvider (0.00s) + --- PASS: TestCoverageBoost_NotificationService_Providers/DeleteProvider (0.00s) +=== RUN TestCoverageBoost_NotificationService_CRUD +=== RUN TestCoverageBoost_NotificationService_CRUD/List_EmptyDB +=== RUN TestCoverageBoost_NotificationService_CRUD/MarkAllAsRead_Success +--- PASS: TestCoverageBoost_NotificationService_CRUD (0.00s) + --- PASS: TestCoverageBoost_NotificationService_CRUD/List_EmptyDB (0.00s) + --- PASS: TestCoverageBoost_NotificationService_CRUD/MarkAllAsRead_Success (0.00s) +=== RUN TestReconcileCrowdSecOnStartup_NilDB +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=crowdsec data_dir=/tmp/crowdsec +--- PASS: TestReconcileCrowdSecOnStartup_NilDB (0.00s) +=== RUN TestReconcileCrowdSecOnStartup_NilExecutor +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=crowdsec data_dir=/tmp/crowdsec +--- PASS: TestReconcileCrowdSecOnStartup_NilExecutor (0.00s) +=== RUN TestReconcileCrowdSecOnStartup_NoSecurityConfig_NoSettings +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-3422589158/crowdsec data_dir=/tmp/crowdsec-test-3422589158/data +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation: no SecurityConfig found, checking Settings table for user preference" +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation: default SecurityConfig created from Settings preference" crowdsec_mode=disabled enabled=false source=settings_table +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation skipped: both SecurityConfig and Settings indicate disabled" db_mode=disabled setting_enabled=false +--- PASS: TestReconcileCrowdSecOnStartup_NoSecurityConfig_NoSettings (0.01s) +=== RUN TestReconcileCrowdSecOnStartup_NoSecurityConfig_SettingsEnabled +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-1248194343/crowdsec data_dir=/tmp/crowdsec-test-1248194343/data +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation: no SecurityConfig found, checking Settings table for user preference" +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation: found existing Settings table preference" enabled=true setting_value=true +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation: default SecurityConfig created from Settings preference" crowdsec_mode=local enabled=true source=settings_table +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation: starting based on SecurityConfig mode='local'" mode=local +time="2026-01-10T02:17:55Z" level=info msg="CrowdSec reconciliation: starting CrowdSec (mode=local, not currently running)" bin_path=/tmp/crowdsec-test-1248194343/crowdsec data_dir=/tmp/crowdsec-test-1248194343/data +time="2026-01-10T02:17:57Z" level=info msg="CrowdSec reconciliation: successfully started and verified CrowdSec" pid=12345 verified=true +--- PASS: TestReconcileCrowdSecOnStartup_NoSecurityConfig_SettingsEnabled (2.01s) +=== RUN TestReconcileCrowdSecOnStartup_NoSecurityConfig_SettingsDisabled +time="2026-01-10T02:17:57Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-552327634/crowdsec data_dir=/tmp/crowdsec-test-552327634/data +time="2026-01-10T02:17:57Z" level=info msg="CrowdSec reconciliation: no SecurityConfig found, checking Settings table for user preference" +time="2026-01-10T02:17:57Z" level=info msg="CrowdSec reconciliation: found existing Settings table preference" enabled=false setting_value=false +time="2026-01-10T02:17:57Z" level=info msg="CrowdSec reconciliation: default SecurityConfig created from Settings preference" crowdsec_mode=disabled enabled=false source=settings_table +time="2026-01-10T02:17:57Z" level=info msg="CrowdSec reconciliation skipped: both SecurityConfig and Settings indicate disabled" db_mode=disabled setting_enabled=false +--- PASS: TestReconcileCrowdSecOnStartup_NoSecurityConfig_SettingsDisabled (0.01s) +=== RUN TestReconcileCrowdSecOnStartup_ModeDisabled +time="2026-01-10T02:17:57Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=crowdsec data_dir=/tmp/crowdsec +time="2026-01-10T02:17:57Z" level=info msg="CrowdSec reconciliation skipped: both SecurityConfig and Settings indicate disabled" db_mode=disabled setting_enabled=false +--- PASS: TestReconcileCrowdSecOnStartup_ModeDisabled (0.00s) +=== RUN TestReconcileCrowdSecOnStartup_ModeLocal_AlreadyRunning +time="2026-01-10T02:17:57Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-3437819381/crowdsec data_dir=/tmp/crowdsec-test-3437819381/data +time="2026-01-10T02:17:57Z" level=info msg="CrowdSec reconciliation: starting based on SecurityConfig mode='local'" mode=local +time="2026-01-10T02:17:57Z" level=info msg="CrowdSec reconciliation: already running" pid=12345 +--- PASS: TestReconcileCrowdSecOnStartup_ModeLocal_AlreadyRunning (0.00s) +=== RUN TestReconcileCrowdSecOnStartup_ModeLocal_NotRunning_Starts +time="2026-01-10T02:17:58Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-3905439510/crowdsec data_dir=/tmp/crowdsec-test-3905439510/data +time="2026-01-10T02:17:58Z" level=info msg="CrowdSec reconciliation: starting based on SecurityConfig mode='local'" mode=local +time="2026-01-10T02:17:58Z" level=info msg="CrowdSec reconciliation: starting CrowdSec (mode=local, not currently running)" bin_path=/tmp/crowdsec-test-3905439510/crowdsec data_dir=/tmp/crowdsec-test-3905439510/data +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: successfully started and verified CrowdSec" pid=99999 verified=true +--- PASS: TestReconcileCrowdSecOnStartup_ModeLocal_NotRunning_Starts (2.01s) +=== RUN TestReconcileCrowdSecOnStartup_ModeLocal_StartError +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-2846461424/crowdsec data_dir=/tmp/crowdsec-test-2846461424/data +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting based on SecurityConfig mode='local'" mode=local +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting CrowdSec (mode=local, not currently running)" bin_path=/tmp/crowdsec-test-2846461424/crowdsec data_dir=/tmp/crowdsec-test-2846461424/data +time="2026-01-10T02:18:00Z" level=error msg="CrowdSec reconciliation: FAILED to start CrowdSec - check binary and config" bin_path=/tmp/crowdsec-test-2846461424/crowdsec data_dir=/tmp/crowdsec-test-2846461424/data error="assert.AnError general error for testing" +--- PASS: TestReconcileCrowdSecOnStartup_ModeLocal_StartError (0.01s) +=== RUN TestReconcileCrowdSecOnStartup_StatusError +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-2368966688/crowdsec data_dir=/tmp/crowdsec-test-2368966688/data +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting based on SecurityConfig mode='local'" mode=local +time="2026-01-10T02:18:00Z" level=warning msg="CrowdSec reconciliation: failed to check status" error="assert.AnError general error for testing" +--- PASS: TestReconcileCrowdSecOnStartup_StatusError (0.01s) +=== RUN TestReconcileCrowdSecOnStartup_BinaryNotFound +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-1992083819/data/nonexistent_binary data_dir=/tmp/crowdsec-test-1992083819/data +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting based on SecurityConfig mode='local'" mode=local +time="2026-01-10T02:18:00Z" level=error msg="CrowdSec reconciliation: binary not found, cannot start" path=/tmp/crowdsec-test-1992083819/data/nonexistent_binary +--- PASS: TestReconcileCrowdSecOnStartup_BinaryNotFound (0.01s) +=== RUN TestReconcileCrowdSecOnStartup_ConfigDirNotFound +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-1748862425/crowdsec data_dir=/tmp/crowdsec-test-1748862425/data +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting based on SecurityConfig mode='local'" mode=local +time="2026-01-10T02:18:00Z" level=error msg="CrowdSec reconciliation: config directory not found, cannot start" path=/tmp/crowdsec-test-1748862425/data/config +--- PASS: TestReconcileCrowdSecOnStartup_ConfigDirNotFound (0.01s) +=== RUN TestReconcileCrowdSecOnStartup_SettingsOverrideEnabled +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-3010292195/crowdsec data_dir=/tmp/crowdsec-test-3010292195/data +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting based on Settings table override" setting=true +time="2026-01-10T02:18:00Z" level=info msg="CrowdSec reconciliation: starting CrowdSec (mode=local, not currently running)" bin_path=/tmp/crowdsec-test-3010292195/crowdsec data_dir=/tmp/crowdsec-test-3010292195/data +time="2026-01-10T02:18:02Z" level=info msg="CrowdSec reconciliation: successfully started and verified CrowdSec" pid=12345 verified=true +--- PASS: TestReconcileCrowdSecOnStartup_SettingsOverrideEnabled (2.01s) +=== RUN TestReconcileCrowdSecOnStartup_VerificationFails +time="2026-01-10T02:18:02Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-903539656/crowdsec data_dir=/tmp/crowdsec-test-903539656/data +time="2026-01-10T02:18:02Z" level=info msg="CrowdSec reconciliation: starting based on SecurityConfig mode='local'" mode=local +time="2026-01-10T02:18:02Z" level=info msg="CrowdSec reconciliation: starting CrowdSec (mode=local, not currently running)" bin_path=/tmp/crowdsec-test-903539656/crowdsec data_dir=/tmp/crowdsec-test-903539656/data +time="2026-01-10T02:18:04Z" level=error msg="CrowdSec reconciliation: process started but is no longer running - may have crashed" actual_pid=0 expected_pid=12345 running=false +--- PASS: TestReconcileCrowdSecOnStartup_VerificationFails (2.01s) +=== RUN TestReconcileCrowdSecOnStartup_VerificationError +time="2026-01-10T02:18:04Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-265968278/crowdsec data_dir=/tmp/crowdsec-test-265968278/data +time="2026-01-10T02:18:04Z" level=info msg="CrowdSec reconciliation: starting based on SecurityConfig mode='local'" mode=local +time="2026-01-10T02:18:04Z" level=info msg="CrowdSec reconciliation: starting CrowdSec (mode=local, not currently running)" bin_path=/tmp/crowdsec-test-265968278/crowdsec data_dir=/tmp/crowdsec-test-265968278/data +time="2026-01-10T02:18:06Z" level=warning msg="CrowdSec reconciliation: started but failed to verify status" error="assert.AnError general error for testing" expected_pid=12345 +--- PASS: TestReconcileCrowdSecOnStartup_VerificationError (2.01s) +=== RUN TestReconcileCrowdSecOnStartup_DBError +time="2026-01-10T02:18:06Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-3340642405/crowdsec data_dir=/tmp/crowdsec-test-3340642405/data +time="2026-01-10T02:18:06Z" level=warning msg="CrowdSec reconciliation skipped: SecurityConfig table not found - run 'charon migrate' to fix" +--- PASS: TestReconcileCrowdSecOnStartup_DBError (0.00s) +=== RUN TestReconcileCrowdSecOnStartup_CreateConfigDBError +time="2026-01-10T02:18:06Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-3639284342/crowdsec data_dir=/tmp/crowdsec-test-3639284342/data +time="2026-01-10T02:18:06Z" level=warning msg="CrowdSec reconciliation skipped: SecurityConfig table not found - run 'charon migrate' to fix" +--- PASS: TestReconcileCrowdSecOnStartup_CreateConfigDBError (0.00s) +=== RUN TestReconcileCrowdSecOnStartup_SettingsTableQueryError +time="2026-01-10T02:18:06Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-3773669688/crowdsec data_dir=/tmp/crowdsec-test-3773669688/data +time="2026-01-10T02:18:06Z" level=info msg="CrowdSec reconciliation skipped: both SecurityConfig and Settings indicate disabled" db_mode=remote setting_enabled=false +--- PASS: TestReconcileCrowdSecOnStartup_SettingsTableQueryError (0.00s) +=== RUN TestReconcileCrowdSecOnStartup_SettingsOverrideNonLocalMode +time="2026-01-10T02:18:06Z" level=info msg="CrowdSec reconciliation: starting startup check" bin_path=/tmp/crowdsec-test-1355592154/crowdsec data_dir=/tmp/crowdsec-test-1355592154/data +time="2026-01-10T02:18:06Z" level=info msg="CrowdSec reconciliation: starting based on Settings table override" setting=true +time="2026-01-10T02:18:06Z" level=info msg="CrowdSec reconciliation: starting CrowdSec (mode=local, not currently running)" bin_path=/tmp/crowdsec-test-1355592154/crowdsec data_dir=/tmp/crowdsec-test-1355592154/data +time="2026-01-10T02:18:08Z" level=info msg="CrowdSec reconciliation: successfully started and verified CrowdSec" pid=12345 verified=true +--- PASS: TestReconcileCrowdSecOnStartup_SettingsOverrideNonLocalMode (2.01s) +=== RUN TestNewDNSDetectionService +--- PASS: TestNewDNSDetectionService (0.00s) +=== RUN TestGetNameserverPatterns +--- PASS: TestGetNameserverPatterns (0.00s) +=== RUN TestMatchNameservers +=== RUN TestMatchNameservers/Cloudflare_-_high_confidence +=== RUN TestMatchNameservers/Route53_-_high_confidence +=== RUN TestMatchNameservers/DigitalOcean_-_high_confidence +=== RUN TestMatchNameservers/Hetzner_-_high_confidence +=== RUN TestMatchNameservers/Mixed_nameservers_-_medium_confidence +=== RUN TestMatchNameservers/Single_match_-_low_confidence +=== RUN TestMatchNameservers/No_match +=== RUN TestMatchNameservers/Empty_nameservers +--- PASS: TestMatchNameservers (0.00s) + --- PASS: TestMatchNameservers/Cloudflare_-_high_confidence (0.00s) + --- PASS: TestMatchNameservers/Route53_-_high_confidence (0.00s) + --- PASS: TestMatchNameservers/DigitalOcean_-_high_confidence (0.00s) + --- PASS: TestMatchNameservers/Hetzner_-_high_confidence (0.00s) + --- PASS: TestMatchNameservers/Mixed_nameservers_-_medium_confidence (0.00s) + --- PASS: TestMatchNameservers/Single_match_-_low_confidence (0.00s) + --- PASS: TestMatchNameservers/No_match (0.00s) + --- PASS: TestMatchNameservers/Empty_nameservers (0.00s) +=== RUN TestDetectProvider_WithMockedDNS +=== RUN TestDetectProvider_WithMockedDNS/handles_wildcard_domain +=== RUN TestDetectProvider_WithMockedDNS/handles_empty_domain +=== RUN TestDetectProvider_WithMockedDNS/normalizes_domain +--- PASS: TestDetectProvider_WithMockedDNS (0.00s) + --- PASS: TestDetectProvider_WithMockedDNS/handles_wildcard_domain (0.00s) + --- PASS: TestDetectProvider_WithMockedDNS/handles_empty_domain (0.00s) + --- PASS: TestDetectProvider_WithMockedDNS/normalizes_domain (0.00s) +=== RUN TestCaching +=== RUN TestCaching/retrieves_cached_result +=== RUN TestCaching/returns_nil_for_non-existent_cache +=== RUN TestCaching/expires_old_cache_entries +--- PASS: TestCaching (0.01s) + --- PASS: TestCaching/retrieves_cached_result (0.00s) + --- PASS: TestCaching/returns_nil_for_non-existent_cache (0.00s) + --- PASS: TestCaching/expires_old_cache_entries (0.01s) +=== RUN TestSuggestConfiguredProvider +=== RUN TestSuggestConfiguredProvider/suggests_default_cloudflare_provider +=== RUN TestSuggestConfiguredProvider/suggests_route53_provider +=== RUN TestSuggestConfiguredProvider/returns_nil_for_disabled_provider +=== RUN TestSuggestConfiguredProvider/returns_nil_for_unknown_provider +--- PASS: TestSuggestConfiguredProvider (0.01s) + --- PASS: TestSuggestConfiguredProvider/suggests_default_cloudflare_provider (0.00s) + --- PASS: TestSuggestConfiguredProvider/suggests_route53_provider (0.00s) + --- PASS: TestSuggestConfiguredProvider/returns_nil_for_disabled_provider (0.00s) + --- PASS: TestSuggestConfiguredProvider/returns_nil_for_unknown_provider (0.00s) +=== RUN TestDetectionResult_Validation +=== RUN TestDetectionResult_Validation/result_with_all_fields +=== RUN TestDetectionResult_Validation/result_with_error +--- PASS: TestDetectionResult_Validation (0.00s) + --- PASS: TestDetectionResult_Validation/result_with_all_fields (0.00s) + --- PASS: TestDetectionResult_Validation/result_with_error (0.00s) +=== RUN TestBuiltInNameserversCompleteness +--- PASS: TestBuiltInNameserversCompleteness (0.00s) +=== RUN TestCaseInsensitiveMatching +=== RUN TestCaseInsensitiveMatching/lowercase +=== RUN TestCaseInsensitiveMatching/uppercase +=== RUN TestCaseInsensitiveMatching/mixed_case +--- PASS: TestCaseInsensitiveMatching (0.00s) + --- PASS: TestCaseInsensitiveMatching/lowercase (0.00s) + --- PASS: TestCaseInsensitiveMatching/uppercase (0.00s) + --- PASS: TestCaseInsensitiveMatching/mixed_case (0.00s) +=== RUN TestConcurrentCacheAccess +--- PASS: TestConcurrentCacheAccess (0.00s) +=== RUN TestDatabaseError + +2026/01/10 02:18:08 /projects/Charon/backend/internal/services/dns_detection_service.go:182 sql: database is closed +[0.358ms] [rows:0] SELECT * FROM `dns_providers` WHERE provider_type = "cloudflare" AND enabled = true ORDER BY is_default DESC, name ASC +--- PASS: TestDatabaseError (0.00s) +=== RUN TestDNSProviderService_Create +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +=== RUN TestDNSProviderService_Create/valid_cloudflare_provider +=== RUN TestDNSProviderService_Create/valid_route53_provider_with_defaults +=== RUN TestDNSProviderService_Create/invalid_provider_type +=== RUN TestDNSProviderService_Create/missing_required_credentials +--- PASS: TestDNSProviderService_Create (0.01s) + --- PASS: TestDNSProviderService_Create/valid_cloudflare_provider (0.00s) + --- PASS: TestDNSProviderService_Create/valid_route53_provider_with_defaults (0.00s) + --- PASS: TestDNSProviderService_Create/invalid_provider_type (0.00s) + --- PASS: TestDNSProviderService_Create/missing_required_credentials (0.00s) +=== RUN TestDNSProviderService_DefaultProviderLogic +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_DefaultProviderLogic (0.01s) +=== RUN TestDNSProviderService_List +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_List (0.01s) +=== RUN TestDNSProviderService_Get +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Get (0.01s) +=== RUN TestDNSProviderService_Update +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +=== RUN TestDNSProviderService_Update/update_name_only +=== RUN TestDNSProviderService_Update/update_credentials +=== RUN TestDNSProviderService_Update/update_enabled_status +=== RUN TestDNSProviderService_Update/update_non-existent_provider +=== RUN TestDNSProviderService_Update/update_to_set_default +--- PASS: TestDNSProviderService_Update (0.01s) + --- PASS: TestDNSProviderService_Update/update_name_only (0.00s) + --- PASS: TestDNSProviderService_Update/update_credentials (0.00s) + --- PASS: TestDNSProviderService_Update/update_enabled_status (0.00s) + --- PASS: TestDNSProviderService_Update/update_non-existent_provider (0.00s) + --- PASS: TestDNSProviderService_Update/update_to_set_default (0.00s) +=== RUN TestDNSProviderService_Delete +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Delete (0.01s) +=== RUN TestDNSProviderService_GetDecryptedCredentials +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_GetDecryptedCredentials (0.01s) +=== RUN TestDNSProviderService_TestCredentials +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +=== RUN TestDNSProviderService_TestCredentials/valid_credentials +=== RUN TestDNSProviderService_TestCredentials/invalid_provider_type +=== RUN TestDNSProviderService_TestCredentials/missing_credentials +--- PASS: TestDNSProviderService_TestCredentials (0.00s) + --- PASS: TestDNSProviderService_TestCredentials/valid_credentials (0.00s) + --- PASS: TestDNSProviderService_TestCredentials/invalid_provider_type (0.00s) + --- PASS: TestDNSProviderService_TestCredentials/missing_credentials (0.00s) +=== RUN TestDNSProviderService_Test +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Test (0.01s) +=== RUN TestValidateCredentials +=== RUN TestValidateCredentials/valid_cloudflare +=== RUN TestValidateCredentials/valid_route53 +=== RUN TestValidateCredentials/missing_field +=== RUN TestValidateCredentials/empty_field_value +=== RUN TestValidateCredentials/invalid_provider_type +--- PASS: TestValidateCredentials (0.00s) + --- PASS: TestValidateCredentials/valid_cloudflare (0.00s) + --- PASS: TestValidateCredentials/valid_route53 (0.00s) + --- PASS: TestValidateCredentials/missing_field (0.00s) + --- PASS: TestValidateCredentials/empty_field_value (0.00s) + --- PASS: TestValidateCredentials/invalid_provider_type (0.00s) +=== RUN TestIsValidProviderType +=== RUN TestIsValidProviderType/cloudflare +=== RUN TestIsValidProviderType/route53 +=== RUN TestIsValidProviderType/digitalocean +=== RUN TestIsValidProviderType/invalid +=== RUN TestIsValidProviderType/empty +--- PASS: TestIsValidProviderType (0.00s) + --- PASS: TestIsValidProviderType/cloudflare (0.00s) + --- PASS: TestIsValidProviderType/route53 (0.00s) + --- PASS: TestIsValidProviderType/digitalocean (0.00s) + --- PASS: TestIsValidProviderType/invalid (0.00s) + --- PASS: TestIsValidProviderType/empty (0.00s) +=== RUN TestCredentialEncryptionRoundtrip +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialEncryptionRoundtrip (0.01s) +=== RUN TestUpdatePreservesCredentials +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestUpdatePreservesCredentials (0.01s) +=== RUN TestEncryptionServiceIntegration +--- PASS: TestEncryptionServiceIntegration (0.01s) +=== RUN TestDNSProviderService_TestFailure +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_TestFailure (0.00s) +=== RUN TestDNSProviderService_GetDecryptedCredentialsError +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_GetDecryptedCredentialsError (0.01s) +=== RUN TestDNSProviderService_GetDecryptedCredentials_InvalidJSON_ReturnsErrDecryptionFailed +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_GetDecryptedCredentials_InvalidJSON_ReturnsErrDecryptionFailed (0.00s) +=== RUN TestDNSProviderService_UpdateDefaultLogic +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_UpdateDefaultLogic (0.01s) +=== RUN TestAllProviderTypes +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +=== RUN TestAllProviderTypes/route53 +=== RUN TestAllProviderTypes/digitalocean +=== RUN TestAllProviderTypes/googleclouddns +=== RUN TestAllProviderTypes/vultr +=== RUN TestAllProviderTypes/cloudflare +=== RUN TestAllProviderTypes/namecheap +=== RUN TestAllProviderTypes/godaddy +=== RUN TestAllProviderTypes/azure +=== RUN TestAllProviderTypes/hetzner +=== RUN TestAllProviderTypes/dnsimple +--- PASS: TestAllProviderTypes (0.03s) + --- PASS: TestAllProviderTypes/route53 (0.00s) + --- PASS: TestAllProviderTypes/digitalocean (0.00s) + --- PASS: TestAllProviderTypes/googleclouddns (0.00s) + --- PASS: TestAllProviderTypes/vultr (0.00s) + --- PASS: TestAllProviderTypes/cloudflare (0.00s) + --- PASS: TestAllProviderTypes/namecheap (0.00s) + --- PASS: TestAllProviderTypes/godaddy (0.00s) + --- PASS: TestAllProviderTypes/azure (0.00s) + --- PASS: TestAllProviderTypes/hetzner (0.00s) + --- PASS: TestAllProviderTypes/dnsimple (0.00s) +=== RUN TestDNSProviderService_UpdateInvalidCredentials +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_UpdateInvalidCredentials (0.01s) +=== RUN TestDNSProviderService_CreateEncryptionError +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_CreateEncryptionError (0.00s) +=== RUN TestDNSProviderService_Update_PropagationTimeoutAndPollingInterval +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +=== RUN TestDNSProviderService_Update_PropagationTimeoutAndPollingInterval/update_propagation_timeout +=== RUN TestDNSProviderService_Update_PropagationTimeoutAndPollingInterval/update_polling_interval +=== RUN TestDNSProviderService_Update_PropagationTimeoutAndPollingInterval/update_both_timeout_and_interval +--- PASS: TestDNSProviderService_Update_PropagationTimeoutAndPollingInterval (0.01s) + --- PASS: TestDNSProviderService_Update_PropagationTimeoutAndPollingInterval/update_propagation_timeout (0.00s) + --- PASS: TestDNSProviderService_Update_PropagationTimeoutAndPollingInterval/update_polling_interval (0.00s) + --- PASS: TestDNSProviderService_Update_PropagationTimeoutAndPollingInterval/update_both_timeout_and_interval (0.00s) +=== RUN TestDNSProviderService_Test_NonExistentProvider +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Test_NonExistentProvider (0.00s) +=== RUN TestDNSProviderService_GetDecryptedCredentials_NonExistentProvider +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_GetDecryptedCredentials_NonExistentProvider (0.01s) +=== RUN TestDNSProviderService_TestWithFailedCredentials +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_TestWithFailedCredentials (0.01s) +=== RUN TestDNSProviderService_CreateWithEmptyCredentialValue +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_CreateWithEmptyCredentialValue (0.01s) +=== RUN TestDNSProviderService_Update_EmptyCredentials +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Update_EmptyCredentials (0.01s) +=== RUN TestDNSProviderService_Update_NilCredentials +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Update_NilCredentials (0.00s) +=== RUN TestDNSProviderService_Create_WithExistingDefault +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Create_WithExistingDefault (0.01s) +=== RUN TestDNSProviderService_Delete_AlreadyDeleted +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Delete_AlreadyDeleted (0.01s) +=== RUN TestTestDNSProviderCredentials_Validation +=== RUN TestTestDNSProviderCredentials_Validation/valid_cloudflare_credentials +=== RUN TestTestDNSProviderCredentials_Validation/missing_required_field +=== RUN TestTestDNSProviderCredentials_Validation/empty_required_field +--- PASS: TestTestDNSProviderCredentials_Validation (0.00s) + --- PASS: TestTestDNSProviderCredentials_Validation/valid_cloudflare_credentials (0.00s) + --- PASS: TestTestDNSProviderCredentials_Validation/missing_required_field (0.00s) + --- PASS: TestTestDNSProviderCredentials_Validation/empty_required_field (0.00s) +=== RUN TestDNSProviderService_Update_CredentialValidationError +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Update_CredentialValidationError (0.01s) +=== RUN TestDNSProviderService_TestCredentials_AllProviders +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +=== RUN TestDNSProviderService_TestCredentials_AllProviders/dnsimple +=== RUN TestDNSProviderService_TestCredentials_AllProviders/route53 +=== RUN TestDNSProviderService_TestCredentials_AllProviders/digitalocean +=== RUN TestDNSProviderService_TestCredentials_AllProviders/googleclouddns +=== RUN TestDNSProviderService_TestCredentials_AllProviders/azure +=== RUN TestDNSProviderService_TestCredentials_AllProviders/hetzner +=== RUN TestDNSProviderService_TestCredentials_AllProviders/vultr +=== RUN TestDNSProviderService_TestCredentials_AllProviders/cloudflare +=== RUN TestDNSProviderService_TestCredentials_AllProviders/namecheap +=== RUN TestDNSProviderService_TestCredentials_AllProviders/godaddy +--- PASS: TestDNSProviderService_TestCredentials_AllProviders (0.01s) + --- PASS: TestDNSProviderService_TestCredentials_AllProviders/dnsimple (0.00s) + --- PASS: TestDNSProviderService_TestCredentials_AllProviders/route53 (0.00s) + --- PASS: TestDNSProviderService_TestCredentials_AllProviders/digitalocean (0.00s) + --- PASS: TestDNSProviderService_TestCredentials_AllProviders/googleclouddns (0.00s) + --- PASS: TestDNSProviderService_TestCredentials_AllProviders/azure (0.00s) + --- PASS: TestDNSProviderService_TestCredentials_AllProviders/hetzner (0.00s) + --- PASS: TestDNSProviderService_TestCredentials_AllProviders/vultr (0.00s) + --- PASS: TestDNSProviderService_TestCredentials_AllProviders/cloudflare (0.00s) + --- PASS: TestDNSProviderService_TestCredentials_AllProviders/namecheap (0.00s) + --- PASS: TestDNSProviderService_TestCredentials_AllProviders/godaddy (0.00s) +=== RUN TestDNSProviderService_List_Empty +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_List_Empty (0.01s) +=== RUN TestDNSProviderService_Create_DefaultsApplied +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Create_DefaultsApplied (0.01s) +=== RUN TestDNSProviderService_Create_CustomTimeouts +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Create_CustomTimeouts (0.00s) +=== RUN TestDNSProviderService_List_OrderByDefault +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_List_OrderByDefault (0.01s) +=== RUN TestDNSProviderService_Update_MultipleFields +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Update_MultipleFields (0.01s) +=== RUN TestDNSProviderService_GetDecryptedCredentials_UpdatesLastUsed +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_GetDecryptedCredentials_UpdatesLastUsed (0.01s) +=== RUN TestDNSProviderService_Test_UpdatesStatistics +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Test_UpdatesStatistics (0.01s) +=== RUN TestDNSProviderService_Test_FailureUpdatesStatistics +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Test_FailureUpdatesStatistics (0.01s) +=== RUN TestDNSProviderService_List_DBError +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_List_DBError (0.00s) +=== RUN TestDNSProviderService_Get_DBError +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Get_DBError (0.00s) +=== RUN TestDNSProviderService_Create_DBErrorOnDefaultUnset +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Create_DBErrorOnDefaultUnset (0.01s) +=== RUN TestDNSProviderService_Create_DBErrorOnCreate +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Create_DBErrorOnCreate (0.00s) +=== RUN TestDNSProviderService_Update_DBErrorOnSave +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Update_DBErrorOnSave (0.01s) +=== RUN TestDNSProviderService_Update_DBErrorOnDefaultUnset +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Update_DBErrorOnDefaultUnset (0.01s) +=== RUN TestDNSProviderService_Delete_DBError +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_Delete_DBError (0.01s) +=== RUN TestDNSProviderService_AuditLogging_Create +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_AuditLogging_Create (0.11s) +=== RUN TestDNSProviderService_AuditLogging_Update +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_AuditLogging_Update (0.31s) +=== RUN TestDNSProviderService_AuditLogging_Delete +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_AuditLogging_Delete (0.31s) +=== RUN TestDNSProviderService_AuditLogging_Test +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_AuditLogging_Test (0.31s) +=== RUN TestDNSProviderService_AuditLogging_GetDecryptedCredentials +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestDNSProviderService_AuditLogging_GetDecryptedCredentials (0.31s) +=== RUN TestDNSProviderService_AuditLogging_ContextHelpers +--- PASS: TestDNSProviderService_AuditLogging_ContextHelpers (0.00s) +=== RUN TestDockerService_New +--- PASS: TestDockerService_New (0.00s) +=== RUN TestDockerService_ListContainers +--- PASS: TestDockerService_ListContainers (0.01s) +=== RUN TestDockerUnavailableError_ErrorMethods +--- PASS: TestDockerUnavailableError_ErrorMethods (0.00s) +=== RUN TestIsDockerConnectivityError +=== RUN TestIsDockerConnectivityError/nil_error +=== RUN TestIsDockerConnectivityError/daemon_not_running +=== RUN TestIsDockerConnectivityError/daemon_running_check +=== RUN TestIsDockerConnectivityError/error_during_connect +=== RUN TestIsDockerConnectivityError/connection_refused +=== RUN TestIsDockerConnectivityError/no_such_file +=== RUN TestIsDockerConnectivityError/context_timeout +=== RUN TestIsDockerConnectivityError/permission_denied_-_EACCES +=== RUN TestIsDockerConnectivityError/permission_denied_-_EPERM +=== RUN TestIsDockerConnectivityError/no_entry_-_ENOENT +=== RUN TestIsDockerConnectivityError/random_error +=== RUN TestIsDockerConnectivityError/empty_error +--- PASS: TestIsDockerConnectivityError (0.00s) + --- PASS: TestIsDockerConnectivityError/nil_error (0.00s) + --- PASS: TestIsDockerConnectivityError/daemon_not_running (0.00s) + --- PASS: TestIsDockerConnectivityError/daemon_running_check (0.00s) + --- PASS: TestIsDockerConnectivityError/error_during_connect (0.00s) + --- PASS: TestIsDockerConnectivityError/connection_refused (0.00s) + --- PASS: TestIsDockerConnectivityError/no_such_file (0.00s) + --- PASS: TestIsDockerConnectivityError/context_timeout (0.00s) + --- PASS: TestIsDockerConnectivityError/permission_denied_-_EACCES (0.00s) + --- PASS: TestIsDockerConnectivityError/permission_denied_-_EPERM (0.00s) + --- PASS: TestIsDockerConnectivityError/no_entry_-_ENOENT (0.00s) + --- PASS: TestIsDockerConnectivityError/random_error (0.00s) + --- PASS: TestIsDockerConnectivityError/empty_error (0.00s) +=== RUN TestIsDockerConnectivityError_URLError +--- PASS: TestIsDockerConnectivityError_URLError (0.00s) +=== RUN TestIsDockerConnectivityError_OpError +--- PASS: TestIsDockerConnectivityError_OpError (0.00s) +=== RUN TestIsDockerConnectivityError_SyscallError +--- PASS: TestIsDockerConnectivityError_SyscallError (0.00s) +=== RUN TestIsDockerConnectivityError_NetErrorTimeout +--- PASS: TestIsDockerConnectivityError_NetErrorTimeout (0.00s) +=== RUN TestNewGeoIPService_InvalidPath +--- PASS: TestNewGeoIPService_InvalidPath (0.00s) +=== RUN TestGeoIPService_NotLoaded +--- PASS: TestGeoIPService_NotLoaded (0.00s) +=== RUN TestGeoIPService_InvalidIP +--- PASS: TestGeoIPService_InvalidIP (0.00s) +=== RUN TestGeoIPService_LookupCountry_CountryNotFound +--- PASS: TestGeoIPService_LookupCountry_CountryNotFound (0.00s) +=== RUN TestGeoIPService_LookupCountry_Success +--- PASS: TestGeoIPService_LookupCountry_Success (0.00s) +=== RUN TestGeoIPService_LookupCountry_ReaderError +--- PASS: TestGeoIPService_LookupCountry_ReaderError (0.00s) +=== RUN TestGeoIPService_Close +--- PASS: TestGeoIPService_Close (0.00s) +=== RUN TestGeoIPService_GetDatabasePath +--- PASS: TestGeoIPService_GetDatabasePath (0.00s) +=== RUN TestGeoIPService_ConcurrentAccess +--- PASS: TestGeoIPService_ConcurrentAccess (0.00s) +=== RUN TestGeoIPService_Integration + geoip_service_test.go:134: GeoIP database not found, skipping integration test +--- SKIP: TestGeoIPService_Integration (0.00s) +=== RUN TestGeoIPService_ErrorTypes +--- PASS: TestGeoIPService_ErrorTypes (0.00s) +=== RUN TestLogService +--- PASS: TestLogService (0.00s) +=== RUN TestNewLogWatcher +=== PAUSE TestNewLogWatcher +=== RUN TestLogWatcherStartStop +=== PAUSE TestLogWatcherStartStop +=== RUN TestLogWatcherSubscribeUnsubscribe +=== PAUSE TestLogWatcherSubscribeUnsubscribe +=== RUN TestLogWatcherBroadcast +=== PAUSE TestLogWatcherBroadcast +=== RUN TestLogWatcherBroadcastNonBlocking +=== PAUSE TestLogWatcherBroadcastNonBlocking +=== RUN TestParseLogEntryValidJSON +=== PAUSE TestParseLogEntryValidJSON +=== RUN TestParseLogEntryInvalidJSON +=== PAUSE TestParseLogEntryInvalidJSON +=== RUN TestParseLogEntryBlockedByWAF +=== PAUSE TestParseLogEntryBlockedByWAF +=== RUN TestParseLogEntryBlockedByRateLimit +=== PAUSE TestParseLogEntryBlockedByRateLimit +=== RUN TestParseLogEntry403CrowdSec +=== PAUSE TestParseLogEntry403CrowdSec +=== RUN TestParseLogEntry401Auth +=== PAUSE TestParseLogEntry401Auth +=== RUN TestParseLogEntry500Error +=== PAUSE TestParseLogEntry500Error +=== RUN TestHasHeader +=== PAUSE TestHasHeader +=== RUN TestLogWatcherIntegration +=== PAUSE TestLogWatcherIntegration +=== RUN TestLogWatcherConcurrentSubscribers +=== PAUSE TestLogWatcherConcurrentSubscribers +=== RUN TestLogWatcherMissingFile +=== PAUSE TestLogWatcherMissingFile +=== RUN TestMin +=== PAUSE TestMin +=== RUN TestLogWatcher_ReadLoop_EOFRetry +=== PAUSE TestLogWatcher_ReadLoop_EOFRetry +=== RUN TestDetectSecurityEvent_WAFWithCorazaId +=== PAUSE TestDetectSecurityEvent_WAFWithCorazaId +=== RUN TestDetectSecurityEvent_WAFWithCorazaRuleId +=== PAUSE TestDetectSecurityEvent_WAFWithCorazaRuleId +=== RUN TestDetectSecurityEvent_CrowdSecWithDecisionHeader +=== PAUSE TestDetectSecurityEvent_CrowdSecWithDecisionHeader +=== RUN TestDetectSecurityEvent_CrowdSecWithOriginHeader +=== PAUSE TestDetectSecurityEvent_CrowdSecWithOriginHeader +=== RUN TestDetectSecurityEvent_ACLDeniedHeader +=== PAUSE TestDetectSecurityEvent_ACLDeniedHeader +=== RUN TestDetectSecurityEvent_ACLBlockedHeader +=== PAUSE TestDetectSecurityEvent_ACLBlockedHeader +=== RUN TestDetectSecurityEvent_RateLimitAllHeaders +=== PAUSE TestDetectSecurityEvent_RateLimitAllHeaders +=== RUN TestDetectSecurityEvent_RateLimitPartialHeaders +=== PAUSE TestDetectSecurityEvent_RateLimitPartialHeaders +=== RUN TestDetectSecurityEvent_403WithoutHeaders +=== PAUSE TestDetectSecurityEvent_403WithoutHeaders +=== RUN TestMailService_SaveAndGetSMTPConfig +--- PASS: TestMailService_SaveAndGetSMTPConfig (0.00s) +=== RUN TestMailService_UpdateSMTPConfig +--- PASS: TestMailService_UpdateSMTPConfig (0.01s) +=== RUN TestMailService_IsConfigured +=== RUN TestMailService_IsConfigured/configured_with_all_fields +=== RUN TestMailService_IsConfigured/not_configured_-_missing_host +=== RUN TestMailService_IsConfigured/not_configured_-_missing_from_address +--- PASS: TestMailService_IsConfigured (0.02s) + --- PASS: TestMailService_IsConfigured/configured_with_all_fields (0.00s) + --- PASS: TestMailService_IsConfigured/not_configured_-_missing_host (0.01s) + --- PASS: TestMailService_IsConfigured/not_configured_-_missing_from_address (0.00s) +=== RUN TestMailService_GetSMTPConfig_Defaults +--- PASS: TestMailService_GetSMTPConfig_Defaults (0.00s) +=== RUN TestMailService_BuildEmail +--- PASS: TestMailService_BuildEmail (0.00s) +=== RUN TestParseEmailAddressForHeader +=== RUN TestParseEmailAddressForHeader/valid_email +=== RUN TestParseEmailAddressForHeader/valid_email_with_name +=== RUN TestParseEmailAddressForHeader/empty_email +=== RUN TestParseEmailAddressForHeader/invalid_format +=== RUN TestParseEmailAddressForHeader/missing_domain +=== RUN TestParseEmailAddressForHeader/injection_attempt +--- PASS: TestParseEmailAddressForHeader (0.00s) + --- PASS: TestParseEmailAddressForHeader/valid_email (0.00s) + --- PASS: TestParseEmailAddressForHeader/valid_email_with_name (0.00s) + --- PASS: TestParseEmailAddressForHeader/empty_email (0.00s) + --- PASS: TestParseEmailAddressForHeader/invalid_format (0.00s) + --- PASS: TestParseEmailAddressForHeader/missing_domain (0.00s) + --- PASS: TestParseEmailAddressForHeader/injection_attempt (0.00s) +=== RUN TestMailService_BuildEmail_RejectsCRLFInSubject +--- PASS: TestMailService_BuildEmail_RejectsCRLFInSubject (0.00s) +=== RUN TestMailService_BuildEmail_RejectsCRLFInReplyTo +--- PASS: TestMailService_BuildEmail_RejectsCRLFInReplyTo (0.00s) +=== RUN TestMailService_SMTPDotStuffing +=== RUN TestMailService_SMTPDotStuffing/body_with_leading_period_on_line +=== RUN TestMailService_SMTPDotStuffing/body_with_SMTP_terminator_sequence +=== RUN TestMailService_SMTPDotStuffing/body_with_multiple_leading_periods +=== RUN TestMailService_SMTPDotStuffing/body_without_leading_periods +--- PASS: TestMailService_SMTPDotStuffing (0.00s) + --- PASS: TestMailService_SMTPDotStuffing/body_with_leading_period_on_line (0.00s) + --- PASS: TestMailService_SMTPDotStuffing/body_with_SMTP_terminator_sequence (0.00s) + --- PASS: TestMailService_SMTPDotStuffing/body_with_multiple_leading_periods (0.00s) + --- PASS: TestMailService_SMTPDotStuffing/body_without_leading_periods (0.00s) +=== RUN TestSanitizeEmailBody +=== RUN TestSanitizeEmailBody/single_leading_period +=== RUN TestSanitizeEmailBody/period_in_middle +=== RUN TestSanitizeEmailBody/multiple_lines_with_periods +=== RUN TestSanitizeEmailBody/SMTP_terminator +=== RUN TestSanitizeEmailBody/no_periods +=== RUN TestSanitizeEmailBody/empty_string +--- PASS: TestSanitizeEmailBody (0.00s) + --- PASS: TestSanitizeEmailBody/single_leading_period (0.00s) + --- PASS: TestSanitizeEmailBody/period_in_middle (0.00s) + --- PASS: TestSanitizeEmailBody/multiple_lines_with_periods (0.00s) + --- PASS: TestSanitizeEmailBody/SMTP_terminator (0.00s) + --- PASS: TestSanitizeEmailBody/no_periods (0.00s) + --- PASS: TestSanitizeEmailBody/empty_string (0.00s) +=== RUN TestMailService_TestConnection_NotConfigured +--- PASS: TestMailService_TestConnection_NotConfigured (0.00s) +=== RUN TestMailService_SendEmail_NotConfigured +--- PASS: TestMailService_SendEmail_NotConfigured (0.00s) +=== RUN TestMailService_SendEmail_RejectsCRLFInSubject +--- PASS: TestMailService_SendEmail_RejectsCRLFInSubject (0.00s) +=== RUN TestSMTPConfigSerialization +--- PASS: TestSMTPConfigSerialization (0.01s) +=== RUN TestMailService_SendInvite_Template +time="2026-01-10T02:18:09Z" level=info msg="Sending invite email" email=test@example.com +--- PASS: TestMailService_SendInvite_Template (0.00s) +=== RUN TestMailService_SendInvite_InvalidBaseURL_CRLF +--- PASS: TestMailService_SendInvite_InvalidBaseURL_CRLF (0.00s) +=== RUN TestMailService_SendInvite_InvalidBaseURL_Path +--- PASS: TestMailService_SendInvite_InvalidBaseURL_Path (0.00s) +=== RUN TestMailService_Integration + mail_service_test.go:422: Integration test requires SMTP server +--- SKIP: TestMailService_Integration (0.00s) +=== RUN TestMailService_SendInvite_TokenFormat +time="2026-01-10T02:18:09Z" level=info msg="Sending invite email" email=test@example.com +time="2026-01-10T02:18:09Z" level=info msg="Sending invite email" email=test@example.com +--- PASS: TestMailService_SendInvite_TokenFormat (0.01s) +=== RUN TestMailService_SaveSMTPConfig_Concurrent + mail_service_test.go:444: In-memory SQLite doesn't support concurrent writes - test real DB in integration +--- SKIP: TestMailService_SaveSMTPConfig_Concurrent (0.00s) +=== RUN TestMailService_SendEmail_InvalidRecipient +--- PASS: TestMailService_SendEmail_InvalidRecipient (0.00s) +=== RUN TestMailService_SendEmail_InvalidFromAddress +--- PASS: TestMailService_SendEmail_InvalidFromAddress (0.00s) +=== RUN TestMailService_SendEmail_EncryptionModes +=== RUN TestMailService_SendEmail_EncryptionModes/ssl +=== RUN TestMailService_SendEmail_EncryptionModes/starttls +=== RUN TestMailService_SendEmail_EncryptionModes/none +=== RUN TestMailService_SendEmail_EncryptionModes/empty +--- PASS: TestMailService_SendEmail_EncryptionModes (0.02s) + --- PASS: TestMailService_SendEmail_EncryptionModes/ssl (0.01s) + --- PASS: TestMailService_SendEmail_EncryptionModes/starttls (0.00s) + --- PASS: TestMailService_SendEmail_EncryptionModes/none (0.00s) + --- PASS: TestMailService_SendEmail_EncryptionModes/empty (0.00s) +=== RUN TestMailService_SendEmail_CRLFInjection_Comprehensive +=== RUN TestMailService_SendEmail_CRLFInjection_Comprehensive/CRLF_in_recipient_address +=== RUN TestMailService_SendEmail_CRLFInjection_Comprehensive/CRLF_in_subject_line +=== RUN TestMailService_SendEmail_CRLFInjection_Comprehensive/LF_only_in_recipient +=== RUN TestMailService_SendEmail_CRLFInjection_Comprehensive/LF_only_in_subject +=== RUN TestMailService_SendEmail_CRLFInjection_Comprehensive/CR_only_in_recipient +=== RUN TestMailService_SendEmail_CRLFInjection_Comprehensive/multiple_CRLF_sequences +--- PASS: TestMailService_SendEmail_CRLFInjection_Comprehensive (0.01s) + --- PASS: TestMailService_SendEmail_CRLFInjection_Comprehensive/CRLF_in_recipient_address (0.00s) + --- PASS: TestMailService_SendEmail_CRLFInjection_Comprehensive/CRLF_in_subject_line (0.00s) + --- PASS: TestMailService_SendEmail_CRLFInjection_Comprehensive/LF_only_in_recipient (0.00s) + --- PASS: TestMailService_SendEmail_CRLFInjection_Comprehensive/LF_only_in_subject (0.00s) + --- PASS: TestMailService_SendEmail_CRLFInjection_Comprehensive/CR_only_in_recipient (0.00s) + --- PASS: TestMailService_SendEmail_CRLFInjection_Comprehensive/multiple_CRLF_sequences (0.00s) +=== RUN TestMailService_SendInvite_CRLFInjection +=== RUN TestMailService_SendInvite_CRLFInjection/CRLF_in_email_address +=== RUN TestMailService_SendInvite_CRLFInjection/CRLF_in_baseURL +=== RUN TestMailService_SendInvite_CRLFInjection/CRLF_in_app_name_(subject) +--- PASS: TestMailService_SendInvite_CRLFInjection (0.01s) + --- PASS: TestMailService_SendInvite_CRLFInjection/CRLF_in_email_address (0.00s) + --- PASS: TestMailService_SendInvite_CRLFInjection/CRLF_in_baseURL (0.00s) + --- PASS: TestMailService_SendInvite_CRLFInjection/CRLF_in_app_name_(subject) (0.00s) +=== RUN TestSupportsJSONTemplates +=== RUN TestSupportsJSONTemplates/webhook +=== RUN TestSupportsJSONTemplates/discord +=== RUN TestSupportsJSONTemplates/slack +=== RUN TestSupportsJSONTemplates/gotify +=== RUN TestSupportsJSONTemplates/generic +=== RUN TestSupportsJSONTemplates/telegram +=== RUN TestSupportsJSONTemplates/unknown +=== RUN TestSupportsJSONTemplates/WEBHOOK_uppercase +=== RUN TestSupportsJSONTemplates/Discord_mixed_case +--- PASS: TestSupportsJSONTemplates (0.00s) + --- PASS: TestSupportsJSONTemplates/webhook (0.00s) + --- PASS: TestSupportsJSONTemplates/discord (0.00s) + --- PASS: TestSupportsJSONTemplates/slack (0.00s) + --- PASS: TestSupportsJSONTemplates/gotify (0.00s) + --- PASS: TestSupportsJSONTemplates/generic (0.00s) + --- PASS: TestSupportsJSONTemplates/telegram (0.00s) + --- PASS: TestSupportsJSONTemplates/unknown (0.00s) + --- PASS: TestSupportsJSONTemplates/WEBHOOK_uppercase (0.00s) + --- PASS: TestSupportsJSONTemplates/Discord_mixed_case (0.00s) +=== RUN TestSendJSONPayload_Discord +--- PASS: TestSendJSONPayload_Discord (0.00s) +=== RUN TestSendJSONPayload_Slack +--- PASS: TestSendJSONPayload_Slack (0.00s) +=== RUN TestSendJSONPayload_Gotify +--- PASS: TestSendJSONPayload_Gotify (0.00s) +=== RUN TestSendJSONPayload_TemplateTimeout +--- PASS: TestSendJSONPayload_TemplateTimeout (0.00s) +=== RUN TestSendJSONPayload_TemplateSizeLimit +--- PASS: TestSendJSONPayload_TemplateSizeLimit (0.00s) +=== RUN TestSendJSONPayload_DiscordValidation +--- PASS: TestSendJSONPayload_DiscordValidation (0.00s) +=== RUN TestSendJSONPayload_SlackValidation +--- PASS: TestSendJSONPayload_SlackValidation (0.00s) +=== RUN TestSendJSONPayload_GotifyValidation +--- PASS: TestSendJSONPayload_GotifyValidation (0.00s) +=== RUN TestSendJSONPayload_InvalidJSON +--- PASS: TestSendJSONPayload_InvalidJSON (0.00s) +=== RUN TestNormalizeURL_DiscordWebhook_ConvertsToDiscordScheme +--- PASS: TestNormalizeURL_DiscordWebhook_ConvertsToDiscordScheme (0.00s) +=== RUN TestSendExternal_SkipsInvalidHTTPDestination +time="2026-01-10T02:18:09Z" level=warning msg="Skipping notification for provider due to invalid destination" provider=bad +--- PASS: TestSendExternal_SkipsInvalidHTTPDestination (0.16s) +=== RUN TestSendExternal_UsesJSONForSupportedServices +--- PASS: TestSendExternal_UsesJSONForSupportedServices (0.10s) +=== RUN TestTestProvider_UsesJSONForSupportedServices +--- PASS: TestTestProvider_UsesJSONForSupportedServices (0.00s) +=== RUN TestNotificationService_TemplateCRUD +=== PAUSE TestNotificationService_TemplateCRUD +=== RUN TestNotificationService_Create +--- PASS: TestNotificationService_Create (0.00s) +=== RUN TestNotificationService_List +--- PASS: TestNotificationService_List (0.00s) +=== RUN TestNotificationService_MarkAsRead +--- PASS: TestNotificationService_MarkAsRead (0.01s) +=== RUN TestNotificationService_MarkAllAsRead +--- PASS: TestNotificationService_MarkAllAsRead (0.00s) +=== RUN TestNotificationService_Providers +--- PASS: TestNotificationService_Providers (0.01s) +=== RUN TestNotificationService_TestProvider_Webhook +--- PASS: TestNotificationService_TestProvider_Webhook (0.01s) +=== RUN TestNotificationService_SendExternal +--- PASS: TestNotificationService_SendExternal (0.01s) +=== RUN TestNotificationService_SendExternal_MinimalVsDetailedTemplates +--- PASS: TestNotificationService_SendExternal_MinimalVsDetailedTemplates (0.01s) +=== RUN TestNotificationService_SendExternal_Filtered +--- PASS: TestNotificationService_SendExternal_Filtered (0.11s) +=== RUN TestNotificationService_SendExternal_Shoutrrr +time="2026-01-10T02:18:10Z" level=error msg="Failed to send JSON notification" error="invalid webhook url" provider="Test Discord" +--- PASS: TestNotificationService_SendExternal_Shoutrrr (0.10s) +=== RUN TestNormalizeURL +=== RUN TestNormalizeURL/Discord_HTTPS +=== RUN TestNormalizeURL/Discord_HTTPS_with_app +=== RUN TestNormalizeURL/Discord_Shoutrrr +=== RUN TestNormalizeURL/Other_Service +--- PASS: TestNormalizeURL (0.00s) + --- PASS: TestNormalizeURL/Discord_HTTPS (0.00s) + --- PASS: TestNormalizeURL/Discord_HTTPS_with_app (0.00s) + --- PASS: TestNormalizeURL/Discord_Shoutrrr (0.00s) + --- PASS: TestNormalizeURL/Other_Service (0.00s) +=== RUN TestNotificationService_SendCustomWebhook_Errors +=== RUN TestNotificationService_SendCustomWebhook_Errors/invalid_URL +=== RUN TestNotificationService_SendCustomWebhook_Errors/unreachable_host +=== RUN TestNotificationService_SendCustomWebhook_Errors/server_returns_error +=== RUN TestNotificationService_SendCustomWebhook_Errors/valid_custom_payload_template +=== RUN TestNotificationService_SendCustomWebhook_Errors/default_payload_without_template +--- PASS: TestNotificationService_SendCustomWebhook_Errors (0.01s) + --- PASS: TestNotificationService_SendCustomWebhook_Errors/invalid_URL (0.00s) + --- PASS: TestNotificationService_SendCustomWebhook_Errors/unreachable_host (0.00s) + --- PASS: TestNotificationService_SendCustomWebhook_Errors/server_returns_error (0.00s) + --- PASS: TestNotificationService_SendCustomWebhook_Errors/valid_custom_payload_template (0.00s) + --- PASS: TestNotificationService_SendCustomWebhook_Errors/default_payload_without_template (0.00s) +=== RUN TestNotificationService_SendCustomWebhook_PropagatesRequestID +--- PASS: TestNotificationService_SendCustomWebhook_PropagatesRequestID (0.01s) +=== RUN TestNotificationService_TestProvider_Errors +=== RUN TestNotificationService_TestProvider_Errors/unsupported_provider_type +=== RUN TestNotificationService_TestProvider_Errors/webhook_with_invalid_URL +=== RUN TestNotificationService_TestProvider_Errors/discord_with_invalid_URL_format +=== RUN TestNotificationService_TestProvider_Errors/slack_with_unreachable_webhook +=== RUN TestNotificationService_TestProvider_Errors/webhook_success +--- PASS: TestNotificationService_TestProvider_Errors (0.01s) + --- PASS: TestNotificationService_TestProvider_Errors/unsupported_provider_type (0.00s) + --- PASS: TestNotificationService_TestProvider_Errors/webhook_with_invalid_URL (0.00s) + --- PASS: TestNotificationService_TestProvider_Errors/discord_with_invalid_URL_format (0.00s) + --- PASS: TestNotificationService_TestProvider_Errors/slack_with_unreachable_webhook (0.00s) + --- PASS: TestNotificationService_TestProvider_Errors/webhook_success (0.00s) +=== RUN TestSSRF_URLValidation_PrivateIP +--- PASS: TestSSRF_URLValidation_PrivateIP (0.00s) +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/10.0.0.0/8 +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/10.255.255.254 +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/172.16.0.0/12 +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/172.31.255.254 +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/192.168.0.0/16 +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/172.15.x_(not_private) +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/172.32.x_(not_private) +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/169.254.169.254 +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/localhost +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/127.0.0.1 +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/::1 +=== RUN TestSSRF_URLValidation_ComprehensiveBlocking/google.com +--- PASS: TestSSRF_URLValidation_ComprehensiveBlocking (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/10.0.0.0/8 (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/10.255.255.254 (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/172.16.0.0/12 (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/172.31.255.254 (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/192.168.0.0/16 (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/172.15.x_(not_private) (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/172.32.x_(not_private) (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/169.254.169.254 (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/localhost (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/127.0.0.1 (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/::1 (0.00s) + --- PASS: TestSSRF_URLValidation_ComprehensiveBlocking/google.com (0.00s) +=== RUN TestSSRF_WebhookIntegration +=== RUN TestSSRF_WebhookIntegration/blocks_private_IP_webhook +=== RUN TestSSRF_WebhookIntegration/blocks_cloud_metadata_endpoint +=== RUN TestSSRF_WebhookIntegration/allows_localhost_for_testing +--- PASS: TestSSRF_WebhookIntegration (0.01s) + --- PASS: TestSSRF_WebhookIntegration/blocks_private_IP_webhook (0.00s) + --- PASS: TestSSRF_WebhookIntegration/blocks_cloud_metadata_endpoint (0.00s) + --- PASS: TestSSRF_WebhookIntegration/allows_localhost_for_testing (0.00s) +=== RUN TestNotificationService_SendExternal_EdgeCases +=== RUN TestNotificationService_SendExternal_EdgeCases/no_enabled_providers +=== RUN TestNotificationService_SendExternal_EdgeCases/provider_filtered_by_category +=== RUN TestNotificationService_SendExternal_EdgeCases/custom_data_passed_to_webhook +--- PASS: TestNotificationService_SendExternal_EdgeCases (0.22s) + --- PASS: TestNotificationService_SendExternal_EdgeCases/no_enabled_providers (0.05s) + --- PASS: TestNotificationService_SendExternal_EdgeCases/provider_filtered_by_category (0.06s) + --- PASS: TestNotificationService_SendExternal_EdgeCases/custom_data_passed_to_webhook (0.11s) +=== RUN TestNotificationService_RenderTemplate +--- PASS: TestNotificationService_RenderTemplate (0.00s) +=== RUN TestNotificationService_CreateProvider_Validation +=== RUN TestNotificationService_CreateProvider_Validation/creates_provider_with_defaults +=== RUN TestNotificationService_CreateProvider_Validation/updates_existing_provider +=== RUN TestNotificationService_CreateProvider_Validation/deletes_non-existent_provider +--- PASS: TestNotificationService_CreateProvider_Validation (0.01s) + --- PASS: TestNotificationService_CreateProvider_Validation/creates_provider_with_defaults (0.00s) + --- PASS: TestNotificationService_CreateProvider_Validation/updates_existing_provider (0.00s) + --- PASS: TestNotificationService_CreateProvider_Validation/deletes_non-existent_provider (0.00s) +=== RUN TestNotificationService_IsPrivateIP +=== RUN TestNotificationService_IsPrivateIP/loopback_ipv4 +=== RUN TestNotificationService_IsPrivateIP/loopback_ipv6 +=== RUN TestNotificationService_IsPrivateIP/private_10.x +=== RUN TestNotificationService_IsPrivateIP/private_10.x_high +=== RUN TestNotificationService_IsPrivateIP/private_172.16-31 +=== RUN TestNotificationService_IsPrivateIP/private_172.31 +=== RUN TestNotificationService_IsPrivateIP/private_192.168 +=== RUN TestNotificationService_IsPrivateIP/public_172.32 +=== RUN TestNotificationService_IsPrivateIP/public_172.15 +=== RUN TestNotificationService_IsPrivateIP/public_ip +=== RUN TestNotificationService_IsPrivateIP/public_ipv6 +=== RUN TestNotificationService_IsPrivateIP/link_local_ipv4 +=== RUN TestNotificationService_IsPrivateIP/link_local_ipv6 +=== RUN TestNotificationService_IsPrivateIP/unique_local_ipv6_fc +=== RUN TestNotificationService_IsPrivateIP/unique_local_ipv6_fc_high +=== RUN TestNotificationService_IsPrivateIP/unique_local_ipv6_fd +--- PASS: TestNotificationService_IsPrivateIP (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/loopback_ipv4 (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/loopback_ipv6 (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/private_10.x (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/private_10.x_high (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/private_172.16-31 (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/private_172.31 (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/private_192.168 (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/public_172.32 (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/public_172.15 (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/public_ip (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/public_ipv6 (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/link_local_ipv4 (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/link_local_ipv6 (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/unique_local_ipv6_fc (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/unique_local_ipv6_fc_high (0.00s) + --- PASS: TestNotificationService_IsPrivateIP/unique_local_ipv6_fd (0.00s) +=== RUN TestNotificationService_CreateProvider_InvalidCustomTemplate +=== RUN TestNotificationService_CreateProvider_InvalidCustomTemplate/invalid_custom_template_on_create +=== RUN TestNotificationService_CreateProvider_InvalidCustomTemplate/invalid_custom_template_on_update +--- PASS: TestNotificationService_CreateProvider_InvalidCustomTemplate (0.00s) + --- PASS: TestNotificationService_CreateProvider_InvalidCustomTemplate/invalid_custom_template_on_create (0.00s) + --- PASS: TestNotificationService_CreateProvider_InvalidCustomTemplate/invalid_custom_template_on_update (0.00s) +=== RUN TestRenderTemplate_TemplateParseError +--- PASS: TestRenderTemplate_TemplateParseError (0.00s) +=== RUN TestRenderTemplate_TemplateExecutionError +--- PASS: TestRenderTemplate_TemplateExecutionError (0.00s) +=== RUN TestRenderTemplate_InvalidJSONOutput +--- PASS: TestRenderTemplate_InvalidJSONOutput (0.00s) +=== RUN TestSendCustomWebhook_HTTPStatusCodeErrors +=== RUN TestSendCustomWebhook_HTTPStatusCodeErrors/status_400 +=== RUN TestSendCustomWebhook_HTTPStatusCodeErrors/status_404 +=== RUN TestSendCustomWebhook_HTTPStatusCodeErrors/status_500 +=== RUN TestSendCustomWebhook_HTTPStatusCodeErrors/status_502 +=== RUN TestSendCustomWebhook_HTTPStatusCodeErrors/status_503 +--- PASS: TestSendCustomWebhook_HTTPStatusCodeErrors (0.01s) + --- PASS: TestSendCustomWebhook_HTTPStatusCodeErrors/status_400 (0.00s) + --- PASS: TestSendCustomWebhook_HTTPStatusCodeErrors/status_404 (0.00s) + --- PASS: TestSendCustomWebhook_HTTPStatusCodeErrors/status_500 (0.00s) + --- PASS: TestSendCustomWebhook_HTTPStatusCodeErrors/status_502 (0.00s) + --- PASS: TestSendCustomWebhook_HTTPStatusCodeErrors/status_503 (0.00s) +=== RUN TestSendCustomWebhook_TemplateSelection +=== RUN TestSendCustomWebhook_TemplateSelection/minimal_template +=== RUN TestSendCustomWebhook_TemplateSelection/detailed_template +=== RUN TestSendCustomWebhook_TemplateSelection/custom_template +=== RUN TestSendCustomWebhook_TemplateSelection/empty_template_defaults_to_minimal +=== RUN TestSendCustomWebhook_TemplateSelection/unknown_template_defaults_to_minimal +--- PASS: TestSendCustomWebhook_TemplateSelection (0.01s) + --- PASS: TestSendCustomWebhook_TemplateSelection/minimal_template (0.00s) + --- PASS: TestSendCustomWebhook_TemplateSelection/detailed_template (0.00s) + --- PASS: TestSendCustomWebhook_TemplateSelection/custom_template (0.00s) + --- PASS: TestSendCustomWebhook_TemplateSelection/empty_template_defaults_to_minimal (0.00s) + --- PASS: TestSendCustomWebhook_TemplateSelection/unknown_template_defaults_to_minimal (0.00s) +=== RUN TestSendCustomWebhook_EmptyCustomTemplateDefaultsToMinimal +--- PASS: TestSendCustomWebhook_EmptyCustomTemplateDefaultsToMinimal (0.00s) +=== RUN TestCreateProvider_EmptyCustomTemplateAllowed +--- PASS: TestCreateProvider_EmptyCustomTemplateAllowed (0.00s) +=== RUN TestUpdateProvider_NonCustomTemplateSkipsValidation +--- PASS: TestUpdateProvider_NonCustomTemplateSkipsValidation (0.01s) +=== RUN TestIsPrivateIP_EdgeCases +=== RUN TestIsPrivateIP_EdgeCases/172.15.255.255_(just_before_private) +=== RUN TestIsPrivateIP_EdgeCases/172.16.0.0_(start_of_private) +=== RUN TestIsPrivateIP_EdgeCases/172.31.255.255_(end_of_private) +=== RUN TestIsPrivateIP_EdgeCases/172.32.0.0_(just_after_private) +=== RUN TestIsPrivateIP_EdgeCases/fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff_(before_ULA) +=== RUN TestIsPrivateIP_EdgeCases/fc00::0_(start_of_ULA) +=== RUN TestIsPrivateIP_EdgeCases/fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff_(end_of_ULA) +=== RUN TestIsPrivateIP_EdgeCases/fe00::0_(after_ULA) +=== RUN TestIsPrivateIP_EdgeCases/fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff_(before_link-local) +=== RUN TestIsPrivateIP_EdgeCases/fe80::0_(start_of_link-local) +=== RUN TestIsPrivateIP_EdgeCases/febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff_(end_of_link-local) +=== RUN TestIsPrivateIP_EdgeCases/fec0::0_(after_link-local) +--- PASS: TestIsPrivateIP_EdgeCases (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/172.15.255.255_(just_before_private) (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/172.16.0.0_(start_of_private) (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/172.31.255.255_(end_of_private) (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/172.32.0.0_(just_after_private) (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff_(before_ULA) (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/fc00::0_(start_of_ULA) (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff_(end_of_ULA) (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/fe00::0_(after_ULA) (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff_(before_link-local) (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/fe80::0_(start_of_link-local) (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff_(end_of_link-local) (0.00s) + --- PASS: TestIsPrivateIP_EdgeCases/fec0::0_(after_link-local) (0.00s) +=== RUN TestSendCustomWebhook_ContextCancellation +--- PASS: TestSendCustomWebhook_ContextCancellation (0.00s) +=== RUN TestSendExternal_UnknownEventTypeSendsToAll +--- PASS: TestSendExternal_UnknownEventTypeSendsToAll (0.11s) +=== RUN TestCreateProvider_ValidCustomTemplate +--- PASS: TestCreateProvider_ValidCustomTemplate (0.01s) +=== RUN TestUpdateProvider_ValidCustomTemplate +--- PASS: TestUpdateProvider_ValidCustomTemplate (0.01s) +=== RUN TestRenderTemplate_MinimalAndDetailedTemplates +=== RUN TestRenderTemplate_MinimalAndDetailedTemplates/minimal_template +=== RUN TestRenderTemplate_MinimalAndDetailedTemplates/detailed_template +--- PASS: TestRenderTemplate_MinimalAndDetailedTemplates (0.01s) + --- PASS: TestRenderTemplate_MinimalAndDetailedTemplates/minimal_template (0.00s) + --- PASS: TestRenderTemplate_MinimalAndDetailedTemplates/detailed_template (0.00s) +=== RUN TestSendJSONPayload_ServiceSpecificValidation +=== RUN TestSendJSONPayload_ServiceSpecificValidation/discord_requires_content_or_embeds +=== RUN TestSendJSONPayload_ServiceSpecificValidation/discord_with_content_succeeds +=== RUN TestSendJSONPayload_ServiceSpecificValidation/discord_with_embeds_succeeds +=== RUN TestSendJSONPayload_ServiceSpecificValidation/slack_requires_text_or_blocks +=== RUN TestSendJSONPayload_ServiceSpecificValidation/slack_with_text_succeeds +=== RUN TestSendJSONPayload_ServiceSpecificValidation/slack_with_blocks_succeeds +=== RUN TestSendJSONPayload_ServiceSpecificValidation/gotify_requires_message +=== RUN TestSendJSONPayload_ServiceSpecificValidation/gotify_with_message_succeeds +--- PASS: TestSendJSONPayload_ServiceSpecificValidation (0.01s) + --- PASS: TestSendJSONPayload_ServiceSpecificValidation/discord_requires_content_or_embeds (0.00s) + --- PASS: TestSendJSONPayload_ServiceSpecificValidation/discord_with_content_succeeds (0.00s) + --- PASS: TestSendJSONPayload_ServiceSpecificValidation/discord_with_embeds_succeeds (0.00s) + --- PASS: TestSendJSONPayload_ServiceSpecificValidation/slack_requires_text_or_blocks (0.00s) + --- PASS: TestSendJSONPayload_ServiceSpecificValidation/slack_with_text_succeeds (0.00s) + --- PASS: TestSendJSONPayload_ServiceSpecificValidation/slack_with_blocks_succeeds (0.00s) + --- PASS: TestSendJSONPayload_ServiceSpecificValidation/gotify_requires_message (0.00s) + --- PASS: TestSendJSONPayload_ServiceSpecificValidation/gotify_with_message_succeeds (0.00s) +=== RUN TestSendExternal_AllEventTypes +=== RUN TestSendExternal_AllEventTypes/proxy_host +=== RUN TestSendExternal_AllEventTypes/remote_server +=== RUN TestSendExternal_AllEventTypes/domain +=== RUN TestSendExternal_AllEventTypes/cert +=== RUN TestSendExternal_AllEventTypes/uptime +=== RUN TestSendExternal_AllEventTypes/test +=== RUN TestSendExternal_AllEventTypes/unknown +--- PASS: TestSendExternal_AllEventTypes (0.75s) + --- PASS: TestSendExternal_AllEventTypes/proxy_host (0.11s) + --- PASS: TestSendExternal_AllEventTypes/remote_server (0.11s) + --- PASS: TestSendExternal_AllEventTypes/domain (0.11s) + --- PASS: TestSendExternal_AllEventTypes/cert (0.11s) + --- PASS: TestSendExternal_AllEventTypes/uptime (0.11s) + --- PASS: TestSendExternal_AllEventTypes/test (0.11s) + --- PASS: TestSendExternal_AllEventTypes/unknown (0.11s) +=== RUN TestIsValidRedirectURL +=== RUN TestIsValidRedirectURL/valid_http +=== RUN TestIsValidRedirectURL/valid_https +=== RUN TestIsValidRedirectURL/invalid_scheme_ftp +=== RUN TestIsValidRedirectURL/invalid_scheme_file +=== RUN TestIsValidRedirectURL/no_scheme +=== RUN TestIsValidRedirectURL/empty_hostname +=== RUN TestIsValidRedirectURL/invalid_url +=== RUN TestIsValidRedirectURL/javascript_scheme +=== RUN TestIsValidRedirectURL/data_scheme +--- PASS: TestIsValidRedirectURL (0.00s) + --- PASS: TestIsValidRedirectURL/valid_http (0.00s) + --- PASS: TestIsValidRedirectURL/valid_https (0.00s) + --- PASS: TestIsValidRedirectURL/invalid_scheme_ftp (0.00s) + --- PASS: TestIsValidRedirectURL/invalid_scheme_file (0.00s) + --- PASS: TestIsValidRedirectURL/no_scheme (0.00s) + --- PASS: TestIsValidRedirectURL/empty_hostname (0.00s) + --- PASS: TestIsValidRedirectURL/invalid_url (0.00s) + --- PASS: TestIsValidRedirectURL/javascript_scheme (0.00s) + --- PASS: TestIsValidRedirectURL/data_scheme (0.00s) +=== RUN TestSendExternal_ShoutrrrPath +--- PASS: TestSendExternal_ShoutrrrPath (0.10s) +=== RUN TestSendExternal_ShoutrrrPathWithHTTPValidation +--- PASS: TestSendExternal_ShoutrrrPathWithHTTPValidation (0.11s) +=== RUN TestSendExternal_ShoutrrrPathBlocksPrivateIP +time="2026-01-10T02:18:11Z" level=warning msg="Skipping notification for provider due to invalid destination" provider=private-ip +--- PASS: TestSendExternal_ShoutrrrPathBlocksPrivateIP (0.10s) +=== RUN TestSendExternal_ShoutrrrError +time="2026-01-10T02:18:12Z" level=error msg="Failed to send notification" error="shoutrrr error: connection failed" provider=error-test +--- PASS: TestSendExternal_ShoutrrrError (0.01s) +=== RUN TestTestProvider_ShoutrrrPath +--- PASS: TestTestProvider_ShoutrrrPath (0.01s) +=== RUN TestTestProvider_HTTPURLValidation +=== RUN TestTestProvider_HTTPURLValidation/blocks_private_IP +=== RUN TestTestProvider_HTTPURLValidation/allows_localhost +--- PASS: TestTestProvider_HTTPURLValidation (0.00s) + --- PASS: TestTestProvider_HTTPURLValidation/blocks_private_IP (0.00s) + --- PASS: TestTestProvider_HTTPURLValidation/allows_localhost (0.00s) +=== RUN TestSendJSONPayload_TemplateExecutionError +--- PASS: TestSendJSONPayload_TemplateExecutionError (0.00s) +=== RUN TestSendJSONPayload_InvalidJSONFromTemplate +--- PASS: TestSendJSONPayload_InvalidJSONFromTemplate (0.01s) +=== RUN TestSendJSONPayload_RequestCreationError +--- PASS: TestSendJSONPayload_RequestCreationError (0.00s) +=== RUN TestRenderTemplate_CustomTemplateWithWhitespace +=== RUN TestRenderTemplate_CustomTemplateWithWhitespace/detailed_with_spaces +=== RUN TestRenderTemplate_CustomTemplateWithWhitespace/minimal_with_tabs +=== RUN TestRenderTemplate_CustomTemplateWithWhitespace/custom_with_newlines +=== RUN TestRenderTemplate_CustomTemplateWithWhitespace/DETAILED_uppercase +=== RUN TestRenderTemplate_CustomTemplateWithWhitespace/MiNiMaL_mixed_case +--- PASS: TestRenderTemplate_CustomTemplateWithWhitespace (0.01s) + --- PASS: TestRenderTemplate_CustomTemplateWithWhitespace/detailed_with_spaces (0.00s) + --- PASS: TestRenderTemplate_CustomTemplateWithWhitespace/minimal_with_tabs (0.00s) + --- PASS: TestRenderTemplate_CustomTemplateWithWhitespace/custom_with_newlines (0.00s) + --- PASS: TestRenderTemplate_CustomTemplateWithWhitespace/DETAILED_uppercase (0.00s) + --- PASS: TestRenderTemplate_CustomTemplateWithWhitespace/MiNiMaL_mixed_case (0.00s) +=== RUN TestListTemplates_DBError + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/notification_service.go:449 sql: database is closed +[0.028ms] [rows:0] SELECT * FROM `notification_templates` ORDER BY created_at desc +--- PASS: TestListTemplates_DBError (0.00s) +=== RUN TestSendExternal_DBFetchError + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/notification_service.go:96 sql: database is closed +[0.028ms] [rows:0] SELECT * FROM `notification_providers` WHERE enabled = true +time="2026-01-10T02:18:12Z" level=error msg="Failed to fetch notification providers" error="sql: database is closed" +--- PASS: TestSendExternal_DBFetchError (0.00s) +=== RUN TestSendExternal_JSONPayloadError +time="2026-01-10T02:18:12Z" level=error msg="Failed to send JSON notification" error="discord payload requires 'content' or 'embeds' field" provider=json-error +--- PASS: TestSendExternal_JSONPayloadError (0.10s) +=== RUN TestSendJSONPayload_HTTPScheme +=== RUN TestSendJSONPayload_HTTPScheme/http +=== RUN TestSendJSONPayload_HTTPScheme/https +--- PASS: TestSendJSONPayload_HTTPScheme (0.01s) + --- PASS: TestSendJSONPayload_HTTPScheme/http (0.00s) + --- PASS: TestSendJSONPayload_HTTPScheme/https (0.00s) +=== RUN TestProxyHostService_ValidateUniqueDomain +=== RUN TestProxyHostService_ValidateUniqueDomain/New_unique_domain +=== RUN TestProxyHostService_ValidateUniqueDomain/Duplicate_domain +=== RUN TestProxyHostService_ValidateUniqueDomain/Same_domain_but_excluded_ID_(update_self) +--- PASS: TestProxyHostService_ValidateUniqueDomain (0.02s) + --- PASS: TestProxyHostService_ValidateUniqueDomain/New_unique_domain (0.00s) + --- PASS: TestProxyHostService_ValidateUniqueDomain/Duplicate_domain (0.00s) + --- PASS: TestProxyHostService_ValidateUniqueDomain/Same_domain_but_excluded_ID_(update_self) (0.00s) +=== RUN TestProxyHostService_CRUD + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/proxyhost_service.go:108 record not found +[0.066ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE `proxy_hosts`.`id` = 1 ORDER BY `proxy_hosts`.`id` LIMIT 1 +--- PASS: TestProxyHostService_CRUD (0.02s) +=== RUN TestProxyHostService_TestConnection +--- PASS: TestProxyHostService_TestConnection (0.02s) +=== RUN TestProxyHostService_AdvancedConfig +=== RUN TestProxyHostService_AdvancedConfig/Empty_advanced_config +=== RUN TestProxyHostService_AdvancedConfig/Valid_JSON_object +=== RUN TestProxyHostService_AdvancedConfig/Valid_JSON_array +=== RUN TestProxyHostService_AdvancedConfig/Invalid_JSON +=== RUN TestProxyHostService_AdvancedConfig/Valid_nested_config +--- PASS: TestProxyHostService_AdvancedConfig (0.02s) + --- PASS: TestProxyHostService_AdvancedConfig/Empty_advanced_config (0.00s) + --- PASS: TestProxyHostService_AdvancedConfig/Valid_JSON_object (0.00s) + --- PASS: TestProxyHostService_AdvancedConfig/Valid_JSON_array (0.00s) + --- PASS: TestProxyHostService_AdvancedConfig/Invalid_JSON (0.00s) + --- PASS: TestProxyHostService_AdvancedConfig/Valid_nested_config (0.00s) +=== RUN TestProxyHostService_UpdateAdvancedConfig +--- PASS: TestProxyHostService_UpdateAdvancedConfig (0.01s) +=== RUN TestProxyHostService_EmptyDomain +--- PASS: TestProxyHostService_EmptyDomain (0.01s) +=== RUN TestRemoteServerService_ValidateUniqueServer +--- PASS: TestRemoteServerService_ValidateUniqueServer (0.00s) +=== RUN TestRemoteServerService_CRUD + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/remoteserver_service.go:68 record not found +[0.084ms] [rows:0] SELECT * FROM `remote_servers` WHERE `remote_servers`.`id` = 2 ORDER BY `remote_servers`.`id` LIMIT 1 +--- PASS: TestRemoteServerService_CRUD (0.01s) +=== RUN TestGetPresets +--- PASS: TestGetPresets (0.00s) +=== RUN TestEnsurePresetsExist_Creates + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.092ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-basic" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.077ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-api-friendly" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.075ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-strict" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.091ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-paranoid" ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestEnsurePresetsExist_Creates (0.00s) +=== RUN TestEnsurePresetsExist_NoOp + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.080ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-basic" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.073ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-api-friendly" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.092ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-strict" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.071ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-paranoid" ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestEnsurePresetsExist_NoOp (0.01s) +=== RUN TestEnsurePresetsExist_Updates + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.093ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-api-friendly" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.148ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-strict" ORDER BY `security_header_profiles`.`id` LIMIT 1 + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_headers_service.go:116 record not found +[0.107ms] [rows:0] SELECT * FROM `security_header_profiles` WHERE uuid = "preset-paranoid" ORDER BY `security_header_profiles`.`id` LIMIT 1 +--- PASS: TestEnsurePresetsExist_Updates (0.01s) +=== RUN TestApplyPreset_Success +--- PASS: TestApplyPreset_Success (0.00s) +=== RUN TestApplyPreset_StrictPreset +--- PASS: TestApplyPreset_StrictPreset (0.00s) +=== RUN TestApplyPreset_ParanoidPreset +--- PASS: TestApplyPreset_ParanoidPreset (0.00s) +=== RUN TestApplyPreset_APIFriendlyPreset +--- PASS: TestApplyPreset_APIFriendlyPreset (0.00s) +=== RUN TestGetPresets_IncludesAPIFriendly +--- PASS: TestGetPresets_IncludesAPIFriendly (0.00s) +=== RUN TestGetPresets_OrderByScore +--- PASS: TestGetPresets_OrderByScore (0.00s) +=== RUN TestApplyPreset_InvalidPreset +--- PASS: TestApplyPreset_InvalidPreset (0.00s) +=== RUN TestApplyPreset_MultipleProfiles +--- PASS: TestApplyPreset_MultipleProfiles (0.01s) +=== RUN TestNewSecurityNotificationService +--- PASS: TestNewSecurityNotificationService (0.00s) +=== RUN TestSecurityNotificationService_GetSettings_Default + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:32 record not found +[0.101ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestSecurityNotificationService_GetSettings_Default (0.00s) +=== RUN TestSecurityNotificationService_UpdateSettings + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.066ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestSecurityNotificationService_UpdateSettings (0.00s) +=== RUN TestSecurityNotificationService_UpdateSettings_Existing + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.096ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestSecurityNotificationService_UpdateSettings_Existing (0.00s) +=== RUN TestSecurityNotificationService_Send_Disabled + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:32 record not found +[0.086ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestSecurityNotificationService_Send_Disabled (0.00s) +=== RUN TestSecurityNotificationService_Send_FilteredByEventType + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.069ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestSecurityNotificationService_Send_FilteredByEventType (0.00s) +=== RUN TestSecurityNotificationService_Send_FilteredBySeverity + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.060ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestSecurityNotificationService_Send_FilteredBySeverity (0.00s) +=== RUN TestSecurityNotificationService_Send_WebhookFailure + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.078ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +time="2026-01-10T02:18:12Z" level=error msg="Failed to send webhook notification" error="webhook returned status 500" +--- PASS: TestSecurityNotificationService_Send_WebhookFailure (0.00s) +=== RUN TestShouldNotify +=== RUN TestShouldNotify/error_>=_error +=== RUN TestShouldNotify/warn_<_error +=== RUN TestShouldNotify/error_>=_warn +=== RUN TestShouldNotify/info_>=_info +=== RUN TestShouldNotify/debug_<_info +=== RUN TestShouldNotify/error_>=_debug +--- PASS: TestShouldNotify (0.00s) + --- PASS: TestShouldNotify/error_>=_error (0.00s) + --- PASS: TestShouldNotify/warn_<_error (0.00s) + --- PASS: TestShouldNotify/error_>=_warn (0.00s) + --- PASS: TestShouldNotify/info_>=_info (0.00s) + --- PASS: TestShouldNotify/debug_<_info (0.00s) + --- PASS: TestShouldNotify/error_>=_debug (0.00s) +=== RUN TestSecurityNotificationService_Send_ACLDeny + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.075ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestSecurityNotificationService_Send_ACLDeny (0.00s) +=== RUN TestSecurityNotificationService_Send_ContextTimeout + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.102ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +time="2026-01-10T02:18:12Z" level=error msg="Failed to send webhook notification" error="execute request: Post \"http://127.0.0.1:41325\": context deadline exceeded" +--- PASS: TestSecurityNotificationService_Send_ContextTimeout (0.10s) +=== RUN TestSecurityNotificationService_Send_EventTypeFiltering_WAFDisabled + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.088ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestSecurityNotificationService_Send_EventTypeFiltering_WAFDisabled (0.00s) +=== RUN TestSecurityNotificationService_Send_EventTypeFiltering_ACLDisabled + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.107ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestSecurityNotificationService_Send_EventTypeFiltering_ACLDisabled (0.00s) +=== RUN TestSecurityNotificationService_Send_SeverityBelowThreshold + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.120ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestSecurityNotificationService_Send_SeverityBelowThreshold (0.00s) +=== RUN TestSecurityNotificationService_Send_WebhookSuccess + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.095ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +--- PASS: TestSecurityNotificationService_Send_WebhookSuccess (0.00s) +=== RUN TestSecurityNotificationService_sendWebhook_SSRFBlocked +=== RUN TestSecurityNotificationService_sendWebhook_SSRFBlocked/http://169.254.169.254/latest/meta-data/ +time="2026-01-10T02:18:12Z" level=warning msg="Blocked SSRF attempt in security notification webhook" error="connection to private ip addresses is blocked for security (detected IPv4-mapped IPv6: 169.254.169.254)" event_type=ssrf_blocked severity=HIGH url="http://169.254.169.254/latest/meta-data/" +=== RUN TestSecurityNotificationService_sendWebhook_SSRFBlocked/http://10.0.0.1/admin +time="2026-01-10T02:18:12Z" level=warning msg="Blocked SSRF attempt in security notification webhook" error="connection to private ip addresses is blocked for security (detected IPv4-mapped IPv6: 10.0.0.1)" event_type=ssrf_blocked severity=HIGH url="http://10.0.0.1/admin" +=== RUN TestSecurityNotificationService_sendWebhook_SSRFBlocked/http://172.16.0.1/config +time="2026-01-10T02:18:12Z" level=warning msg="Blocked SSRF attempt in security notification webhook" error="connection to private ip addresses is blocked for security (detected IPv4-mapped IPv6: 172.16.0.1)" event_type=ssrf_blocked severity=HIGH url="http://172.16.0.1/config" +=== RUN TestSecurityNotificationService_sendWebhook_SSRFBlocked/http://192.168.1.1/api +time="2026-01-10T02:18:12Z" level=warning msg="Blocked SSRF attempt in security notification webhook" error="connection to private ip addresses is blocked for security (detected IPv4-mapped IPv6: 192.168.1.1)" event_type=ssrf_blocked severity=HIGH url="http://192.168.1.1/api" +--- PASS: TestSecurityNotificationService_sendWebhook_SSRFBlocked (0.00s) + --- PASS: TestSecurityNotificationService_sendWebhook_SSRFBlocked/http://169.254.169.254/latest/meta-data/ (0.00s) + --- PASS: TestSecurityNotificationService_sendWebhook_SSRFBlocked/http://10.0.0.1/admin (0.00s) + --- PASS: TestSecurityNotificationService_sendWebhook_SSRFBlocked/http://172.16.0.1/config (0.00s) + --- PASS: TestSecurityNotificationService_sendWebhook_SSRFBlocked/http://192.168.1.1/api (0.00s) +=== RUN TestSecurityNotificationService_sendWebhook_MarshalError + security_notification_service_test.go:448: JSON marshal error cannot be easily triggered with current SecurityEvent structure +--- SKIP: TestSecurityNotificationService_sendWebhook_MarshalError (0.00s) +=== RUN TestSecurityNotificationService_sendWebhook_RequestCreationError +--- PASS: TestSecurityNotificationService_sendWebhook_RequestCreationError (0.00s) +=== RUN TestSecurityNotificationService_sendWebhook_RequestExecutionError +time="2026-01-10T02:18:12Z" level=warning msg="Blocked SSRF attempt in security notification webhook" error="dns resolution failed for invalid-nonexistent-domain-12345.test: lookup invalid-nonexistent-domain-12345.test on 127.0.0.53:53: no such host" event_type=ssrf_blocked severity=HIGH url="https://invalid-nonexistent-domain-12345.test/hook" +--- PASS: TestSecurityNotificationService_sendWebhook_RequestExecutionError (0.00s) +=== RUN TestSecurityNotificationService_sendWebhook_Non200Status +=== RUN TestSecurityNotificationService_sendWebhook_Non200Status/Bad_Request + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_notification_service.go:48 record not found +[0.130ms] [rows:0] SELECT * FROM `notification_configs` ORDER BY `notification_configs`.`id` LIMIT 1 +time="2026-01-10T02:18:12Z" level=error msg="Failed to send webhook notification" error="webhook returned status 400" +=== RUN TestSecurityNotificationService_sendWebhook_Non200Status/Not_Found +time="2026-01-10T02:18:12Z" level=error msg="Failed to send webhook notification" error="webhook returned status 404" +=== RUN TestSecurityNotificationService_sendWebhook_Non200Status/Internal_Server_Error +time="2026-01-10T02:18:12Z" level=error msg="Failed to send webhook notification" error="webhook returned status 500" +=== RUN TestSecurityNotificationService_sendWebhook_Non200Status/Bad_Gateway +time="2026-01-10T02:18:12Z" level=error msg="Failed to send webhook notification" error="webhook returned status 502" +=== RUN TestSecurityNotificationService_sendWebhook_Non200Status/Service_Unavailable +time="2026-01-10T02:18:12Z" level=error msg="Failed to send webhook notification" error="webhook returned status 503" +--- PASS: TestSecurityNotificationService_sendWebhook_Non200Status (0.01s) + --- PASS: TestSecurityNotificationService_sendWebhook_Non200Status/Bad_Request (0.00s) + --- PASS: TestSecurityNotificationService_sendWebhook_Non200Status/Not_Found (0.00s) + --- PASS: TestSecurityNotificationService_sendWebhook_Non200Status/Internal_Server_Error (0.00s) + --- PASS: TestSecurityNotificationService_sendWebhook_Non200Status/Bad_Gateway (0.00s) + --- PASS: TestSecurityNotificationService_sendWebhook_Non200Status/Service_Unavailable (0.00s) +=== RUN TestShouldNotify_AllSeverityCombinations +=== RUN TestShouldNotify_AllSeverityCombinations/debug_>=_debug +=== RUN TestShouldNotify_AllSeverityCombinations/debug_<_info +=== RUN TestShouldNotify_AllSeverityCombinations/debug_<_warn +=== RUN TestShouldNotify_AllSeverityCombinations/debug_<_error +=== RUN TestShouldNotify_AllSeverityCombinations/info_>=_debug +=== RUN TestShouldNotify_AllSeverityCombinations/info_>=_info +=== RUN TestShouldNotify_AllSeverityCombinations/info_<_warn +=== RUN TestShouldNotify_AllSeverityCombinations/info_<_error +=== RUN TestShouldNotify_AllSeverityCombinations/warn_>=_debug +=== RUN TestShouldNotify_AllSeverityCombinations/warn_>=_info +=== RUN TestShouldNotify_AllSeverityCombinations/warn_>=_warn +=== RUN TestShouldNotify_AllSeverityCombinations/warn_<_error +=== RUN TestShouldNotify_AllSeverityCombinations/error_>=_debug +=== RUN TestShouldNotify_AllSeverityCombinations/error_>=_info +=== RUN TestShouldNotify_AllSeverityCombinations/error_>=_warn +=== RUN TestShouldNotify_AllSeverityCombinations/error_>=_error +--- PASS: TestShouldNotify_AllSeverityCombinations (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/debug_>=_debug (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/debug_<_info (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/debug_<_warn (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/debug_<_error (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/info_>=_debug (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/info_>=_info (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/info_<_warn (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/info_<_error (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/warn_>=_debug (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/warn_>=_info (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/warn_>=_warn (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/warn_<_error (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/error_>=_debug (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/error_>=_info (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/error_>=_warn (0.00s) + --- PASS: TestShouldNotify_AllSeverityCombinations/error_>=_error (0.00s) +=== RUN TestCalculateSecurityScore_AllEnabled +--- PASS: TestCalculateSecurityScore_AllEnabled (0.00s) +=== RUN TestCalculateSecurityScore_HSTSOnly +--- PASS: TestCalculateSecurityScore_HSTSOnly (0.00s) +=== RUN TestCalculateSecurityScore_NoHeaders +--- PASS: TestCalculateSecurityScore_NoHeaders (0.00s) +=== RUN TestCalculateSecurityScore_UnsafeCSP +--- PASS: TestCalculateSecurityScore_UnsafeCSP (0.00s) +=== RUN TestCalculateSecurityScore_PartialCrossOrigin +--- PASS: TestCalculateSecurityScore_PartialCrossOrigin (0.00s) +=== RUN TestCalculateSecurityScore_WeakReferrerPolicy +--- PASS: TestCalculateSecurityScore_WeakReferrerPolicy (0.00s) +=== RUN TestCalculateSecurityScore_UnknownReferrerPolicy +--- PASS: TestCalculateSecurityScore_UnknownReferrerPolicy (0.00s) +=== RUN TestCalculateSecurityScore_ShortHSTSMaxAge +--- PASS: TestCalculateSecurityScore_ShortHSTSMaxAge (0.00s) +=== RUN TestSecurityService_Upsert_ValidateAdminWhitelist + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_service.go:106 record not found +[0.097ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_Upsert_ValidateAdminWhitelist (0.01s) +=== RUN TestSecurityService_BreakGlassTokenLifecycle + +2026/01/10 02:18:12 /projects/Charon/backend/internal/services/security_service.go:106 record not found +[0.120ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_BreakGlassTokenLifecycle (2.19s) +=== RUN TestSecurityService_LogDecisionAndList +--- PASS: TestSecurityService_LogDecisionAndList (0.01s) +=== RUN TestSecurityService_UpsertRuleSet + +2026/01/10 02:18:14 /projects/Charon/backend/internal/services/security_service.go:366 record not found +[0.082ms] [rows:0] SELECT * FROM `security_rule_sets` WHERE name = "owasp-crs" ORDER BY `security_rule_sets`.`id` LIMIT 1 +--- PASS: TestSecurityService_UpsertRuleSet (0.01s) +=== RUN TestSecurityService_UpsertRuleSet_ContentTooLarge +--- PASS: TestSecurityService_UpsertRuleSet_ContentTooLarge (0.02s) +=== RUN TestSecurityService_DeleteRuleSet + +2026/01/10 02:18:14 /projects/Charon/backend/internal/services/security_service.go:366 record not found +[0.093ms] [rows:0] SELECT * FROM `security_rule_sets` WHERE name = "owasp-crs" ORDER BY `security_rule_sets`.`id` LIMIT 1 +--- PASS: TestSecurityService_DeleteRuleSet (0.01s) +=== RUN TestSecurityService_Upsert_RejectExternalMode + +2026/01/10 02:18:14 /projects/Charon/backend/internal/services/security_service.go:106 record not found +[0.100ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_Upsert_RejectExternalMode (0.01s) +=== RUN TestSecurityService_GenerateBreakGlassToken_NewConfig + +2026/01/10 02:18:15 /projects/Charon/backend/internal/services/security_service.go:154 record not found +[0.245ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "newconfig" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_GenerateBreakGlassToken_NewConfig (1.54s) +=== RUN TestSecurityService_GenerateBreakGlassToken_UpdateExisting + +2026/01/10 02:18:16 /projects/Charon/backend/internal/services/security_service.go:106 record not found +[0.118ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_GenerateBreakGlassToken_UpdateExisting (2.98s) +=== RUN TestSecurityService_VerifyBreakGlassToken_NoConfig + +2026/01/10 02:18:19 /projects/Charon/backend/internal/services/security_service.go:175 record not found +[0.125ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "nonexistent" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_VerifyBreakGlassToken_NoConfig (0.01s) +=== RUN TestSecurityService_VerifyBreakGlassToken_NoHash + +2026/01/10 02:18:19 /projects/Charon/backend/internal/services/security_service.go:106 record not found +[0.103ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_VerifyBreakGlassToken_NoHash (0.01s) +=== RUN TestSecurityService_VerifyBreakGlassToken_WrongToken + +2026/01/10 02:18:20 /projects/Charon/backend/internal/services/security_service.go:154 record not found +[0.274ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_VerifyBreakGlassToken_WrongToken (4.42s) +=== RUN TestSecurityService_Get_NotFound + +2026/01/10 02:18:23 /projects/Charon/backend/internal/services/security_service.go:70 record not found +[0.109ms] [rows:0] SELECT * FROM `security_configs` ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_Get_NotFound (0.01s) +=== RUN TestSecurityService_Upsert_PreserveBreakGlassHash + +2026/01/10 02:18:24 /projects/Charon/backend/internal/services/security_service.go:154 record not found +[0.259ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_Upsert_PreserveBreakGlassHash (1.43s) +=== RUN TestSecurityService_Upsert_RateLimitFieldsPersist + +2026/01/10 02:18:25 /projects/Charon/backend/internal/services/security_service.go:106 record not found +[0.101ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_Upsert_RateLimitFieldsPersist (0.01s) +=== RUN TestSecurityService_LogAudit +--- PASS: TestSecurityService_LogAudit (0.16s) +=== RUN TestSecurityService_DeleteRuleSet_NotFound + +2026/01/10 02:18:25 /projects/Charon/backend/internal/services/security_service.go:388 record not found +[0.070ms] [rows:0] SELECT * FROM `security_rule_sets` WHERE `security_rule_sets`.`id` = 9999 ORDER BY `security_rule_sets`.`id` LIMIT 1 +--- PASS: TestSecurityService_DeleteRuleSet_NotFound (0.01s) +=== RUN TestSecurityService_ListDecisions_UnlimitedAndLimited +--- PASS: TestSecurityService_ListDecisions_UnlimitedAndLimited (0.01s) +=== RUN TestSecurityService_LogDecision_Nil +--- PASS: TestSecurityService_LogDecision_Nil (0.01s) +=== RUN TestSecurityService_LogDecision_PrefilledUUID +--- PASS: TestSecurityService_LogDecision_PrefilledUUID (0.01s) +=== RUN TestSecurityService_ListRuleSets_Empty +--- PASS: TestSecurityService_ListRuleSets_Empty (0.01s) +=== RUN TestSecurityService_Upsert_InvalidCrowdSecMode + +2026/01/10 02:18:25 /projects/Charon/backend/internal/services/security_service.go:106 record not found +[0.097ms] [rows:0] SELECT * FROM `security_configs` WHERE name = "default" ORDER BY `security_configs`.`id` LIMIT 1 +--- PASS: TestSecurityService_Upsert_InvalidCrowdSecMode (0.01s) +=== RUN TestSecurityService_ListAuditLogs +--- PASS: TestSecurityService_ListAuditLogs (0.01s) +=== RUN TestSecurityService_GetAuditLogByUUID + +2026/01/10 02:18:25 /projects/Charon/backend/internal/services/security_service.go:321 record not found +[0.049ms] [rows:0] SELECT * FROM `security_audits` WHERE uuid = "non-existent-uuid" ORDER BY `security_audits`.`id` LIMIT 1 +--- PASS: TestSecurityService_GetAuditLogByUUID (0.01s) +=== RUN TestSecurityService_ListAuditLogsByProvider +--- PASS: TestSecurityService_ListAuditLogsByProvider (0.01s) +=== RUN TestSecurityService_AsyncAuditLogging +--- PASS: TestSecurityService_AsyncAuditLogging (0.11s) +=== RUN TestUpdateService_CheckForUpdates +--- PASS: TestUpdateService_CheckForUpdates (0.00s) +=== RUN TestUpdateService_SetAPIURL_GitHubValidation +=== RUN TestUpdateService_SetAPIURL_GitHubValidation/valid_GitHub_API_HTTPS +=== RUN TestUpdateService_SetAPIURL_GitHubValidation/GitHub_with_HTTP_scheme +=== RUN TestUpdateService_SetAPIURL_GitHubValidation/non-GitHub_domain +=== RUN TestUpdateService_SetAPIURL_GitHubValidation/localhost_allowed +=== RUN TestUpdateService_SetAPIURL_GitHubValidation/127.0.0.1_allowed +=== RUN TestUpdateService_SetAPIURL_GitHubValidation/::1_IPv6_localhost_allowed +=== RUN TestUpdateService_SetAPIURL_GitHubValidation/invalid_URL +=== RUN TestUpdateService_SetAPIURL_GitHubValidation/ftp_scheme_not_allowed +=== RUN TestUpdateService_SetAPIURL_GitHubValidation/github.com_domain_allowed_with_HTTPS +=== RUN TestUpdateService_SetAPIURL_GitHubValidation/github.com_domain_with_HTTP_rejected +--- PASS: TestUpdateService_SetAPIURL_GitHubValidation (0.00s) + --- PASS: TestUpdateService_SetAPIURL_GitHubValidation/valid_GitHub_API_HTTPS (0.00s) + --- PASS: TestUpdateService_SetAPIURL_GitHubValidation/GitHub_with_HTTP_scheme (0.00s) + --- PASS: TestUpdateService_SetAPIURL_GitHubValidation/non-GitHub_domain (0.00s) + --- PASS: TestUpdateService_SetAPIURL_GitHubValidation/localhost_allowed (0.00s) + --- PASS: TestUpdateService_SetAPIURL_GitHubValidation/127.0.0.1_allowed (0.00s) + --- PASS: TestUpdateService_SetAPIURL_GitHubValidation/::1_IPv6_localhost_allowed (0.00s) + --- PASS: TestUpdateService_SetAPIURL_GitHubValidation/invalid_URL (0.00s) + --- PASS: TestUpdateService_SetAPIURL_GitHubValidation/ftp_scheme_not_allowed (0.00s) + --- PASS: TestUpdateService_SetAPIURL_GitHubValidation/github.com_domain_allowed_with_HTTPS (0.00s) + --- PASS: TestUpdateService_SetAPIURL_GitHubValidation/github.com_domain_with_HTTP_rejected (0.00s) +=== RUN TestUptimeService_sendRecoveryNotification +=== PAUSE TestUptimeService_sendRecoveryNotification +=== RUN TestCheckHost_RetryLogic +time="2026-01-10T02:18:25Z" level=info msg="Retrying TCP check" host_name="Test Host" max=2 retry=1 +time="2026-01-10T02:18:27Z" level=info msg="Retrying TCP check" host_name="Test Host" max=2 retry=2 +time="2026-01-10T02:18:29Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="Test Host" last_error="dial tcp 127.0.0.1:9: connect: connection refused" threshold=2 +--- PASS: TestCheckHost_RetryLogic (4.03s) +=== RUN TestCheckHost_Debouncing +time="2026-01-10T02:18:30Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="Test Host" last_error="dial tcp 192.0.2.1:9999: i/o timeout" threshold=2 + +2026/01/10 02:18:30 /projects/Charon/backend/internal/services/uptime_service_race_test.go:100 unrecognized token: "176d" +[0.064ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE d5b1c420-176d-4157-b59e-c1f70a24303b AND `uptime_hosts`.`id` = "d5b1c420-176d-4157-b59e-c1f70a24303b" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:31Z" level=info msg="Host status changed" host_ip=192.0.2.1 host_name="Test Host" message="TCP check failed: dial tcp 192.0.2.1:9999: i/o timeout" new=down old=up + +2026/01/10 02:18:31 /projects/Charon/backend/internal/services/uptime_service_race_test.go:106 unrecognized token: "176d" +[0.083ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE d5b1c420-176d-4157-b59e-c1f70a24303b AND `uptime_hosts`.`id` = "d5b1c420-176d-4157-b59e-c1f70a24303b" ORDER BY `uptime_hosts`.`id` LIMIT 1 +--- PASS: TestCheckHost_Debouncing (2.03s) +=== RUN TestCheckHost_FailureCountReset +time="2026-01-10T02:18:31Z" level=info msg="Host status changed" host_ip=127.0.0.1 host_name="Test Host" message="TCP connection to 127.0.0.1:40061 successful (retry 0)" new=up old=down + +2026/01/10 02:18:31 /projects/Charon/backend/internal/services/uptime_service_race_test.go:152 unrecognized token: "0251331e-0aa2" +[0.046ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE 0251331e-0aa2-485c-acee-3427ee4c31d5 AND `uptime_hosts`.`id` = "0251331e-0aa2-485c-acee-3427ee4c31d5" ORDER BY `uptime_hosts`.`id` LIMIT 1 +--- PASS: TestCheckHost_FailureCountReset (0.03s) +=== RUN TestCheckAllHosts_Synchronization +time="2026-01-10T02:18:31Z" level=info msg="Starting host checks" host_count=5 +time="2026-01-10T02:18:32Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="Host 1" last_error="dial tcp 192.0.2.1:9999: i/o timeout" threshold=2 +time="2026-01-10T02:18:32Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="Host 2" last_error="dial tcp 192.0.2.2:9999: i/o timeout" threshold=2 +time="2026-01-10T02:18:32Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="Host 3" last_error="dial tcp 192.0.2.3:9999: i/o timeout" threshold=2 +time="2026-01-10T02:18:32Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="Host 4" last_error="dial tcp 192.0.2.4:9999: i/o timeout" threshold=2 +time="2026-01-10T02:18:32Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="Host 5" last_error="dial tcp 192.0.2.5:9999: i/o timeout" threshold=2 +time="2026-01-10T02:18:32Z" level=info msg="All host checks completed" host_count=5 +--- PASS: TestCheckAllHosts_Synchronization (0.93s) +=== RUN TestCheckHost_ConcurrentChecks +--- PASS: TestCheckHost_ConcurrentChecks (0.03s) +=== RUN TestCheckHost_ContextCancellation +time="2026-01-10T02:18:32Z" level=warning msg="TCP check cancelled" host_name="Test Host" +--- PASS: TestCheckHost_ContextCancellation (0.03s) +=== RUN TestCheckAllHosts_StaggeredStartup +time="2026-01-10T02:18:32Z" level=info msg="Starting host checks" host_count=3 +time="2026-01-10T02:18:33Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="Host 1" last_error="dial tcp 192.0.2.1:9999: i/o timeout" threshold=2 +time="2026-01-10T02:18:33Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="Host 2" last_error="dial tcp 192.0.2.2:9999: i/o timeout" threshold=2 +time="2026-01-10T02:18:33Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="Host 3" last_error="dial tcp 192.0.2.3:9999: i/o timeout" threshold=2 +time="2026-01-10T02:18:33Z" level=info msg="All host checks completed" host_count=3 +--- PASS: TestCheckAllHosts_StaggeredStartup (0.62s) +=== RUN TestUptimeConfig_Defaults +--- PASS: TestUptimeConfig_Defaults (0.02s) +=== RUN TestCheckHost_HostMutexPreventsRaceCondition +--- PASS: TestCheckHost_HostMutexPreventsRaceCondition (0.03s) +=== RUN TestUptimeService_CheckAll + +2026/01/10 02:18:33 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.125ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:33 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.089ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "127.0.0.1" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:33Z" level=info msg="Created UptimeHost" host=127.0.0.1 host_id=e6915ec0-389e-4416-90d5-a31a824969f1 + +2026/01/10 02:18:33 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.088ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 2 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:33 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.087ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "127.0.0.2" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:33Z" level=info msg="Created UptimeHost" host=127.0.0.2 host_id=b8fb24fb-381f-4d60-9e94-9fde1d7981f3 +time="2026-01-10T02:18:33Z" level=info msg="Starting host checks" host_count=2 +time="2026-01-10T02:18:33Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="127.0.0.2:42303" last_error="dial tcp 127.0.0.2:42303: connect: connection refused" threshold=2 +time="2026-01-10T02:18:33Z" level=info msg="All host checks completed" host_count=2 +time="2026-01-10T02:18:33Z" level=info msg="Starting host checks" host_count=2 +time="2026-01-10T02:18:33Z" level=info msg="All host checks completed" host_count=2 +time="2026-01-10T02:18:33Z" level=info msg="Starting host checks" host_count=2 +time="2026-01-10T02:18:33Z" level=info msg="All host checks completed" host_count=2 +time="2026-01-10T02:18:34Z" level=info msg="Starting host checks" host_count=2 +time="2026-01-10T02:18:34Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="127.0.0.1:35477" last_error="dial tcp 127.0.0.1:35477: connect: connection refused" threshold=2 +time="2026-01-10T02:18:34Z" level=info msg="All host checks completed" host_count=2 +time="2026-01-10T02:18:34Z" level=info msg="Starting host checks" host_count=2 +time="2026-01-10T02:18:34Z" level=info msg="Host status changed" host_ip=127.0.0.1 host_name="127.0.0.1:35477" message="TCP check failed: dial tcp 127.0.0.1:35477: connect: connection refused" new=down old=up +time="2026-01-10T02:18:34Z" level=info msg="All host checks completed" host_count=2 +time="2026-01-10T02:18:34Z" level=info msg="Sent consolidated DOWN notification" host_name="127.0.0.1:35477" service_count=1 +time="2026-01-10T02:18:34Z" level=info msg="Starting host checks" host_count=2 +time="2026-01-10T02:18:34Z" level=info msg="All host checks completed" host_count=2 +--- PASS: TestUptimeService_CheckAll (1.78s) +=== RUN TestUptimeService_ListMonitors +--- PASS: TestUptimeService_ListMonitors (0.04s) +=== RUN TestUptimeService_GetMonitorByID +=== RUN TestUptimeService_GetMonitorByID/get_existing_monitor +=== RUN TestUptimeService_GetMonitorByID/get_non-existent_monitor + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:1045 record not found +[0.138ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE id = "non-existent" ORDER BY `uptime_monitors`.`id` LIMIT 1 +--- PASS: TestUptimeService_GetMonitorByID (0.04s) + --- PASS: TestUptimeService_GetMonitorByID/get_existing_monitor (0.00s) + --- PASS: TestUptimeService_GetMonitorByID/get_non-existent_monitor (0.00s) +=== RUN TestUptimeService_GetMonitorHistory +--- PASS: TestUptimeService_GetMonitorHistory (0.03s) +=== RUN TestUptimeService_SyncMonitors_Errors +=== RUN TestUptimeService_SyncMonitors_Errors/database_error_during_proxy_host_fetch + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:129 sql: database is closed +[0.025ms] [rows:0] SELECT * FROM `proxy_hosts` +=== RUN TestUptimeService_SyncMonitors_Errors/creates_monitors_for_new_hosts + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.116ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.067ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host= host_id=2137c317-4661-4772-9158-45d5e83005c5 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.105ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 2 ORDER BY `uptime_monitors`.`id` LIMIT 1 +=== RUN TestUptimeService_SyncMonitors_Errors/orphaned_monitors_persist_after_host_deletion + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.128ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.084ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host= host_id=3dca6b43-458c-4d6e-bfa4-4a50d4891384 +--- PASS: TestUptimeService_SyncMonitors_Errors (0.10s) + --- PASS: TestUptimeService_SyncMonitors_Errors/database_error_during_proxy_host_fetch (0.03s) + --- PASS: TestUptimeService_SyncMonitors_Errors/creates_monitors_for_new_hosts (0.03s) + --- PASS: TestUptimeService_SyncMonitors_Errors/orphaned_monitors_persist_after_host_deletion (0.03s) +=== RUN TestUptimeService_SyncMonitors_NameSync +=== RUN TestUptimeService_SyncMonitors_NameSync/syncs_name_from_proxy_host_when_changed + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.105ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.076ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host= host_id=c69fc6e8-989f-4b3d-92c2-d3f689aba3da +=== RUN TestUptimeService_SyncMonitors_NameSync/uses_domain_name_when_proxy_host_name_is_empty + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.102ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.093ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host= host_id=78f116c2-01a9-4e44-bbe6-caf207ae87d9 +=== RUN TestUptimeService_SyncMonitors_NameSync/updates_monitor_name_when_host_name_becomes_empty + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.102ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.098ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host= host_id=3e6136a3-8060-4333-8f49-68e669908345 +--- PASS: TestUptimeService_SyncMonitors_NameSync (0.11s) + --- PASS: TestUptimeService_SyncMonitors_NameSync/syncs_name_from_proxy_host_when_changed (0.04s) + --- PASS: TestUptimeService_SyncMonitors_NameSync/uses_domain_name_when_proxy_host_name_is_empty (0.04s) + --- PASS: TestUptimeService_SyncMonitors_NameSync/updates_monitor_name_when_host_name_becomes_empty (0.04s) +=== RUN TestUptimeService_SyncMonitors_TCPMigration +=== RUN TestUptimeService_SyncMonitors_TCPMigration/migrates_TCP_monitor_to_HTTP_for_public_URL + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.093ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "backend.local" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host=backend.local host_id=d67854eb-c642-4b7f-bf4b-d53da0f93214 +time="2026-01-10T02:18:35Z" level=info msg="Migrated monitor for host 1 to check public URL: http://public.com" host_id=1 +=== RUN TestUptimeService_SyncMonitors_TCPMigration/does_not_migrate_TCP_monitor_with_custom_URL + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.103ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "backend.local" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host=backend.local host_id=7dba5d0e-a022-48c5-8d9f-0c5b672c8f07 +--- PASS: TestUptimeService_SyncMonitors_TCPMigration (0.08s) + --- PASS: TestUptimeService_SyncMonitors_TCPMigration/migrates_TCP_monitor_to_HTTP_for_public_URL (0.04s) + --- PASS: TestUptimeService_SyncMonitors_TCPMigration/does_not_migrate_TCP_monitor_with_custom_URL (0.04s) +=== RUN TestUptimeService_SyncMonitors_HTTPSUpgrade +=== RUN TestUptimeService_SyncMonitors_HTTPSUpgrade/upgrades_HTTP_to_HTTPS_when_SSL_forced + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.106ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host= host_id=94b82ed6-4790-4bc8-9c7d-fa9b355e903c +time="2026-01-10T02:18:35Z" level=info msg="Upgraded monitor for host 1 to HTTPS: https://secure.com" host_id=1 +=== RUN TestUptimeService_SyncMonitors_HTTPSUpgrade/does_not_downgrade_HTTPS_when_SSL_not_forced + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.067ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host= host_id=091224d1-161c-4a6a-9196-c8e23b6f7d58 +--- PASS: TestUptimeService_SyncMonitors_HTTPSUpgrade (0.07s) + --- PASS: TestUptimeService_SyncMonitors_HTTPSUpgrade/upgrades_HTTP_to_HTTPS_when_SSL_forced (0.04s) + --- PASS: TestUptimeService_SyncMonitors_HTTPSUpgrade/does_not_downgrade_HTTPS_when_SSL_not_forced (0.03s) +=== RUN TestUptimeService_SyncMonitors_RemoteServers +=== RUN TestUptimeService_SyncMonitors_RemoteServers/creates_monitor_for_new_remote_server + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:234 record not found +[0.103ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE remote_server_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.062ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "backend.local" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host=backend.local host_id=24a184ca-2b50-45a3-a068-c90e570460eb +=== RUN TestUptimeService_SyncMonitors_RemoteServers/creates_TCP_monitor_for_remote_server_without_scheme + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:234 record not found +[0.085ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE remote_server_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.068ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "tcp.backend" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host=tcp.backend host_id=a2fded70-8b8b-433c-b7e1-8642785d0587 +=== RUN TestUptimeService_SyncMonitors_RemoteServers/syncs_remote_server_name_changes + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:234 record not found +[0.090ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE remote_server_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.097ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "server.local" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host=server.local host_id=fc5ca180-6106-495e-98ce-9495a0f4b4fb +=== RUN TestUptimeService_SyncMonitors_RemoteServers/syncs_remote_server_URL_changes + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:234 record not found +[0.145ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE remote_server_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.089ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "old.host" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host=old.host host_id=c6102d5c-f657-4c2d-ab13-fe52c54d1e06 +=== RUN TestUptimeService_SyncMonitors_RemoteServers/syncs_remote_server_enabled_status + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:234 record not found +[0.099ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE remote_server_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.092ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "server.local" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host=server.local host_id=bfc3d116-534e-4086-92f8-37f311fae8c4 +=== RUN TestUptimeService_SyncMonitors_RemoteServers/syncs_scheme_change_from_TCP_to_HTTPS + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:234 record not found +[0.087ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE remote_server_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.068ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "server.local" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host=server.local host_id=d5af8ec7-caa3-43fb-8804-d6f606f0e25d +--- PASS: TestUptimeService_SyncMonitors_RemoteServers (0.23s) + --- PASS: TestUptimeService_SyncMonitors_RemoteServers/creates_monitor_for_new_remote_server (0.04s) + --- PASS: TestUptimeService_SyncMonitors_RemoteServers/creates_TCP_monitor_for_remote_server_without_scheme (0.03s) + --- PASS: TestUptimeService_SyncMonitors_RemoteServers/syncs_remote_server_name_changes (0.05s) + --- PASS: TestUptimeService_SyncMonitors_RemoteServers/syncs_remote_server_URL_changes (0.03s) + --- PASS: TestUptimeService_SyncMonitors_RemoteServers/syncs_remote_server_enabled_status (0.04s) + --- PASS: TestUptimeService_SyncMonitors_RemoteServers/syncs_scheme_change_from_TCP_to_HTTPS (0.03s) +=== RUN TestUptimeService_CheckAll_Errors +=== RUN TestUptimeService_CheckAll_Errors/handles_empty_monitor_list +=== RUN TestUptimeService_CheckAll_Errors/orphan_monitors_don't_prevent_check_execution +=== RUN TestUptimeService_CheckAll_Errors/handles_timeout_for_slow_hosts + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.125ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:18:35 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.109ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "192.0.2.1" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:18:35Z" level=info msg="Created UptimeHost" host=192.0.2.1 host_id=213383a2-0a2c-4133-8296-591fd0750afa +time="2026-01-10T02:18:35Z" level=info msg="Starting host checks" host_count=1 +time="2026-01-10T02:18:45Z" level=info msg="Retrying TCP check" host_name="192.0.2.1:9999" max=2 retry=1 +time="2026-01-10T02:18:57Z" level=info msg="Retrying TCP check" host_name="192.0.2.1:9999" max=2 retry=2 +time="2026-01-10T02:19:09Z" level=warning msg="Host check failed, waiting for threshold" failure_count=1 host_name="192.0.2.1:9999" last_error="dial tcp 192.0.2.1:9999: i/o timeout" threshold=2 +time="2026-01-10T02:19:09Z" level=info msg="All host checks completed" host_count=1 +--- PASS: TestUptimeService_CheckAll_Errors (37.28s) + --- PASS: TestUptimeService_CheckAll_Errors/handles_empty_monitor_list (0.09s) + --- PASS: TestUptimeService_CheckAll_Errors/orphan_monitors_don't_prevent_check_execution (0.14s) + --- PASS: TestUptimeService_CheckAll_Errors/handles_timeout_for_slow_hosts (37.06s) +=== RUN TestUptimeService_CheckMonitor_EdgeCases +=== RUN TestUptimeService_CheckMonitor_EdgeCases/invalid_URL_format +=== RUN TestUptimeService_CheckMonitor_EdgeCases/http_404_response_treated_as_down + +2026/01/10 02:19:13 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.148ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:19:13 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.101ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "127.0.0.1" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:19:13Z" level=info msg="Created UptimeHost" host=127.0.0.1 host_id=b82fccf5-3b66-41d1-9418-58cd622ff533 +time="2026-01-10T02:19:13Z" level=info msg="Starting host checks" host_count=1 +time="2026-01-10T02:19:13Z" level=info msg="All host checks completed" host_count=1 +time="2026-01-10T02:19:13Z" level=info msg="Starting host checks" host_count=1 +time="2026-01-10T02:19:13Z" level=info msg="All host checks completed" host_count=1 +time="2026-01-10T02:19:13Z" level=info msg="Starting host checks" host_count=1 +time="2026-01-10T02:19:13Z" level=info msg="All host checks completed" host_count=1 +=== RUN TestUptimeService_CheckMonitor_EdgeCases/https_URL_without_valid_certificate +--- PASS: TestUptimeService_CheckMonitor_EdgeCases (3.98s) + --- PASS: TestUptimeService_CheckMonitor_EdgeCases/invalid_URL_format (0.54s) + --- PASS: TestUptimeService_CheckMonitor_EdgeCases/http_404_response_treated_as_down (0.40s) + --- PASS: TestUptimeService_CheckMonitor_EdgeCases/https_URL_without_valid_certificate (3.04s) +=== RUN TestUptimeService_GetMonitorHistory_EdgeCases +=== RUN TestUptimeService_GetMonitorHistory_EdgeCases/non-existent_monitor +=== RUN TestUptimeService_GetMonitorHistory_EdgeCases/limit_parameter_respected +--- PASS: TestUptimeService_GetMonitorHistory_EdgeCases (0.09s) + --- PASS: TestUptimeService_GetMonitorHistory_EdgeCases/non-existent_monitor (0.04s) + --- PASS: TestUptimeService_GetMonitorHistory_EdgeCases/limit_parameter_respected (0.05s) +=== RUN TestUptimeService_ListMonitors_EdgeCases +=== RUN TestUptimeService_ListMonitors_EdgeCases/empty_database +=== RUN TestUptimeService_ListMonitors_EdgeCases/monitors_with_associated_proxy_hosts +--- PASS: TestUptimeService_ListMonitors_EdgeCases (0.08s) + --- PASS: TestUptimeService_ListMonitors_EdgeCases/empty_database (0.04s) + --- PASS: TestUptimeService_ListMonitors_EdgeCases/monitors_with_associated_proxy_hosts (0.04s) +=== RUN TestUptimeService_UpdateMonitor +=== RUN TestUptimeService_UpdateMonitor/update_max_retries +=== RUN TestUptimeService_UpdateMonitor/update_interval +=== RUN TestUptimeService_UpdateMonitor/update_non-existent_monitor + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:1059 record not found +[0.113ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE id = "non-existent" ORDER BY `uptime_monitors`.`id` LIMIT 1 +=== RUN TestUptimeService_UpdateMonitor/update_multiple_fields +--- PASS: TestUptimeService_UpdateMonitor (0.14s) + --- PASS: TestUptimeService_UpdateMonitor/update_max_retries (0.04s) + --- PASS: TestUptimeService_UpdateMonitor/update_interval (0.03s) + --- PASS: TestUptimeService_UpdateMonitor/update_non-existent_monitor (0.03s) + --- PASS: TestUptimeService_UpdateMonitor/update_multiple_fields (0.03s) +=== RUN TestUptimeService_NotificationBatching +=== RUN TestUptimeService_NotificationBatching/batches_multiple_service_failures_on_same_host +time="2026-01-10T02:19:17Z" level=info msg="Created pending notification batch" host="Test Server" monitor="Service A" +time="2026-01-10T02:19:17Z" level=info msg="Added to pending notification batch" count=2 host="Test Server" monitor="Service B" +time="2026-01-10T02:19:17Z" level=info msg="Added to pending notification batch" count=3 host="Test Server" monitor="Service C" +time="2026-01-10T02:19:17Z" level=info msg="Sent batched DOWN notification" count=3 host="Test Server" +=== RUN TestUptimeService_NotificationBatching/single_service_down_gets_individual_notification +time="2026-01-10T02:19:17Z" level=info msg="Created pending notification batch" host="Single Service Host" monitor="Lonely Service" +time="2026-01-10T02:19:17Z" level=info msg="Sent batched DOWN notification" count=1 host="Single Service Host" +--- PASS: TestUptimeService_NotificationBatching (0.07s) + --- PASS: TestUptimeService_NotificationBatching/batches_multiple_service_failures_on_same_host (0.03s) + --- PASS: TestUptimeService_NotificationBatching/single_service_down_gets_individual_notification (0.03s) +=== RUN TestUptimeService_HostLevelCheck +=== RUN TestUptimeService_HostLevelCheck/creates_uptime_host_during_sync + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.147ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.093ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "10.0.0.50" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:19:17Z" level=info msg="Created UptimeHost" host=10.0.0.50 host_id=87948aad-f1f9-4a9f-a940-d5e1729e1d0a +=== RUN TestUptimeService_HostLevelCheck/groups_multiple_services_on_same_host + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.116ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.119ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "10.0.0.100" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:19:17Z" level=info msg="Created UptimeHost" host=10.0.0.100 host_id=ee3c3763-ed2c-4f91-9d9e-470f2f6cc6da + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.104ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 2 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.078ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 3 ORDER BY `uptime_monitors`.`id` LIMIT 1 +--- PASS: TestUptimeService_HostLevelCheck (0.08s) + --- PASS: TestUptimeService_HostLevelCheck/creates_uptime_host_during_sync (0.03s) + --- PASS: TestUptimeService_HostLevelCheck/groups_multiple_services_on_same_host (0.04s) +=== RUN TestFormatDuration +--- PASS: TestFormatDuration (0.00s) +=== RUN TestUptimeService_SyncMonitorForHost +=== RUN TestUptimeService_SyncMonitorForHost/updates_monitor_when_proxy_host_is_edited + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.142ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.105ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "10.0.0.1" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:19:17Z" level=info msg="Created UptimeHost" host=10.0.0.1 host_id=ec41d123-8daf-499f-84da-d1991ab2db39 +=== RUN TestUptimeService_SyncMonitorForHost/returns_nil_when_no_monitor_exists + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:1004 record not found +[0.139ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 +=== RUN TestUptimeService_SyncMonitorForHost/returns_error_when_host_does_not_exist + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:999 record not found +[0.131ms] [rows:0] SELECT * FROM `proxy_hosts` WHERE `proxy_hosts`.`id` = 99999 ORDER BY `proxy_hosts`.`id` LIMIT 1 +=== RUN TestUptimeService_SyncMonitorForHost/uses_domain_name_when_proxy_host_name_is_empty + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.139ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.091ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "10.0.0.4" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:19:17Z" level=info msg="Created UptimeHost" host=10.0.0.4 host_id=a829a6d4-3a36-4c53-b23d-d77baba6f48b +=== RUN TestUptimeService_SyncMonitorForHost/handles_multiple_domains_correctly + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:135 record not found +[0.162ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE proxy_host_id = 1 ORDER BY `uptime_monitors`.`id` LIMIT 1 + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:309 record not found +[0.126ms] [rows:0] SELECT * FROM `uptime_hosts` WHERE host = "10.0.0.5" ORDER BY `uptime_hosts`.`id` LIMIT 1 +time="2026-01-10T02:19:17Z" level=info msg="Created UptimeHost" host=10.0.0.5 host_id=0ff2ee7f-55d2-4b95-9e5a-21d3039dc2e4 +--- PASS: TestUptimeService_SyncMonitorForHost (0.18s) + --- PASS: TestUptimeService_SyncMonitorForHost/updates_monitor_when_proxy_host_is_edited (0.04s) + --- PASS: TestUptimeService_SyncMonitorForHost/returns_nil_when_no_monitor_exists (0.04s) + --- PASS: TestUptimeService_SyncMonitorForHost/returns_error_when_host_does_not_exist (0.04s) + --- PASS: TestUptimeService_SyncMonitorForHost/uses_domain_name_when_proxy_host_name_is_empty (0.04s) + --- PASS: TestUptimeService_SyncMonitorForHost/handles_multiple_domains_correctly (0.04s) +=== RUN TestUptimeService_DeleteMonitor +=== RUN TestUptimeService_DeleteMonitor/deletes_monitor_and_heartbeats + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service_test.go:1416 record not found +[0.082ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE id = "delete-test-1" ORDER BY `uptime_monitors`.`id` LIMIT 1 +=== RUN TestUptimeService_DeleteMonitor/returns_error_for_non-existent_monitor + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service.go:1087 record not found +[0.081ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE id = "non-existent-id" ORDER BY `uptime_monitors`.`id` LIMIT 1 +=== RUN TestUptimeService_DeleteMonitor/deletes_monitor_without_heartbeats + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service_test.go:1456 record not found +[0.080ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE id = "delete-no-hb" ORDER BY `uptime_monitors`.`id` LIMIT 1 +--- PASS: TestUptimeService_DeleteMonitor (0.10s) + --- PASS: TestUptimeService_DeleteMonitor/deletes_monitor_and_heartbeats (0.04s) + --- PASS: TestUptimeService_DeleteMonitor/returns_error_for_non-existent_monitor (0.03s) + --- PASS: TestUptimeService_DeleteMonitor/deletes_monitor_without_heartbeats (0.03s) +=== RUN TestUptimeService_UpdateMonitor_EnabledField +--- PASS: TestUptimeService_UpdateMonitor_EnabledField (0.03s) +=== RUN TestExtractPort +=== RUN TestExtractPort/http_url_default +=== RUN TestExtractPort/https_url_default +=== RUN TestExtractPort/http_with_port +=== RUN TestExtractPort/https_with_port +=== RUN TestExtractPort/host:port +=== RUN TestExtractPort/plain_host +=== RUN TestExtractPort/localhost_with_port +=== RUN TestExtractPort/ip_with_port +=== RUN TestExtractPort/ipv6_with_port +--- PASS: TestExtractPort (0.00s) + --- PASS: TestExtractPort/http_url_default (0.00s) + --- PASS: TestExtractPort/https_url_default (0.00s) + --- PASS: TestExtractPort/http_with_port (0.00s) + --- PASS: TestExtractPort/https_with_port (0.00s) + --- PASS: TestExtractPort/host:port (0.00s) + --- PASS: TestExtractPort/plain_host (0.00s) + --- PASS: TestExtractPort/localhost_with_port (0.00s) + --- PASS: TestExtractPort/ip_with_port (0.00s) + --- PASS: TestExtractPort/ipv6_with_port (0.00s) +=== RUN TestUpdateMonitorEnabled_Unit +--- PASS: TestUpdateMonitorEnabled_Unit (0.02s) +=== RUN TestDeleteMonitorDeletesHeartbeats_Unit + +2026/01/10 02:19:17 /projects/Charon/backend/internal/services/uptime_service_unit_test.go:77 record not found +[0.070ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE id = "c57faec9-44b0-4ec7-a50e-7acefc4aeba6" ORDER BY `uptime_monitors`.`id` LIMIT 1 +--- PASS: TestDeleteMonitorDeletesHeartbeats_Unit (0.06s) +=== RUN TestCheckMonitor_PublicAPI +--- PASS: TestCheckMonitor_PublicAPI (0.23s) +=== RUN TestCheckMonitor_InvalidURL +--- PASS: TestCheckMonitor_InvalidURL (0.08s) +=== RUN TestCheckMonitor_TCPSuccess +--- PASS: TestCheckMonitor_TCPSuccess (0.08s) +=== RUN TestCheckMonitor_TCPFailure +--- PASS: TestCheckMonitor_TCPFailure (10.07s) +=== RUN TestCheckMonitor_UnknownType +--- PASS: TestCheckMonitor_UnknownType (0.06s) +=== RUN TestDeleteMonitor_NonExistent + +2026/01/10 02:19:28 /projects/Charon/backend/internal/services/uptime_service.go:1087 record not found +[0.101ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE id = "non-existent-id" ORDER BY `uptime_monitors`.`id` LIMIT 1 +--- PASS: TestDeleteMonitor_NonExistent (0.06s) +=== RUN TestUpdateMonitor_NonExistent + +2026/01/10 02:19:28 /projects/Charon/backend/internal/services/uptime_service.go:1059 record not found +[0.111ms] [rows:0] SELECT * FROM `uptime_monitors` WHERE id = "non-existent-id" ORDER BY `uptime_monitors`.`id` LIMIT 1 +--- PASS: TestUpdateMonitor_NonExistent (0.08s) +=== RUN TestNewWebSocketTracker +--- PASS: TestNewWebSocketTracker (0.00s) +=== RUN TestWebSocketTracker_Register +--- PASS: TestWebSocketTracker_Register (0.00s) +=== RUN TestWebSocketTracker_Unregister +--- PASS: TestWebSocketTracker_Unregister (0.00s) +=== RUN TestWebSocketTracker_UnregisterNonExistent +--- PASS: TestWebSocketTracker_UnregisterNonExistent (0.00s) +=== RUN TestWebSocketTracker_UpdateActivity +--- PASS: TestWebSocketTracker_UpdateActivity (0.01s) +=== RUN TestWebSocketTracker_UpdateActivityNonExistent +--- PASS: TestWebSocketTracker_UpdateActivityNonExistent (0.00s) +=== RUN TestWebSocketTracker_GetAllConnections +--- PASS: TestWebSocketTracker_GetAllConnections (0.00s) +=== RUN TestWebSocketTracker_GetStats +--- PASS: TestWebSocketTracker_GetStats (0.00s) +=== RUN TestWebSocketTracker_GetStatsEmpty +--- PASS: TestWebSocketTracker_GetStatsEmpty (0.00s) +=== RUN TestWebSocketTracker_ConcurrentAccess +--- PASS: TestWebSocketTracker_ConcurrentAccess (0.00s) +=== RUN TestCredentialService_Create +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_Create (0.01s) +=== RUN TestCredentialService_Create_MultiCredentialNotEnabled +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_Create_MultiCredentialNotEnabled (0.01s) +=== RUN TestCredentialService_Create_InvalidCredentials +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_Create_InvalidCredentials (0.01s) +=== RUN TestCredentialService_List +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required + +2026/01/10 02:19:28 /projects/Charon/backend/internal/services/credential_service.go:196 database table is locked +[0.253ms] [rows:0] INSERT INTO `dns_provider_credentials` (`uuid`,`dns_provider_id`,`label`,`zone_filter`,`enabled`,`credentials_encrypted`,`key_version`,`propagation_timeout`,`polling_interval`,`last_used_at`,`success_count`,`failure_count`,`last_error`,`created_at`,`updated_at`) VALUES ("a60759d1-40a0-4022-8e9f-5a06705b0176",1,"Credential B","",true,"BYNMQRrcsf8KNGUlXIMrgWbXW0atvE1fizPtKfyYxLLKfiXTu4RhMoSIQGwL9baDAA==",1,120,5,NULL,0,0,"","2026-01-10 02:19:28.539","2026-01-10 02:19:28.539") RETURNING `id` + credential_service_test.go:149: + Error Trace: /projects/Charon/backend/internal/services/credential_service_test.go:149 + Error: Received unexpected error: + database table is locked + Test: TestCredentialService_List +--- FAIL: TestCredentialService_List (0.01s) +=== RUN TestCredentialService_Get +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_Get (0.01s) +=== RUN TestCredentialService_Get_NotFound +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required + +2026/01/10 02:19:28 /projects/Charon/backend/internal/services/credential_service.go:111 record not found +[0.308ms] [rows:0] SELECT * FROM `dns_provider_credentials` WHERE id = 9999 AND dns_provider_id = 1 ORDER BY `dns_provider_credentials`.`id` LIMIT 1 +--- PASS: TestCredentialService_Get_NotFound (0.01s) +=== RUN TestCredentialService_Update +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_Update (0.01s) +=== RUN TestCredentialService_Delete +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required + +2026/01/10 02:19:28 /projects/Charon/backend/internal/services/credential_service.go:111 record not found +[0.069ms] [rows:0] SELECT * FROM `dns_provider_credentials` WHERE id = 1 AND dns_provider_id = 1 ORDER BY `dns_provider_credentials`.`id` LIMIT 1 +--- PASS: TestCredentialService_Delete (0.01s) +=== RUN TestCredentialService_Test +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_Test (0.01s) +=== RUN TestCredentialService_GetCredentialForDomain_ExactMatch +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required + +2026/01/10 02:19:28 /projects/Charon/backend/internal/services/credential_service.go:196 database table is locked +[0.286ms] [rows:0] INSERT INTO `dns_provider_credentials` (`uuid`,`dns_provider_id`,`label`,`zone_filter`,`enabled`,`credentials_encrypted`,`key_version`,`propagation_timeout`,`polling_interval`,`last_used_at`,`success_count`,`failure_count`,`last_error`,`created_at`,`updated_at`) VALUES ("86f40fa1-bee2-4664-b3d1-d7bd1544248c",1,"Catch All","",true,"l3KUYfElVhFaDhUFgA45u5QLw4A067zoEoWT3yvrmgFeja/XfgPXsa+lgKh33p/YsmgSbR8ZRZA7Hw==",1,120,5,NULL,0,0,"","2026-01-10 02:19:28.578","2026-01-10 02:19:28.578") RETURNING `id` + credential_service_test.go:283: + Error Trace: /projects/Charon/backend/internal/services/credential_service_test.go:283 + Error: Received unexpected error: + database table is locked + Test: TestCredentialService_GetCredentialForDomain_ExactMatch +--- FAIL: TestCredentialService_GetCredentialForDomain_ExactMatch (0.01s) +=== RUN TestCredentialService_GetCredentialForDomain_WildcardMatch +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_GetCredentialForDomain_WildcardMatch (0.01s) +=== RUN TestCredentialService_GetCredentialForDomain_CatchAll +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_GetCredentialForDomain_CatchAll (0.01s) +=== RUN TestCredentialService_GetCredentialForDomain_NoMatch +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_GetCredentialForDomain_NoMatch (0.01s) +=== RUN TestCredentialService_GetCredentialForDomain_MultiCredNotEnabled +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_GetCredentialForDomain_MultiCredNotEnabled (0.01s) +=== RUN TestCredentialService_GetCredentialForDomain_MultipleZones +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_GetCredentialForDomain_MultipleZones (0.01s) +=== RUN TestCredentialService_EnableMultiCredentials +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_EnableMultiCredentials (0.01s) +=== RUN TestCredentialService_EnableMultiCredentials_AlreadyEnabled +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_EnableMultiCredentials_AlreadyEnabled (0.01s) +=== RUN TestCredentialService_EnableMultiCredentials_NoCredentials +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_EnableMultiCredentials_NoCredentials (0.01s) +=== RUN TestCredentialService_GetCredentialForDomain_IDN +Warning: RotationService initialization failed, using basic encryption: CHARON_ENCRYPTION_KEY is required +--- PASS: TestCredentialService_GetCredentialForDomain_IDN (0.01s) +=== CONT TestBackupService_GetAvailableSpace +=== CONT TestLogWatcherConcurrentSubscribers +=== RUN TestBackupService_GetAvailableSpace/returns_space_for_existing_directory +=== PAUSE TestBackupService_GetAvailableSpace/returns_space_for_existing_directory +=== CONT TestLogWatcherIntegration +time="2026-01-10T02:19:28Z" level=info msg="LogWatcher started" path=/tmp/TestLogWatcherIntegration833592932/001/access.log +=== CONT TestHasHeader +--- PASS: TestHasHeader (0.00s) +=== RUN TestBackupService_GetAvailableSpace/errors_for_missing_directory +=== CONT TestParseLogEntryValidJSON +--- PASS: TestParseLogEntryValidJSON (0.00s) +=== CONT TestParseLogEntry401Auth +--- PASS: TestParseLogEntry401Auth (0.00s) +=== PAUSE TestBackupService_GetAvailableSpace/errors_for_missing_directory +=== CONT TestParseLogEntry403CrowdSec +--- PASS: TestParseLogEntry403CrowdSec (0.00s) +=== CONT TestParseLogEntry500Error +--- PASS: TestParseLogEntry500Error (0.00s) +=== CONT TestParseLogEntryBlockedByRateLimit +--- PASS: TestParseLogEntryBlockedByRateLimit (0.00s) +=== CONT TestParseLogEntryBlockedByWAF +--- PASS: TestParseLogEntryBlockedByWAF (0.00s) +=== CONT TestParseLogEntryInvalidJSON +=== RUN TestParseLogEntryInvalidJSON/empty +=== CONT TestUptimeService_sendRecoveryNotification +=== RUN TestParseLogEntryInvalidJSON/not_json +=== RUN TestParseLogEntryInvalidJSON/incomplete_json +=== RUN TestParseLogEntryInvalidJSON/array_instead_of_object +--- PASS: TestParseLogEntryInvalidJSON (0.01s) + --- PASS: TestParseLogEntryInvalidJSON/empty (0.00s) + --- PASS: TestParseLogEntryInvalidJSON/not_json (0.00s) + --- PASS: TestParseLogEntryInvalidJSON/incomplete_json (0.00s) + --- PASS: TestParseLogEntryInvalidJSON/array_instead_of_object (0.00s) +=== CONT TestDetectSecurityEvent_403WithoutHeaders +--- PASS: TestDetectSecurityEvent_403WithoutHeaders (0.00s) +=== CONT TestNotificationService_TemplateCRUD +--- PASS: TestUptimeService_sendRecoveryNotification (0.01s) +=== CONT TestDetectSecurityEvent_RateLimitPartialHeaders +--- PASS: TestDetectSecurityEvent_RateLimitPartialHeaders (0.00s) +=== CONT TestDetectSecurityEvent_RateLimitAllHeaders +--- PASS: TestDetectSecurityEvent_RateLimitAllHeaders (0.00s) +=== CONT TestDetectSecurityEvent_ACLBlockedHeader +--- PASS: TestDetectSecurityEvent_ACLBlockedHeader (0.00s) +=== CONT TestDetectSecurityEvent_CrowdSecWithOriginHeader +--- PASS: TestDetectSecurityEvent_CrowdSecWithOriginHeader (0.00s) +=== CONT TestDetectSecurityEvent_CrowdSecWithDecisionHeader +--- PASS: TestDetectSecurityEvent_CrowdSecWithDecisionHeader (0.00s) +=== CONT TestDetectSecurityEvent_ACLDeniedHeader +--- PASS: TestDetectSecurityEvent_ACLDeniedHeader (0.00s) +=== CONT TestDetectSecurityEvent_WAFWithCorazaRuleId +--- PASS: TestDetectSecurityEvent_WAFWithCorazaRuleId (0.00s) +=== CONT TestLogWatcher_ReadLoop_EOFRetry +time="2026-01-10T02:19:28Z" level=info msg="LogWatcher started" path=/tmp/TestLogWatcher_ReadLoop_EOFRetry3222960328/001/access.log +--- PASS: TestNotificationService_TemplateCRUD (0.01s) +=== CONT TestMin +--- PASS: TestMin (0.00s) +=== CONT TestDetectSecurityEvent_WAFWithCorazaId +--- PASS: TestDetectSecurityEvent_WAFWithCorazaId (0.00s) +=== CONT TestLogWatcherMissingFile +time="2026-01-10T02:19:28Z" level=info msg="LogWatcher started" path=/tmp/TestLogWatcherMissingFile4258081303/001/nonexistent/access.log +--- PASS: TestLogWatcherConcurrentSubscribers (0.02s) +=== CONT TestLogWatcherBroadcastNonBlocking +--- PASS: TestLogWatcherBroadcastNonBlocking (0.00s) +=== CONT TestLogWatcherSubscribeUnsubscribe +--- PASS: TestLogWatcherSubscribeUnsubscribe (0.00s) +=== CONT TestLogWatcherBroadcast +--- PASS: TestLogWatcherBroadcast (0.00s) +=== CONT TestLogWatcherStartStop +time="2026-01-10T02:19:28Z" level=info msg="LogWatcher started" path=/tmp/TestLogWatcherStartStop1684556410/001/access.log +time="2026-01-10T02:19:28Z" level=info msg="LogWatcher stopped" +--- PASS: TestLogWatcherStartStop (0.00s) +=== CONT TestNewLogWatcher +--- PASS: TestNewLogWatcher (0.00s) +=== CONT TestBackupService_GetAvailableSpace/returns_space_for_existing_directory +=== CONT TestBackupService_GetAvailableSpace/errors_for_missing_directory +--- PASS: TestBackupService_GetAvailableSpace (0.00s) + --- PASS: TestBackupService_GetAvailableSpace/returns_space_for_existing_directory (0.00s) + --- PASS: TestBackupService_GetAvailableSpace/errors_for_missing_directory (0.00s) +time="2026-01-10T02:19:28Z" level=info msg="LogWatcher stopped" +--- PASS: TestLogWatcherIntegration (0.20s) +time="2026-01-10T02:19:28Z" level=info msg="LogWatcher stopped" +--- PASS: TestLogWatcherMissingFile (0.20s) +time="2026-01-10T02:19:28Z" level=info msg="LogWatcher stopped" +--- PASS: TestLogWatcher_ReadLoop_EOFRetry (0.20s) +FAIL +coverage: 80.8% of statements +FAIL github.com/Wikid82/charon/backend/internal/services 111.017s +=== RUN TestWithTx_Success +--- PASS: TestWithTx_Success (0.00s) +=== RUN TestWithTx_Panic +--- PASS: TestWithTx_Panic (0.00s) +=== RUN TestWithTx_MultipleOperations +--- PASS: TestWithTx_MultipleOperations (0.00s) +=== RUN TestGetTestTx_Cleanup +=== RUN TestGetTestTx_Cleanup/Subtest +--- PASS: TestGetTestTx_Cleanup (0.01s) + --- PASS: TestGetTestTx_Cleanup/Subtest (0.00s) +=== RUN TestGetTestTx_MultipleTransactions +=== RUN TestGetTestTx_MultipleTransactions/Transaction1 +=== RUN TestGetTestTx_MultipleTransactions/Transaction2 +--- PASS: TestGetTestTx_MultipleTransactions (0.00s) + --- PASS: TestGetTestTx_MultipleTransactions/Transaction1 (0.00s) + --- PASS: TestGetTestTx_MultipleTransactions/Transaction2 (0.00s) +=== RUN TestGetTestTx_UsageInMultipleFunctions +=== RUN TestGetTestTx_UsageInMultipleFunctions/MultiFunction +--- PASS: TestGetTestTx_UsageInMultipleFunctions (0.00s) + --- PASS: TestGetTestTx_UsageInMultipleFunctions/MultiFunction (0.00s) +=== RUN TestGetTestTx_Parallel +=== RUN TestGetTestTx_Parallel/Isolation1 +=== RUN TestGetTestTx_Parallel/Isolation2 +--- PASS: TestGetTestTx_Parallel (0.00s) + --- PASS: TestGetTestTx_Parallel/Isolation1 (0.00s) + --- PASS: TestGetTestTx_Parallel/Isolation2 (0.00s) +=== RUN TestGetTestTx_WithActualTestFailure +=== RUN TestGetTestTx_WithActualTestFailure/FailingSubtest +--- PASS: TestGetTestTx_WithActualTestFailure (0.00s) + --- PASS: TestGetTestTx_WithActualTestFailure/FailingSubtest (0.00s) +PASS +coverage: 100.0% of statements +ok github.com/Wikid82/charon/backend/internal/testutil (cached) coverage: 100.0% of statements +? github.com/Wikid82/charon/backend/internal/trace [no test files] +=== RUN TestConstantTimeCompare +=== PAUSE TestConstantTimeCompare +=== RUN TestConstantTimeCompareBytes +=== PAUSE TestConstantTimeCompareBytes +=== RUN TestSanitizeForLog +=== PAUSE TestSanitizeForLog +=== CONT TestSanitizeForLog +=== RUN TestSanitizeForLog/empty_string +=== CONT TestConstantTimeCompareBytes +=== RUN TestConstantTimeCompareBytes/equal_bytes +=== CONT TestConstantTimeCompare +=== RUN TestConstantTimeCompare/equal_strings +=== RUN TestSanitizeForLog/clean_string +=== RUN TestConstantTimeCompare/different_strings +=== RUN TestConstantTimeCompareBytes/different_bytes +=== RUN TestConstantTimeCompare/different_lengths +=== RUN TestConstantTimeCompareBytes/different_lengths +=== RUN TestSanitizeForLog/string_with_newline +=== RUN TestConstantTimeCompareBytes/empty_slices +=== RUN TestSanitizeForLog/string_with_carriage_return_and_newline +=== RUN TestConstantTimeCompare/empty_strings +=== RUN TestConstantTimeCompareBytes/nil_slices +--- PASS: TestConstantTimeCompareBytes (0.00s) + --- PASS: TestConstantTimeCompareBytes/equal_bytes (0.00s) + --- PASS: TestConstantTimeCompareBytes/different_bytes (0.00s) + --- PASS: TestConstantTimeCompareBytes/different_lengths (0.00s) + --- PASS: TestConstantTimeCompareBytes/empty_slices (0.00s) + --- PASS: TestConstantTimeCompareBytes/nil_slices (0.00s) +=== RUN TestSanitizeForLog/string_with_multiple_newlines +=== RUN TestConstantTimeCompare/one_empty +=== RUN TestConstantTimeCompare/unicode_equal +=== RUN TestSanitizeForLog/string_with_control_characters +=== RUN TestConstantTimeCompare/unicode_different +=== RUN TestSanitizeForLog/string_with_DEL_character_(0x7F) +=== RUN TestConstantTimeCompare/special_chars_equal +=== RUN TestSanitizeForLog/complex_string_with_mixed_control_chars +=== RUN TestConstantTimeCompare/whitespace_matters +--- PASS: TestConstantTimeCompare (0.00s) + --- PASS: TestConstantTimeCompare/equal_strings (0.00s) + --- PASS: TestConstantTimeCompare/different_strings (0.00s) + --- PASS: TestConstantTimeCompare/different_lengths (0.00s) + --- PASS: TestConstantTimeCompare/empty_strings (0.00s) + --- PASS: TestConstantTimeCompare/one_empty (0.00s) + --- PASS: TestConstantTimeCompare/unicode_equal (0.00s) + --- PASS: TestConstantTimeCompare/unicode_different (0.00s) + --- PASS: TestConstantTimeCompare/special_chars_equal (0.00s) + --- PASS: TestConstantTimeCompare/whitespace_matters (0.00s) +=== RUN TestSanitizeForLog/string_with_tabs_(0x09_is_control_char) +=== RUN TestSanitizeForLog/string_with_only_control_chars +--- PASS: TestSanitizeForLog (0.01s) + --- PASS: TestSanitizeForLog/empty_string (0.00s) + --- PASS: TestSanitizeForLog/clean_string (0.00s) + --- PASS: TestSanitizeForLog/string_with_newline (0.00s) + --- PASS: TestSanitizeForLog/string_with_carriage_return_and_newline (0.00s) + --- PASS: TestSanitizeForLog/string_with_multiple_newlines (0.00s) + --- PASS: TestSanitizeForLog/string_with_control_characters (0.00s) + --- PASS: TestSanitizeForLog/string_with_DEL_character_(0x7F) (0.00s) + --- PASS: TestSanitizeForLog/complex_string_with_mixed_control_chars (0.00s) + --- PASS: TestSanitizeForLog/string_with_tabs_(0x09_is_control_char) (0.00s) + --- PASS: TestSanitizeForLog/string_with_only_control_chars (0.00s) +PASS +coverage: 100.0% of statements +ok github.com/Wikid82/charon/backend/internal/util (cached) coverage: 100.0% of statements +=== RUN TestIsPrivateIP +=== RUN TestIsPrivateIP/10.0.0.1_is_private +=== RUN TestIsPrivateIP/10.255.255.255_is_private +=== RUN TestIsPrivateIP/10.10.10.10_is_private +=== RUN TestIsPrivateIP/172.16.0.1_is_private +=== RUN TestIsPrivateIP/172.31.255.255_is_private +=== RUN TestIsPrivateIP/172.20.0.1_is_private +=== RUN TestIsPrivateIP/192.168.1.1_is_private +=== RUN TestIsPrivateIP/192.168.0.1_is_private +=== RUN TestIsPrivateIP/192.168.255.255_is_private +=== RUN TestIsPrivateIP/172.17.0.2_is_private +=== RUN TestIsPrivateIP/172.18.0.5_is_private +=== RUN TestIsPrivateIP/8.8.8.8_is_public +=== RUN TestIsPrivateIP/1.1.1.1_is_public +=== RUN TestIsPrivateIP/142.250.80.14_is_public +=== RUN TestIsPrivateIP/203.0.113.50_is_public +=== RUN TestIsPrivateIP/172.15.0.1_is_public +=== RUN TestIsPrivateIP/172.32.0.1_is_public +=== RUN TestIsPrivateIP/nginx_hostname +=== RUN TestIsPrivateIP/my-app_hostname +=== RUN TestIsPrivateIP/app.local_hostname +=== RUN TestIsPrivateIP/example.com_hostname +=== RUN TestIsPrivateIP/my-container.internal_hostname +=== RUN TestIsPrivateIP/empty_string +=== RUN TestIsPrivateIP/malformed_IP +=== RUN TestIsPrivateIP/too_many_octets +=== RUN TestIsPrivateIP/negative_octet +=== RUN TestIsPrivateIP/octet_out_of_range +=== RUN TestIsPrivateIP/letters_in_IP +=== RUN TestIsPrivateIP/IPv6_address +=== RUN TestIsPrivateIP/IPv6_full_address +=== RUN TestIsPrivateIP/localhost_127.0.0.1 +=== RUN TestIsPrivateIP/0.0.0.0 +--- PASS: TestIsPrivateIP (0.00s) + --- PASS: TestIsPrivateIP/10.0.0.1_is_private (0.00s) + --- PASS: TestIsPrivateIP/10.255.255.255_is_private (0.00s) + --- PASS: TestIsPrivateIP/10.10.10.10_is_private (0.00s) + --- PASS: TestIsPrivateIP/172.16.0.1_is_private (0.00s) + --- PASS: TestIsPrivateIP/172.31.255.255_is_private (0.00s) + --- PASS: TestIsPrivateIP/172.20.0.1_is_private (0.00s) + --- PASS: TestIsPrivateIP/192.168.1.1_is_private (0.00s) + --- PASS: TestIsPrivateIP/192.168.0.1_is_private (0.00s) + --- PASS: TestIsPrivateIP/192.168.255.255_is_private (0.00s) + --- PASS: TestIsPrivateIP/172.17.0.2_is_private (0.00s) + --- PASS: TestIsPrivateIP/172.18.0.5_is_private (0.00s) + --- PASS: TestIsPrivateIP/8.8.8.8_is_public (0.00s) + --- PASS: TestIsPrivateIP/1.1.1.1_is_public (0.00s) + --- PASS: TestIsPrivateIP/142.250.80.14_is_public (0.00s) + --- PASS: TestIsPrivateIP/203.0.113.50_is_public (0.00s) + --- PASS: TestIsPrivateIP/172.15.0.1_is_public (0.00s) + --- PASS: TestIsPrivateIP/172.32.0.1_is_public (0.00s) + --- PASS: TestIsPrivateIP/nginx_hostname (0.00s) + --- PASS: TestIsPrivateIP/my-app_hostname (0.00s) + --- PASS: TestIsPrivateIP/app.local_hostname (0.00s) + --- PASS: TestIsPrivateIP/example.com_hostname (0.00s) + --- PASS: TestIsPrivateIP/my-container.internal_hostname (0.00s) + --- PASS: TestIsPrivateIP/empty_string (0.00s) + --- PASS: TestIsPrivateIP/malformed_IP (0.00s) + --- PASS: TestIsPrivateIP/too_many_octets (0.00s) + --- PASS: TestIsPrivateIP/negative_octet (0.00s) + --- PASS: TestIsPrivateIP/octet_out_of_range (0.00s) + --- PASS: TestIsPrivateIP/letters_in_IP (0.00s) + --- PASS: TestIsPrivateIP/IPv6_address (0.00s) + --- PASS: TestIsPrivateIP/IPv6_full_address (0.00s) + --- PASS: TestIsPrivateIP/localhost_127.0.0.1 (0.00s) + --- PASS: TestIsPrivateIP/0.0.0.0 (0.00s) +=== RUN TestIsDockerBridgeIP +=== RUN TestIsDockerBridgeIP/172.17.0.1_is_Docker_bridge +=== RUN TestIsDockerBridgeIP/172.17.0.2_is_Docker_bridge +=== RUN TestIsDockerBridgeIP/172.17.255.255_is_Docker_bridge +=== RUN TestIsDockerBridgeIP/172.18.0.1_is_Docker_bridge +=== RUN TestIsDockerBridgeIP/172.18.0.5_is_Docker_bridge +=== RUN TestIsDockerBridgeIP/172.20.0.1_is_Docker_bridge +=== RUN TestIsDockerBridgeIP/172.31.0.1_is_Docker_bridge +=== RUN TestIsDockerBridgeIP/172.31.255.255_is_Docker_bridge +=== RUN TestIsDockerBridgeIP/172.16.0.1_is_in_Docker_range +=== RUN TestIsDockerBridgeIP/10.0.0.1_is_not_Docker_bridge +=== RUN TestIsDockerBridgeIP/192.168.1.1_is_not_Docker_bridge +=== RUN TestIsDockerBridgeIP/8.8.8.8_is_public +=== RUN TestIsDockerBridgeIP/1.1.1.1_is_public +=== RUN TestIsDockerBridgeIP/172.15.0.1_is_outside_Docker_range +=== RUN TestIsDockerBridgeIP/172.32.0.1_is_outside_Docker_range +=== RUN TestIsDockerBridgeIP/nginx_hostname +=== RUN TestIsDockerBridgeIP/my-app_hostname +=== RUN TestIsDockerBridgeIP/container-name_hostname +=== RUN TestIsDockerBridgeIP/empty_string +=== RUN TestIsDockerBridgeIP/malformed_IP +=== RUN TestIsDockerBridgeIP/too_many_octets +=== RUN TestIsDockerBridgeIP/letters_in_IP +=== RUN TestIsDockerBridgeIP/IPv6_address +=== RUN TestIsDockerBridgeIP/localhost_127.0.0.1 +=== RUN TestIsDockerBridgeIP/0.0.0.0 +--- PASS: TestIsDockerBridgeIP (0.00s) + --- PASS: TestIsDockerBridgeIP/172.17.0.1_is_Docker_bridge (0.00s) + --- PASS: TestIsDockerBridgeIP/172.17.0.2_is_Docker_bridge (0.00s) + --- PASS: TestIsDockerBridgeIP/172.17.255.255_is_Docker_bridge (0.00s) + --- PASS: TestIsDockerBridgeIP/172.18.0.1_is_Docker_bridge (0.00s) + --- PASS: TestIsDockerBridgeIP/172.18.0.5_is_Docker_bridge (0.00s) + --- PASS: TestIsDockerBridgeIP/172.20.0.1_is_Docker_bridge (0.00s) + --- PASS: TestIsDockerBridgeIP/172.31.0.1_is_Docker_bridge (0.00s) + --- PASS: TestIsDockerBridgeIP/172.31.255.255_is_Docker_bridge (0.00s) + --- PASS: TestIsDockerBridgeIP/172.16.0.1_is_in_Docker_range (0.00s) + --- PASS: TestIsDockerBridgeIP/10.0.0.1_is_not_Docker_bridge (0.00s) + --- PASS: TestIsDockerBridgeIP/192.168.1.1_is_not_Docker_bridge (0.00s) + --- PASS: TestIsDockerBridgeIP/8.8.8.8_is_public (0.00s) + --- PASS: TestIsDockerBridgeIP/1.1.1.1_is_public (0.00s) + --- PASS: TestIsDockerBridgeIP/172.15.0.1_is_outside_Docker_range (0.00s) + --- PASS: TestIsDockerBridgeIP/172.32.0.1_is_outside_Docker_range (0.00s) + --- PASS: TestIsDockerBridgeIP/nginx_hostname (0.00s) + --- PASS: TestIsDockerBridgeIP/my-app_hostname (0.00s) + --- PASS: TestIsDockerBridgeIP/container-name_hostname (0.00s) + --- PASS: TestIsDockerBridgeIP/empty_string (0.00s) + --- PASS: TestIsDockerBridgeIP/malformed_IP (0.00s) + --- PASS: TestIsDockerBridgeIP/too_many_octets (0.00s) + --- PASS: TestIsDockerBridgeIP/letters_in_IP (0.00s) + --- PASS: TestIsDockerBridgeIP/IPv6_address (0.00s) + --- PASS: TestIsDockerBridgeIP/localhost_127.0.0.1 (0.00s) + --- PASS: TestIsDockerBridgeIP/0.0.0.0 (0.00s) +=== RUN TestIsPrivateIP_IPv4Mapped +=== RUN TestIsPrivateIP_IPv4Mapped/::ffff:10.0.0.1_mapped +=== RUN TestIsPrivateIP_IPv4Mapped/::ffff:192.168.1.1_mapped +=== RUN TestIsPrivateIP_IPv4Mapped/::ffff:8.8.8.8_mapped +--- PASS: TestIsPrivateIP_IPv4Mapped (0.00s) + --- PASS: TestIsPrivateIP_IPv4Mapped/::ffff:10.0.0.1_mapped (0.00s) + --- PASS: TestIsPrivateIP_IPv4Mapped/::ffff:192.168.1.1_mapped (0.00s) + --- PASS: TestIsPrivateIP_IPv4Mapped/::ffff:8.8.8.8_mapped (0.00s) +=== RUN TestIsPrivateIP_CIDRParseError +=== RUN TestIsPrivateIP_CIDRParseError/10.0.0.1/8 +=== RUN TestIsPrivateIP_CIDRParseError/10.0.0.256 +=== RUN TestIsPrivateIP_CIDRParseError/999.999.999.999 +=== RUN TestIsPrivateIP_CIDRParseError/10.0.0 +=== RUN TestIsPrivateIP_CIDRParseError/not-an-ip +=== RUN TestIsPrivateIP_CIDRParseError/#00 +=== RUN TestIsPrivateIP_CIDRParseError/10.0.0.1.1 +--- PASS: TestIsPrivateIP_CIDRParseError (0.00s) + --- PASS: TestIsPrivateIP_CIDRParseError/10.0.0.1/8 (0.00s) + --- PASS: TestIsPrivateIP_CIDRParseError/10.0.0.256 (0.00s) + --- PASS: TestIsPrivateIP_CIDRParseError/999.999.999.999 (0.00s) + --- PASS: TestIsPrivateIP_CIDRParseError/10.0.0 (0.00s) + --- PASS: TestIsPrivateIP_CIDRParseError/not-an-ip (0.00s) + --- PASS: TestIsPrivateIP_CIDRParseError/#00 (0.00s) + --- PASS: TestIsPrivateIP_CIDRParseError/10.0.0.1.1 (0.00s) +=== RUN TestIsDockerBridgeIP_CIDRParseError +=== RUN TestIsDockerBridgeIP_CIDRParseError/172.17.0.1/16 +=== RUN TestIsDockerBridgeIP_CIDRParseError/172.17.0.256 +=== RUN TestIsDockerBridgeIP_CIDRParseError/999.999.999.999 +=== RUN TestIsDockerBridgeIP_CIDRParseError/172.17 +=== RUN TestIsDockerBridgeIP_CIDRParseError/not-an-ip +=== RUN TestIsDockerBridgeIP_CIDRParseError/#00 +--- PASS: TestIsDockerBridgeIP_CIDRParseError (0.00s) + --- PASS: TestIsDockerBridgeIP_CIDRParseError/172.17.0.1/16 (0.00s) + --- PASS: TestIsDockerBridgeIP_CIDRParseError/172.17.0.256 (0.00s) + --- PASS: TestIsDockerBridgeIP_CIDRParseError/999.999.999.999 (0.00s) + --- PASS: TestIsDockerBridgeIP_CIDRParseError/172.17 (0.00s) + --- PASS: TestIsDockerBridgeIP_CIDRParseError/not-an-ip (0.00s) + --- PASS: TestIsDockerBridgeIP_CIDRParseError/#00 (0.00s) +=== RUN TestIsPrivateIP_IPv6Comprehensive +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback_expanded +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_2 +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fc00 +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fd00 +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fdff +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_public_Google_DNS +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_public_Cloudflare +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_mapped_private +=== RUN TestIsPrivateIP_IPv6Comprehensive/IPv6_mapped_public +=== RUN TestIsPrivateIP_IPv6Comprehensive/Invalid_IPv6 +=== RUN TestIsPrivateIP_IPv6Comprehensive/Incomplete_IPv6 +--- PASS: TestIsPrivateIP_IPv6Comprehensive (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_loopback_expanded (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_link-local_2 (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fc00 (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fd00 (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_unique_local_fdff (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_public_Google_DNS (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_public_Cloudflare (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_mapped_private (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/IPv6_mapped_public (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/Invalid_IPv6 (0.00s) + --- PASS: TestIsPrivateIP_IPv6Comprehensive/Incomplete_IPv6 (0.00s) +=== RUN TestIsDockerBridgeIP_EdgeCases +=== RUN TestIsDockerBridgeIP_EdgeCases/Lower_boundary_-_1 +=== RUN TestIsDockerBridgeIP_EdgeCases/Lower_boundary +=== RUN TestIsDockerBridgeIP_EdgeCases/Lower_boundary_+_1 +=== RUN TestIsDockerBridgeIP_EdgeCases/Upper_boundary_-_1 +=== RUN TestIsDockerBridgeIP_EdgeCases/Upper_boundary +=== RUN TestIsDockerBridgeIP_EdgeCases/Upper_boundary_+_1 +=== RUN TestIsDockerBridgeIP_EdgeCases/Upper_boundary_+_2 +=== RUN TestIsDockerBridgeIP_EdgeCases/Docker_default_bridge_start +=== RUN TestIsDockerBridgeIP_EdgeCases/Docker_default_bridge_gateway +=== RUN TestIsDockerBridgeIP_EdgeCases/Docker_default_bridge_host +=== RUN TestIsDockerBridgeIP_EdgeCases/Docker_default_bridge_end +=== RUN TestIsDockerBridgeIP_EdgeCases/User_network_1 +=== RUN TestIsDockerBridgeIP_EdgeCases/User_network_2 +=== RUN TestIsDockerBridgeIP_EdgeCases/User_network_30 +=== RUN TestIsDockerBridgeIP_EdgeCases/User_network_31 +=== RUN TestIsDockerBridgeIP_EdgeCases/172.0.0.1 +=== RUN TestIsDockerBridgeIP_EdgeCases/172.15.0.1 +=== RUN TestIsDockerBridgeIP_EdgeCases/172.32.0.1 +=== RUN TestIsDockerBridgeIP_EdgeCases/172.255.255.255 +--- PASS: TestIsDockerBridgeIP_EdgeCases (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/Lower_boundary_-_1 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/Lower_boundary (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/Lower_boundary_+_1 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/Upper_boundary_-_1 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/Upper_boundary (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/Upper_boundary_+_1 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/Upper_boundary_+_2 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/Docker_default_bridge_start (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/Docker_default_bridge_gateway (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/Docker_default_bridge_host (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/Docker_default_bridge_end (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/User_network_1 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/User_network_2 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/User_network_30 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/User_network_31 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/172.0.0.1 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/172.15.0.1 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/172.32.0.1 (0.00s) + --- PASS: TestIsDockerBridgeIP_EdgeCases/172.255.255.255 (0.00s) +=== RUN TestTestURLConnectivity_Success +--- PASS: TestTestURLConnectivity_Success (0.00s) +=== RUN TestTestURLConnectivity_Redirect +--- PASS: TestTestURLConnectivity_Redirect (0.00s) +=== RUN TestTestURLConnectivity_TooManyRedirects +--- PASS: TestTestURLConnectivity_TooManyRedirects (0.00s) +=== RUN TestTestURLConnectivity_StatusCodes +=== RUN TestTestURLConnectivity_StatusCodes/200_OK +=== RUN TestTestURLConnectivity_StatusCodes/201_Created +=== RUN TestTestURLConnectivity_StatusCodes/204_No_Content +=== RUN TestTestURLConnectivity_StatusCodes/301_Moved_Permanently +=== RUN TestTestURLConnectivity_StatusCodes/302_Found +=== RUN TestTestURLConnectivity_StatusCodes/400_Bad_Request +=== RUN TestTestURLConnectivity_StatusCodes/401_Unauthorized +=== RUN TestTestURLConnectivity_StatusCodes/403_Forbidden +=== RUN TestTestURLConnectivity_StatusCodes/404_Not_Found +=== RUN TestTestURLConnectivity_StatusCodes/500_Internal_Server_Error +=== RUN TestTestURLConnectivity_StatusCodes/503_Service_Unavailable +--- PASS: TestTestURLConnectivity_StatusCodes (0.00s) + --- PASS: TestTestURLConnectivity_StatusCodes/200_OK (0.00s) + --- PASS: TestTestURLConnectivity_StatusCodes/201_Created (0.00s) + --- PASS: TestTestURLConnectivity_StatusCodes/204_No_Content (0.00s) + --- PASS: TestTestURLConnectivity_StatusCodes/301_Moved_Permanently (0.00s) + --- PASS: TestTestURLConnectivity_StatusCodes/302_Found (0.00s) + --- PASS: TestTestURLConnectivity_StatusCodes/400_Bad_Request (0.00s) + --- PASS: TestTestURLConnectivity_StatusCodes/401_Unauthorized (0.00s) + --- PASS: TestTestURLConnectivity_StatusCodes/403_Forbidden (0.00s) + --- PASS: TestTestURLConnectivity_StatusCodes/404_Not_Found (0.00s) + --- PASS: TestTestURLConnectivity_StatusCodes/500_Internal_Server_Error (0.00s) + --- PASS: TestTestURLConnectivity_StatusCodes/503_Service_Unavailable (0.00s) +=== RUN TestTestURLConnectivity_InvalidURL +=== RUN TestTestURLConnectivity_InvalidURL/Empty_URL +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"","request_id":"test-1768011443906317603","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_InvalidURL/Invalid_scheme +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011443906663215","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_InvalidURL/Malformed_URL +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"http://[invalid","request_id":"test-1768011443906774805","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_InvalidURL/No_scheme +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"","request_id":"test-1768011443906885665","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestTestURLConnectivity_InvalidURL (0.00s) + --- PASS: TestTestURLConnectivity_InvalidURL/Empty_URL (0.00s) + --- PASS: TestTestURLConnectivity_InvalidURL/Invalid_scheme (0.00s) + --- PASS: TestTestURLConnectivity_InvalidURL/Malformed_URL (0.00s) + --- PASS: TestTestURLConnectivity_InvalidURL/No_scheme (0.00s) +=== RUN TestTestURLConnectivity_DNSFailure +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"nonexistent-domain-12345.invalid","request_id":"test-1768011443907026076","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestTestURLConnectivity_DNSFailure (0.00s) +=== RUN TestTestURLConnectivity_Timeout +--- PASS: TestTestURLConnectivity_Timeout (0.00s) +=== RUN TestIsPrivateIP_PrivateIPv4Ranges +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/10.0.0.0/8_start +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/10.0.0.0/8_mid +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/10.0.0.0/8_end +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/172.16.0.0/12_start +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/172.16.0.0/12_mid +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/172.16.0.0/12_end +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/192.168.0.0/16_start +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/192.168.0.0/16_end +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/127.0.0.1_localhost +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/127.0.0.0/8_start +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/127.0.0.0/8_end +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/169.254.0.0/16_start +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/169.254.169.254_AWS_metadata +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/169.254.0.0/16_end +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/0.0.0.0/8 +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/240.0.0.0/4 +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/255.255.255.255_broadcast +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/8.8.8.8_Google_DNS +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/1.1.1.1_Cloudflare_DNS +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/93.184.216.34_example.com +=== RUN TestIsPrivateIP_PrivateIPv4Ranges/151.101.1.140_GitHub +--- PASS: TestIsPrivateIP_PrivateIPv4Ranges (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/10.0.0.0/8_start (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/10.0.0.0/8_mid (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/10.0.0.0/8_end (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/172.16.0.0/12_start (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/172.16.0.0/12_mid (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/172.16.0.0/12_end (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/192.168.0.0/16_start (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/192.168.0.0/16_end (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/127.0.0.1_localhost (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/127.0.0.0/8_start (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/127.0.0.0/8_end (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/169.254.0.0/16_start (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/169.254.169.254_AWS_metadata (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/169.254.0.0/16_end (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/0.0.0.0/8 (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/240.0.0.0/4 (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/255.255.255.255_broadcast (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/8.8.8.8_Google_DNS (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/1.1.1.1_Cloudflare_DNS (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/93.184.216.34_example.com (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv4Ranges/151.101.1.140_GitHub (0.00s) +=== RUN TestIsPrivateIP_PrivateIPv6Ranges +=== RUN TestIsPrivateIP_PrivateIPv6Ranges/::1_loopback +=== RUN TestIsPrivateIP_PrivateIPv6Ranges/fe80::/10_start +=== RUN TestIsPrivateIP_PrivateIPv6Ranges/fe80::/10_mid +=== RUN TestIsPrivateIP_PrivateIPv6Ranges/fc00::/7_start +=== RUN TestIsPrivateIP_PrivateIPv6Ranges/fc00::/7_mid +=== RUN TestIsPrivateIP_PrivateIPv6Ranges/2001:4860:4860::8888_Google_DNS +=== RUN TestIsPrivateIP_PrivateIPv6Ranges/2606:4700:4700::1111_Cloudflare_DNS +--- PASS: TestIsPrivateIP_PrivateIPv6Ranges (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv6Ranges/::1_loopback (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv6Ranges/fe80::/10_start (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv6Ranges/fe80::/10_mid (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv6Ranges/fc00::/7_start (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv6Ranges/fc00::/7_mid (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv6Ranges/2001:4860:4860::8888_Google_DNS (0.00s) + --- PASS: TestIsPrivateIP_PrivateIPv6Ranges/2606:4700:4700::1111_Cloudflare_DNS (0.00s) +=== RUN TestTestURLConnectivity_PrivateIP_Blocked +=== RUN TestTestURLConnectivity_PrivateIP_Blocked/localhost +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"localhost","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_PrivateIP_Blocked/127.0.0.1 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"127.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_PrivateIP_Blocked/Private_IP_10.x +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"10.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_PrivateIP_Blocked/Private_IP_192.168.x +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"192.168.1.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_PrivateIP_Blocked/AWS_metadata +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"169.254.169.254","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +--- PASS: TestTestURLConnectivity_PrivateIP_Blocked (0.00s) + --- PASS: TestTestURLConnectivity_PrivateIP_Blocked/localhost (0.00s) + --- PASS: TestTestURLConnectivity_PrivateIP_Blocked/127.0.0.1 (0.00s) + --- PASS: TestTestURLConnectivity_PrivateIP_Blocked/Private_IP_10.x (0.00s) + --- PASS: TestTestURLConnectivity_PrivateIP_Blocked/Private_IP_192.168.x (0.00s) + --- PASS: TestTestURLConnectivity_PrivateIP_Blocked/AWS_metadata (0.00s) +=== RUN TestTestURLConnectivity_SSRF_Protection_Comprehensive +=== RUN TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://localhost:8080 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"localhost","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://127.0.0.1:8080 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"127.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://0.0.0.0:8080 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"0.0.0.0","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://[::1]:8080 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"::1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://169.254.169.254/latest/meta-data/ +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"169.254.169.254","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://metadata.google.internal/computeMetadata/v1/ +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"metadata.google.internal","request_id":"test-1768011443914517171","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestTestURLConnectivity_SSRF_Protection_Comprehensive (0.00s) + --- PASS: TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://localhost:8080 (0.00s) + --- PASS: TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://127.0.0.1:8080 (0.00s) + --- PASS: TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://0.0.0.0:8080 (0.00s) + --- PASS: TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://[::1]:8080 (0.00s) + --- PASS: TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://169.254.169.254/latest/meta-data/ (0.00s) + --- PASS: TestTestURLConnectivity_SSRF_Protection_Comprehensive/http://metadata.google.internal/computeMetadata/v1/ (0.00s) +=== RUN TestTestURLConnectivity_HTTPSSupport +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"127.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} + url_connectivity_test.go:345: HTTPS test failed (expected with self-signed cert): security validation failed: connection to private IP addresses is blocked for security (detected IPv4-mapped IPv6: 127.0.0.1) +--- PASS: TestTestURLConnectivity_HTTPSSupport (0.00s) +=== RUN TestTestURLConnectivity_RedirectLimit_ProductionPath +--- PASS: TestTestURLConnectivity_RedirectLimit_ProductionPath (0.00s) +=== RUN TestTestURLConnectivity_InvalidPortFormat +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"http://example.com:badport","request_id":"test-1768011443920778663","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestTestURLConnectivity_InvalidPortFormat (0.00s) +=== RUN TestTestURLConnectivity_EmptyDNSResult +--- PASS: TestTestURLConnectivity_EmptyDNSResult (0.00s) +=== RUN TestGetPublicURL_WithConfiguredURL +--- PASS: TestGetPublicURL_WithConfiguredURL (0.01s) +=== RUN TestGetPublicURL_WithTrailingSlash +--- PASS: TestGetPublicURL_WithTrailingSlash (0.00s) +=== RUN TestGetPublicURL_Fallback_HTTPSWithTLS + +2026/01/10 02:17:23 /projects/Charon/backend/internal/utils/url.go:17 record not found +[0.108ms] [rows:0] SELECT * FROM `settings` WHERE key = "app.public_url" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestGetPublicURL_Fallback_HTTPSWithTLS (0.00s) +=== RUN TestGetPublicURL_Fallback_HTTP + +2026/01/10 02:17:23 /projects/Charon/backend/internal/utils/url.go:17 record not found +[0.078ms] [rows:0] SELECT * FROM `settings` WHERE key = "app.public_url" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestGetPublicURL_Fallback_HTTP (0.00s) +=== RUN TestGetPublicURL_Fallback_XForwardedProto + +2026/01/10 02:17:23 /projects/Charon/backend/internal/utils/url.go:17 record not found +[0.076ms] [rows:0] SELECT * FROM `settings` WHERE key = "app.public_url" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestGetPublicURL_Fallback_XForwardedProto (0.00s) +=== RUN TestGetPublicURL_EmptyValue +--- PASS: TestGetPublicURL_EmptyValue (0.00s) +=== RUN TestGetPublicURL_NoSettingInDB + +2026/01/10 02:17:23 /projects/Charon/backend/internal/utils/url.go:17 record not found +[0.064ms] [rows:0] SELECT * FROM `settings` WHERE key = "app.public_url" ORDER BY `settings`.`id` LIMIT 1 +--- PASS: TestGetPublicURL_NoSettingInDB (0.00s) +=== RUN TestValidateURL_ValidHTTPS +=== RUN TestValidateURL_ValidHTTPS/HTTPS_with_trailing_slash +=== RUN TestValidateURL_ValidHTTPS/HTTPS_without_path +=== RUN TestValidateURL_ValidHTTPS/HTTPS_with_port +=== RUN TestValidateURL_ValidHTTPS/HTTPS_with_subdomain +--- PASS: TestValidateURL_ValidHTTPS (0.00s) + --- PASS: TestValidateURL_ValidHTTPS/HTTPS_with_trailing_slash (0.00s) + --- PASS: TestValidateURL_ValidHTTPS/HTTPS_without_path (0.00s) + --- PASS: TestValidateURL_ValidHTTPS/HTTPS_with_port (0.00s) + --- PASS: TestValidateURL_ValidHTTPS/HTTPS_with_subdomain (0.00s) +=== RUN TestValidateURL_ValidHTTP +=== RUN TestValidateURL_ValidHTTP/HTTP_with_trailing_slash +=== RUN TestValidateURL_ValidHTTP/HTTP_without_path +=== RUN TestValidateURL_ValidHTTP/HTTP_with_port +--- PASS: TestValidateURL_ValidHTTP (0.00s) + --- PASS: TestValidateURL_ValidHTTP/HTTP_with_trailing_slash (0.00s) + --- PASS: TestValidateURL_ValidHTTP/HTTP_without_path (0.00s) + --- PASS: TestValidateURL_ValidHTTP/HTTP_with_port (0.00s) +=== RUN TestValidateURL_InvalidScheme +=== RUN TestValidateURL_InvalidScheme/ftp://example.com +=== RUN TestValidateURL_InvalidScheme/file:///etc/passwd +=== RUN TestValidateURL_InvalidScheme/javascript:alert(1) +=== RUN TestValidateURL_InvalidScheme/data:text/html, +=== RUN TestValidateURL_InvalidScheme/ssh://user@host +--- PASS: TestValidateURL_InvalidScheme (0.00s) + --- PASS: TestValidateURL_InvalidScheme/ftp://example.com (0.00s) + --- PASS: TestValidateURL_InvalidScheme/file:///etc/passwd (0.00s) + --- PASS: TestValidateURL_InvalidScheme/javascript:alert(1) (0.00s) + --- PASS: TestValidateURL_InvalidScheme/data:text/html, (0.00s) + --- PASS: TestValidateURL_InvalidScheme/ssh://user@host (0.00s) +=== RUN TestValidateURL_WithPath +=== RUN TestValidateURL_WithPath/https://example.com/api/v1 +=== RUN TestValidateURL_WithPath/https://example.com/admin +=== RUN TestValidateURL_WithPath/http://example.com/path/to/resource +=== RUN TestValidateURL_WithPath/https://example.com/index.html +--- PASS: TestValidateURL_WithPath (0.00s) + --- PASS: TestValidateURL_WithPath/https://example.com/api/v1 (0.00s) + --- PASS: TestValidateURL_WithPath/https://example.com/admin (0.00s) + --- PASS: TestValidateURL_WithPath/http://example.com/path/to/resource (0.00s) + --- PASS: TestValidateURL_WithPath/https://example.com/index.html (0.00s) +=== RUN TestValidateURL_RootPathAllowed +=== RUN TestValidateURL_RootPathAllowed/https://example.com/ +=== RUN TestValidateURL_RootPathAllowed/http://example.com/ +--- PASS: TestValidateURL_RootPathAllowed (0.00s) + --- PASS: TestValidateURL_RootPathAllowed/https://example.com/ (0.00s) + --- PASS: TestValidateURL_RootPathAllowed/http://example.com/ (0.00s) +=== RUN TestValidateURL_MalformedURL +=== RUN TestValidateURL_MalformedURL/not_a_url +=== RUN TestValidateURL_MalformedURL/://missing-scheme +=== RUN TestValidateURL_MalformedURL/http:// +=== RUN TestValidateURL_MalformedURL/https://[invalid +=== RUN TestValidateURL_MalformedURL/#00 +--- PASS: TestValidateURL_MalformedURL (0.00s) + --- PASS: TestValidateURL_MalformedURL/not_a_url (0.00s) + --- PASS: TestValidateURL_MalformedURL/://missing-scheme (0.00s) + --- PASS: TestValidateURL_MalformedURL/http:// (0.00s) + --- PASS: TestValidateURL_MalformedURL/https://[invalid (0.00s) + --- PASS: TestValidateURL_MalformedURL/#00 (0.00s) +=== RUN TestValidateURL_SpecialCharacters +=== RUN TestValidateURL_SpecialCharacters/Punycode_domain +=== RUN TestValidateURL_SpecialCharacters/Port_with_special_chars +=== RUN TestValidateURL_SpecialCharacters/Query_string_(no_path_component) +=== RUN TestValidateURL_SpecialCharacters/Fragment_(no_path_component) +=== RUN TestValidateURL_SpecialCharacters/Userinfo +--- PASS: TestValidateURL_SpecialCharacters (0.00s) + --- PASS: TestValidateURL_SpecialCharacters/Punycode_domain (0.00s) + --- PASS: TestValidateURL_SpecialCharacters/Port_with_special_chars (0.00s) + --- PASS: TestValidateURL_SpecialCharacters/Query_string_(no_path_component) (0.00s) + --- PASS: TestValidateURL_SpecialCharacters/Fragment_(no_path_component) (0.00s) + --- PASS: TestValidateURL_SpecialCharacters/Userinfo (0.00s) +=== RUN TestValidateURL_Normalization +=== RUN TestValidateURL_Normalization/https://EXAMPLE.COM +=== RUN TestValidateURL_Normalization/https://example.com/ +=== RUN TestValidateURL_Normalization/https://example.com/// +=== RUN TestValidateURL_Normalization/http://example.com:80 +=== RUN TestValidateURL_Normalization/https://example.com:443 +--- PASS: TestValidateURL_Normalization (0.00s) + --- PASS: TestValidateURL_Normalization/https://EXAMPLE.COM (0.00s) + --- PASS: TestValidateURL_Normalization/https://example.com/ (0.00s) + --- PASS: TestValidateURL_Normalization/https://example.com/// (0.00s) + --- PASS: TestValidateURL_Normalization/http://example.com:80 (0.00s) + --- PASS: TestValidateURL_Normalization/https://example.com:443 (0.00s) +=== RUN TestGetBaseURL +=== RUN TestGetBaseURL/HTTPS_with_TLS +=== RUN TestGetBaseURL/HTTP_without_TLS +=== RUN TestGetBaseURL/X-Forwarded-Proto_HTTPS +=== RUN TestGetBaseURL/X-Forwarded-Proto_HTTP +=== RUN TestGetBaseURL/With_port +=== RUN TestGetBaseURL/IPv4_host +=== RUN TestGetBaseURL/IPv6_host +--- PASS: TestGetBaseURL (0.00s) + --- PASS: TestGetBaseURL/HTTPS_with_TLS (0.00s) + --- PASS: TestGetBaseURL/HTTP_without_TLS (0.00s) + --- PASS: TestGetBaseURL/X-Forwarded-Proto_HTTPS (0.00s) + --- PASS: TestGetBaseURL/X-Forwarded-Proto_HTTP (0.00s) + --- PASS: TestGetBaseURL/With_port (0.00s) + --- PASS: TestGetBaseURL/IPv4_host (0.00s) + --- PASS: TestGetBaseURL/IPv6_host (0.00s) +=== RUN TestGetBaseURL_PrecedenceOrder +--- PASS: TestGetBaseURL_PrecedenceOrder (0.00s) +=== RUN TestGetBaseURL_EmptyHost +--- PASS: TestGetBaseURL_EmptyHost (0.00s) +=== RUN TestTestURLConnectivity_EnhancedSSRF +=== RUN TestTestURLConnectivity_EnhancedSSRF/AWS_metadata_endpoint +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"169.254.169.254","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_EnhancedSSRF/GCP_metadata_endpoint +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"metadata.google.internal","request_id":"test-1768011443951185546","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_EnhancedSSRF/Azure_metadata_endpoint +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"169.254.169.254","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_EnhancedSSRF/Private_10.0.0.0/8 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"10.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_EnhancedSSRF/Private_172.16.0.0/12 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"172.16.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_EnhancedSSRF/Private_192.168.0.0/16 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"192.168.1.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_EnhancedSSRF/IPv4_loopback +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"127.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_EnhancedSSRF/IPv6_loopback +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"::1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_EnhancedSSRF/Link-local_IPv4 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"169.254.1.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_EnhancedSSRF/Link-local_IPv6 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"fe80::1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +--- PASS: TestTestURLConnectivity_EnhancedSSRF (0.00s) + --- PASS: TestTestURLConnectivity_EnhancedSSRF/AWS_metadata_endpoint (0.00s) + --- PASS: TestTestURLConnectivity_EnhancedSSRF/GCP_metadata_endpoint (0.00s) + --- PASS: TestTestURLConnectivity_EnhancedSSRF/Azure_metadata_endpoint (0.00s) + --- PASS: TestTestURLConnectivity_EnhancedSSRF/Private_10.0.0.0/8 (0.00s) + --- PASS: TestTestURLConnectivity_EnhancedSSRF/Private_172.16.0.0/12 (0.00s) + --- PASS: TestTestURLConnectivity_EnhancedSSRF/Private_192.168.0.0/16 (0.00s) + --- PASS: TestTestURLConnectivity_EnhancedSSRF/IPv4_loopback (0.00s) + --- PASS: TestTestURLConnectivity_EnhancedSSRF/IPv6_loopback (0.00s) + --- PASS: TestTestURLConnectivity_EnhancedSSRF/Link-local_IPv4 (0.00s) + --- PASS: TestTestURLConnectivity_EnhancedSSRF/Link-local_IPv6 (0.00s) +=== RUN TestTestURLConnectivity_RedirectValidation +=== RUN TestTestURLConnectivity_RedirectValidation/Redirect_to_private_IP_should_be_blocked + url_testing_enhanced_test.go:123: Redirect validation test requires complex HTTP client mocking +=== RUN TestTestURLConnectivity_RedirectValidation/Too_many_redirects_should_be_blocked +--- PASS: TestTestURLConnectivity_RedirectValidation (0.00s) + --- SKIP: TestTestURLConnectivity_RedirectValidation/Redirect_to_private_IP_should_be_blocked (0.00s) + --- PASS: TestTestURLConnectivity_RedirectValidation/Too_many_redirects_should_be_blocked (0.00s) +=== RUN TestTestURLConnectivity_UnicodeHomograph +=== RUN TestTestURLConnectivity_UnicodeHomograph/Cyrillic_homograph +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"gооgle.com","request_id":"test-1768011443957532178","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_UnicodeHomograph/Mixed_script_attack +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"раypal.com","request_id":"test-1768011443957740167","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestTestURLConnectivity_UnicodeHomograph (0.00s) + --- PASS: TestTestURLConnectivity_UnicodeHomograph/Cyrillic_homograph (0.00s) + --- PASS: TestTestURLConnectivity_UnicodeHomograph/Mixed_script_attack (0.00s) +=== RUN TestTestURLConnectivity_LongHostname +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com","request_id":"test-1768011443957966949","result":"blocked","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestTestURLConnectivity_LongHostname (0.00s) +=== RUN TestTestURLConnectivity_RequestTracingHeaders +--- PASS: TestTestURLConnectivity_RequestTracingHeaders (0.00s) +=== RUN TestTestURLConnectivity_MetricsIntegration +=== RUN TestTestURLConnectivity_MetricsIntegration/Valid_URL_records_metrics +=== RUN TestTestURLConnectivity_MetricsIntegration/Blocked_URL_records_metrics +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"127.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_MetricsIntegration/Invalid_URL_records_metrics +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"","request_id":"test-1768011443961052630","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestTestURLConnectivity_MetricsIntegration (0.00s) + --- PASS: TestTestURLConnectivity_MetricsIntegration/Valid_URL_records_metrics (0.00s) + --- PASS: TestTestURLConnectivity_MetricsIntegration/Blocked_URL_records_metrics (0.00s) + --- PASS: TestTestURLConnectivity_MetricsIntegration/Invalid_URL_records_metrics (0.00s) +=== RUN TestValidateRedirectTarget +=== RUN TestValidateRedirectTarget/Localhost_redirect_allowed +=== RUN TestValidateRedirectTarget/127.0.0.1_redirect_allowed +=== RUN TestValidateRedirectTarget/IPv6_loopback_allowed +=== RUN TestValidateRedirectTarget/Too_many_redirects +=== RUN TestValidateRedirectTarget/Three_redirects +=== RUN TestValidateRedirectTarget/Scheme_downgrade_blocked_(https_->_http) +--- PASS: TestValidateRedirectTarget (0.00s) + --- PASS: TestValidateRedirectTarget/Localhost_redirect_allowed (0.00s) + --- PASS: TestValidateRedirectTarget/127.0.0.1_redirect_allowed (0.00s) + --- PASS: TestValidateRedirectTarget/IPv6_loopback_allowed (0.00s) + --- PASS: TestValidateRedirectTarget/Too_many_redirects (0.00s) + --- PASS: TestValidateRedirectTarget/Three_redirects (0.00s) + --- PASS: TestValidateRedirectTarget/Scheme_downgrade_blocked_(https_->_http) (0.00s) +=== RUN TestTestURLConnectivity_AuditLogging +=== RUN TestTestURLConnectivity_AuditLogging/Invalid_URL_format_logs_audit_event +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"://invalid","request_id":"test-1768011443962054942","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_AuditLogging/Invalid_scheme_logs_audit_event +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011443962234283","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_AuditLogging/Private_IP_logs_SSRF_block_audit_event +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"10.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_AuditLogging/Metadata_endpoint_logs_SSRF_block_audit_event +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"169.254.169.254","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_AuditLogging/Valid_URL_with_mock_transport_logs_success +--- PASS: TestTestURLConnectivity_AuditLogging (0.00s) + --- PASS: TestTestURLConnectivity_AuditLogging/Invalid_URL_format_logs_audit_event (0.00s) + --- PASS: TestTestURLConnectivity_AuditLogging/Invalid_scheme_logs_audit_event (0.00s) + --- PASS: TestTestURLConnectivity_AuditLogging/Private_IP_logs_SSRF_block_audit_event (0.00s) + --- PASS: TestTestURLConnectivity_AuditLogging/Metadata_endpoint_logs_SSRF_block_audit_event (0.00s) + --- PASS: TestTestURLConnectivity_AuditLogging/Valid_URL_with_mock_transport_logs_success (0.00s) +=== RUN TestTestURLConnectivity_RequestIDConsistency +--- PASS: TestTestURLConnectivity_RequestIDConsistency (0.00s) +=== RUN TestResolveAllowedIP_EmptyHostname +--- PASS: TestResolveAllowedIP_EmptyHostname (0.00s) +=== RUN TestResolveAllowedIP_LoopbackIPLiteral +=== RUN TestResolveAllowedIP_LoopbackIPLiteral/127.0.0.1_without_allowLocalhost +=== RUN TestResolveAllowedIP_LoopbackIPLiteral/127.0.0.1_with_allowLocalhost +=== RUN TestResolveAllowedIP_LoopbackIPLiteral/::1_without_allowLocalhost +=== RUN TestResolveAllowedIP_LoopbackIPLiteral/::1_with_allowLocalhost +--- PASS: TestResolveAllowedIP_LoopbackIPLiteral (0.00s) + --- PASS: TestResolveAllowedIP_LoopbackIPLiteral/127.0.0.1_without_allowLocalhost (0.00s) + --- PASS: TestResolveAllowedIP_LoopbackIPLiteral/127.0.0.1_with_allowLocalhost (0.00s) + --- PASS: TestResolveAllowedIP_LoopbackIPLiteral/::1_without_allowLocalhost (0.00s) + --- PASS: TestResolveAllowedIP_LoopbackIPLiteral/::1_with_allowLocalhost (0.00s) +=== RUN TestResolveAllowedIP_PrivateIPLiterals +=== RUN TestResolveAllowedIP_PrivateIPLiterals/IP_10.0.0.1 +=== RUN TestResolveAllowedIP_PrivateIPLiterals/IP_172.16.0.1 +=== RUN TestResolveAllowedIP_PrivateIPLiterals/IP_192.168.1.1 +=== RUN TestResolveAllowedIP_PrivateIPLiterals/IP_169.254.169.254 +=== RUN TestResolveAllowedIP_PrivateIPLiterals/IP_fc00::1 +=== RUN TestResolveAllowedIP_PrivateIPLiterals/IP_fe80::1 +--- PASS: TestResolveAllowedIP_PrivateIPLiterals (0.00s) + --- PASS: TestResolveAllowedIP_PrivateIPLiterals/IP_10.0.0.1 (0.00s) + --- PASS: TestResolveAllowedIP_PrivateIPLiterals/IP_172.16.0.1 (0.00s) + --- PASS: TestResolveAllowedIP_PrivateIPLiterals/IP_192.168.1.1 (0.00s) + --- PASS: TestResolveAllowedIP_PrivateIPLiterals/IP_169.254.169.254 (0.00s) + --- PASS: TestResolveAllowedIP_PrivateIPLiterals/IP_fc00::1 (0.00s) + --- PASS: TestResolveAllowedIP_PrivateIPLiterals/IP_fe80::1 (0.00s) +=== RUN TestResolveAllowedIP_PublicIPLiteral +=== RUN TestResolveAllowedIP_PublicIPLiteral/IP_8.8.8.8 +=== RUN TestResolveAllowedIP_PublicIPLiteral/IP_1.1.1.1 +=== RUN TestResolveAllowedIP_PublicIPLiteral/IP_2001:4860:4860::8888 +--- PASS: TestResolveAllowedIP_PublicIPLiteral (0.00s) + --- PASS: TestResolveAllowedIP_PublicIPLiteral/IP_8.8.8.8 (0.00s) + --- PASS: TestResolveAllowedIP_PublicIPLiteral/IP_1.1.1.1 (0.00s) + --- PASS: TestResolveAllowedIP_PublicIPLiteral/IP_2001:4860:4860::8888 (0.00s) +=== RUN TestResolveAllowedIP_Timeout +--- PASS: TestResolveAllowedIP_Timeout (0.00s) +=== RUN TestResolveAllowedIP_NoIPsResolved + url_testing_security_test.go:150: Requires custom DNS resolver to return empty IP list +--- SKIP: TestResolveAllowedIP_NoIPsResolved (0.00s) +=== RUN TestSSRFSafeDialer_Concept + url_testing_security_test.go:163: ssrfSafeDialer validates IPs at dial time to prevent DNS rebinding + url_testing_security_test.go:164: All resolved IPs must pass private IP check before connection +--- PASS: TestSSRFSafeDialer_Concept (0.00s) +=== RUN TestSSRFSafeDialer_InvalidAddress +=== RUN TestSSRFSafeDialer_InvalidAddress/No_port +=== RUN TestSSRFSafeDialer_InvalidAddress/Invalid_format +=== RUN TestSSRFSafeDialer_InvalidAddress/Empty_address +--- PASS: TestSSRFSafeDialer_InvalidAddress (0.00s) + --- PASS: TestSSRFSafeDialer_InvalidAddress/No_port (0.00s) + --- PASS: TestSSRFSafeDialer_InvalidAddress/Invalid_format (0.00s) + --- PASS: TestSSRFSafeDialer_InvalidAddress/Empty_address (0.00s) +=== RUN TestSSRFSafeDialer_ContextCancellation +--- PASS: TestSSRFSafeDialer_ContextCancellation (0.00s) +=== RUN TestTestURLConnectivity_ErrorPaths +=== RUN TestTestURLConnectivity_ErrorPaths/Invalid_URL_format +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"://invalid","request_id":"test-1768011443967837572","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_ErrorPaths/Unsupported_scheme_FTP +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011443967979832","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_ErrorPaths/Embedded_credentials +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011443968099643","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_ErrorPaths/Private_IP_10.x +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"10.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_ErrorPaths/Private_IP_192.168.x +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"192.168.1.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_ErrorPaths/AWS_metadata_endpoint +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"169.254.169.254","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +--- PASS: TestTestURLConnectivity_ErrorPaths (0.00s) + --- PASS: TestTestURLConnectivity_ErrorPaths/Invalid_URL_format (0.00s) + --- PASS: TestTestURLConnectivity_ErrorPaths/Unsupported_scheme_FTP (0.00s) + --- PASS: TestTestURLConnectivity_ErrorPaths/Embedded_credentials (0.00s) + --- PASS: TestTestURLConnectivity_ErrorPaths/Private_IP_10.x (0.00s) + --- PASS: TestTestURLConnectivity_ErrorPaths/Private_IP_192.168.x (0.00s) + --- PASS: TestTestURLConnectivity_ErrorPaths/AWS_metadata_endpoint (0.00s) +=== RUN TestTestURLConnectivity_InvalidPort +=== RUN TestTestURLConnectivity_InvalidPort/Port_out_of_range_(too_high) +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011443968785745","result":"blocked","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_InvalidPort/Port_zero +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011443968989516","result":"blocked","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestTestURLConnectivity_InvalidPort/Negative_port +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"https://example.com:-1","request_id":"test-1768011443969192356","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestTestURLConnectivity_InvalidPort (0.00s) + --- PASS: TestTestURLConnectivity_InvalidPort/Port_out_of_range_(too_high) (0.00s) + --- PASS: TestTestURLConnectivity_InvalidPort/Port_zero (0.00s) + --- PASS: TestTestURLConnectivity_InvalidPort/Negative_port (0.00s) +=== RUN TestSSRFSafeDialer_ValidPublicIP +--- PASS: TestSSRFSafeDialer_ValidPublicIP (0.01s) +=== RUN TestSSRFSafeDialer_PrivateIPBlocking +=== RUN TestSSRFSafeDialer_PrivateIPBlocking/10.0.0.1:80 +=== RUN TestSSRFSafeDialer_PrivateIPBlocking/192.168.1.1:80 +=== RUN TestSSRFSafeDialer_PrivateIPBlocking/172.16.0.1:80 +=== RUN TestSSRFSafeDialer_PrivateIPBlocking/127.0.0.1:80 +--- PASS: TestSSRFSafeDialer_PrivateIPBlocking (0.00s) + --- PASS: TestSSRFSafeDialer_PrivateIPBlocking/10.0.0.1:80 (0.00s) + --- PASS: TestSSRFSafeDialer_PrivateIPBlocking/192.168.1.1:80 (0.00s) + --- PASS: TestSSRFSafeDialer_PrivateIPBlocking/172.16.0.1:80 (0.00s) + --- PASS: TestSSRFSafeDialer_PrivateIPBlocking/127.0.0.1:80 (0.00s) +=== RUN TestSSRFSafeDialer_DNSResolutionFailure +--- PASS: TestSSRFSafeDialer_DNSResolutionFailure (0.00s) +=== RUN TestSSRFSafeDialer_MultipleIPsWithPrivate +--- PASS: TestSSRFSafeDialer_MultipleIPsWithPrivate (0.00s) +=== RUN TestURLConnectivity_ProductionPathValidation +=== RUN TestURLConnectivity_ProductionPathValidation/localhost_blocked_at_dial_time +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"localhost","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_ProductionPathValidation/127.0.0.1_blocked_at_dial_time +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"127.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_ProductionPathValidation/private_10.x_blocked_at_validation +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"10.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_ProductionPathValidation/private_192.168.x_blocked_at_validation +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"192.168.1.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_ProductionPathValidation/AWS_metadata_blocked_at_validation +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"169.254.169.254","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_ProductionPathValidation (0.00s) + --- PASS: TestURLConnectivity_ProductionPathValidation/localhost_blocked_at_dial_time (0.00s) + --- PASS: TestURLConnectivity_ProductionPathValidation/127.0.0.1_blocked_at_dial_time (0.00s) + --- PASS: TestURLConnectivity_ProductionPathValidation/private_10.x_blocked_at_validation (0.00s) + --- PASS: TestURLConnectivity_ProductionPathValidation/private_192.168.x_blocked_at_validation (0.00s) + --- PASS: TestURLConnectivity_ProductionPathValidation/AWS_metadata_blocked_at_validation (0.00s) +=== RUN TestURLConnectivity_TestHook_AllowsLocalhostWithInjectedTransport +--- PASS: TestURLConnectivity_TestHook_AllowsLocalhostWithInjectedTransport (0.00s) +=== RUN TestValidateRedirectTarget_AllowsLocalhost +--- PASS: TestValidateRedirectTarget_AllowsLocalhost (0.00s) +=== RUN TestValidateRedirectTarget_BlocksInvalidExternalRedirect +--- PASS: TestValidateRedirectTarget_BlocksInvalidExternalRedirect (0.00s) +=== RUN TestURLConnectivity_RejectsUserinfo +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011443983562626","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_RejectsUserinfo (0.00s) +=== RUN TestURLConnectivity_InvalidScheme +=== RUN TestURLConnectivity_InvalidScheme/ftp://example.com +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011443983744726","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_InvalidScheme/file:///etc/passwd +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"","request_id":"test-1768011443983856636","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_InvalidScheme/javascript:alert(1) +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"","request_id":"test-1768011443985204971","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_InvalidScheme/data:text/html, +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"","request_id":"test-1768011443985477132","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_InvalidScheme/gopher://example.com +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011443985719173","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_InvalidScheme (0.00s) + --- PASS: TestURLConnectivity_InvalidScheme/ftp://example.com (0.00s) + --- PASS: TestURLConnectivity_InvalidScheme/file:///etc/passwd (0.00s) + --- PASS: TestURLConnectivity_InvalidScheme/javascript:alert(1) (0.00s) + --- PASS: TestURLConnectivity_InvalidScheme/data:text/html, (0.00s) + --- PASS: TestURLConnectivity_InvalidScheme/gopher://example.com (0.00s) +=== RUN TestURLConnectivity_SSRFValidationFailure +=== RUN TestURLConnectivity_SSRFValidationFailure/http://10.0.0.1 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"10.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_SSRFValidationFailure/http://192.168.1.1 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"192.168.1.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_SSRFValidationFailure/http://172.16.0.1 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"172.16.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_SSRFValidationFailure/http://localhost +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"localhost","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_SSRFValidationFailure/http://127.0.0.1 +2026/01/10 02:17:23 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:23Z","action":"ssrf_block","host":"127.0.0.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_SSRFValidationFailure (0.00s) + --- PASS: TestURLConnectivity_SSRFValidationFailure/http://10.0.0.1 (0.00s) + --- PASS: TestURLConnectivity_SSRFValidationFailure/http://192.168.1.1 (0.00s) + --- PASS: TestURLConnectivity_SSRFValidationFailure/http://172.16.0.1 (0.00s) + --- PASS: TestURLConnectivity_SSRFValidationFailure/http://localhost (0.00s) + --- PASS: TestURLConnectivity_SSRFValidationFailure/http://127.0.0.1 (0.00s) +=== RUN TestURLConnectivity_HTTPRequestFailure +--- PASS: TestURLConnectivity_HTTPRequestFailure (0.00s) +=== RUN TestURLConnectivity_RedirectHandling +--- PASS: TestURLConnectivity_RedirectHandling (0.00s) +=== RUN TestURLConnectivity_2xxSuccess +=== RUN TestURLConnectivity_2xxSuccess/status_200 +=== RUN TestURLConnectivity_2xxSuccess/status_201 +=== RUN TestURLConnectivity_2xxSuccess/status_204 +--- PASS: TestURLConnectivity_2xxSuccess (0.00s) + --- PASS: TestURLConnectivity_2xxSuccess/status_200 (0.00s) + --- PASS: TestURLConnectivity_2xxSuccess/status_201 (0.00s) + --- PASS: TestURLConnectivity_2xxSuccess/status_204 (0.00s) +=== RUN TestURLConnectivity_3xxSuccess +=== RUN TestURLConnectivity_3xxSuccess/status_301 +=== RUN TestURLConnectivity_3xxSuccess/status_302 +=== RUN TestURLConnectivity_3xxSuccess/status_307 +=== RUN TestURLConnectivity_3xxSuccess/status_308 +--- PASS: TestURLConnectivity_3xxSuccess (0.01s) + --- PASS: TestURLConnectivity_3xxSuccess/status_301 (0.00s) + --- PASS: TestURLConnectivity_3xxSuccess/status_302 (0.00s) + --- PASS: TestURLConnectivity_3xxSuccess/status_307 (0.00s) + --- PASS: TestURLConnectivity_3xxSuccess/status_308 (0.00s) +=== RUN TestURLConnectivity_4xxFailure +=== RUN TestURLConnectivity_4xxFailure/status_400 +=== RUN TestURLConnectivity_4xxFailure/status_401 +=== RUN TestURLConnectivity_4xxFailure/status_403 +=== RUN TestURLConnectivity_4xxFailure/status_404 +=== RUN TestURLConnectivity_4xxFailure/status_429 +--- PASS: TestURLConnectivity_4xxFailure (0.01s) + --- PASS: TestURLConnectivity_4xxFailure/status_400 (0.00s) + --- PASS: TestURLConnectivity_4xxFailure/status_401 (0.00s) + --- PASS: TestURLConnectivity_4xxFailure/status_403 (0.00s) + --- PASS: TestURLConnectivity_4xxFailure/status_404 (0.00s) + --- PASS: TestURLConnectivity_4xxFailure/status_429 (0.00s) +=== RUN TestIsPrivateIP_AllReservedRanges +=== RUN TestIsPrivateIP_AllReservedRanges/10.0.0.1 +=== RUN TestIsPrivateIP_AllReservedRanges/10.255.255.255 +=== RUN TestIsPrivateIP_AllReservedRanges/172.16.0.1 +=== RUN TestIsPrivateIP_AllReservedRanges/172.31.255.255 +=== RUN TestIsPrivateIP_AllReservedRanges/192.168.0.1 +=== RUN TestIsPrivateIP_AllReservedRanges/192.168.255.255 +=== RUN TestIsPrivateIP_AllReservedRanges/127.0.0.1 +=== RUN TestIsPrivateIP_AllReservedRanges/127.0.0.2 +=== RUN TestIsPrivateIP_AllReservedRanges/127.255.255.255 +=== RUN TestIsPrivateIP_AllReservedRanges/169.254.0.1 +=== RUN TestIsPrivateIP_AllReservedRanges/169.254.169.254 +=== RUN TestIsPrivateIP_AllReservedRanges/169.254.255.255 +=== RUN TestIsPrivateIP_AllReservedRanges/0.0.0.0 +=== RUN TestIsPrivateIP_AllReservedRanges/0.0.0.1 +=== RUN TestIsPrivateIP_AllReservedRanges/240.0.0.1 +=== RUN TestIsPrivateIP_AllReservedRanges/255.255.255.255 +=== RUN TestIsPrivateIP_AllReservedRanges/::1 +=== RUN TestIsPrivateIP_AllReservedRanges/fc00::1 +=== RUN TestIsPrivateIP_AllReservedRanges/fd00::1 +=== RUN TestIsPrivateIP_AllReservedRanges/fe80::1 +=== RUN TestIsPrivateIP_AllReservedRanges/8.8.8.8 +=== RUN TestIsPrivateIP_AllReservedRanges/1.1.1.1 +=== RUN TestIsPrivateIP_AllReservedRanges/93.184.216.34 +=== RUN TestIsPrivateIP_AllReservedRanges/2606:2800:220:1:248:1893:25c8:1946 +--- PASS: TestIsPrivateIP_AllReservedRanges (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/10.0.0.1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/10.255.255.255 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/172.16.0.1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/172.31.255.255 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/192.168.0.1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/192.168.255.255 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/127.0.0.1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/127.0.0.2 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/127.255.255.255 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/169.254.0.1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/169.254.169.254 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/169.254.255.255 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/0.0.0.0 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/0.0.0.1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/240.0.0.1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/255.255.255.255 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/::1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/fc00::1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/fd00::1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/fe80::1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/8.8.8.8 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/1.1.1.1 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/93.184.216.34 (0.00s) + --- PASS: TestIsPrivateIP_AllReservedRanges/2606:2800:220:1:248:1893:25c8:1946 (0.00s) +=== RUN TestURLConnectivity_ErrorWrapping +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"url_connectivity_test","host":"://invalid","request_id":"test-1768011444010591117","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_ErrorWrapping (0.00s) +=== RUN TestURLConnectivity_UserAgent +--- PASS: TestURLConnectivity_UserAgent (0.00s) +=== RUN TestResolveAllowedIP_EmptyHost +--- PASS: TestResolveAllowedIP_EmptyHost (0.00s) +=== RUN TestResolveAllowedIP_IPLiteralPublic +--- PASS: TestResolveAllowedIP_IPLiteralPublic (0.00s) +=== RUN TestResolveAllowedIP_IPLiteralPrivateBlocked +=== RUN TestResolveAllowedIP_IPLiteralPrivateBlocked/10.0.0.1 +=== RUN TestResolveAllowedIP_IPLiteralPrivateBlocked/192.168.1.1 +=== RUN TestResolveAllowedIP_IPLiteralPrivateBlocked/172.16.0.1 +--- PASS: TestResolveAllowedIP_IPLiteralPrivateBlocked (0.00s) + --- PASS: TestResolveAllowedIP_IPLiteralPrivateBlocked/10.0.0.1 (0.00s) + --- PASS: TestResolveAllowedIP_IPLiteralPrivateBlocked/192.168.1.1 (0.00s) + --- PASS: TestResolveAllowedIP_IPLiteralPrivateBlocked/172.16.0.1 (0.00s) +=== RUN TestResolveAllowedIP_DNSResolutionFailure +--- PASS: TestResolveAllowedIP_DNSResolutionFailure (0.00s) +=== RUN TestSSRFSafeDialer_InvalidAddressFormat +--- PASS: TestSSRFSafeDialer_InvalidAddressFormat (0.00s) +=== RUN TestSSRFSafeDialer_NoIPsFound +--- PASS: TestSSRFSafeDialer_NoIPsFound (0.00s) +=== RUN TestURLConnectivity_5xxServerErrors +=== RUN TestURLConnectivity_5xxServerErrors/status_500 +=== RUN TestURLConnectivity_5xxServerErrors/status_502 +=== RUN TestURLConnectivity_5xxServerErrors/status_503 +=== RUN TestURLConnectivity_5xxServerErrors/status_504 +--- PASS: TestURLConnectivity_5xxServerErrors (0.01s) + --- PASS: TestURLConnectivity_5xxServerErrors/status_500 (0.00s) + --- PASS: TestURLConnectivity_5xxServerErrors/status_502 (0.00s) + --- PASS: TestURLConnectivity_5xxServerErrors/status_503 (0.00s) + --- PASS: TestURLConnectivity_5xxServerErrors/status_504 (0.00s) +=== RUN TestURLConnectivity_TooManyRedirects +--- PASS: TestURLConnectivity_TooManyRedirects (0.00s) +=== RUN TestValidateRedirectTarget_TooManyRedirects +--- PASS: TestValidateRedirectTarget_TooManyRedirects (0.00s) +=== RUN TestValidateRedirectTarget_SchemeChangeBlocked +--- PASS: TestValidateRedirectTarget_SchemeChangeBlocked (0.00s) +=== RUN TestValidateRedirectTarget_HTTPToHTTPSAllowed +--- PASS: TestValidateRedirectTarget_HTTPToHTTPSAllowed (0.00s) +=== RUN TestValidateRedirectTarget_HTTPToHTTPSBlockedWhenNotAllowed +--- PASS: TestValidateRedirectTarget_HTTPToHTTPSBlockedWhenNotAllowed (0.00s) +=== RUN TestURLConnectivity_CloudMetadataBlocked +=== RUN TestURLConnectivity_CloudMetadataBlocked/http://169.254.169.254/latest/meta-data/ +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"ssrf_block","host":"169.254.169.254","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_CloudMetadataBlocked/http://169.254.169.254 +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"ssrf_block","host":"169.254.169.254","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_CloudMetadataBlocked (0.00s) + --- PASS: TestURLConnectivity_CloudMetadataBlocked/http://169.254.169.254/latest/meta-data/ (0.00s) + --- PASS: TestURLConnectivity_CloudMetadataBlocked/http://169.254.169.254 (0.00s) +=== RUN TestURLConnectivity_InvalidPort +=== RUN TestURLConnectivity_InvalidPort/port_zero +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011444027089363","result":"blocked","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_InvalidPort/port_negative +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"url_connectivity_test","host":"http://example.com:-1/path","request_id":"test-1768011444027214553","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_InvalidPort/port_too_large +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"url_connectivity_test","host":"example.com","request_id":"test-1768011444027317454","result":"blocked","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_InvalidPort/port_non_numeric +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"url_connectivity_test","host":"http://example.com:abc/path","request_id":"test-1768011444027442354","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_InvalidPort (0.00s) + --- PASS: TestURLConnectivity_InvalidPort/port_zero (0.00s) + --- PASS: TestURLConnectivity_InvalidPort/port_negative (0.00s) + --- PASS: TestURLConnectivity_InvalidPort/port_too_large (0.00s) + --- PASS: TestURLConnectivity_InvalidPort/port_non_numeric (0.00s) +=== RUN TestURLConnectivity_HTTPSScheme +--- PASS: TestURLConnectivity_HTTPSScheme (0.01s) +=== RUN TestURLConnectivity_ExplicitPort +--- PASS: TestURLConnectivity_ExplicitPort (0.00s) +=== RUN TestURLConnectivity_DefaultHTTPPort +--- PASS: TestURLConnectivity_DefaultHTTPPort (0.00s) +=== RUN TestURLConnectivity_ConnectionTimeout +--- PASS: TestURLConnectivity_ConnectionTimeout (0.10s) +=== RUN TestURLConnectivity_RequestHeaders +--- PASS: TestURLConnectivity_RequestHeaders (0.00s) +=== RUN TestURLConnectivity_EmptyURL +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"url_connectivity_test","host":"","request_id":"test-1768011444140529788","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_EmptyURL (0.00s) +=== RUN TestURLConnectivity_MalformedURL +=== RUN TestURLConnectivity_MalformedURL/://missing-scheme +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"url_connectivity_test","host":"://missing-scheme","request_id":"test-1768011444140750969","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_MalformedURL/http:// +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"url_connectivity_test","host":"","request_id":"test-1768011444140886880","result":"blocked","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_MalformedURL/http:///no-host +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"url_connectivity_test","host":"","request_id":"test-1768011444141011500","result":"blocked","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_MalformedURL (0.00s) + --- PASS: TestURLConnectivity_MalformedURL/://missing-scheme (0.00s) + --- PASS: TestURLConnectivity_MalformedURL/http:// (0.00s) + --- PASS: TestURLConnectivity_MalformedURL/http:///no-host (0.00s) +=== RUN TestURLConnectivity_IPv6Loopback +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"ssrf_block","host":"::1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_IPv6Loopback (0.00s) +=== RUN TestURLConnectivity_HeadMethod +--- PASS: TestURLConnectivity_HeadMethod (0.00s) +=== RUN TestResolveAllowedIP_LoopbackWithAllowLocalhost +--- PASS: TestResolveAllowedIP_LoopbackWithAllowLocalhost (0.00s) +=== RUN TestResolveAllowedIP_LoopbackWithoutAllowLocalhost +--- PASS: TestResolveAllowedIP_LoopbackWithoutAllowLocalhost (0.00s) +=== RUN TestURLConnectivity_HTTPSDefaultPort +--- PASS: TestURLConnectivity_HTTPSDefaultPort (0.01s) +=== RUN TestURLConnectivity_ValidPortNumber +--- PASS: TestURLConnectivity_ValidPortNumber (0.00s) +=== RUN TestURLConnectivity_PublicIPLiteralHTTP +--- PASS: TestURLConnectivity_PublicIPLiteralHTTP (0.00s) +=== RUN TestURLConnectivity_DNSResolutionError +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"url_connectivity_test","host":"nonexistent-domain-xyz123456.invalid","request_id":"test-1768011444155460119","result":"error","resolved_ips":null,"blocked_reason":"","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_DNSResolutionError (0.00s) +=== RUN TestResolveAllowedIP_PublicIPv4Literal +--- PASS: TestResolveAllowedIP_PublicIPv4Literal (0.00s) +=== RUN TestResolveAllowedIP_PublicIPv6Literal +--- PASS: TestResolveAllowedIP_PublicIPv6Literal (0.00s) +=== RUN TestResolveAllowedIP_PrivateIPBlocked +=== RUN TestResolveAllowedIP_PrivateIPBlocked/RFC1918_10x +=== RUN TestResolveAllowedIP_PrivateIPBlocked/RFC1918_172x +=== RUN TestResolveAllowedIP_PrivateIPBlocked/RFC1918_192x +=== RUN TestResolveAllowedIP_PrivateIPBlocked/LinkLocal +=== RUN TestResolveAllowedIP_PrivateIPBlocked/Metadata +--- PASS: TestResolveAllowedIP_PrivateIPBlocked (0.00s) + --- PASS: TestResolveAllowedIP_PrivateIPBlocked/RFC1918_10x (0.00s) + --- PASS: TestResolveAllowedIP_PrivateIPBlocked/RFC1918_172x (0.00s) + --- PASS: TestResolveAllowedIP_PrivateIPBlocked/RFC1918_192x (0.00s) + --- PASS: TestResolveAllowedIP_PrivateIPBlocked/LinkLocal (0.00s) + --- PASS: TestResolveAllowedIP_PrivateIPBlocked/Metadata (0.00s) +=== RUN TestURLConnectivity_PrivateNetworkRanges +=== RUN TestURLConnectivity_PrivateNetworkRanges/RFC1918_10x +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"ssrf_block","host":"10.255.255.255","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_PrivateNetworkRanges/RFC1918_172x +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"ssrf_block","host":"172.31.255.255","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_PrivateNetworkRanges/RFC1918_192x +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"ssrf_block","host":"192.168.255.255","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_PrivateNetworkRanges/LinkLocal +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"ssrf_block","host":"169.254.1.1","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_PrivateNetworkRanges/ZeroNet +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"ssrf_block","host":"0.0.0.0","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +=== RUN TestURLConnectivity_PrivateNetworkRanges/Broadcast +2026/01/10 02:17:24 [SECURITY AUDIT] {"timestamp":"2026-01-10T02:17:24Z","action":"ssrf_block","host":"255.255.255.255","request_id":"","result":"blocked","resolved_ips":null,"blocked_reason":"private_ip","user_id":"system","source_ip":""} +--- PASS: TestURLConnectivity_PrivateNetworkRanges (0.00s) + --- PASS: TestURLConnectivity_PrivateNetworkRanges/RFC1918_10x (0.00s) + --- PASS: TestURLConnectivity_PrivateNetworkRanges/RFC1918_172x (0.00s) + --- PASS: TestURLConnectivity_PrivateNetworkRanges/RFC1918_192x (0.00s) + --- PASS: TestURLConnectivity_PrivateNetworkRanges/LinkLocal (0.00s) + --- PASS: TestURLConnectivity_PrivateNetworkRanges/ZeroNet (0.00s) + --- PASS: TestURLConnectivity_PrivateNetworkRanges/Broadcast (0.00s) +=== RUN TestURLConnectivity_MultipleStatusCodes +=== RUN TestURLConnectivity_MultipleStatusCodes/200_OK +=== RUN TestURLConnectivity_MultipleStatusCodes/201_Created +=== RUN TestURLConnectivity_MultipleStatusCodes/204_NoContent +=== RUN TestURLConnectivity_MultipleStatusCodes/400_BadRequest +=== RUN TestURLConnectivity_MultipleStatusCodes/401_Unauthorized +=== RUN TestURLConnectivity_MultipleStatusCodes/403_Forbidden +=== RUN TestURLConnectivity_MultipleStatusCodes/404_NotFound +=== RUN TestURLConnectivity_MultipleStatusCodes/429_TooManyRequests +=== RUN TestURLConnectivity_MultipleStatusCodes/500_InternalServerError +=== RUN TestURLConnectivity_MultipleStatusCodes/502_BadGateway +=== RUN TestURLConnectivity_MultipleStatusCodes/503_ServiceUnavailable +=== RUN TestURLConnectivity_MultipleStatusCodes/504_GatewayTimeout +--- PASS: TestURLConnectivity_MultipleStatusCodes (0.02s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/200_OK (0.00s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/201_Created (0.00s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/204_NoContent (0.00s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/400_BadRequest (0.00s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/401_Unauthorized (0.00s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/403_Forbidden (0.00s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/404_NotFound (0.00s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/429_TooManyRequests (0.00s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/500_InternalServerError (0.00s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/502_BadGateway (0.00s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/503_ServiceUnavailable (0.00s) + --- PASS: TestURLConnectivity_MultipleStatusCodes/504_GatewayTimeout (0.00s) +=== RUN TestURLConnectivity_RedirectToPrivateIP +--- PASS: TestURLConnectivity_RedirectToPrivateIP (0.00s) +=== RUN TestValidateRedirectTarget_ValidExternalRedirect +--- PASS: TestValidateRedirectTarget_ValidExternalRedirect (0.00s) +=== RUN TestValidateRedirectTarget_SameSchemeAllowed +--- PASS: TestValidateRedirectTarget_SameSchemeAllowed (0.00s) +=== RUN TestURLConnectivity_NetworkError +--- PASS: TestURLConnectivity_NetworkError (0.00s) +=== RUN TestURLConnectivity_HTTPSWithDefaultPort +--- PASS: TestURLConnectivity_HTTPSWithDefaultPort (0.01s) +=== RUN TestURLConnectivity_HTTPWithExplicitPortValidation +--- PASS: TestURLConnectivity_HTTPWithExplicitPortValidation (0.00s) +=== RUN TestIsDockerBridgeIP_AllCases +=== RUN TestIsDockerBridgeIP_AllCases/docker_bridge_172_17 +=== RUN TestIsDockerBridgeIP_AllCases/docker_bridge_172_18 +=== RUN TestIsDockerBridgeIP_AllCases/docker_bridge_172_31 +=== RUN TestIsDockerBridgeIP_AllCases/public_ip +=== RUN TestIsDockerBridgeIP_AllCases/localhost +=== RUN TestIsDockerBridgeIP_AllCases/private_10x +=== RUN TestIsDockerBridgeIP_AllCases/private_192x +=== RUN TestIsDockerBridgeIP_AllCases/empty +=== RUN TestIsDockerBridgeIP_AllCases/invalid +=== RUN TestIsDockerBridgeIP_AllCases/hostname +=== RUN TestIsDockerBridgeIP_AllCases/ipv6_loopback +--- PASS: TestIsDockerBridgeIP_AllCases (0.00s) + --- PASS: TestIsDockerBridgeIP_AllCases/docker_bridge_172_17 (0.00s) + --- PASS: TestIsDockerBridgeIP_AllCases/docker_bridge_172_18 (0.00s) + --- PASS: TestIsDockerBridgeIP_AllCases/docker_bridge_172_31 (0.00s) + --- PASS: TestIsDockerBridgeIP_AllCases/public_ip (0.00s) + --- PASS: TestIsDockerBridgeIP_AllCases/localhost (0.00s) + --- PASS: TestIsDockerBridgeIP_AllCases/private_10x (0.00s) + --- PASS: TestIsDockerBridgeIP_AllCases/private_192x (0.00s) + --- PASS: TestIsDockerBridgeIP_AllCases/empty (0.00s) + --- PASS: TestIsDockerBridgeIP_AllCases/invalid (0.00s) + --- PASS: TestIsDockerBridgeIP_AllCases/hostname (0.00s) + --- PASS: TestIsDockerBridgeIP_AllCases/ipv6_loopback (0.00s) +=== RUN TestURLConnectivity_RedirectChain +--- PASS: TestURLConnectivity_RedirectChain (0.00s) +=== RUN TestValidateRedirectTarget_FirstRedirect +--- PASS: TestValidateRedirectTarget_FirstRedirect (0.00s) +=== RUN TestURLConnectivity_ResponseBodyClosed +--- PASS: TestURLConnectivity_ResponseBodyClosed (0.00s) +PASS +coverage: 89.2% of statements +ok github.com/Wikid82/charon/backend/internal/utils (cached) coverage: 89.2% of statements +=== RUN TestFull +=== PAUSE TestFull +=== CONT TestFull +--- PASS: TestFull (0.00s) +PASS +coverage: 100.0% of statements +ok github.com/Wikid82/charon/backend/internal/version (cached) coverage: 100.0% of statements + github.com/Wikid82/charon/backend/pkg/dnsprovider coverage: 0.0% of statements +=== RUN TestCloudflareProvider +--- PASS: TestCloudflareProvider (0.00s) +=== RUN TestRoute53Provider +--- PASS: TestRoute53Provider (0.00s) +=== RUN TestDigitalOceanProvider +--- PASS: TestDigitalOceanProvider (0.00s) +=== RUN TestGoogleCloudDNSProvider +--- PASS: TestGoogleCloudDNSProvider (0.00s) +=== RUN TestAzureProvider +--- PASS: TestAzureProvider (0.00s) +=== RUN TestNamecheapProvider +--- PASS: TestNamecheapProvider (0.00s) +=== RUN TestGoDaddyProvider +--- PASS: TestGoDaddyProvider (0.00s) +=== RUN TestHetznerProvider +--- PASS: TestHetznerProvider (0.00s) +=== RUN TestVultrProvider +--- PASS: TestVultrProvider (0.00s) +=== RUN TestDNSimpleProvider +--- PASS: TestDNSimpleProvider (0.00s) +=== RUN TestProviderRegistration +--- PASS: TestProviderRegistration (0.00s) +PASS +coverage: 30.4% of statements +ok github.com/Wikid82/charon/backend/pkg/dnsprovider/builtin (cached) coverage: 30.4% of statements +FAIL diff --git a/docs/plans/current_spec.md b/docs/plans/current_spec.md index 7baf2719..10d10f39 100644 --- a/docs/plans/current_spec.md +++ b/docs/plans/current_spec.md @@ -1,297 +1,137 @@ -# Patch Coverage Remediation Plan (Codecov) — Backend + +# Security Remediation Plan — DoD Failures (CodeQL + Trivy) **Created:** 2026-01-09 +This plan addresses the **HIGH/CRITICAL security findings** reported in [docs/reports/qa_report.md](docs/reports/qa_report.md). + +> The prior Codecov patch-coverage plan was moved to [docs/plans/patch_coverage_spec.md](docs/plans/patch_coverage_spec.md). + ## Goal -Restore **Codecov patch coverage** to green by ensuring **100% of modified lines** are executed by tests. +Restore DoD to ✅ PASS by eliminating **all HIGH/CRITICAL** findings from: -- **Codecov patch coverage:** 92.93866% -- **Reported missing patch lines:** ~99 +- CodeQL (Go + JS) results produced by **Security: CodeQL All (CI-Aligned)** +- Trivy results produced by **Security: Trivy Scan** Hard constraints: -- Do **not** lower Codecov thresholds. -- Fix with **targeted tests** (only add micro “test hooks” if absolutely unavoidable). +- Do **not** weaken gates (no suppressing findings unless a false-positive is proven and documented). +- Prefer minimal, targeted changes. +- Avoid adding new runtime dependencies. -## Scope (two workstreams; both in-scope) +## Scope -### Workstream A (required): Patch coverage fixes for 10 backend files +From the QA report: -This workstream fixes Codecov **patch coverage** by adding targeted tests (and only minimal seams if unavoidable) for the following backend files: +### CodeQL Go -1. backend/internal/api/handlers/plugin_handler.go -2. backend/internal/api/handlers/encryption_handler.go -3. backend/internal/api/handlers/credential_handler.go -4. backend/internal/api/handlers/settings_handler.go -5. backend/internal/api/handlers/crowdsec_handler.go -6. backend/internal/api/handlers/proxy_host_handler.go -7. backend/internal/api/handlers/security_handler.go -8. backend/internal/caddy/config.go -9. backend/internal/caddy/client.go -10. backend/internal/api/handlers/testdb.go (special: ignored in `.codecov.yml` but may still show up) +- Rule: `go/email-injection` (**CRITICAL**) +- Location: `backend/internal/services/mail_service.go` (reported around lines ~222, ~340, ~393) -### Workstream B (required): Prevention updates (instructions + agent files) +### CodeQL JS -This workstream updates the following files to ensure future production-code changes include the patch-coverage triage + tests needed to keep Codecov patch coverage green: +- Rule: `js/incomplete-hostname-regexp` (**HIGH**) +- Location: `frontend/src/pages/__tests__/ProxyHosts-extra.test.tsx` (reported around line ~252) -- .github/instructions/testing.instructions.md -- .github/instructions/copilot-instructions.md -- .github/instructions/taming-copilot.instructions.md -- .github/agents/Backend_Dev.agent.md -- .github/agents/QA_Security.agent.md -- .github/agents/Supervisor.agent.md -- .github/agents/Frontend_Dev.agent.md (only if frontend changes are involved; otherwise leave untouched) +### Trivy -Non-goals: -- No unrelated refactors. -- No new integration tests requiring external services. +QA report note: Trivy filesystem scan may be picking up **workspace caches/artifacts** (e.g., `.cache/go/pkg/mod/...` and other generated directories) in addition to repo-tracked files, while the **image scan may already be clean**. -## Missing Files Table (copy from Codecov patch report) +## Step 0 — Trivy triage (required first) -Codecov “Patch” view is the source of truth. Paste the **exact missing/partial line ranges** into this table. +Objective: Re-run the current Trivy task and determine whether HIGH/CRITICAL findings are attributable to: +- **Repo-tracked paths** (e.g., `backend/go.mod`, `backend/go.sum`, `Dockerfile`, `frontend/`, etc.), or +- **Generated/cache paths** under the workspace (e.g., `.cache/`, `**/*.cover`, `codeql-db-*`, temporary build outputs). -Note: Codecov “Patch” view is the source of truth. This table is only for tracking what you copied from Codecov and what test you’ll add. +Steps: +1. Run **Security: Trivy Scan**. +2. For each HIGH/CRITICAL item, record the affected file path(s) reported by Trivy. +3. Classify each finding: + - **Repo-tracked**: path is under version control (or clearly part of the shipped build artifact, e.g., the built `app/charon` binary or image layers). + - **Scan-scope noise**: path is a workspace cache/artifact directory not intended as deliverable input. -| File | Missing patch line ranges (Codecov) | Partial patch line ranges (Codecov) | Primary test strategy | -|------|-------------------------------------|-------------------------------------|-----------------------| -| backend/internal/api/handlers/plugin_handler.go | (paste) | (paste) | Gin handler tests (httptest) to hit error + warn-but-200 branches | -| backend/internal/api/handlers/encryption_handler.go | (paste) | (paste) | Handler tests for rotate/validate error + success branches | -| backend/internal/api/handlers/credential_handler.go | (paste) | (paste) | Handler tests for parse/notfound/service-error branches | -| backend/internal/api/handlers/settings_handler.go | (paste) | (paste) | Handler tests for URL test/SSRF + “reachable=false” mapping | -| backend/internal/api/handlers/crowdsec_handler.go | (paste) | (paste) | Handler tests using httptest.Server to force non-200/non-JSON branches | -| backend/internal/api/handlers/proxy_host_handler.go | (paste) | (paste) | Handler tests for JSON type coercion and invalid payload validation | -| backend/internal/api/handlers/security_handler.go | (paste) | (paste) | Handler tests for effective-status branches + validation branches | -| backend/internal/caddy/config.go | (paste) | (paste) | Unit tests for helper branches + config generation edge cases | -| backend/internal/caddy/client.go | (paste) | (paste) | Unit tests for HTTP non-200 + endpoint/parse branches | -| backend/internal/api/handlers/testdb.go | (paste) | (paste) | Prefer moving helpers into *_test.go; else fix ignore/path mismatch | +Decision outcomes: +- If HIGH/CRITICAL are **repo-tracked / shipped** → remediate by upgrading only the affected components to Trivy’s fixed versions (see Workstreams C/D). +- If HIGH/CRITICAL are **only cache/artifact paths** → treat as scan-scope noise and align Trivy scan scope to repo contents by excluding those directories (without disabling scanners or suppressing findings). -## Why patch coverage missed (common patterns) +## Workstreams (by role) -Patch coverage misses are usually caused by newly-changed lines being in branches that existing tests don’t naturally hit: +### Workstream A — Backend (Backend_Dev): Fix `go/email-injection` -- Error-only branches (DB errors, JSON decode failures, upstream non-200) -- “Warn but succeed” flows (HTTP 200 with a warning field) -- JSON type coercion (e.g., `map[string]any` decoding numbers vs strings) -- Env-driven branches (missing `t.Setenv` in tests) -- Codecov ignore/path normalization mismatch (repo-relative path vs module path in coverprofile) +Objective: Ensure no untrusted data can inject additional headers/body content into SMTP `DATA`. -## Handling partial (yellow) patch lines +Implementation direction (minimal + CodeQL-friendly): -Partial patch lines (yellow) usually mean the line executed, but **not all branches associated with that line** did. +1. **Centralize email header construction** (avoid raw `fmt.Sprintf("%s: %s\r\n", ...)` with untrusted input). +2. **Reject** header values containing `\r` or `\n` (and other control characters if feasible). +3. Ensure email addresses are created using strict parsing/formatting (`net/mail`) and avoid concatenating raw address strings. +4. Add unit tests that attempt CRLF injection in subject/from/to and assert the send/build path rejects it. -Common causes: -- Short-circuit boolean logic (e.g., `a && b`, `a || b`) where tests only exercise one side. -- Multi-branch conditionals (`if/else if/else`, `switch`) where only one case is hit. -- Error wrapping/logging paths that run only when an upstream returns an error. +Acceptance criteria: +- CodeQL Go scan shows **0** `go/email-injection` findings. +- Backend unit tests cover the rejection paths. -Guidance: -- Treat yellow lines like “missing branch coverage”, not “missing statement coverage”. -- Write the smallest additional test that triggers the unhit branch (invalid input, not-found, service error, upstream non-200, etc.). -- Prefer deterministic seams: `httptest.Server` for upstream failures; fake services/mocks for DB/service errors; explicit `t.Setenv` for env-driven branches. +### Workstream B — Frontend (Frontend_Dev): Fix `js/incomplete-hostname-regexp` -## How to locate exact missing lines in Codecov (triage) +Objective: Remove an “incomplete hostname regex” pattern flagged by CodeQL. -1. Open the PR in GitHub. -2. Open the Codecov check details (“Details” / “View report”). -3. Switch to **Patch** (not Project). -4. Filter to the 10 scoped files. -5. For each file, copy the exact missing (red) and partial (yellow) line ranges. -6. Map each range to a minimal test that executes the branch. +Preferred change: +- Replace hostname regex usage with an exact string match (or an anchored + escaped regex like `^link\.example\.com$`). -Local assist (for understanding branches; Codecov still authoritative): -- Run VS Code task: **Test: Backend with Coverage**. -- View coverage HTML: `go tool cover -html=backend/coverage.txt`. +Acceptance criteria: +- CodeQL JS scan shows **0** `js/incomplete-hostname-regexp` findings. -## Remediation Plan (tight, test-first) +### Workstream C — Container / embedded binaries (DevOps): Fix Trivy image finding -1. Extract missing patch line ranges from Codecov and fill the table above. -2. For each missing range: - - Identify the branch (if/switch/early return) causing it. - - Add the shortest test that executes it. -3. Re-run VS Code task **Test: Backend with Coverage**. -4. Confirm Codecov patch view is green (100% patch coverage). +Objective: Ensure the built image does not ship `crowdsec`/`cscli` binaries that embed vulnerable `github.com/expr-lang/expr v1.17.2`. -## Per-File Testing Strategy (scoped) +Implementation direction: -Only add tests that hit the lines Codecov marks missing. +1. If any changes are made to `Dockerfile` (including the CrowdSec build stage), rebuild the image (**no-cache recommended**) before validating. +2. Prefer **bumping the pinned CrowdSec version** in `Dockerfile` to a release that already depends on `expr >= 1.17.7`. +3. If no suitable CrowdSec release is available, patch the build in the CrowdSec build stage similarly to the existing Caddy stage override (force `expr@1.17.7` before building). -### 1) backend/internal/api/handlers/plugin_handler.go +Acceptance criteria: +- Trivy image scan reports **0 HIGH/CRITICAL**. -- Prioritize handler tests that cover: - - invalid `:id` parsing → 400 - - not found → 404 - - DB update failures → 500 - - loader reload/load failures (often warn-but-200) → assert response body, not just status +### Workstream D — Go module upgrades (Backend_Dev + QA_Security): Fix Trivy repo scan findings -### 2) backend/internal/api/handlers/encryption_handler.go +Objective: Eliminate Trivy filesystem-scan HIGH/CRITICAL findings without over-upgrading unrelated dependencies. -- Add/extend tests to cover: - - request bind/validation errors → 400 - - service-layer failures → 500 - - success path + any new audit/log branches +Implementation direction (conditional; driven by Step 0 triage): -### 3) backend/internal/api/handlers/credential_handler.go +1. If Trivy attributes HIGH/CRITICAL to `backend/go.mod` / `backend/go.sum` **or** to the built `app/charon` binary: + - Bump **only the specific Go modules Trivy flags** to Trivy’s fixed versions. + - Run `go mod tidy` and ensure builds/tests stay green. +2. If Trivy attributes HIGH/CRITICAL **only** to workspace caches / generated artifacts (e.g., `.cache/go/pkg/mod/...`): + - Treat as scan-scope noise and align Trivy’s filesystem scan scope to repo-tracked content by excluding those directories. + - This is **not** gate weakening: scanners stay enabled and the project must still achieve **0 HIGH/CRITICAL** in Trivy outputs. -- Add/extend tests to cover: - - invalid ID parse → 400 - - not found → 404 - - create/update invalid provider / credential validation failures (where applicable) - - “test credentials” endpoint: service error mapping +Acceptance criteria: +- Trivy scan reports **0 HIGH/CRITICAL**. -### 4) backend/internal/api/handlers/settings_handler.go +## Validation (VS Code tasks) -- Add/extend tests to cover: - - invalid URL input → 400 - - SSRF-blocked / unreachable cases that return 200 but set `reachable=false` - - network failures and how they’re represented in the response +Run tasks in this order (only run frontend ones if Workstream B changes anything under `frontend/`): -### 5) backend/internal/api/handlers/crowdsec_handler.go +1. **Build: Backend** +2. **Test: Backend with Coverage** +3. **Security: CodeQL All (CI-Aligned)** +4. **Security: Trivy Scan** (explicitly verify **both** filesystem-scan and image-scan outputs are **0 HIGH/CRITICAL**) +5. **Lint: Pre-commit (All Files)** -- Use `httptest.Server` to deterministically cover: - - upstream non-200 responses - - upstream non-JSON responses - - query param forwarding (capture server request URL) +If any changes are made to `Dockerfile` / CrowdSec build stage: -### 6) backend/internal/api/handlers/proxy_host_handler.go +6. **Build & Run: Local Docker Image No-Cache** (recommended) +7. **Security: Trivy Scan** (re-verify image scan after rebuild) -- Add/extend tests that submit JSON payloads exercising type coercion: - - wrong types (string vs number) → 400 - - boundary values (negative ports, missing required fields) - - normalization branches (if Codecov points to them) +If `frontend/` changes are made: -### 7) backend/internal/api/handlers/security_handler.go +6. **Lint: TypeScript Check** +7. **Test: Frontend with Coverage** +8. **Lint: Frontend** -- Add/extend tests to cover: - - effective-status branch selection (settings vs DB vs defaults) - - validation branches (IP/CIDR parsing, enum validation, etc.) +## Handoff checklist -### 8) backend/internal/caddy/config.go - -- Prefer helper-level unit tests (cheaper and more targeted) to cover: - - header normalization helpers - - CSP/security header composition - - CIDR parsing and bypass list validation - - skip/empty branches (e.g., “missing provider config → skip policy”) - -### 9) backend/internal/caddy/client.go - -- Add/extend tests for: - - endpoint validation failures - - HTTP non-200 response branches - - parse/marshal error branches (only if Codecov points to them) - -### 10) backend/internal/api/handlers/testdb.go (special) - -If Codecov patch view includes this file: - -What’s happening: -- `.codecov.yml` currently ignores `backend/internal/api/handlers/testdb.go`, but Go coverprofiles can report paths as module-import paths (example from `backend/coverage.txt`): `github.com/Wikid82/charon/backend/internal/.../testdb.go`. -- If Codecov is matching against the coverprofile path form, the repo-relative ignore may not apply. - -Make it actionable: -1. Confirm what path form the coverprofile is using: - - `grep -n "testdb.go" backend/coverage.txt` -2. If the result is a module/import-path form (example: `github.com/Wikid82/charon/backend/internal/api/handlers/testdb.go`), add one additional ignore entry to `.codecov.yml` so it matches what Codecov is actually seeing. - - Minimal, explicit (exact repo/module path): `"**/github.com/Wikid82/charon/backend/internal/api/handlers/testdb.go"` - - More resilient (still narrow): `"**/github.com/**/backend/internal/api/handlers/testdb.go"` - -Note: -- Do not add `"**/backend/internal/api/handlers/testdb.go"` unless it’s missing; the repo-relative ignore is already present. - -Why this is minimal: -- `.codecov.yml` already ignores the repo-relative path, but ignore matching can fail if Codecov consumes coverprofile paths that include the module/import prefix. -- Only add the extra ignore if the grep confirms the import-path form is present. - -Preferred approach (in order): -1. Move test-only helpers into a `_test.go` file. - - Caution: this is only safe if **no other packages’ tests import those helpers**. Anything in `*_test.go` cannot be imported by other packages. -2. Otherwise, keep the helper in non-test code but rely on `.codecov.yml` ignores that match the coverprofile path form. - -## Prevention: required instruction/agent updates (diff-style) - -These are the minimal guardrails to prevent future patch-coverage regressions. - -### A) .github/instructions/testing.instructions.md - -```diff -@@ - ## 3. Coverage & Completion - * **Coverage Gate:** A task is not "Complete" until a coverage report is generated. - * **Threshold Compliance:** You must compare the final coverage percentage against the project's threshold (Default: 85% unless specified otherwise). If coverage drops, you must identify the "uncovered lines" and add targeted tests. -+* **Patch Coverage Gate (Codecov):** If production code is modified, Codecov **patch coverage must be 100%** for the modified lines. Do not relax thresholds; add targeted tests. -+* **Patch Triage Requirement:** Plans must include the exact missing/partial patch line ranges copied from Codecov’s **Patch** view. -``` - -### B) .github/instructions/taming-copilot.instructions.md - -```diff -@@ - ## Surgical Code Modification -- **Focus on the Core Request**: Generate code that directly addresses the user's request, without adding extra features or handling edge cases that were not mentioned. -+ **Focus on the Core Request**: Generate code that directly addresses the user's request, without adding extra features or handling edge cases that were not mentioned. -+ **Spec Hygiene**: When asked to update a plan/spec file, do not append unrelated/archived plans; keep it strictly scoped to the current task. -``` - -### C) .github/instructions/copilot-instructions.md - -```diff -@@ - 3. **Coverage Testing** (MANDATORY - Non-negotiable): -- - **MANDATORY**: Patch coverage must cover 100% of new/modified code. This prevents CodeCov Report failing CI. -+ - **MANDATORY**: Patch coverage must cover 100% of modified lines (Codecov Patch view must be green). If patch coverage fails, add targeted tests for the missing patch line ranges. -``` - -### D) .github/agents/Backend_Dev.agent.md - -```diff -@@ - 3. **Verification (Definition of Done)**: -@@ -- - **Coverage (MANDATORY)**: Run the coverage script explicitly. This is NOT run by pre-commit automatically. -+ - **Coverage (MANDATORY)**: Run the coverage task/script explicitly and confirm Codecov patch view is green for modified lines. -``` - -### E) .github/agents/Frontend_Dev.agent.md (only if frontend changes are involved) - -```diff -@@ - 3. **Verification (Quality Gates)**: -@@ - - **Gate 3: Coverage (MANDATORY)**: -@@ - - **MANDATORY**: Patch coverage must cover 100% of new/modified code. This prevents CodeCov Report failing CI. -+ - If patch coverage fails, identify missing patch line ranges in Codecov Patch view and add targeted tests. -``` - -### F) .github/agents/QA_Security.agent.md - -```diff -@@ -- - When creating tests, if there are folders that don't require testing make sure to update `.codecov.yml` to exclude them from coverage reports or this throws off the difference between local and CI coverage. -+ - Prefer fixing patch coverage with tests. Only adjust `.codecov.yml` ignores when code is truly non-production (e.g., test-only helpers), and document why. -``` - -### G) .github/agents/Supervisor.agent.md - -```diff -@@ -- - **Plan Completeness**: Does the plan cover all edge cases? Are there any missing components or unclear requirements? -+ - **Plan Completeness**: Does the plan cover all edge cases? Are there any missing components or unclear requirements? -+ - **Patch Coverage Completeness**: If coverage is in scope, does the plan include Codecov Patch missing/partial line ranges and the exact tests needed to execute them? -``` - -## Validation Checklist (patch-coverage scope) - -Required vs optional alignment: -- Required for this plan to be considered complete: Workstream A + Workstream B changes landed. -- This validation checklist is intentionally focused on Workstream A (patch coverage remediation). For additional repo-wide Definition of Done items, follow `.github/instructions/copilot-instructions.md`. -- Workstream B validation is “diff applied + reviewer confirmation” (it doesn’t impact Go patch coverage directly). - -Run these tasks in order: - -1. **Test: Backend with Coverage** - - Pass criteria: task succeeds; `backend/coverage.txt` generated; zero failing tests. - - Outcome criteria: Codecov patch status becomes green (100% patch coverage). - -2. **Lint: Pre-commit (All Files)** (optional; general DoD) - - Pass criteria: all hooks pass. +- Attach updated `codeql-results-*.sarif` and Trivy artifacts for **both filesystem and image** outputs to the QA rerun. +- Confirm the QA report’s pass/fail criteria are satisfied (no HIGH/CRITICAL findings). diff --git a/docs/plans/patch_coverage_spec.md b/docs/plans/patch_coverage_spec.md new file mode 100644 index 00000000..6fe59ed5 --- /dev/null +++ b/docs/plans/patch_coverage_spec.md @@ -0,0 +1,299 @@ +# Patch Coverage Remediation Plan (Codecov) — Backend + +**Created:** 2026-01-09 + +> Note: This plan was moved from `docs/plans/current_spec.md` to keep the current plan focused on security remediation. + +## Goal + +Restore **Codecov patch coverage** to green by ensuring **100% of modified lines** are executed by tests. + +- **Codecov patch coverage:** 92.93866% +- **Reported missing patch lines:** ~99 + +Hard constraints: +- Do **not** lower Codecov thresholds. +- Fix with **targeted tests** (only add micro “test hooks” if absolutely unavoidable). + +## Scope (two workstreams; both in-scope) + +### Workstream A (required): Patch coverage fixes for 10 backend files + +This workstream fixes Codecov **patch coverage** by adding targeted tests (and only minimal seams if unavoidable) for the following backend files: + +1. backend/internal/api/handlers/plugin_handler.go +2. backend/internal/api/handlers/encryption_handler.go +3. backend/internal/api/handlers/credential_handler.go +4. backend/internal/api/handlers/settings_handler.go +5. backend/internal/api/handlers/crowdsec_handler.go +6. backend/internal/api/handlers/proxy_host_handler.go +7. backend/internal/api/handlers/security_handler.go +8. backend/internal/caddy/config.go +9. backend/internal/caddy/client.go +10. backend/internal/api/handlers/testdb.go (special: ignored in `.codecov.yml` but may still show up) + +### Workstream B (required): Prevention updates (instructions + agent files) + +This workstream updates the following files to ensure future production-code changes include the patch-coverage triage + tests needed to keep Codecov patch coverage green: + +- .github/instructions/testing.instructions.md +- .github/instructions/copilot-instructions.md +- .github/instructions/taming-copilot.instructions.md +- .github/agents/Backend_Dev.agent.md +- .github/agents/QA_Security.agent.md +- .github/agents/Supervisor.agent.md +- .github/agents/Frontend_Dev.agent.md (only if frontend changes are involved; otherwise leave untouched) + +Non-goals: +- No unrelated refactors. +- No new integration tests requiring external services. + +## Missing Files Table (copy from Codecov patch report) + +Codecov “Patch” view is the source of truth. Paste the **exact missing/partial line ranges** into this table. + +Note: Codecov “Patch” view is the source of truth. This table is only for tracking what you copied from Codecov and what test you’ll add. + +| File | Missing patch line ranges (Codecov) | Partial patch line ranges (Codecov) | Primary test strategy | +|------|-------------------------------------|-------------------------------------|-----------------------| +| backend/internal/api/handlers/plugin_handler.go | (paste) | (paste) | Gin handler tests (httptest) to hit error + warn-but-200 branches | +| backend/internal/api/handlers/encryption_handler.go | (paste) | (paste) | Handler tests for rotate/validate error + success branches | +| backend/internal/api/handlers/credential_handler.go | (paste) | (paste) | Handler tests for parse/notfound/service-error branches | +| backend/internal/api/handlers/settings_handler.go | (paste) | (paste) | Handler tests for URL test/SSRF + “reachable=false” mapping | +| backend/internal/api/handlers/crowdsec_handler.go | (paste) | (paste) | Handler tests using httptest.Server to force non-200/non-JSON branches | +| backend/internal/api/handlers/proxy_host_handler.go | (paste) | (paste) | Handler tests for JSON type coercion and invalid payload validation | +| backend/internal/api/handlers/security_handler.go | (paste) | (paste) | Handler tests for effective-status branches + validation branches | +| backend/internal/caddy/config.go | (paste) | (paste) | Unit tests for helper branches + config generation edge cases | +| backend/internal/caddy/client.go | (paste) | (paste) | Unit tests for HTTP non-200 + endpoint/parse branches | +| backend/internal/api/handlers/testdb.go | (paste) | (paste) | Prefer moving helpers into *_test.go; else fix ignore/path mismatch | + +## Why patch coverage missed (common patterns) + +Patch coverage misses are usually caused by newly-changed lines being in branches that existing tests don’t naturally hit: + +- Error-only branches (DB errors, JSON decode failures, upstream non-200) +- “Warn but succeed” flows (HTTP 200 with a warning field) +- JSON type coercion (e.g., `map[string]any` decoding numbers vs strings) +- Env-driven branches (missing `t.Setenv` in tests) +- Codecov ignore/path normalization mismatch (repo-relative path vs module path in coverprofile) + +## Handling partial (yellow) patch lines + +Partial patch lines (yellow) usually mean the line executed, but **not all branches associated with that line** did. + +Common causes: +- Short-circuit boolean logic (e.g., `a && b`, `a || b`) where tests only exercise one side. +- Multi-branch conditionals (`if/else if/else`, `switch`) where only one case is hit. +- Error wrapping/logging paths that run only when an upstream returns an error. + +Guidance: +- Treat yellow lines like “missing branch coverage”, not “missing statement coverage”. +- Write the smallest additional test that triggers the unhit branch (invalid input, not-found, service error, upstream non-200, etc.). +- Prefer deterministic seams: `httptest.Server` for upstream failures; fake services/mocks for DB/service errors; explicit `t.Setenv` for env-driven branches. + +## How to locate exact missing lines in Codecov (triage) + +1. Open the PR in GitHub. +2. Open the Codecov check details (“Details” / “View report”). +3. Switch to **Patch** (not Project). +4. Filter to the 10 scoped files. +5. For each file, copy the exact missing (red) and partial (yellow) line ranges. +6. Map each range to a minimal test that executes the branch. + +Local assist (for understanding branches; Codecov still authoritative): +- Run VS Code task: **Test: Backend with Coverage**. +- View coverage HTML: `go tool cover -html=backend/coverage.txt`. + +## Remediation Plan (tight, test-first) + +1. Extract missing patch line ranges from Codecov and fill the table above. +2. For each missing range: + - Identify the branch (if/switch/early return) causing it. + - Add the shortest test that executes it. +3. Re-run VS Code task **Test: Backend with Coverage**. +4. Confirm Codecov patch view is green (100% patch coverage). + +## Per-File Testing Strategy (scoped) + +Only add tests that hit the lines Codecov marks missing. + +### 1) backend/internal/api/handlers/plugin_handler.go + +- Prioritize handler tests that cover: + - invalid `:id` parsing → 400 + - not found → 404 + - DB update failures → 500 + - loader reload/load failures (often warn-but-200) → assert response body, not just status + +### 2) backend/internal/api/handlers/encryption_handler.go + +- Add/extend tests to cover: + - request bind/validation errors → 400 + - service-layer failures → 500 + - success path + any new audit/log branches + +### 3) backend/internal/api/handlers/credential_handler.go + +- Add/extend tests to cover: + - invalid ID parse → 400 + - not found → 404 + - create/update invalid provider / credential validation failures (where applicable) + - “test credentials” endpoint: service error mapping + +### 4) backend/internal/api/handlers/settings_handler.go + +- Add/extend tests to cover: + - invalid URL input → 400 + - SSRF-blocked / unreachable cases that return 200 but set `reachable=false` + - network failures and how they’re represented in the response + +### 5) backend/internal/api/handlers/crowdsec_handler.go + +- Use `httptest.Server` to deterministically cover: + - upstream non-200 responses + - upstream non-JSON responses + - query param forwarding (capture server request URL) + +### 6) backend/internal/api/handlers/proxy_host_handler.go + +- Add/extend tests that submit JSON payloads exercising type coercion: + - wrong types (string vs number) → 400 + - boundary values (negative ports, missing required fields) + - normalization branches (if Codecov points to them) + +### 7) backend/internal/api/handlers/security_handler.go + +- Add/extend tests to cover: + - effective-status branch selection (settings vs DB vs defaults) + - validation branches (IP/CIDR parsing, enum validation, etc.) + +### 8) backend/internal/caddy/config.go + +- Prefer helper-level unit tests (cheaper and more targeted) to cover: + - header normalization helpers + - CSP/security header composition + - CIDR parsing and bypass list validation + - skip/empty branches (e.g., “missing provider config → skip policy”) + +### 9) backend/internal/caddy/client.go + +- Add/extend tests for: + - endpoint validation failures + - HTTP non-200 response branches + - parse/marshal error branches (only if Codecov points to them) + +### 10) backend/internal/api/handlers/testdb.go (special) + +If Codecov patch view includes this file: + +What’s happening: +- `.codecov.yml` currently ignores `backend/internal/api/handlers/testdb.go`, but Go coverprofiles can report paths as module-import paths (example from `backend/coverage.txt`): `github.com/Wikid82/charon/backend/internal/.../testdb.go`. +- If Codecov is matching against the coverprofile path form, the repo-relative ignore may not apply. + +Make it actionable: +1. Confirm what path form the coverprofile is using: + - `grep -n "testdb.go" backend/coverage.txt` +2. If the result is a module/import-path form (example: `github.com/Wikid82/charon/backend/internal/api/handlers/testdb.go`), add one additional ignore entry to `.codecov.yml` so it matches what Codecov is actually seeing. + - Minimal, explicit (exact repo/module path): `"**/github.com/Wikid82/charon/backend/internal/api/handlers/testdb.go"` + - More resilient (still narrow): `"**/github.com/**/backend/internal/api/handlers/testdb.go"` + +Note: +- Do not add `"**/backend/internal/api/handlers/testdb.go"` unless it’s missing; the repo-relative ignore is already present. + +Why this is minimal: +- `.codecov.yml` already ignores the repo-relative path, but ignore matching can fail if Codecov consumes coverprofile paths that include the module/import prefix. +- Only add the extra ignore if the grep confirms the import-path form is present. + +Preferred approach (in order): +1. Move test-only helpers into a `_test.go` file. + - Caution: this is only safe if **no other packages’ tests import those helpers**. Anything in `*_test.go` cannot be imported by other packages. +2. Otherwise, keep the helper in non-test code but rely on `.codecov.yml` ignores that match the coverprofile path form. + +## Prevention: required instruction/agent updates (diff-style) + +These are the minimal guardrails to prevent future patch-coverage regressions. + +### A) .github/instructions/testing.instructions.md + +```diff +@@ + ## 3. Coverage & Completion + * **Coverage Gate:** A task is not "Complete" until a coverage report is generated. + * **Threshold Compliance:** You must compare the final coverage percentage against the project's threshold (Default: 85% unless specified otherwise). If coverage drops, you must identify the "uncovered lines" and add targeted tests. ++* **Patch Coverage Gate (Codecov):** If production code is modified, Codecov **patch coverage must be 100%** for the modified lines. Do not relax thresholds; add targeted tests. ++* **Patch Triage Requirement:** Plans must include the exact missing/partial patch line ranges copied from Codecov’s **Patch** view. +``` + +### B) .github/instructions/taming-copilot.instructions.md + +```diff +@@ + ## Surgical Code Modification +- **Focus on the Core Request**: Generate code that directly addresses the user's request, without adding extra features or handling edge cases that were not mentioned. ++ **Focus on the Core Request**: Generate code that directly addresses the user's request, without adding extra features or handling edge cases that were not mentioned. ++ **Spec Hygiene**: When asked to update a plan/spec file, do not append unrelated/archived plans; keep it strictly scoped to the current task. +``` + +### C) .github/instructions/copilot-instructions.md + +```diff +@@ + 3. **Coverage Testing** (MANDATORY - Non-negotiable): +- - **MANDATORY**: Patch coverage must cover 100% of new/modified code. This prevents CodeCov Report failing CI. ++ - **MANDATORY**: Patch coverage must cover 100% of modified lines (Codecov Patch view must be green). If patch coverage fails, add targeted tests for the missing patch line ranges. +``` + +### D) .github/agents/Backend_Dev.agent.md + +```diff +@@ + 3. **Verification (Definition of Done)**: +@@ +- - **Coverage (MANDATORY)**: Run the coverage script explicitly. This is NOT run by pre-commit automatically. ++ - **Coverage (MANDATORY)**: Run the coverage task/script explicitly and confirm Codecov patch view is green for modified lines. +``` + +### E) .github/agents/Frontend_Dev.agent.md (only if frontend changes are involved) + +```diff +@@ + 3. **Verification (Quality Gates)**: +@@ + - **Gate 3: Coverage (MANDATORY)**: +@@ + - **MANDATORY**: Patch coverage must cover 100% of new/modified code. This prevents CodeCov Report failing CI. ++ - If patch coverage fails, identify missing patch line ranges in Codecov Patch view and add targeted tests. +``` + +### F) .github/agents/QA_Security.agent.md + +```diff +@@ +- - When creating tests, if there are folders that don't require testing make sure to update `.codecov.yml` to exclude them from coverage reports or this throws off the difference between local and CI coverage. ++ - Prefer fixing patch coverage with tests. Only adjust `.codecov.yml` ignores when code is truly non-production (e.g., test-only helpers), and document why. +``` + +### G) .github/agents/Supervisor.agent.md + +```diff +@@ +- - **Plan Completeness**: Does the plan cover all edge cases? Are there any missing components or unclear requirements? ++ - **Plan Completeness**: Does the plan cover all edge cases? Are there any missing components or unclear requirements? ++ - **Patch Coverage Completeness**: If coverage is in scope, does the plan include Codecov Patch missing/partial line ranges and the exact tests needed to execute them? +``` + +## Validation Checklist (patch-coverage scope) + +Required vs optional alignment: +- Required for this plan to be considered complete: Workstream A + Workstream B changes landed. +- This validation checklist is intentionally focused on Workstream A (patch coverage remediation). For additional repo-wide Definition of Done items, follow `.github/instructions/copilot-instructions.md`. +- Workstream B validation is “diff applied + reviewer confirmation” (it doesn’t impact Go patch coverage directly). + +Run these tasks in order: + +1. **Test: Backend with Coverage** + - Pass criteria: task succeeds; `backend/coverage.txt` generated; zero failing tests. + - Outcome criteria: Codecov patch status becomes green (100% patch coverage). + +2. **Lint: Pre-commit (All Files)** (optional; general DoD) + - Pass criteria: all hooks pass.