Skip to content

Commit 142a0a9

Browse files
committed
chore: add even more tests
Hunting for better coverage Signed-off-by: MTRNord <MTRNord@users.noreply.github.com>
1 parent b0365fe commit 142a0a9

4 files changed

Lines changed: 733 additions & 0 deletions

File tree

crates/migration/src/lib.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,40 @@ impl MigratorTrait for Migrator {
6262
]
6363
}
6464
}
65+
66+
#[cfg(test)]
67+
mod tests {
68+
use super::*;
69+
use sea_orm::Database;
70+
71+
async fn open_db() -> sea_orm::DatabaseConnection {
72+
Database::connect("sqlite::memory:").await.unwrap()
73+
}
74+
75+
#[tokio::test]
76+
async fn migrate_up_succeeds() {
77+
let db = open_db().await;
78+
Migrator::up(&db, None).await.unwrap();
79+
}
80+
81+
#[tokio::test]
82+
async fn migrate_up_then_down_succeeds() {
83+
let db = open_db().await;
84+
Migrator::up(&db, None).await.unwrap();
85+
Migrator::down(&db, None).await.unwrap();
86+
}
87+
88+
#[tokio::test]
89+
async fn migrate_up_down_up_is_idempotent() {
90+
let db = open_db().await;
91+
Migrator::up(&db, None).await.unwrap();
92+
Migrator::down(&db, None).await.unwrap();
93+
Migrator::up(&db, None).await.unwrap();
94+
}
95+
96+
#[tokio::test]
97+
async fn partial_migration_up_one_step() {
98+
let db = open_db().await;
99+
Migrator::up(&db, Some(1)).await.unwrap();
100+
}
101+
}

