mod common;
use backbone_support::application::service::support_write_service::{
NewIssue, NewSla, NewSlaPriority, NewWarrantyClaim, SupportError, SupportWriteService,
};
use common::*;
use uuid::Uuid;
async fn sla_high(svc: &SupportWriteService) -> Uuid {
svc.create_sla(NewSla {
name: "Standard".into(),
is_default: true,
priorities: vec![NewSlaPriority {
priority: "high".into(),
response_time_mins: 60,
resolution_time_mins: 240,
}],
})
.await
.unwrap()
}
fn an_issue(sla: Option<Uuid>, customer: Option<Uuid>) -> NewIssue {
NewIssue {
customer_id: customer,
subject: "Printer down".into(),
description: None,
priority: "high".into(),
sla_id: sla,
}
}
#[tokio::test]
async fn ip1_escalate_idempotent() {
let pool = pool().await;
let svc = SupportWriteService::new(pool.clone());
let project = FakeProject::new();
let sink = LoggingSink;
let sla = sla_high(&svc).await;
let issue = svc
.raise_issue(
an_issue(Some(sla), Some(Uuid::new_v4())),
dt("2026-07-07T09:00:00Z"),
)
.await
.unwrap();
let (a, b) = with_company_scope(&pool, Uuid::new_v4(), async {
let a = svc
.escalate_to_project(issue, &project, &sink)
.await
.unwrap();
let b = svc
.escalate_to_project(issue, &project, &sink)
.await
.unwrap();
(a, b)
})
.await;
assert_eq!(a, b, "same project on retry");
assert_eq!(project.open_count(), 1, "project seam driven exactly once");
let pid: Option<Uuid> =
sqlx::query_scalar("SELECT escalated_project_id FROM support.issues WHERE id=$1")
.bind(issue)
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(pid, Some(a));
}
#[tokio::test]
async fn ip2_escalate_requires_customer() {
let pool = pool().await;
let svc = SupportWriteService::new(pool.clone());
let project = FakeProject::new();
let sink = LoggingSink;
let sla = sla_high(&svc).await;
let issue = svc
.raise_issue(an_issue(Some(sla), None), dt("2026-07-07T09:00:00Z"))
.await
.unwrap();
let err = svc
.escalate_to_project(issue, &project, &sink)
.await
.unwrap_err();
assert!(
matches!(err, SupportError::Invalid(_)),
"no customer → refused"
);
assert_eq!(project.open_count(), 0, "seam not driven");
}
#[tokio::test]
async fn ip3_resolved_terminal() {
let pool = pool().await;
let svc = SupportWriteService::new(pool.clone());
let project = FakeProject::new();
let sink = LoggingSink;
let sla = sla_high(&svc).await;
let issue = svc
.raise_issue(
an_issue(Some(sla), Some(Uuid::new_v4())),
dt("2026-07-07T09:00:00Z"),
)
.await
.unwrap();
svc.resolve_issue(issue, dt("2026-07-07T10:00:00Z"), &sink)
.await
.unwrap();
assert!(matches!(
svc.resolve_issue(issue, dt("2026-07-07T11:00:00Z"), &sink)
.await,
Err(SupportError::InvalidState(_))
));
assert!(matches!(
svc.pause_sla(issue, dt("2026-07-07T11:00:00Z")).await,
Err(SupportError::InvalidState(_))
));
assert!(matches!(
svc.escalate_to_project(issue, &project, &sink).await,
Err(SupportError::InvalidState(_))
));
let _ = &pool;
svc.close_issue(issue).await.unwrap(); }
#[tokio::test]
async fn ip4_no_sla_always_fulfilled() {
let pool = pool().await;
let svc = SupportWriteService::new(pool.clone());
let sink = LoggingSink;
let sla = sla_high(&svc).await;
let issue = svc
.raise_issue(
an_issue(Some(sla), Some(Uuid::new_v4())),
dt("2026-07-07T09:00:00Z"),
)
.await
.unwrap();
sqlx::query(
"UPDATE support.issues SET sla_id=NULL, response_by=NULL, resolution_by=NULL WHERE id=$1",
)
.bind(issue)
.execute(&pool)
.await
.unwrap();
let (rb, resb): (
Option<chrono::DateTime<chrono::Utc>>,
Option<chrono::DateTime<chrono::Utc>>,
) = sqlx::query_as("SELECT response_by, resolution_by FROM support.issues WHERE id=$1")
.bind(issue)
.fetch_one(&pool)
.await
.unwrap();
assert!(rb.is_none() && resb.is_none(), "no SLA → no deadlines");
let fulfilled = svc
.resolve_issue(issue, dt("2026-07-09T09:00:00Z"), &sink)
.await
.unwrap();
assert!(fulfilled, "no deadline can't be breached");
}
#[tokio::test]
async fn ip5_warranty_coverage_computed() {
let pool = pool().await;
let svc = SupportWriteService::new(pool.clone());
let sink = LoggingSink;
let under = svc
.file_warranty_claim(
NewWarrantyClaim {
customer_id: Some(Uuid::new_v4()),
item_id: Uuid::new_v4(),
serial_no: Some("SN-1".into()),
warranty_expiry: Some(dt("2026-12-31T00:00:00Z")),
issue_id: None,
description: None,
},
dt("2026-07-07T09:00:00Z"),
&sink,
)
.await
.unwrap();
let expired = svc
.file_warranty_claim(
NewWarrantyClaim {
customer_id: Some(Uuid::new_v4()),
item_id: Uuid::new_v4(),
serial_no: Some("SN-2".into()),
warranty_expiry: Some(dt("2026-01-01T00:00:00Z")),
issue_id: None,
description: None,
},
dt("2026-07-07T09:00:00Z"),
&sink,
)
.await
.unwrap();
let unknown = svc
.file_warranty_claim(
NewWarrantyClaim {
customer_id: Some(Uuid::new_v4()),
item_id: Uuid::new_v4(),
serial_no: None,
warranty_expiry: None,
issue_id: None,
description: None,
},
dt("2026-07-07T09:00:00Z"),
&sink,
)
.await
.unwrap();
let cov = |id: Uuid, pool: sqlx::PgPool| async move {
sqlx::query_scalar::<_, bool>(
"SELECT is_under_warranty FROM support.warranty_claims WHERE id=$1",
)
.bind(id)
.fetch_one(&pool)
.await
.unwrap()
};
assert!(
cov(under, pool.clone()).await,
"expiry in the future → under warranty"
);
assert!(
!cov(expired, pool.clone()).await,
"past expiry → not under warranty"
);
assert!(
!cov(unknown, pool.clone()).await,
"unknown expiry → not under warranty"
);
}
#[tokio::test]
async fn ip7_first_response_breach_fails_sla() {
let pool = pool().await;
let svc = SupportWriteService::new(pool.clone());
let sink = LoggingSink;
let sla = sla_high(&svc).await;
let late = svc
.raise_issue(
an_issue(Some(sla), Some(Uuid::new_v4())),
dt("2026-07-07T09:00:00Z"),
)
.await
.unwrap();
svc.record_first_response(late, dt("2026-07-07T11:30:00Z"))
.await
.unwrap();
let (agr, breached): (String, bool) = sqlx::query_as(
"SELECT agreement_status::text, response_breached FROM support.issues WHERE id=$1",
)
.bind(late)
.fetch_one(&pool)
.await
.unwrap();
assert!(breached, "a late first response is a breach");
assert_eq!(agr, "failed", "a missed first response fails the SLA");
let fulfilled = svc
.resolve_issue(late, dt("2026-07-07T12:00:00Z"), &sink)
.await
.unwrap();
assert!(
!fulfilled,
"a met resolution can't mask a blown first response"
);
let silent = svc
.raise_issue(
an_issue(Some(sla), Some(Uuid::new_v4())),
dt("2026-07-07T09:00:00Z"),
)
.await
.unwrap();
let f2 = svc
.resolve_issue(silent, dt("2026-07-07T12:30:00Z"), &sink)
.await
.unwrap();
assert!(
!f2,
"resolving past response_by with no reply is a response breach"
);
let ok = svc
.raise_issue(
an_issue(Some(sla), Some(Uuid::new_v4())),
dt("2026-07-07T09:00:00Z"),
)
.await
.unwrap();
svc.record_first_response(ok, dt("2026-07-07T09:30:00Z"))
.await
.unwrap();
assert!(
svc.resolve_issue(ok, dt("2026-07-07T12:00:00Z"), &sink)
.await
.unwrap(),
"both legs met → fulfilled"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn ip6_resolve_verdict_atomic_with_deadline() {
use chrono::{DateTime, Utc};
let pool = pool().await;
let setup = SupportWriteService::new(pool.clone());
let sla = sla_high(&setup).await; let issue = setup
.raise_issue(
an_issue(Some(sla), Some(Uuid::new_v4())),
dt("2026-07-07T09:00:00Z"),
)
.await
.unwrap();
setup
.record_first_response(issue, dt("2026-07-07T09:30:00Z"))
.await
.unwrap();
let mut txb = pool.begin().await.unwrap();
sqlx::query(
"UPDATE support.issues SET status='on_hold'::issue_status, paused_at=$2 WHERE id=$1",
)
.bind(issue)
.bind(dt("2026-07-07T09:00:00Z"))
.execute(&mut *txb)
.await
.unwrap();
let s1 = SupportWriteService::new(pool.clone());
let resolve = tokio::spawn(async move {
let sink = LoggingSink;
s1.resolve_issue(issue, dt("2026-07-07T14:00:00Z"), &sink)
.await
});
tokio::time::sleep(std::time::Duration::from_millis(400)).await;
sqlx::query(
r#"UPDATE support.issues SET status='open'::issue_status, paused_at=NULL, total_paused_mins=120,
resolution_by = resolution_by + interval '120 minutes' WHERE id=$1"#)
.bind(issue).execute(&mut *txb).await.unwrap();
txb.commit().await.unwrap();
let _ = resolve.await.unwrap();
let (status, agr, resb, resolved): (String, String, DateTime<Utc>, DateTime<Utc>) = sqlx::query_as(
"SELECT status::text, agreement_status::text, resolution_by, resolved_at FROM support.issues WHERE id=$1")
.bind(issue).fetch_one(&pool).await.unwrap();
assert_eq!(status, "resolved");
assert_eq!(
resb,
dt("2026-07-07T15:00:00Z"),
"deadline was extended by the 2h hold"
);
assert!(
resolved <= resb,
"resolved 14:00 is within the extended 15:00 deadline"
);
assert_eq!(
agr, "fulfilled",
"the verdict must reflect the pause-extended deadline, not a stale pre-extension read"
);
}