use super::*;
#[test]
fn extract_jira_fields_handles_full_payload() {
let json = r#"{
"fields": {
"created": "2025-01-01T00:00:00.000+0000",
"resolutiondate": "2025-01-01T02:00:00.000+0000",
"priority": { "name": "High" }
}
}"#;
let (d, r, sev) = extract_jira_fields(Some(json));
assert!(d.is_some());
assert!(r.is_some());
assert_eq!(sev.as_deref(), Some("High"));
let mttr = (r.unwrap().signed_duration_since(d.unwrap()).num_seconds() as f64) / 3600.0;
assert!((mttr - 2.0).abs() < 1e-6);
}
#[test]
fn extract_jira_fields_handles_empty_payload() {
let (d, r, sev) = extract_jira_fields(None);
assert!(d.is_none() && r.is_none() && sev.is_none());
let (d, r, sev) = extract_jira_fields(Some("{}"));
assert!(d.is_none() && r.is_none() && sev.is_none());
}
#[test]
fn ingest_jira_sre_with_empty_db_inserts_nothing() {
let mut db = Database::open_in_memory().expect("db");
let (scanned, inserted) = ingest_jira_sre(&mut db).expect("ingest");
assert_eq!(scanned, 0);
assert_eq!(inserted, 0);
}
use tga::core::config::DoraConfig;
fn unique_tmp_dir(label: &str) -> std::path::PathBuf {
let pid = std::process::id();
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let dir = std::env::temp_dir().join(format!("tga-datadog-{label}-{pid}-{nanos}"));
std::fs::create_dir_all(&dir).expect("create temp dir");
dir
}
#[test]
fn parse_unix_or_iso_handles_epoch_integer() {
let v = serde_json::json!(1_700_000_000_i64);
let dt = parse_unix_or_iso(&v).expect("parses epoch");
assert_eq!(dt.timestamp(), 1_700_000_000);
}
#[test]
fn parse_unix_or_iso_handles_iso_string() {
let v = serde_json::json!("2025-01-01T02:00:00Z");
let dt = parse_unix_or_iso(&v).expect("parses iso");
assert_eq!(dt.to_rfc3339(), "2025-01-01T02:00:00+00:00");
}
#[test]
fn parse_unix_or_iso_handles_stringified_epoch() {
let v = serde_json::json!("1700000000");
let dt = parse_unix_or_iso(&v).expect("parses stringified epoch");
assert_eq!(dt.timestamp(), 1_700_000_000);
}
#[test]
fn parse_unix_or_iso_returns_none_for_unparseable_inputs() {
assert!(parse_unix_or_iso(&serde_json::json!(null)).is_none());
assert!(parse_unix_or_iso(&serde_json::json!("not-a-date")).is_none());
assert!(parse_unix_or_iso(&serde_json::json!(true)).is_none());
}
#[test]
fn ingest_datadog_skips_missing_dir() {
let mut db = Database::open_in_memory().expect("db");
let config = Config {
dora: Some(DoraConfig {
datadog_dir: Some(std::path::PathBuf::from(
"/definitely/does/not/exist/dd-xyz-zzz",
)),
..DoraConfig::default()
}),
..Config::default()
};
let (files, inserted) = ingest_datadog(&mut db, &config).expect("ingest");
assert_eq!(files, 0);
assert_eq!(inserted, 0);
}
#[test]
fn ingest_datadog_skips_unset_dir() {
let mut db = Database::open_in_memory().expect("db");
let config = Config::default();
let (files, inserted) = ingest_datadog(&mut db, &config).expect("ingest");
assert_eq!(files, 0);
assert_eq!(inserted, 0);
}
#[test]
fn ingest_datadog_parses_incident_api_shape() {
let dir = unique_tmp_dir("incident-shape");
let file = dir.join("incident-001.json");
std::fs::write(
&file,
r#"{
"data": {
"id": "abc-123",
"attributes": {
"created": "2025-01-01T00:00:00Z",
"resolved": "2025-01-01T02:00:00Z",
"severity": "SEV-1"
}
}
}"#,
)
.expect("write file");
let mut db = Database::open_in_memory().expect("db");
let config = Config {
dora: Some(DoraConfig {
datadog_dir: Some(dir.clone()),
..DoraConfig::default()
}),
..Config::default()
};
let (files, inserted) = ingest_datadog(&mut db, &config).expect("ingest");
assert_eq!(files, 1);
assert_eq!(inserted, 1);
let conn = db.connection();
let (id, severity, mttr): (String, Option<String>, Option<f64>) = conn
.query_row(
"SELECT incident_id, severity, mttr_hours FROM fact_incidents",
[],
|r| Ok((r.get(0)?, r.get(1)?, r.get(2)?)),
)
.expect("row");
assert_eq!(id, "datadog:abc-123");
assert_eq!(severity.as_deref(), Some("SEV-1"));
assert!((mttr.expect("mttr") - 2.0).abs() < 1e-6);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn ingest_datadog_parses_list_shape() {
let dir = unique_tmp_dir("list-shape");
let file = dir.join("incidents-bulk.json");
std::fs::write(
&file,
r#"{
"data": [
{
"id": "i-001",
"attributes": {
"created": "2025-01-01T00:00:00Z",
"resolved": "2025-01-01T01:00:00Z",
"severity": "SEV-2"
}
},
{
"id": "i-002",
"attributes": {
"created": "2025-01-02T00:00:00Z",
"severity": "SEV-3"
}
}
]
}"#,
)
.expect("write file");
let mut db = Database::open_in_memory().expect("db");
let config = Config {
dora: Some(DoraConfig {
datadog_dir: Some(dir.clone()),
..DoraConfig::default()
}),
..Config::default()
};
let (files, inserted) = ingest_datadog(&mut db, &config).expect("ingest");
assert_eq!(files, 1);
assert_eq!(inserted, 2);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn ingest_datadog_parses_monitor_shape() {
let dir = unique_tmp_dir("monitor-shape");
let file = dir.join("monitor-trip.json");
std::fs::write(
&file,
r#"{
"id": 42,
"downtime": { "start": 1700000000, "end": 1700003600 },
"monitor": { "name": "API error rate", "priority": 3 }
}"#,
)
.expect("write file");
let mut db = Database::open_in_memory().expect("db");
let config = Config {
dora: Some(DoraConfig {
datadog_dir: Some(dir.clone()),
..DoraConfig::default()
}),
..Config::default()
};
let (files, inserted) = ingest_datadog(&mut db, &config).expect("ingest");
assert_eq!(files, 1);
assert_eq!(inserted, 1);
let conn = db.connection();
let (id, severity, mttr): (String, Option<String>, Option<f64>) = conn
.query_row(
"SELECT incident_id, severity, mttr_hours FROM fact_incidents",
[],
|r| Ok((r.get(0)?, r.get(1)?, r.get(2)?)),
)
.expect("row");
assert_eq!(id, "datadog:42");
assert_eq!(severity.as_deref(), Some("P2"));
assert!((mttr.expect("mttr") - 1.0).abs() < 1e-6);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn ingest_datadog_skips_unparseable_files() {
let dir = unique_tmp_dir("bad-files");
std::fs::write(dir.join("garbage.json"), "this is not json at all").expect("write garbage");
std::fs::write(
dir.join("good.json"),
r#"{
"data": {
"id": "ok-1",
"attributes": {
"created": "2025-03-01T00:00:00Z",
"resolved": "2025-03-01T00:30:00Z",
"severity": "SEV-2"
}
}
}"#,
)
.expect("write good");
let mut db = Database::open_in_memory().expect("db");
let config = Config {
dora: Some(DoraConfig {
datadog_dir: Some(dir.clone()),
..DoraConfig::default()
}),
..Config::default()
};
let (files, inserted) = ingest_datadog(&mut db, &config).expect("ingest");
assert_eq!(files, 2);
assert_eq!(inserted, 1);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn ingest_datadog_replaces_on_reingest() {
let dir = unique_tmp_dir("idempotent");
let path = dir.join("incident.json");
let payload = r#"{
"data": {
"id": "dup-1",
"attributes": {
"created": "2025-01-01T00:00:00Z",
"resolved": "2025-01-01T01:00:00Z",
"severity": "SEV-3"
}
}
}"#;
std::fs::write(&path, payload).expect("write");
let mut db = Database::open_in_memory().expect("db");
let config = Config {
dora: Some(DoraConfig {
datadog_dir: Some(dir.clone()),
..DoraConfig::default()
}),
..Config::default()
};
let _ = ingest_datadog(&mut db, &config).expect("ingest 1");
let _ = ingest_datadog(&mut db, &config).expect("ingest 2");
let n: i64 = db
.connection()
.query_row("SELECT COUNT(*) FROM fact_incidents", [], |r| r.get(0))
.expect("count");
assert_eq!(n, 1, "INSERT OR REPLACE must dedupe on incident_id PK");
let _ = std::fs::remove_dir_all(&dir);
}