crates/server/src/api/alerts_v2.rs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,8 @@ async fn test_email(
15801580
mod tests {
15811581
use super::*;
15821582
use crate::entity::alert_status_history;
1583+
use migration::{Migrator, MigratorTrait};
1584+
use sea_orm::{ActiveModelTrait, ActiveValue::Set, Database};
15831585

15841586
// ── is_private_ip ─────────────────────────────────────────────────────────
15851587

@@ -1729,4 +1731,192 @@ mod tests {
17291731
assert!(detail.is_none());
17301732
assert_eq!(kind, "info");
17311733
}
1734+
1735+
// ── build_alert_dtos ──────────────────────────────────────────────────────
1736+
1737+
fn base_alert_model(id: i32, email: &str) -> alert::Model {
1738+
alert::Model {
1739+
id,
1740+
email: email.to_string(),
1741+
server_name: "matrix.example.com".to_string(),
1742+
verified: true,
1743+
magic_token: None,
1744+
created_at: time::OffsetDateTime::UNIX_EPOCH,
1745+
last_check_at: None,
1746+
last_failure_at: None,
1747+
last_success_at: None,
1748+
last_email_sent_at: None,
1749+
failure_count: 0,
1750+
is_currently_failing: false,
1751+
last_recovery_at: None,
1752+
user_id: None,
1753+
notify_server_name_change: false,
1754+
notify_version_change: false,
1755+
notify_tls_cert_change: false,
1756+
notify_tls_expiry: false,
1757+
quiet_hours_enabled: false,
1758+
quiet_hours_from: "22:00".to_string(),
1759+
quiet_hours_to: "07:00".to_string(),
1760+
}
1761+
}
1762+
1763+
#[test]
1764+
fn build_alert_dtos_falls_back_to_alert_email() {
1765+
let alert = base_alert_model(1, "owner@example.com");
1766+
let result = build_alert_dtos(vec![alert], &HashMap::new(), &HashMap::new());
1767+
assert_eq!(result.len(), 1);
1768+
assert_eq!(result[0].notify_emails, vec!["owner@example.com"]);
1769+
}
1770+
1771+
#[test]
1772+
fn build_alert_dtos_uses_notification_emails_map() {
1773+
let alert = base_alert_model(1, "owner@example.com");
1774+
let mut emails: HashMap<i32, Vec<String>> = HashMap::new();
1775+
emails.insert(
1776+
1,
1777+
vec!["a@example.com".to_string(), "b@example.com".to_string()],
1778+
);
1779+
let result = build_alert_dtos(vec![alert], &emails, &HashMap::new());
1780+
assert_eq!(
1781+
result[0].notify_emails,
1782+
vec!["a@example.com", "b@example.com"]
1783+
);
1784+
}
1785+
1786+
#[test]
1787+
fn build_alert_dtos_empty_email_and_no_map_returns_empty() {
1788+
let alert = base_alert_model(1, "");
1789+
let result = build_alert_dtos(vec![alert], &HashMap::new(), &HashMap::new());
1790+
assert!(result[0].notify_emails.is_empty());
1791+
}
1792+
1793+
#[test]
1794+
fn build_alert_dtos_empty_map_entry_falls_back_to_alert_email() {
1795+
let alert = base_alert_model(1, "owner@example.com");
1796+
let mut emails: HashMap<i32, Vec<String>> = HashMap::new();
1797+
emails.insert(1, vec![]);
1798+
let result = build_alert_dtos(vec![alert], &emails, &HashMap::new());
1799+
assert_eq!(result[0].notify_emails, vec!["owner@example.com"]);
1800+
}
1801+
1802+
#[test]
1803+
fn build_alert_dtos_includes_webhooks() {
1804+
let webhook = WebhookDto {
1805+
id: 99,
1806+
url: "https://hook.example.com".to_string(),
1807+
hmac_header: "X-Sig".to_string(),
1808+
respect_quiet_hours: false,
1809+
created_at: time::OffsetDateTime::UNIX_EPOCH,
1810+
};
1811+
let mut webhooks: HashMap<i32, Vec<WebhookDto>> = HashMap::new();
1812+
webhooks.insert(5, vec![webhook]);
1813+
let result = build_alert_dtos(
1814+
vec![base_alert_model(5, "a@example.com")],
1815+
&HashMap::new(),
1816+
&webhooks,
1817+
);
1818+
assert_eq!(result[0].notify_webhooks.len(), 1);
1819+
assert_eq!(result[0].notify_webhooks[0].url, "https://hook.example.com");
1820+
}
1821+
1822+
// ── load_emails_by_alert / load_webhooks_by_alert ─────────────────────────
1823+
1824+
async fn make_db() -> sea_orm::DatabaseConnection {
1825+
let db = Database::connect("sqlite::memory:").await.unwrap();
1826+
Migrator::up(&db, None).await.unwrap();
1827+
db
1828+
}
1829+
1830+
async fn insert_minimal_alert(db: &sea_orm::DatabaseConnection, id: i32) {
1831+
use sea_orm::{ConnectionTrait, DbBackend, Statement};
1832+
db.execute(Statement::from_string(
1833+
DbBackend::Sqlite,
1834+
format!(
1835+
"INSERT INTO alert (id, email, server_name, verified, failure_count, \
1836+
is_currently_failing, notify_server_name_change, notify_version_change, \
1837+
notify_tls_cert_change, notify_tls_expiry, quiet_hours_enabled, \
1838+
quiet_hours_from, quiet_hours_to, created_at) \
1839+
VALUES ({id}, 'alert{id}@example.com', 'server{id}.example.com', 1, 0, 0, 0, 0, 0, 0, 0, '22:00', '07:00', datetime('now'))"
1840+
),
1841+
))
1842+
.await
1843+
.unwrap();
1844+
}
1845+
1846+
#[tokio::test]
1847+
async fn load_emails_by_alert_empty_ids_returns_empty_map() {
1848+
let db = make_db().await;
1849+
let result = load_emails_by_alert(&db, &[]).await.unwrap();
1850+
assert!(result.is_empty());
1851+
}
1852+
1853+
#[tokio::test]
1854+
async fn load_emails_by_alert_no_rows_returns_empty_map() {
1855+
let db = make_db().await;
1856+
let result = load_emails_by_alert(&db, &[1, 2, 3]).await.unwrap();
1857+
assert!(result.is_empty());
1858+
}
1859+
1860+
#[tokio::test]
1861+
async fn load_emails_by_alert_groups_by_alert_id() {
1862+
let db = make_db().await;
1863+
let now = time::OffsetDateTime::now_utc();
1864+
insert_minimal_alert(&db, 1).await;
1865+
insert_minimal_alert(&db, 2).await;
1866+
1867+
// Insert two emails for alert 1, one for alert 2
1868+
for (alert_id, email) in [
1869+
(1, "a@example.com"),
1870+
(1, "b@example.com"),
1871+
(2, "c@example.com"),
1872+
] {
1873+
crate::entity::alert_notification_email::ActiveModel {
1874+
id: sea_orm::ActiveValue::NotSet,
1875+
alert_id: Set(alert_id),
1876+
email: Set(email.to_string()),
1877+
created_at: Set(now),
1878+
}
1879+
.insert(&db)
1880+
.await
1881+
.unwrap();
1882+
}
1883+
1884+
let result = load_emails_by_alert(&db, &[1, 2]).await.unwrap();
1885+
let mut emails_1 = result[&1].clone();
1886+
emails_1.sort();
1887+
assert_eq!(emails_1, vec!["a@example.com", "b@example.com"]);
1888+
assert_eq!(result[&2], vec!["c@example.com"]);
1889+
}
1890+
1891+
#[tokio::test]
1892+
async fn load_webhooks_by_alert_empty_ids_returns_empty_map() {
1893+
let db = make_db().await;
1894+
let result = load_webhooks_by_alert(&db, &[]).await.unwrap();
1895+
assert!(result.is_empty());
1896+
}
1897+
1898+
#[tokio::test]
1899+
async fn load_webhooks_by_alert_groups_webhooks() {
1900+
let db = make_db().await;
1901+
let now = time::OffsetDateTime::now_utc();
1902+
insert_minimal_alert(&db, 7).await;
1903+
1904+
crate::entity::alert_notification_webhook::ActiveModel {
1905+
id: sea_orm::ActiveValue::NotSet,
1906+
alert_id: Set(7),
1907+
url: Set("https://hook.example.com/a".to_string()),
1908+
hmac_secret: Set(None),
1909+
hmac_header: Set("X-Sig".to_string()),
1910+
respect_quiet_hours: Set(false),
1911+
created_at: Set(now),
1912+
}
1913+
.insert(&db)
1914+
.await
1915+
.unwrap();
1916+
1917+
let result = load_webhooks_by_alert(&db, &[7]).await.unwrap();
1918+
assert_eq!(result[&7].len(), 1);
1919+
assert_eq!(result[&7][0].url, "https://hook.example.com/a");
1920+
assert_eq!(result[&7][0].hmac_header, "X-Sig");
1921+
}
17321922
}

0 commit comments

Comments
 (0)