use chrono::{Duration, NaiveDate, Utc};
use tiny_counter::EventStore;
#[test]
fn record_single_event() {
let store = EventStore::new();
store.record("app_launch");
let count = store.query("app_launch").last_days(1).sum();
assert_eq!(count, Some(1));
}
#[test]
fn record_multiple_events_accumulate() {
let store = EventStore::new();
store.record("button_click");
store.record("button_click");
store.record("button_click");
let count = store.query("button_click").last_days(1).sum();
assert_eq!(count, Some(3));
}
#[test]
fn record_count_adds_multiple() {
let store = EventStore::new();
store.record_count("api_call", 10);
store.record_count("api_call", 20);
let count = store.query("api_call").last_days(1).sum();
assert_eq!(count, Some(30));
}
#[test]
fn record_different_events_independently() {
let store = EventStore::new();
store.record("event_a");
store.record("event_a");
store.record("event_b");
store.record("event_b");
store.record("event_b");
assert_eq!(store.query("event_a").last_days(1).sum(), Some(2));
assert_eq!(store.query("event_b").last_days(1).sum(), Some(3));
}
#[test]
fn record_creates_event_automatically() {
let store = EventStore::new();
store.record("new_event");
assert_eq!(store.query("new_event").last_days(1).sum(), Some(1));
}
#[test]
fn record_zero_count_is_noop() {
let store = EventStore::new();
store.record_count("event", 0);
assert_eq!(store.query("event").last_days(1).sum(), Some(0));
}
#[test]
fn record_at_specific_time() {
let store = EventStore::new();
let two_days_ago = Utc::now() - Duration::days(2);
store.record_at("event", two_days_ago).unwrap();
assert_eq!(store.query("event").last_days(1).sum(), Some(0));
assert_eq!(store.query("event").last_days(7).sum(), Some(1));
}
#[test]
fn record_count_at_specific_time() {
let store = EventStore::new();
let yesterday = Utc::now() - Duration::days(1);
store.record_count_at("batch_event", 50, yesterday).unwrap();
assert_eq!(store.query("batch_event").last_days(7).sum(), Some(50));
}
#[test]
fn record_ago_relative_duration() {
let store = EventStore::new();
store.record_ago("event", Duration::hours(3));
assert_eq!(store.query("event").last_hours(24).sum(), Some(1));
store.record_ago("older_event", Duration::days(2));
assert_eq!(store.query("older_event").last_days(3).sum(), Some(1));
}
#[test]
fn record_count_ago_relative_duration() {
let store = EventStore::new();
store.record_count_ago("event", 25, Duration::hours(12));
assert_eq!(store.query("event").last_hours(24).sum(), Some(25));
store.record_count_ago("older_event", 10, Duration::days(2));
assert_eq!(store.query("older_event").last_days(3).sum(), Some(10));
}
#[test]
fn record_at_rejects_future_time() {
let store = EventStore::new();
let future = Utc::now() + Duration::days(1);
let result = store.record_at("event", future);
assert!(result.is_err());
}
#[test]
fn record_count_at_rejects_future_time() {
let store = EventStore::new();
let future = Utc::now() + Duration::hours(1);
let result = store.record_count_at("event", 10, future);
assert!(result.is_err());
}
#[test]
fn record_at_midnight_boundary() {
let store = EventStore::builder().track_months(24).build().unwrap();
let midnight = NaiveDate::from_ymd_opt(2024, 6, 15)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc();
store.record_at("midnight_event", midnight).unwrap();
assert_eq!(store.query("midnight_event").ever().sum(), Some(1));
}
#[test]
fn record_at_hour_boundaries() {
let store = EventStore::builder().track_months(24).build().unwrap();
for hour in 0..24 {
let timestamp = NaiveDate::from_ymd_opt(2024, 6, 15)
.unwrap()
.and_hms_opt(hour, 0, 0)
.unwrap()
.and_utc();
store.record_at("hourly_event", timestamp).unwrap();
}
assert_eq!(store.query("hourly_event").ever().sum(), Some(24));
}
#[test]
fn record_across_day_boundary() {
let store = EventStore::builder().track_months(24).build().unwrap();
let before_midnight = NaiveDate::from_ymd_opt(2024, 6, 15)
.unwrap()
.and_hms_opt(23, 59, 59)
.unwrap()
.and_utc();
let after_midnight = NaiveDate::from_ymd_opt(2024, 6, 16)
.unwrap()
.and_hms_opt(0, 0, 1)
.unwrap()
.and_utc();
store.record_at("boundary_event", before_midnight).unwrap();
store.record_at("boundary_event", after_midnight).unwrap();
assert_eq!(store.query("boundary_event").ever().sum(), Some(2));
}
#[test]
fn u64_max_count_clamped_to_u32() {
let store = EventStore::new();
store.record_count("overflow_test", u32::MAX - 1000);
let sum = store.query("overflow_test").last_days(1).sum();
assert_eq!(sum, Some(u32::MAX - 1000));
}
#[test]
fn multiple_large_counts() {
let store = EventStore::new();
store.record_count("large_event", u32::MAX / 2);
store.record_count("large_event", u32::MAX / 2);
let sum = store.query("large_event").last_days(1).sum();
assert!(sum.is_some());
}
#[test]
fn long_event_id() {
let store = EventStore::new();
let long_id = "a".repeat(1000);
store.record(&long_id);
assert_eq!(store.query(&long_id).last_days(1).sum(), Some(1));
}
#[test]
fn special_characters_in_event_id() {
let store = EventStore::new();
let special_id = "event:with/special.chars-and_numbers123!@#$%";
store.record(special_id);
assert_eq!(store.query(special_id).last_days(1).sum(), Some(1));
}
#[test]
fn unicode_event_id() {
let store = EventStore::new();
let unicode_id = "イベント_事件_🎉";
store.record(unicode_id);
assert_eq!(store.query(unicode_id).last_days(1).sum(), Some(1));
}
#[test]
fn many_different_events() {
let store = EventStore::new();
for i in 0..1000 {
store.record(format!("event_{}", i));
}
for i in 0..1000 {
assert_eq!(
store.query(format!("event_{}", i)).last_days(1).sum(),
Some(1)
);
}
}
#[test]
fn rapid_recording_same_event() {
let store = EventStore::new();
for _ in 0..10_000 {
store.record("rapid_event");
}
assert_eq!(store.query("rapid_event").last_days(1).sum(), Some(10_000));
}