use std::{path::PathBuf, time::Duration};
use jiff::Timestamp;
use super::{
definition::{AlertDefinition, TicketSource},
targets::SendTarget,
templates::build_context,
};
fn interval_context(dur: Duration) -> Option<String> {
let alert = AlertDefinition {
file: PathBuf::from("test.yaml"),
enabled: true,
interval: dur,
source: TicketSource::Sql { sql: "".into() },
send: vec![],
};
build_context(&alert, Timestamp::now())
.get("interval")
.and_then(|v| v.as_str())
.map(|s| s.to_owned())
}
#[test]
fn test_interval_format_minutes() {
assert_eq!(
interval_context(Duration::from_secs(15 * 60)).as_deref(),
Some("15m"),
);
}
#[test]
fn test_interval_format_hour() {
assert_eq!(
interval_context(Duration::from_secs(60 * 60)).as_deref(),
Some("1h"),
);
}
#[test]
fn test_interval_format_day() {
assert_eq!(
interval_context(Duration::from_secs(24 * 60 * 60)).as_deref(),
Some("1d"),
);
}
#[test]
fn test_alert_parse_email() {
let alert = r#"
sql: SELECT $1::timestamptz;
send:
- target: email
addresses: [test@example.com]
subject: "[Tamanu Alert] Example ({{ hostname }})"
template: |
<p>Server: {{ hostname }}</p>
<p>There are {{ rows | length }} rows.</p>
"#;
let alert: AlertDefinition = serde_yaml::from_str(alert).unwrap();
let alert = alert.normalise(&Default::default());
assert_eq!(alert.interval, std::time::Duration::default());
assert!(matches!(alert.source, TicketSource::Sql { sql } if sql == "SELECT $1::timestamptz;"));
assert!(matches!(alert.send[0], SendTarget::Email { .. }));
}
#[test]
fn test_alert_parse_shell() {
let alert = r#"
shell: bash
run: echo foobar
"#;
let alert: AlertDefinition = serde_yaml::from_str(alert).unwrap();
let alert = alert.normalise(&Default::default());
assert_eq!(alert.interval, std::time::Duration::default());
assert!(
matches!(alert.source, TicketSource::Shell { shell, run } if shell == "bash" && run == "echo foobar")
);
}
#[test]
fn test_alert_parse_invalid_source() {
let alert = r#"
shell: bash
"#;
assert!(serde_yaml::from_str::<AlertDefinition>(alert).is_err());
let alert = r#"
run: echo foo
"#;
assert!(serde_yaml::from_str::<AlertDefinition>(alert).is_err());
let alert = r#"
sql: SELECT $1::timestamptz;
run: echo foo
"#;
assert!(serde_yaml::from_str::<AlertDefinition>(alert).is_err());
let alert = r#"
sql: SELECT $1::timestamptz;
shell: bash
"#;
assert!(serde_yaml::from_str::<AlertDefinition>(alert).is_err());
let alert = r#"
sql: SELECT $1::timestamptz;
shell: bash
run: echo foo
"#;
assert!(serde_yaml::from_str::<AlertDefinition>(alert).is_err());
}
#[test]
fn test_alert_parse_zendesk_authorized() {
let alert = r#"
sql: SELECT $1::timestamptz;
send:
- target: zendesk
endpoint: https://example.zendesk.com/api/v2/requests
credentials:
email: foo@example.com
password: pass
subject: "[Tamanu Alert] Example ({{ hostname }})"
template: "Output: {{ output }}""#;
let alert: AlertDefinition = serde_yaml::from_str(alert).unwrap();
assert!(matches!(alert.send[0], SendTarget::Zendesk { .. }));
}
#[test]
fn test_alert_parse_zendesk_anon() {
let alert = r#"
sql: SELECT $1::timestamptz;
send:
- target: zendesk
endpoint: https://example.zendesk.com/api/v2/requests
requester: "{{ hostname }}"
subject: "[Tamanu Alert] Example ({{ hostname }})"
template: "Output: {{ output }}""#;
let alert: AlertDefinition = serde_yaml::from_str(alert).unwrap();
assert!(matches!(alert.send[0], SendTarget::Zendesk { .. }));
}
#[test]
fn test_alert_parse_zendesk_form_fields() {
let alert = r#"
sql: SELECT $1::timestamptz;
send:
- target: zendesk
endpoint: https://example.zendesk.com/api/v2/requests
requester: "{{ hostname }}"
subject: "[Tamanu Alert] Example ({{ hostname }})"
template: "Output: {{ output }}"
ticket_form_id: 500
custom_fields:
- id: 100
value: tamanu_
- id: 200
value: Test
"#;
let alert: AlertDefinition = serde_yaml::from_str(alert).unwrap();
assert!(matches!(alert.send[0], SendTarget::Zendesk { .. }));
}
#[test]
fn test_alert_parse_slack() {
let alert = r#"
sql: SELECT $1::timestamptz;
send:
- target: slack
webhook: https://hooks.slack.com/triggers/
template: Something happened
"#;
let alert: AlertDefinition = serde_yaml::from_str(alert).unwrap();
assert!(matches!(alert.send[0], SendTarget::Slack { .. }));
}
#[test]
fn test_alert_parse_canopy_inline() {
let alert = r#"
sql: SELECT $1::timestamptz;
send:
- target: canopy
source: my-tamanu
severity: warning
subject: "{{ hostname }}: low disk"
template: "There are {{ rows | length }} rows."
"#;
let alert: AlertDefinition = serde_yaml::from_str(alert).unwrap();
assert!(matches!(alert.send[0], SendTarget::Canopy { .. }));
}
#[test]
fn test_alert_parse_canopy_default_url() {
let alert = r#"
sql: SELECT $1::timestamptz;
send:
- target: canopy
source: my-tamanu
subject: "{{ hostname }}: alert"
template: "Something happened"
"#;
let alert: AlertDefinition = serde_yaml::from_str(alert).unwrap();
match &alert.send[0] {
SendTarget::Canopy { conn, .. } => {
assert_eq!(conn.url.as_str(), "https://meta.tamanu.app/");
assert_eq!(conn.source, "my-tamanu");
assert_eq!(conn.severity, None);
}
_ => panic!("expected canopy target"),
}
}
#[test]
fn test_alert_parse_slack_fields() {
let alert = r#"
sql: SELECT $1::timestamptz;
send:
- target: slack
webhook: https://hooks.slack.com/triggers/
template: Something happened
fields:
- name: alertname
field: filename
- name: deployment
value: production
"#;
let alert: AlertDefinition = serde_yaml::from_str(alert).unwrap();
assert!(matches!(alert.send[0], SendTarget::Slack { .. }));
}