changes
All checks were successful
CI / ci (push) Successful in 19m38s

This commit is contained in:
2026-08-26 20:55:30 +02:00
parent a557c183e9
commit 23d052278a
523 changed files with 24448 additions and 2005 deletions

View File

@@ -0,0 +1,179 @@
use sqlx::sqlite::SqlitePoolOptions;
use domain::entry::Date;
use domain::ports::{
CascadeDeletePort, CycleStartCommandPort, CycleStartQueryPort, UserCommandPort,
UserPreferencesCommandPort, UserPreferencesQueryPort,
};
use domain::testing::test_user;
use domain::user::{User, UserId, UserPreferences};
use sqlite::repositories::{
SqliteCascadeDeleteRepository, SqliteCycleStartRepository, SqliteUserCommandRepository,
SqliteUserPreferencesRepository,
};
async fn a_pool_with_a_user() -> (sqlx::SqlitePool, User) {
let pool = SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.unwrap();
sqlite::run_migrations(&pool).await.unwrap();
let user = test_user("alice");
SqliteUserCommandRepository::new(pool.clone())
.save(&user)
.await
.unwrap();
(pool, user)
}
fn on(day: &str) -> Date {
Date::from_persistence(day.parse().unwrap())
}
#[tokio::test]
async fn recording_the_same_start_twice_leaves_one_row() {
let (pool, user) = a_pool_with_a_user().await;
let starts = SqliteCycleStartRepository::new(pool.clone());
starts.record(user.id(), &on("2026-01-01")).await.unwrap();
starts.record(user.id(), &on("2026-01-01")).await.unwrap();
let rows: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM cycle_starts")
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(rows.0, 1);
assert_eq!(starts.find_by_user(user.id()).await.unwrap().len(), 1);
}
#[tokio::test]
async fn starts_come_back_oldest_first() {
let (pool, user) = a_pool_with_a_user().await;
let starts = SqliteCycleStartRepository::new(pool.clone());
for day in ["2026-02-26", "2026-01-01", "2026-01-29"] {
starts.record(user.id(), &on(day)).await.unwrap();
}
let found: Vec<String> = starts
.find_by_user(user.id())
.await
.unwrap()
.iter()
.map(|date| date.to_string())
.collect();
assert_eq!(found, ["2026-01-01", "2026-01-29", "2026-02-26"]);
}
#[tokio::test]
async fn forgetting_a_start_removes_only_that_one() {
let (pool, user) = a_pool_with_a_user().await;
let starts = SqliteCycleStartRepository::new(pool.clone());
starts.record(user.id(), &on("2026-01-01")).await.unwrap();
starts.record(user.id(), &on("2026-01-29")).await.unwrap();
starts.forget(user.id(), &on("2026-01-01")).await.unwrap();
let found: Vec<String> = starts
.find_by_user(user.id())
.await
.unwrap()
.iter()
.map(|date| date.to_string())
.collect();
assert_eq!(found, ["2026-01-29"]);
}
#[tokio::test]
async fn one_accounts_starts_are_not_anothers() {
let (pool, mine) = a_pool_with_a_user().await;
let starts = SqliteCycleStartRepository::new(pool.clone());
starts.record(mine.id(), &on("2026-01-01")).await.unwrap();
assert!(
starts
.find_by_user(&UserId::generate())
.await
.unwrap()
.is_empty()
);
}
#[tokio::test]
async fn an_account_with_no_stored_preference_has_none_to_read() {
let (pool, user) = a_pool_with_a_user().await;
let found = SqliteUserPreferencesRepository::new(pool.clone())
.find_by_user(user.id())
.await
.unwrap();
assert!(
found.is_none(),
"the default belongs to the domain, not the row"
);
}
#[tokio::test]
async fn a_preference_survives_being_written_twice() {
let (pool, user) = a_pool_with_a_user().await;
let preferences = SqliteUserPreferencesRepository::new(pool.clone());
let mut held = UserPreferences::off_by_default(user.id().clone());
held.track_cycle(true);
preferences.save(&held).await.unwrap();
held.track_cycle(false);
preferences.save(&held).await.unwrap();
let found = preferences.find_by_user(user.id()).await.unwrap().unwrap();
assert!(!found.tracks_cycle());
let rows: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_preferences")
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(rows.0, 1);
}
#[tokio::test]
async fn deleting_an_account_takes_its_cycle_and_preferences_with_it() {
let (pool, user) = a_pool_with_a_user().await;
SqliteCycleStartRepository::new(pool.clone())
.record(user.id(), &on("2026-01-01"))
.await
.unwrap();
let mut held = UserPreferences::off_by_default(user.id().clone());
held.track_cycle(true);
SqliteUserPreferencesRepository::new(pool.clone())
.save(&held)
.await
.unwrap();
SqliteCascadeDeleteRepository::new(pool.clone())
.delete_user_account(user.id())
.await
.unwrap();
let starts: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM cycle_starts")
.fetch_one(&pool)
.await
.unwrap();
let preferences: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_preferences")
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(starts.0, 0);
assert_eq!(preferences.0, 0);
}