bestool 1.7.1

BES Deployment tooling
Documentation
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 { .. }));
}