1use crate::{course_module_completions, prelude::*};
2use rand::Rng;
3
4#[derive(Clone, PartialEq, Deserialize, Serialize)]
5pub struct CourseModuleCompletionRegisteredToStudyRegistry {
6 pub id: Uuid,
7 pub created_at: DateTime<Utc>,
8 pub updated_at: DateTime<Utc>,
9 pub deleted_at: Option<DateTime<Utc>>,
10 pub course_id: Uuid,
11 pub course_module_completion_id: Uuid,
12 pub course_module_id: Uuid,
13 pub study_registry_registrar_id: Uuid,
14 pub user_id: Uuid,
15 pub real_student_number: String,
16}
17
18#[derive(Clone, PartialEq, Deserialize, Serialize)]
19pub struct NewCourseModuleCompletionRegisteredToStudyRegistry {
20 pub course_id: Uuid,
21 pub course_module_completion_id: Uuid,
22 pub course_module_id: Uuid,
23 pub study_registry_registrar_id: Uuid,
24 pub user_id: Uuid,
25 pub real_student_number: String,
26}
27
28pub async fn insert(
29 conn: &mut PgConnection,
30 pkey_policy: PKeyPolicy<Uuid>,
31 new_completion_registration: &NewCourseModuleCompletionRegisteredToStudyRegistry,
32) -> ModelResult<Uuid> {
33 let res = sqlx::query!(
34 "
35INSERT INTO course_module_completion_registered_to_study_registries (
36 id,
37 course_id,
38 course_module_completion_id,
39 course_module_id,
40 study_registry_registrar_id,
41 user_id,
42 real_student_number
43 )
44VALUES (
45 $1,
46 $2,
47 $3,
48 $4,
49 $5,
50 $6,
51 $7
52 )
53RETURNING id
54 ",
55 pkey_policy.into_uuid(),
56 new_completion_registration.course_id,
57 new_completion_registration.course_module_completion_id,
58 new_completion_registration.course_module_id,
59 new_completion_registration.study_registry_registrar_id,
60 new_completion_registration.user_id,
61 new_completion_registration.real_student_number,
62 )
63 .fetch_one(conn)
64 .await?;
65 Ok(res.id)
66}
67
68pub async fn insert_bulk(
69 conn: &mut PgConnection,
70 new_completion_registrations: Vec<NewCourseModuleCompletionRegisteredToStudyRegistry>,
71) -> ModelResult<Vec<Uuid>> {
72 if new_completion_registrations.is_empty() {
73 return Ok(vec![]);
74 }
75
76 let ids: Vec<Uuid> = (0..new_completion_registrations.len())
78 .map(|_| Uuid::new_v4())
79 .collect();
80 let course_ids: Vec<Uuid> = new_completion_registrations
81 .iter()
82 .map(|r| r.course_id)
83 .collect();
84 let completion_ids: Vec<Uuid> = new_completion_registrations
85 .iter()
86 .map(|r| r.course_module_completion_id)
87 .collect();
88 let module_ids: Vec<Uuid> = new_completion_registrations
89 .iter()
90 .map(|r| r.course_module_id)
91 .collect();
92 let registrar_ids: Vec<Uuid> = new_completion_registrations
93 .iter()
94 .map(|r| r.study_registry_registrar_id)
95 .collect();
96 let user_ids: Vec<Uuid> = new_completion_registrations
97 .iter()
98 .map(|r| r.user_id)
99 .collect();
100 let student_numbers: Vec<String> = new_completion_registrations
101 .iter()
102 .map(|r| r.real_student_number.clone())
103 .collect();
104
105 let res = sqlx::query!(
106 r#"
107INSERT INTO course_module_completion_registered_to_study_registries (
108 id,
109 course_id,
110 course_module_completion_id,
111 course_module_id,
112 study_registry_registrar_id,
113 user_id,
114 real_student_number
115)
116SELECT * FROM UNNEST(
117 $1::uuid[],
118 $2::uuid[],
119 $3::uuid[],
120 $4::uuid[],
121 $5::uuid[],
122 $6::uuid[],
123 $7::text[]
124)
125RETURNING id
126 "#,
127 &ids[..],
128 &course_ids[..],
129 &completion_ids[..],
130 &module_ids[..],
131 ®istrar_ids[..],
132 &user_ids[..],
133 &student_numbers[..],
134 )
135 .fetch_all(conn)
136 .await?;
137
138 Ok(res.into_iter().map(|r| r.id).collect())
139}
140
141#[derive(Clone, PartialEq, Eq, Deserialize, Serialize, Debug)]
142pub struct RegisteredCompletion {
144 pub completion_id: Uuid,
146 pub student_number: String,
148 pub registration_date: DateTime<Utc>,
150}
151
152pub async fn mark_completions_as_registered_to_study_registry(
153 conn: &mut PgConnection,
154 completions: Vec<RegisteredCompletion>,
155 study_registry_registrar_id: Uuid,
156) -> ModelResult<()> {
157 if completions.is_empty() {
158 return Ok(());
159 }
160
161 let ids: Vec<Uuid> = completions.iter().map(|x| x.completion_id).collect();
162 let completions_by_id = course_module_completions::get_by_ids_as_map(conn, &ids).await?;
163
164 for completion in &completions {
166 if !completions_by_id.contains_key(&completion.completion_id) {
167 return Err(ModelError::new(
168 ModelErrorType::PreconditionFailed,
169 format!(
170 "Cannot find completion with id: {}. This completion does not exist in the database.",
171 completion.completion_id
172 ),
173 None,
174 ));
175 }
176 }
177
178 let new_registrations: Vec<NewCourseModuleCompletionRegisteredToStudyRegistry> = completions
179 .into_iter()
180 .map(|completion| {
181 let module_completion = completions_by_id.get(&completion.completion_id).unwrap();
182 NewCourseModuleCompletionRegisteredToStudyRegistry {
183 course_id: module_completion.course_id,
184 course_module_completion_id: completion.completion_id,
185 course_module_id: module_completion.course_module_id,
186 study_registry_registrar_id,
187 user_id: module_completion.user_id,
188 real_student_number: completion.student_number,
189 }
190 })
191 .collect();
192
193 let mut tx = conn.begin().await?;
194
195 insert_bulk(&mut tx, new_registrations).await?;
196
197 let mut rng = rand::rng();
200 let delete_all = rng.random_range(0..50) == 0; if delete_all {
203 delete_all_duplicates(&mut tx).await?;
204 } else {
205 delete_duplicates_for_specific_completions(&mut tx, &ids).await?;
206 }
207
208 tx.commit().await?;
209
210 Ok(())
211}
212
213pub async fn get_by_id(
214 conn: &mut PgConnection,
215 id: Uuid,
216) -> ModelResult<CourseModuleCompletionRegisteredToStudyRegistry> {
217 let res = sqlx::query_as!(
218 CourseModuleCompletionRegisteredToStudyRegistry,
219 "
220SELECT *
221FROM course_module_completion_registered_to_study_registries
222WHERE id = $1
223 AND deleted_at IS NULL
224 ",
225 id,
226 )
227 .fetch_one(conn)
228 .await?;
229 Ok(res)
230}
231
232pub async fn delete(conn: &mut PgConnection, id: Uuid) -> ModelResult<()> {
233 sqlx::query!(
234 "
235UPDATE course_module_completion_registered_to_study_registries
236SET deleted_at = now()
237WHERE id = $1
238 ",
239 id
240 )
241 .execute(conn)
242 .await?;
243 Ok(())
244}
245
246pub async fn get_count_of_distinct_users_with_registrations_by_course_id(
248 conn: &mut PgConnection,
249 course_id: Uuid,
250) -> ModelResult<i64> {
251 let res = sqlx::query!(
252 "
253SELECT COUNT(DISTINCT user_id) as count
254FROM course_module_completion_registered_to_study_registries
255WHERE course_id = $1
256 AND deleted_at IS NULL
257",
258 course_id,
259 )
260 .fetch_one(conn)
261 .await?;
262 Ok(res.count.unwrap_or(0))
263}
264
265pub async fn get_by_completion_id_and_registrar_id(
266 conn: &mut PgConnection,
267 completion_id: Uuid,
268 study_registry_registrar_id: Uuid,
269) -> ModelResult<Vec<CourseModuleCompletionRegisteredToStudyRegistry>> {
270 let registrations = sqlx::query_as!(
271 CourseModuleCompletionRegisteredToStudyRegistry,
272 r#"
273 SELECT *
274 FROM course_module_completion_registered_to_study_registries
275 WHERE course_module_completion_id = $1 AND study_registry_registrar_id = $2
276 AND deleted_at IS NULL
277 "#,
278 completion_id,
279 study_registry_registrar_id
280 )
281 .fetch_all(conn)
282 .await?;
283
284 Ok(registrations)
285}
286
287async fn delete_duplicates_for_specific_completions(
288 conn: &mut PgConnection,
289 completion_ids: &[Uuid],
290) -> ModelResult<i64> {
291 let res = sqlx::query!(
292 r#"
293WITH duplicate_rows AS (
294 SELECT id,
295 ROW_NUMBER() OVER (
296 PARTITION BY course_module_completion_id
297 ORDER BY created_at ASC -- Keep the oldest, delete the rest
298 ) AS rn
299 FROM course_module_completion_registered_to_study_registries
300 WHERE deleted_at IS NULL
301 AND course_module_completion_id = ANY($1)
302)
303UPDATE course_module_completion_registered_to_study_registries
304SET deleted_at = NOW()
305WHERE id IN (
306 SELECT id
307 FROM duplicate_rows
308 WHERE rn > 1
309 )
310RETURNING id
311 "#,
312 completion_ids,
313 )
314 .fetch_all(conn)
315 .await?;
316
317 Ok(res.len() as i64)
318}
319
320async fn delete_all_duplicates(conn: &mut PgConnection) -> ModelResult<i64> {
321 let res = sqlx::query!(
322 r#"
323WITH duplicate_rows AS (
324 SELECT id,
325 ROW_NUMBER() OVER (
326 PARTITION BY course_module_completion_id
327 ORDER BY created_at ASC -- Keep the oldest, delete the rest
328 ) AS rn
329 FROM course_module_completion_registered_to_study_registries
330 WHERE deleted_at IS NULL
331)
332UPDATE course_module_completion_registered_to_study_registries
333SET deleted_at = NOW()
334WHERE id IN (
335 SELECT id
336 FROM duplicate_rows
337 WHERE rn > 1
338 )
339RETURNING id
340 "#
341 )
342 .fetch_all(conn)
343 .await?;
344
345 Ok(res.len() as i64)
346}
347
348#[cfg(test)]
349mod test {
350 use super::*;
351 use crate::{course_module_completions::CourseModuleCompletionGranter, test_helper::*};
352
353 #[tokio::test]
354 async fn bulk_insert_works() {
355 insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module);
356
357 let registrar_id = crate::study_registry_registrars::insert(
358 tx.as_mut(),
359 PKeyPolicy::Generate,
360 "Test Registrar",
361 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
362 )
363 .await
364 .unwrap();
365
366 let new_completion_id = crate::course_module_completions::insert(
367 tx.as_mut(),
368 PKeyPolicy::Generate,
369 &crate::course_module_completions::NewCourseModuleCompletion {
370 course_id: course,
371 course_module_id: course_module.id,
372 user_id: user,
373 completion_date: Utc::now(),
374 completion_registration_attempt_date: None,
375 completion_language: "en-US".to_string(),
376 eligible_for_ects: true,
377 email: "test@example.com".to_string(),
378 grade: Some(10),
379 passed: true,
380 },
381 CourseModuleCompletionGranter::User(user),
382 )
383 .await
384 .unwrap();
385
386 let registrations = vec![
387 NewCourseModuleCompletionRegisteredToStudyRegistry {
388 course_id: course,
389 course_module_completion_id: new_completion_id.id,
390 course_module_id: course_module.id,
391 study_registry_registrar_id: registrar_id,
392 user_id: user,
393 real_student_number: "12345".to_string(),
394 },
395 NewCourseModuleCompletionRegisteredToStudyRegistry {
396 course_id: course,
397 course_module_completion_id: new_completion_id.id,
398 course_module_id: course_module.id,
399 study_registry_registrar_id: registrar_id,
400 user_id: user,
401 real_student_number: "67890".to_string(),
402 },
403 ];
404
405 let inserted_ids = insert_bulk(tx.as_mut(), registrations).await.unwrap();
406 assert_eq!(inserted_ids.len(), 2);
407
408 for id in inserted_ids {
410 let registration = get_by_id(tx.as_mut(), id).await.unwrap();
411 assert_eq!(registration.course_id, course);
412 assert_eq!(
413 registration.course_module_completion_id,
414 new_completion_id.id
415 );
416 assert_eq!(registration.course_module_id, course_module.id);
417 assert_eq!(registration.study_registry_registrar_id, registrar_id);
418 assert_eq!(registration.user_id, user);
419 }
420 }
421
422 #[tokio::test]
423 async fn bulk_insert_empty_vec_works() {
424 insert_data!(:tx);
425
426 let empty_vec = vec![];
427 let result = insert_bulk(tx.as_mut(), empty_vec).await.unwrap();
428 assert!(result.is_empty());
429 }
430
431 #[tokio::test]
432 async fn insert_completions_works() {
433 insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module);
434
435 let registrar_id = crate::study_registry_registrars::insert(
436 tx.as_mut(),
437 PKeyPolicy::Generate,
438 "Test Registrar",
439 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
440 )
441 .await
442 .unwrap();
443
444 let completion = crate::course_module_completions::insert(
445 tx.as_mut(),
446 PKeyPolicy::Generate,
447 &crate::course_module_completions::NewCourseModuleCompletion {
448 course_id: course,
449 course_module_id: course_module.id,
450 user_id: user,
451 completion_date: Utc::now(),
452 completion_registration_attempt_date: None,
453 completion_language: "en-US".to_string(),
454 eligible_for_ects: true,
455 email: "test@example.com".to_string(),
456 grade: Some(5),
457 passed: true,
458 },
459 CourseModuleCompletionGranter::User(user),
460 )
461 .await
462 .unwrap();
463
464 let registered_completions = vec![RegisteredCompletion {
465 completion_id: completion.id,
466 student_number: "12345".to_string(),
467 registration_date: Utc::now(),
468 }];
469
470 mark_completions_as_registered_to_study_registry(
471 tx.as_mut(),
472 registered_completions,
473 registrar_id,
474 )
475 .await
476 .unwrap();
477
478 let registrations =
479 get_by_completion_id_and_registrar_id(tx.as_mut(), completion.id, registrar_id)
480 .await
481 .unwrap();
482
483 assert_eq!(registrations.len(), 1);
484 assert_eq!(registrations[0].course_id, course);
485 assert_eq!(registrations[0].course_module_id, course_module.id);
486 assert_eq!(registrations[0].user_id, user);
487 assert_eq!(registrations[0].real_student_number, "12345");
488 }
489
490 #[tokio::test]
491 async fn insert_completions_with_invalid_completion_id_fails() {
492 insert_data!(:tx);
493
494 let registrar_id = crate::study_registry_registrars::insert(
495 tx.as_mut(),
496 PKeyPolicy::Generate,
497 "Test Registrar",
498 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
499 )
500 .await
501 .unwrap();
502
503 let invalid_uuid = Uuid::new_v4(); let registered_completions = vec![RegisteredCompletion {
505 completion_id: invalid_uuid,
506 student_number: "12345".to_string(),
507 registration_date: Utc::now(),
508 }];
509
510 let result = mark_completions_as_registered_to_study_registry(
512 tx.as_mut(),
513 registered_completions,
514 registrar_id,
515 )
516 .await;
517
518 assert!(result.is_err());
519 let error = result.unwrap_err();
520 assert_eq!(*error.error_type(), ModelErrorType::PreconditionFailed);
521 assert!(error.message().contains("Cannot find completion with id"));
522 assert!(error.message().contains(&invalid_uuid.to_string()));
523 }
524
525 #[tokio::test]
526 async fn delete_duplicate_registrations_works() {
527 insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module);
528
529 let registrar_id = crate::study_registry_registrars::insert(
530 tx.as_mut(),
531 PKeyPolicy::Generate,
532 "Test Registrar",
533 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
534 )
535 .await
536 .unwrap();
537
538 let completion = crate::course_module_completions::insert(
539 tx.as_mut(),
540 PKeyPolicy::Generate,
541 &crate::course_module_completions::NewCourseModuleCompletion {
542 course_id: course,
543 course_module_id: course_module.id,
544 user_id: user,
545 completion_date: Utc::now(),
546 completion_registration_attempt_date: None,
547 completion_language: "en-US".to_string(),
548 eligible_for_ects: true,
549 email: "test@example.com".to_string(),
550 grade: Some(5),
551 passed: true,
552 },
553 CourseModuleCompletionGranter::User(user),
554 )
555 .await
556 .unwrap();
557
558 let first_registration = NewCourseModuleCompletionRegisteredToStudyRegistry {
560 course_id: course,
561 course_module_completion_id: completion.id,
562 course_module_id: course_module.id,
563 study_registry_registrar_id: registrar_id,
564 user_id: user,
565 real_student_number: "12345".to_string(),
566 };
567 let first_id = insert(tx.as_mut(), PKeyPolicy::Generate, &first_registration)
568 .await
569 .unwrap();
570
571 let later_registrations = vec![
573 NewCourseModuleCompletionRegisteredToStudyRegistry {
574 course_id: course,
575 course_module_completion_id: completion.id,
576 course_module_id: course_module.id,
577 study_registry_registrar_id: registrar_id,
578 user_id: user,
579 real_student_number: "67890".to_string(),
580 },
581 NewCourseModuleCompletionRegisteredToStudyRegistry {
582 course_id: course,
583 course_module_completion_id: completion.id,
584 course_module_id: course_module.id,
585 study_registry_registrar_id: registrar_id,
586 user_id: user,
587 real_student_number: "54321".to_string(),
588 },
589 ];
590 insert_bulk(tx.as_mut(), later_registrations).await.unwrap();
591
592 let before_registrations =
593 get_by_completion_id_and_registrar_id(tx.as_mut(), completion.id, registrar_id)
594 .await
595 .unwrap();
596 assert_eq!(before_registrations.len(), 3);
597
598 let deleted_count =
599 delete_duplicates_for_specific_completions(tx.as_mut(), &[completion.id])
600 .await
601 .unwrap();
602 assert_eq!(deleted_count, 2); let after_registrations =
605 get_by_completion_id_and_registrar_id(tx.as_mut(), completion.id, registrar_id)
606 .await
607 .unwrap();
608 assert_eq!(after_registrations.len(), 1);
609
610 assert_eq!(after_registrations[0].id, first_id);
612 assert_eq!(after_registrations[0].real_student_number, "12345");
613 }
614
615 #[tokio::test]
616 async fn delete_duplicate_registrations_with_no_duplicates() {
617 insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module);
618
619 let registrar_id = crate::study_registry_registrars::insert(
620 tx.as_mut(),
621 PKeyPolicy::Generate,
622 "Test Registrar",
623 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
624 )
625 .await
626 .unwrap();
627
628 let completion1 = crate::course_module_completions::insert(
629 tx.as_mut(),
630 PKeyPolicy::Generate,
631 &crate::course_module_completions::NewCourseModuleCompletion {
632 course_id: course,
633 course_module_id: course_module.id,
634 user_id: user,
635 completion_date: Utc::now(),
636 completion_registration_attempt_date: None,
637 completion_language: "en-US".to_string(),
638 eligible_for_ects: true,
639 email: "test1@example.com".to_string(),
640 grade: Some(5),
641 passed: true,
642 },
643 CourseModuleCompletionGranter::User(user),
644 )
645 .await
646 .unwrap();
647
648 let completion2 = crate::course_module_completions::insert(
649 tx.as_mut(),
650 PKeyPolicy::Generate,
651 &crate::course_module_completions::NewCourseModuleCompletion {
652 course_id: course,
653 course_module_id: course_module.id,
654 user_id: user,
655 completion_date: Utc::now(),
656 completion_registration_attempt_date: None,
657 completion_language: "en-US".to_string(),
658 eligible_for_ects: true,
659 email: "test2@example.com".to_string(),
660 grade: Some(4),
661 passed: true,
662 },
663 CourseModuleCompletionGranter::User(user),
664 )
665 .await
666 .unwrap();
667
668 let registrations = vec![
670 NewCourseModuleCompletionRegisteredToStudyRegistry {
671 course_id: course,
672 course_module_completion_id: completion1.id,
673 course_module_id: course_module.id,
674 study_registry_registrar_id: registrar_id,
675 user_id: user,
676 real_student_number: "12345".to_string(),
677 },
678 NewCourseModuleCompletionRegisteredToStudyRegistry {
679 course_id: course,
680 course_module_completion_id: completion2.id,
681 course_module_id: course_module.id,
682 study_registry_registrar_id: registrar_id,
683 user_id: user,
684 real_student_number: "67890".to_string(),
685 },
686 ];
687 insert_bulk(tx.as_mut(), registrations).await.unwrap();
688
689 let before_reg1 =
691 get_by_completion_id_and_registrar_id(tx.as_mut(), completion1.id, registrar_id)
692 .await
693 .unwrap();
694 let before_reg2 =
695 get_by_completion_id_and_registrar_id(tx.as_mut(), completion2.id, registrar_id)
696 .await
697 .unwrap();
698 assert_eq!(before_reg1.len(), 1);
699 assert_eq!(before_reg2.len(), 1);
700
701 let deleted_count = delete_all_duplicates(tx.as_mut()).await.unwrap();
703 assert_eq!(deleted_count, 0); let after_reg1 =
707 get_by_completion_id_and_registrar_id(tx.as_mut(), completion1.id, registrar_id)
708 .await
709 .unwrap();
710 let after_reg2 =
711 get_by_completion_id_and_registrar_id(tx.as_mut(), completion2.id, registrar_id)
712 .await
713 .unwrap();
714 assert_eq!(after_reg1.len(), 1);
715 assert_eq!(after_reg2.len(), 1);
716 }
717
718 #[tokio::test]
719 async fn delete_duplicate_registrations_filters_by_completion_id() {
720 insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module);
721
722 let registrar_id = crate::study_registry_registrars::insert(
723 tx.as_mut(),
724 PKeyPolicy::Generate,
725 "Test Registrar",
726 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
727 )
728 .await
729 .unwrap();
730
731 let completion1 = crate::course_module_completions::insert(
733 tx.as_mut(),
734 PKeyPolicy::Generate,
735 &crate::course_module_completions::NewCourseModuleCompletion {
736 course_id: course,
737 course_module_id: course_module.id,
738 user_id: user,
739 completion_date: Utc::now(),
740 completion_registration_attempt_date: None,
741 completion_language: "en-US".to_string(),
742 eligible_for_ects: true,
743 email: "test1@example.com".to_string(),
744 grade: Some(5),
745 passed: true,
746 },
747 CourseModuleCompletionGranter::User(user),
748 )
749 .await
750 .unwrap();
751
752 let completion2 = crate::course_module_completions::insert(
753 tx.as_mut(),
754 PKeyPolicy::Generate,
755 &crate::course_module_completions::NewCourseModuleCompletion {
756 course_id: course,
757 course_module_id: course_module.id,
758 user_id: user,
759 completion_date: Utc::now(),
760 completion_registration_attempt_date: None,
761 completion_language: "en-US".to_string(),
762 eligible_for_ects: true,
763 email: "test2@example.com".to_string(),
764 grade: Some(5),
765 passed: true,
766 },
767 CourseModuleCompletionGranter::User(user),
768 )
769 .await
770 .unwrap();
771
772 let first_registrations = vec![
774 NewCourseModuleCompletionRegisteredToStudyRegistry {
775 course_id: course,
776 course_module_completion_id: completion1.id,
777 course_module_id: course_module.id,
778 study_registry_registrar_id: registrar_id,
779 user_id: user,
780 real_student_number: "12345-1".to_string(),
781 },
782 NewCourseModuleCompletionRegisteredToStudyRegistry {
783 course_id: course,
784 course_module_completion_id: completion2.id,
785 course_module_id: course_module.id,
786 study_registry_registrar_id: registrar_id,
787 user_id: user,
788 real_student_number: "12345-2".to_string(),
789 },
790 ];
791 insert_bulk(tx.as_mut(), first_registrations).await.unwrap();
792
793 let second_registrations = vec![
795 NewCourseModuleCompletionRegisteredToStudyRegistry {
796 course_id: course,
797 course_module_completion_id: completion1.id,
798 course_module_id: course_module.id,
799 study_registry_registrar_id: registrar_id,
800 user_id: user,
801 real_student_number: "67890-1".to_string(),
802 },
803 NewCourseModuleCompletionRegisteredToStudyRegistry {
804 course_id: course,
805 course_module_completion_id: completion2.id,
806 course_module_id: course_module.id,
807 study_registry_registrar_id: registrar_id,
808 user_id: user,
809 real_student_number: "67890-2".to_string(),
810 },
811 ];
812 insert_bulk(tx.as_mut(), second_registrations)
813 .await
814 .unwrap();
815
816 let before_reg1 =
818 get_by_completion_id_and_registrar_id(tx.as_mut(), completion1.id, registrar_id)
819 .await
820 .unwrap();
821 let before_reg2 =
822 get_by_completion_id_and_registrar_id(tx.as_mut(), completion2.id, registrar_id)
823 .await
824 .unwrap();
825 assert_eq!(before_reg1.len(), 2);
826 assert_eq!(before_reg2.len(), 2);
827
828 let deleted_count =
830 delete_duplicates_for_specific_completions(tx.as_mut(), &[completion1.id])
831 .await
832 .unwrap();
833 assert_eq!(deleted_count, 1); let after_reg1 =
837 get_by_completion_id_and_registrar_id(tx.as_mut(), completion1.id, registrar_id)
838 .await
839 .unwrap();
840 let after_reg2 =
841 get_by_completion_id_and_registrar_id(tx.as_mut(), completion2.id, registrar_id)
842 .await
843 .unwrap();
844 assert_eq!(after_reg1.len(), 1);
845 assert_eq!(after_reg2.len(), 2);
846
847 assert_eq!(after_reg1[0].real_student_number, "12345-1");
849 }
850}