From 16039e02a999c668d4969a43eb9ed1d4e8d370e1 Mon Sep 17 00:00:00 2001 From: Xevion Date: Fri, 30 Jan 2026 23:32:04 -0600 Subject: [PATCH] fix(metrics): always emit baseline metrics on initial course insertion --- src/data/batch.rs | 6 +++--- tests/db_batch_upsert.rs | 31 +++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/data/batch.rs b/src/data/batch.rs index eff3ec5..9ce9765 100644 --- a/src/data/batch.rs +++ b/src/data/batch.rs @@ -276,14 +276,14 @@ fn compute_diffs(rows: &[UpsertDiffRow]) -> (Vec, Vec) diff_field!(json audits, row, "meeting_times", old_meeting_times, new_meeting_times); diff_field!(json audits, row, "attributes", old_attributes, new_attributes); - // Emit a metric entry when enrollment/wait_count/max_enrollment changed - // Skip fresh inserts (no old data to compare against) + // Emit a metric entry on fresh insert (baseline) or when enrollment data changed + let is_new = row.old_id.is_none(); let enrollment_changed = row.old_id.is_some() && (row.old_enrollment != Some(row.new_enrollment) || row.old_wait_count != Some(row.new_wait_count) || row.old_max_enrollment != Some(row.new_max_enrollment)); - if enrollment_changed { + if is_new || enrollment_changed { metrics.push(MetricEntry { course_id: row.id, enrollment: row.new_enrollment, diff --git a/tests/db_batch_upsert.rs b/tests/db_batch_upsert.rs index 7c71ac7..f9e6a1a 100644 --- a/tests/db_batch_upsert.rs +++ b/tests/db_batch_upsert.rs @@ -214,7 +214,7 @@ async fn test_batch_upsert_unique_constraint_crn_term(pool: PgPool) { #[sqlx::test] async fn test_batch_upsert_creates_audit_and_metric_entries(pool: PgPool) { - // Insert initial data — should NOT create audits/metrics (it's a fresh insert) + // Insert initial data — should create a baseline metric but no audits let initial = vec![helpers::make_course( "50001", "202510", @@ -242,10 +242,21 @@ async fn test_batch_upsert_creates_audit_and_metric_entries(pool: PgPool) { .await .unwrap(); assert_eq!( - metric_count, 0, - "initial insert should not create metric entries" + metric_count, 1, + "initial insert should create a baseline metric" ); + // Verify baseline metric values + let (enrollment, wait_count, seats): (i32, i32, i32) = sqlx::query_as( + "SELECT enrollment, wait_count, seats_available FROM course_metrics ORDER BY timestamp LIMIT 1", + ) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(enrollment, 10); + assert_eq!(wait_count, 0); + assert_eq!(seats, 25); // 35 - 10 + // Update enrollment and wait_count let updated = vec![helpers::make_course( "50001", @@ -270,16 +281,16 @@ async fn test_batch_upsert_creates_audit_and_metric_entries(pool: PgPool) { "should have audit entries for enrollment and wait_count changes, got {audit_count}" ); - // Should have exactly 1 metric entry + // Should have 2 metric entries: baseline + change let (metric_count,): (i64,) = sqlx::query_as("SELECT COUNT(*) FROM course_metrics") .fetch_one(&pool) .await .unwrap(); - assert_eq!(metric_count, 1, "should have 1 metric snapshot"); + assert_eq!(metric_count, 2, "should have baseline + 1 change metric"); - // Verify metric values + // Verify the latest metric values let (enrollment, wait_count, seats): (i32, i32, i32) = sqlx::query_as( - "SELECT enrollment, wait_count, seats_available FROM course_metrics LIMIT 1", + "SELECT enrollment, wait_count, seats_available FROM course_metrics ORDER BY timestamp DESC LIMIT 1", ) .fetch_one(&pool) .await @@ -291,7 +302,7 @@ async fn test_batch_upsert_creates_audit_and_metric_entries(pool: PgPool) { #[sqlx::test] async fn test_batch_upsert_no_change_no_audit(pool: PgPool) { - // Insert then re-insert identical data — should produce zero audits/metrics + // Insert then re-insert identical data — should produce baseline metric but no audits or extra metrics let course = vec![helpers::make_course( "60001", "202510", @@ -320,7 +331,7 @@ async fn test_batch_upsert_no_change_no_audit(pool: PgPool) { .await .unwrap(); assert_eq!( - metric_count, 0, - "identical re-upsert should not create metric entries" + metric_count, 1, + "identical re-upsert should only have the baseline metric" ); }