use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RecoveryPolicy {
#[default]
Strict,
AutoReset,
}
#[derive(Debug, thiserror::Error)]
pub enum RecoveryError {
#[error("Incompatible source '{source_id}' for persistent query '{query_id}': {reason}")]
IncompatibleSource {
query_id: String,
source_id: String,
reason: String,
},
#[error("Auto-reset triggered for query '{query_id}': {reason}")]
AutoResetTriggered { query_id: String, reason: String },
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_strict() {
assert_eq!(RecoveryPolicy::default(), RecoveryPolicy::Strict);
}
#[test]
fn json_round_trip_strict() {
let json = serde_json::to_string(&RecoveryPolicy::Strict).unwrap();
assert_eq!(json, "\"strict\"");
let back: RecoveryPolicy = serde_json::from_str(&json).unwrap();
assert_eq!(back, RecoveryPolicy::Strict);
}
#[test]
fn json_round_trip_auto_reset() {
let json = serde_json::to_string(&RecoveryPolicy::AutoReset).unwrap();
assert_eq!(json, "\"auto_reset\"");
let back: RecoveryPolicy = serde_json::from_str(&json).unwrap();
assert_eq!(back, RecoveryPolicy::AutoReset);
}
#[test]
fn yaml_round_trip() {
for policy in [RecoveryPolicy::Strict, RecoveryPolicy::AutoReset] {
let yaml = serde_yaml::to_string(&policy).unwrap();
let back: RecoveryPolicy = serde_yaml::from_str(&yaml).unwrap();
assert_eq!(back, policy);
}
}
#[test]
fn incompatible_source_message() {
let err = RecoveryError::IncompatibleSource {
query_id: "q1".into(),
source_id: "s1".into(),
reason: "no WAL".into(),
};
let msg = err.to_string();
assert!(msg.contains("q1"));
assert!(msg.contains("s1"));
assert!(msg.contains("no WAL"));
}
#[test]
fn auto_reset_message() {
let err = RecoveryError::AutoResetTriggered {
query_id: "q1".into(),
reason: "gap".into(),
};
let msg = err.to_string();
assert!(msg.contains("q1"));
assert!(msg.contains("gap"));
}
}