{"record":{"id":"29f768f35c23757e","repo":"jdx/mise","slug":"notifications-disabled","errorCode":null,"errorMessage":"notifications disabled","messagePattern":"notifications disabled","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/sync/run.rs","lineNumber":1252,"sourceCode":"            ],\n            ..Default::default()\n        };\n        notify_conflicts_with(&mut status, true, |_, body| {\n            assert!(body.contains(\"… and 1 other file\"));\n            assert!(body.contains(\"Local saves still work.\"));\n            assert!(body.ends_with(\"mise bootstrap dotfiles status\"));\n            assert_eq!(body.lines().count(), 3);\n            assert!(body.chars().count() < 250);\n        });\n    }\n\n    #[test]\n    fn explicit_opt_out_is_preserved() {\n        let mut status = SyncStatus {\n            conflicts: vec![conflict(\"tracked/home/.zshrc\")],\n            ..Default::default()\n        };\n        notify_conflicts_with(&mut status, false, |_, _| panic!(\"notifications disabled\"));\n        assert!(status.conflict_pause_observed);\n    }\n}\n\n#[cfg(test)]\nmod status_tests {\n    use super::*;\n\n    fn conflict() -> Conflict {\n        Conflict {\n            branch_path: \"tracked/home/.zshrc\".to_string(),\n            kind: reconcile::ConflictKind::SameHunk,\n            local: None,\n            remote: None,\n            base: None,\n        }\n    }\n","sourceCodeStart":1234,"sourceCodeEnd":1270,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/sync/run.rs#L1234-L1270","documentation":"A test panic used as a spy assertion in src/system/history/sync/run.rs: the closure passed to `notify_conflicts_with` panics with 'notifications disabled' if it is ever invoked. The test verifies that when the `false` flag disables notifications, conflicts are recorded (`conflict_pause_observed`) but the notify callback is never called.","triggerScenarios":"`notify_conflicts_with(&mut status, false, callback)` invoking the callback despite notifications being disabled — i.e. the function fails to check the enabled flag before notifying.","commonSituations":"A regression in `notify_conflicts_with` that ignores the boolean enable flag; new conflict kinds routed around the gate; refactors that call the callback for pause-observed bookkeeping.","solutions":["Check `notify_conflicts_with` gates every callback invocation behind the enabled flag (`true`/`false` argument).","Ensure `conflict_pause_observed` is set without calling the callback when notifications are off.","Add coverage for each conflict path to confirm none bypass the disable check.","If the callback must run for bookkeeping, split observation from notification instead of calling the notifier."],"exampleFix":"// before\nnotify_conflicts_with(&mut status, false, |_, _| panic!(\"notifications disabled\"));\n// after\n// in notify_conflicts_with:\nif enabled {\n    callback(path, &conflict);\n}\nstatus.conflict_pause_observed = true;","handlingStrategy":"try-catch","validationCode":"// rust: assert the gate before calling\nassert!(!notifications_enabled(), \"test expects notifications disabled\");","typeGuard":null,"tryCatchPattern":"// rust: spy callback must stay uninvoked; catch regressions in CI\nnotify_conflicts_with(&mut status, false, |path, c| {\n    panic!(\"notifications disabled but callback invoked for {path:?}: {c:?}\")\n});","preventionTips":["Gate every notification call site behind the enabled flag.","Use spy/panic callbacks in tests to catch accidental notifications.","Cover all conflict kinds with opt-out tests.","Separate observation state updates from side-effecting callbacks."],"tags":["rust","test-assertion","notifications","spy"],"backgroundTag":"unsupported-operation","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}