{"record":{"id":"dc11f77f808c34a5","repo":"stamparm/maltrail","slug":"config-must-load","errorCode":null,"errorMessage":"config must load","messagePattern":"config must load","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sensor/src/config.rs","lineNumber":1343,"sourceCode":"        assert!(split_endpoints(\"\").is_empty());\n\n        // EVERY endpoint is validated: a typo in the second is as fatal as one in the first,\n        // because forwarding to one of two configured collectors is silent half-failure.\n        assert!(write(\"bad2.conf\", \"SYSLOG_SERVER 1.2.3.4:514, nonsense\\n\").is_err());\n        assert!(write(\"bad1.conf\", \"SYSLOG_SERVER nonsense, 1.2.3.4:514\\n\").is_err());\n        assert!(write(\"badls.conf\", \"LOGSTASH_SERVER 1.2.3.4:5000, 5.6.7.8\\n\").is_err());\n    }\n\n    #[test]\n    fn worker_count_is_opt_in() {\n        let dir = std::env::temp_dir().join(\"mt-cfg-workers\");\n        let _ = std::fs::create_dir_all(&dir);\n        let base = \"MONITOR_INTERFACE any\\nCAPTURE_BUFFER 1MB\\nLOG_DIR /tmp\\nUPDATE_PERIOD 86400\\n\";\n\n        let write = |name: &str, extra: &str| {\n            let path = dir.join(name);\n            std::fs::write(&path, format!(\"{base}{extra}\")).unwrap();\n            Config::load(&path).expect(\"config must load\")\n        };\n\n        // PROCESS_COUNT alone must NOT fan out: it is sensor.py's worker-process count, and\n        // honouring it here degraded the scan heuristics of anyone who never touched the setting.\n        assert_eq!(write(\"pc.conf\", \"PROCESS_COUNT 16\\n\").capture_workers, 1);\n        // Both explicit knobs still work, and still win.\n        assert_eq!(write(\"cw.conf\", \"CAPTURE_WORKERS 4\\n\").capture_workers, 4);\n        assert_eq!(write(\"cf.conf\", \"CAPTURE_FANOUT 8\\n\").capture_workers, 8);\n        assert!(write(\"auto.conf\", \"CAPTURE_WORKERS auto\\n\").capture_workers >= 1);\n    }\n\n    #[test]\n    fn fanout_defaults_to_source_affinity_only_when_it_matters() {\n        let dir = std::env::temp_dir().join(\"mt-cfg-fanout-default\");\n        let _ = std::fs::create_dir_all(&dir);\n        let base = \"MONITOR_INTERFACE any\\nCAPTURE_BUFFER 1MB\\nLOG_DIR /tmp\\nUPDATE_PERIOD 86400\\n\";\n        let write = |name: &str, extra: &str| {\n            let path = dir.join(name);","sourceCodeStart":1325,"sourceCodeEnd":1361,"githubUrl":"https://github.com/stamparm/maltrail/blob/77cfb06d7606506d101bbcec0786c77166c4255e/sensor/src/config.rs#L1325-L1361","documentation":"Same test-helper expect: `Config::load(&path).expect(\"config must load\")` asserts that a base config plus PROCESS_COUNT 16 loads cleanly. The test documents that PROCESS_COUNT must not influence capture_workers; the expect fails only when Config::load rejects the file outright.","triggerScenarios":"Config::load returns Err for the base config (MONITOR_INTERFACE any / CAPTURE_BUFFER 1MB / LOG_DIR /tmp / UPDATE_PERIOD 86400) plus 'PROCESS_COUNT 16', e.g. after a new required-field or range check was introduced.","commonSituations":"Adding validation (e.g. rejecting unknown keys or making a new field mandatory) that breaks previously-loading minimal configs.","solutions":["Inspect the Err from Config::load for the exact failing key","Adjust the new validation to accept the minimal base config","Update the fixture if a genuinely required field is missing"],"exampleFix":"// before\nlet base = \"MONITOR_INTERFACE any\\nCAPTURE_BUFFER 1MB\\nLOG_DIR /tmp\\n\";\n// after\nlet base = \"MONITOR_INTERFACE any\\nCAPTURE_BUFFER 1MB\\nLOG_DIR /tmp\\nUPDATE_PERIOD 86400\\n\";","handlingStrategy":"try-catch","validationCode":"let cfg = Config::load(&path);\nassert!(cfg.is_ok(), \"PROCESS_COUNT config rejected: {:?}\", cfg.err());","typeGuard":null,"tryCatchPattern":"match Config::load(&path) {\n    Ok(cfg) => proceed(cfg),\n    Err(e) => eprintln!(\"config load failed: {e}\"),\n}","preventionTips":["Add any new required field to test base fixtures immediately","Run the full config test module when touching Config::load"],"tags":["rust","tests","config"],"backgroundTag":"invalid-config-value","analyzedSha":"77cfb06d7606506d101bbcec0786c77166c4255e","analyzedAt":"2026-09-13T03:50:16.010Z","contentChangedAt":"2026-09-13T03:50:16.010Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}