From d0c0e2fa34e589c18a8c97bbeae3b9a7941cf918 Mon Sep 17 00:00:00 2001 From: Henrik Nygren Date: Mon, 26 Aug 2024 12:58:00 +0300 Subject: [PATCH] Tests for batch insert --- .../20240823123921_add_code_giveaways.up.sql | 12 ++--- .../models/src/code_giveaway_codes.rs | 54 ++++++++++++++----- .../headless-lms/models/src/code_giveaways.rs | 8 +-- .../models/src/library/progressing.rs | 2 +- 4 files changed, 50 insertions(+), 26 deletions(-) diff --git a/services/headless-lms/migrations/20240823123921_add_code_giveaways.up.sql b/services/headless-lms/migrations/20240823123921_add_code_giveaways.up.sql index 77049b69188..6a374d14824 100644 --- a/services/headless-lms/migrations/20240823123921_add_code_giveaways.up.sql +++ b/services/headless-lms/migrations/20240823123921_add_code_giveaways.up.sql @@ -26,14 +26,12 @@ CREATE TABLE code_giveaway_codes ( code_giveaway_id UUID NOT NULL REFERENCES code_giveaways(id), code_given_to_user_id UUID REFERENCES users(id), added_by_user_id UUID NOT NULL REFERENCES users(id), - code VARCHAR(2048) NOT NULL, - -- A user can only receive one code from a giveaway. - UNIQUE NULLS NOT DISTINCT ( - code_giveaway_id, - code_given_to_user_id, - deleted_at - ) + code VARCHAR(2048) NOT NULL ); +-- A user can only receive one code from a giveaway. We use unique index here because if we used a unique constraint we would like to have NULLS NOT disctinct on the delted_at column but NULLS DISTINCT on the code_given_to_user_id column. This did not seem possible so we use a unique index instead. +CREATE UNIQUE INDEX giveaway_codes_one_code_per_user ON code_giveaway_codes (code_giveaway_id, code_given_to_user_id) +WHERE deleted_at IS NULL; + CREATE TRIGGER set_timestamp BEFORE UPDATE ON code_giveaway_codes FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); COMMENT ON TABLE code_giveaway_codes IS 'A code that is available in a code giveaway. A user can only receive one code from a giveaway.'; diff --git a/services/headless-lms/models/src/code_giveaway_codes.rs b/services/headless-lms/models/src/code_giveaway_codes.rs index cd02d13b89a..5da4150b0e2 100644 --- a/services/headless-lms/models/src/code_giveaway_codes.rs +++ b/services/headless-lms/models/src/code_giveaway_codes.rs @@ -21,6 +21,9 @@ pub async fn insert_many( input: &[String], added_by_user_id: Uuid, ) -> ModelResult> { + if input.is_empty() { + return Ok(vec![]); + } let mut query_builder = QueryBuilder::new( "INSERT INTO code_giveaway_codes (code_giveaway_id, code, added_by_user_id) ", ); @@ -133,27 +136,50 @@ mod tests { #[tokio::test] async fn test_insert_many_empty() { - insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module); + insert_data!(:tx, :user, :org, :course); - let insert_result = insert_many(tx.as_mut(), &[]).await.unwrap(); + let code_giveaway = crate::code_giveaways::insert(tx.as_mut(), course) + .await + .unwrap(); - let inserted_data = get_inserted_data(tx.as_mut()).await.unwrap(); + let insert_result = insert_many(tx.as_mut(), code_giveaway.id, &[], user) + .await + .unwrap(); assert!(insert_result.is_empty()); - assert!(inserted_data.is_empty()); } #[tokio::test] async fn test_insert_many_with_data() { - insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module, chapter: _chapter, page: _page, exercise: exercise_id); - - let data_to_insert = vec![/* your data here */]; - - let insert_result = insert_many(tx.as_mut(), &data_to_insert).await.unwrap(); - - let inserted_data = get_inserted_data(tx.as_mut()).await.unwrap(); - - assert_eq!(insert_result.len(), data_to_insert.len()); - assert_eq!(inserted_data.len(), data_to_insert.len()); + insert_data!(:tx, :user, :org, :course); + + let code_giveaway = crate::code_giveaways::insert(tx.as_mut(), course) + .await + .unwrap(); + + let codes = vec![ + "code1".to_string(), + "code2".to_string(), + "code3".to_string(), + ]; + + let insert_result = insert_many(tx.as_mut(), code_giveaway.id, &codes, user) + .await + .unwrap(); + + assert_eq!(insert_result.len(), codes.len()); + for code in &codes { + let found = insert_result.iter().find(|c| c.code == *code); + assert!(found.is_some()); + } + // Double checking + let all_codes = get_all_by_code_giveaway_id(tx.as_mut(), code_giveaway.id) + .await + .unwrap(); + assert_eq!(all_codes.len(), codes.len()); + for code in &codes { + let found = all_codes.iter().find(|c| c.code == *code); + assert!(found.is_some()); + } } } diff --git a/services/headless-lms/models/src/code_giveaways.rs b/services/headless-lms/models/src/code_giveaways.rs index ae3209cd9c4..c5f2e24f538 100644 --- a/services/headless-lms/models/src/code_giveaways.rs +++ b/services/headless-lms/models/src/code_giveaways.rs @@ -12,7 +12,7 @@ pub struct CodeGiveaway { pub enabled: bool, } -async fn insert(conn: &mut PgConnection, course_id: Uuid) -> ModelResult { +pub async fn insert(conn: &mut PgConnection, course_id: Uuid) -> ModelResult { let res = sqlx::query_as!( CodeGiveaway, r#" @@ -28,7 +28,7 @@ RETURNING * Ok(res) } -async fn get_all_for_course( +pub async fn get_all_for_course( conn: &mut PgConnection, course_id: Uuid, ) -> ModelResult> { @@ -48,7 +48,7 @@ WHERE course_id = $1 Ok(res) } -async fn get_by_id(conn: &mut PgConnection, id: Uuid) -> ModelResult> { +pub async fn get_by_id(conn: &mut PgConnection, id: Uuid) -> ModelResult> { let res = sqlx::query_as!( CodeGiveaway, r#" @@ -64,7 +64,7 @@ WHERE id = $1 Ok(res) } -async fn set_enabled( +pub async fn set_enabled( conn: &mut PgConnection, id: Uuid, enabled: bool, diff --git a/services/headless-lms/models/src/library/progressing.rs b/services/headless-lms/models/src/library/progressing.rs index 30c0d3a4e22..1e9c1464e86 100644 --- a/services/headless-lms/models/src/library/progressing.rs +++ b/services/headless-lms/models/src/library/progressing.rs @@ -1160,7 +1160,7 @@ mod tests { #[tokio::test] async fn tags_suspected_cheater() { - insert_data!(:tx, user:user, :org, course:course, instance:instance, course_module:course_module, :chapter, :page, :exercise, :slide, :task); + insert_data!(:tx, user:user, :org, course:course, instance:instance, course_module:course_module, :chapter, :page, :exercise, :slide); crate::library::course_instances::enroll(tx.as_mut(), user, instance.id, &[]) .await