Skip to main content

max / makenotwork

Fix analytics tests to use relative dates instead of hardcoded timestamps Replace hardcoded 2026-03-0x dates with a days_ago_at() helper that computes timestamps relative to now, preventing test failures as dates drift out of the 7-day and 30-day query windows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-03-09 16:59 UTC
Commit: 67ba4df5a5e07cfba1b4a3a600398d4b59d3007c
Parent: e5b6858
1 file changed, +33 insertions, -19 deletions
@@ -10,6 +10,14 @@ use std::sync::atomic::{AtomicU32, Ordering};
10 10 /// Monotonic counter for unique buyer usernames across all tests.
11 11 static BUYER_COUNTER: AtomicU32 = AtomicU32::new(0);
12 12
13 + /// Returns an ISO 8601 timestamp for `days` days ago at the given `hour` (UTC).
14 + /// Uses date-only arithmetic so two calls with the same `days` but different
15 + /// `hour` values always land on the same calendar day.
16 + fn days_ago_at(days: u32, hour: u32) -> String {
17 + let date = (chrono::Utc::now() - chrono::Duration::days(days as i64)).date_naive();
18 + format!("{}T{:02}:00:00Z", date, hour)
19 + }
20 +
13 21 /// Create a unique buyer user via direct SQL. Avoids the partial unique index
14 22 /// on transactions(buyer_id, item_id) WHERE status = 'completed' by giving
15 23 /// each transaction its own buyer.
@@ -108,10 +116,13 @@ async fn revenue_timeseries_buckets_by_day() {
108 116 let mut h = TestHarness::new().await;
109 117 let (seller_id, _, item_id) = setup_seller_with_item(&mut h, "ts1").await;
110 118
111 - // Insert 3 transactions across 2 days
112 - insert_transaction(&h.db, seller_id, item_id, 1000, "2026-03-03T12:00:00Z").await;
113 - insert_transaction(&h.db, seller_id, item_id, 2000, "2026-03-03T14:00:00Z").await;
114 - insert_transaction(&h.db, seller_id, item_id, 500, "2026-03-02T10:00:00Z").await;
119 + // Insert 3 transactions across 2 days (all within 30-day window)
120 + let day_a = days_ago_at(2, 12);
121 + let day_a2 = days_ago_at(2, 14);
122 + let day_b = days_ago_at(3, 10);
123 + insert_transaction(&h.db, seller_id, item_id, 1000, &day_a).await;
124 + insert_transaction(&h.db, seller_id, item_id, 2000, &day_a2).await;
125 + insert_transaction(&h.db, seller_id, item_id, 500, &day_b).await;
115 126
116 127 // Timeseries bucketed by day, user scope, 30d range
117 128 let rows: Vec<(chrono::DateTime<chrono::Utc>, i64, i64)> = sqlx::query_as(
@@ -136,11 +147,11 @@ async fn revenue_timeseries_buckets_by_day() {
136 147
137 148 assert_eq!(rows.len(), 2, "Expected 2 daily buckets, got {}", rows.len());
138 149
139 - // Mar 2: 1 sale, 500 cents
150 + // Earlier day: 1 sale, 500 cents
140 151 assert_eq!(rows[0].1, 500);
141 152 assert_eq!(rows[0].2, 1);
142 153
143 - // Mar 3: 2 sales, 3000 cents
154 + // Later day: 2 sales, 3000 cents
144 155 assert_eq!(rows[1].1, 3000);
145 156 assert_eq!(rows[1].2, 2);
146 157 }
@@ -177,10 +188,13 @@ async fn revenue_timeseries_item_and_project_scope() {
177 188 .await
178 189 .unwrap();
179 190
180 - // Transactions: item_a on Mar 2 and Mar 3, item_b on Mar 3 only
181 - insert_transaction(&h.db, seller_id, item_a, 1000, "2026-03-02T12:00:00Z").await;
182 - insert_transaction(&h.db, seller_id, item_a, 1000, "2026-03-03T12:00:00Z").await;
183 - insert_transaction(&h.db, seller_id, item_b, 2000, "2026-03-03T13:00:00Z").await;
191 + // Transactions: item_a on day_b and day_a, item_b on day_a only
192 + let day_a = days_ago_at(2, 12);
193 + let day_a2 = days_ago_at(2, 13);
194 + let day_b = days_ago_at(3, 12);
195 + insert_transaction(&h.db, seller_id, item_a, 1000, &day_b).await;
196 + insert_transaction(&h.db, seller_id, item_a, 1000, &day_a).await;
197 + insert_transaction(&h.db, seller_id, item_b, 2000, &day_a2).await;
184 198
185 199 // Item scope: only item_a
186 200 let rows: Vec<(chrono::DateTime<chrono::Utc>, i64, i64)> = sqlx::query_as(
@@ -233,9 +247,9 @@ async fn revenue_timeseries_item_and_project_scope() {
233 247 .unwrap();
234 248
235 249 assert_eq!(rows.len(), 2, "Project scope should have 2 daily buckets");
236 - assert_eq!(rows[0].1, 1000, "Mar 2: only item_a");
237 - assert_eq!(rows[1].1, 3000, "Mar 3: item_a + item_b");
238 - assert_eq!(rows[1].2, 2, "Mar 3: 2 sales total");
250 + assert_eq!(rows[0].1, 1000, "Earlier day: only item_a");
251 + assert_eq!(rows[1].1, 3000, "Later day: item_a + item_b");
252 + assert_eq!(rows[1].2, 2, "Later day: 2 sales total");
239 253 }
240 254
241 255 #[tokio::test]
@@ -244,11 +258,11 @@ async fn period_comparison_current_vs_previous() {
244 258 let (seller_id, _, item_id) = setup_seller_with_item(&mut h, "cmp1").await;
245 259
246 260 // Current period (within last 7 days): 2 sales, 3000 cents
247 - insert_transaction(&h.db, seller_id, item_id, 1000, "2026-03-03T12:00:00Z").await;
248 - insert_transaction(&h.db, seller_id, item_id, 2000, "2026-03-02T12:00:00Z").await;
261 + insert_transaction(&h.db, seller_id, item_id, 1000, &days_ago_at(2, 12)).await;
262 + insert_transaction(&h.db, seller_id, item_id, 2000, &days_ago_at(3, 12)).await;
249 263
250 264 // Previous period (8-14 days ago): 1 sale, 500 cents
251 - insert_transaction(&h.db, seller_id, item_id, 500, "2026-02-24T12:00:00Z").await;
265 + insert_transaction(&h.db, seller_id, item_id, 500, &days_ago_at(10, 12)).await;
252 266
253 267 // Period comparison with FILTER, user scope, 7d
254 268 let row: (i64, i64, i64, i64) = sqlx::query_as(
@@ -290,11 +304,11 @@ async fn follower_comparison() {
290 304 let seller_uuid: uuid::Uuid = seller_id.into();
291 305
292 306 // Current period follows (within last 30 days)
293 - insert_follow(&h.db, fan1, "user", seller_uuid, "2026-03-02T12:00:00Z").await;
294 - insert_follow(&h.db, fan2, "user", seller_uuid, "2026-03-01T12:00:00Z").await;
307 + insert_follow(&h.db, fan1, "user", seller_uuid, &days_ago_at(3, 12)).await;
308 + insert_follow(&h.db, fan2, "user", seller_uuid, &days_ago_at(5, 12)).await;
295 309
296 310 // Previous period follow (31-60 days ago)
297 - insert_follow(&h.db, fan3, "user", seller_uuid, "2026-02-01T12:00:00Z").await;
311 + insert_follow(&h.db, fan3, "user", seller_uuid, &days_ago_at(35, 12)).await;
298 312
299 313 // Follower comparison, user scope, 30d
300 314 let row: (i64, i64) = sqlx::query_as(