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
238AND deleted_at IS NULL
239 ",
240 id
241 )
242 .execute(conn)
243 .await?;
244 Ok(())
245}
246
247pub async fn get_count_of_distinct_users_with_registrations_by_course_id(
249 conn: &mut PgConnection,
250 course_id: Uuid,
251) -> ModelResult<i64> {
252 let res = sqlx::query!(
253 "
254SELECT COUNT(DISTINCT user_id) as count
255FROM course_module_completion_registered_to_study_registries
256WHERE course_id = $1
257 AND deleted_at IS NULL
258",
259 course_id,
260 )
261 .fetch_one(conn)
262 .await?;
263 Ok(res.count.unwrap_or(0))
264}
265
266pub async fn get_by_completion_id_and_registrar_id(
267 conn: &mut PgConnection,
268 completion_id: Uuid,
269 study_registry_registrar_id: Uuid,
270) -> ModelResult<Vec<CourseModuleCompletionRegisteredToStudyRegistry>> {
271 let registrations = sqlx::query_as!(
272 CourseModuleCompletionRegisteredToStudyRegistry,
273 r#"
274 SELECT *
275 FROM course_module_completion_registered_to_study_registries
276 WHERE course_module_completion_id = $1 AND study_registry_registrar_id = $2
277 AND deleted_at IS NULL
278 "#,
279 completion_id,
280 study_registry_registrar_id
281 )
282 .fetch_all(conn)
283 .await?;
284
285 Ok(registrations)
286}
287
288async fn delete_duplicates_for_specific_completions(
289 conn: &mut PgConnection,
290 completion_ids: &[Uuid],
291) -> ModelResult<i64> {
292 let res = sqlx::query!(
293 r#"
294WITH duplicate_rows AS (
295 SELECT id,
296 ROW_NUMBER() OVER (
297 PARTITION BY course_module_completion_id
298 ORDER BY created_at ASC -- Keep the oldest, delete the rest
299 ) AS rn
300 FROM course_module_completion_registered_to_study_registries
301 WHERE deleted_at IS NULL
302 AND course_module_completion_id = ANY($1)
303)
304UPDATE course_module_completion_registered_to_study_registries
305SET deleted_at = NOW()
306WHERE id IN (
307 SELECT id
308 FROM duplicate_rows
309 WHERE rn > 1
310 )
311RETURNING id
312 "#,
313 completion_ids,
314 )
315 .fetch_all(conn)
316 .await?;
317
318 Ok(res.len() as i64)
319}
320
321async fn delete_all_duplicates(conn: &mut PgConnection) -> ModelResult<i64> {
322 let res = sqlx::query!(
323 r#"
324WITH duplicate_rows AS (
325 SELECT id,
326 ROW_NUMBER() OVER (
327 PARTITION BY course_module_completion_id
328 ORDER BY created_at ASC -- Keep the oldest, delete the rest
329 ) AS rn
330 FROM course_module_completion_registered_to_study_registries
331 WHERE deleted_at IS NULL
332)
333UPDATE course_module_completion_registered_to_study_registries
334SET deleted_at = NOW()
335WHERE id IN (
336 SELECT id
337 FROM duplicate_rows
338 WHERE rn > 1
339 )
340RETURNING id
341 "#
342 )
343 .fetch_all(conn)
344 .await?;
345
346 Ok(res.len() as i64)
347}
348
349#[cfg(test)]
350mod test {
351 use super::*;
352 use crate::{course_module_completions::CourseModuleCompletionGranter, test_helper::*};
353
354 #[tokio::test]
355 async fn bulk_insert_works() {
356 insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module);
357
358 let registrar_id = crate::study_registry_registrars::insert(
359 tx.as_mut(),
360 PKeyPolicy::Generate,
361 "Test Registrar",
362 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
363 )
364 .await
365 .unwrap();
366
367 let new_completion_id = crate::course_module_completions::insert(
368 tx.as_mut(),
369 PKeyPolicy::Generate,
370 &crate::course_module_completions::NewCourseModuleCompletion {
371 course_id: course,
372 course_module_id: course_module.id,
373 user_id: user,
374 completion_date: Utc::now(),
375 completion_registration_attempt_date: None,
376 completion_language: "en-US".to_string(),
377 eligible_for_ects: true,
378 email: "test@example.com".to_string(),
379 grade: Some(10),
380 passed: true,
381 },
382 CourseModuleCompletionGranter::User(user),
383 )
384 .await
385 .unwrap();
386
387 let registrations = vec![
388 NewCourseModuleCompletionRegisteredToStudyRegistry {
389 course_id: course,
390 course_module_completion_id: new_completion_id.id,
391 course_module_id: course_module.id,
392 study_registry_registrar_id: registrar_id,
393 user_id: user,
394 real_student_number: "12345".to_string(),
395 },
396 NewCourseModuleCompletionRegisteredToStudyRegistry {
397 course_id: course,
398 course_module_completion_id: new_completion_id.id,
399 course_module_id: course_module.id,
400 study_registry_registrar_id: registrar_id,
401 user_id: user,
402 real_student_number: "67890".to_string(),
403 },
404 ];
405
406 let inserted_ids = insert_bulk(tx.as_mut(), registrations).await.unwrap();
407 assert_eq!(inserted_ids.len(), 2);
408
409 for id in inserted_ids {
411 let registration = get_by_id(tx.as_mut(), id).await.unwrap();
412 assert_eq!(registration.course_id, course);
413 assert_eq!(
414 registration.course_module_completion_id,
415 new_completion_id.id
416 );
417 assert_eq!(registration.course_module_id, course_module.id);
418 assert_eq!(registration.study_registry_registrar_id, registrar_id);
419 assert_eq!(registration.user_id, user);
420 }
421 }
422
423 #[tokio::test]
424 async fn bulk_insert_empty_vec_works() {
425 insert_data!(:tx);
426
427 let empty_vec = vec![];
428 let result = insert_bulk(tx.as_mut(), empty_vec).await.unwrap();
429 assert!(result.is_empty());
430 }
431
432 #[tokio::test]
433 async fn insert_completions_works() {
434 insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module);
435
436 let registrar_id = crate::study_registry_registrars::insert(
437 tx.as_mut(),
438 PKeyPolicy::Generate,
439 "Test Registrar",
440 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
441 )
442 .await
443 .unwrap();
444
445 let completion = crate::course_module_completions::insert(
446 tx.as_mut(),
447 PKeyPolicy::Generate,
448 &crate::course_module_completions::NewCourseModuleCompletion {
449 course_id: course,
450 course_module_id: course_module.id,
451 user_id: user,
452 completion_date: Utc::now(),
453 completion_registration_attempt_date: None,
454 completion_language: "en-US".to_string(),
455 eligible_for_ects: true,
456 email: "test@example.com".to_string(),
457 grade: Some(5),
458 passed: true,
459 },
460 CourseModuleCompletionGranter::User(user),
461 )
462 .await
463 .unwrap();
464
465 let registered_completions = vec![RegisteredCompletion {
466 completion_id: completion.id,
467 student_number: "12345".to_string(),
468 registration_date: Utc::now(),
469 }];
470
471 mark_completions_as_registered_to_study_registry(
472 tx.as_mut(),
473 registered_completions,
474 registrar_id,
475 )
476 .await
477 .unwrap();
478
479 let registrations =
480 get_by_completion_id_and_registrar_id(tx.as_mut(), completion.id, registrar_id)
481 .await
482 .unwrap();
483
484 assert_eq!(registrations.len(), 1);
485 assert_eq!(registrations[0].course_id, course);
486 assert_eq!(registrations[0].course_module_id, course_module.id);
487 assert_eq!(registrations[0].user_id, user);
488 assert_eq!(registrations[0].real_student_number, "12345");
489 }
490
491 #[tokio::test]
492 async fn insert_completions_with_invalid_completion_id_fails() {
493 insert_data!(:tx);
494
495 let registrar_id = crate::study_registry_registrars::insert(
496 tx.as_mut(),
497 PKeyPolicy::Generate,
498 "Test Registrar",
499 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
500 )
501 .await
502 .unwrap();
503
504 let invalid_uuid = Uuid::new_v4(); let registered_completions = vec![RegisteredCompletion {
506 completion_id: invalid_uuid,
507 student_number: "12345".to_string(),
508 registration_date: Utc::now(),
509 }];
510
511 let result = mark_completions_as_registered_to_study_registry(
513 tx.as_mut(),
514 registered_completions,
515 registrar_id,
516 )
517 .await;
518
519 assert!(result.is_err());
520 let error = result.unwrap_err();
521 assert_eq!(*error.error_type(), ModelErrorType::PreconditionFailed);
522 assert!(error.message().contains("Cannot find completion with id"));
523 assert!(error.message().contains(&invalid_uuid.to_string()));
524 }
525
526 #[tokio::test]
527 async fn delete_duplicate_registrations_works() {
528 insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module);
529
530 let registrar_id = crate::study_registry_registrars::insert(
531 tx.as_mut(),
532 PKeyPolicy::Generate,
533 "Test Registrar",
534 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
535 )
536 .await
537 .unwrap();
538
539 let completion = crate::course_module_completions::insert(
540 tx.as_mut(),
541 PKeyPolicy::Generate,
542 &crate::course_module_completions::NewCourseModuleCompletion {
543 course_id: course,
544 course_module_id: course_module.id,
545 user_id: user,
546 completion_date: Utc::now(),
547 completion_registration_attempt_date: None,
548 completion_language: "en-US".to_string(),
549 eligible_for_ects: true,
550 email: "test@example.com".to_string(),
551 grade: Some(5),
552 passed: true,
553 },
554 CourseModuleCompletionGranter::User(user),
555 )
556 .await
557 .unwrap();
558
559 let first_registration = NewCourseModuleCompletionRegisteredToStudyRegistry {
561 course_id: course,
562 course_module_completion_id: completion.id,
563 course_module_id: course_module.id,
564 study_registry_registrar_id: registrar_id,
565 user_id: user,
566 real_student_number: "12345".to_string(),
567 };
568 let first_id = insert(tx.as_mut(), PKeyPolicy::Generate, &first_registration)
569 .await
570 .unwrap();
571
572 let later_registrations = vec![
574 NewCourseModuleCompletionRegisteredToStudyRegistry {
575 course_id: course,
576 course_module_completion_id: completion.id,
577 course_module_id: course_module.id,
578 study_registry_registrar_id: registrar_id,
579 user_id: user,
580 real_student_number: "67890".to_string(),
581 },
582 NewCourseModuleCompletionRegisteredToStudyRegistry {
583 course_id: course,
584 course_module_completion_id: completion.id,
585 course_module_id: course_module.id,
586 study_registry_registrar_id: registrar_id,
587 user_id: user,
588 real_student_number: "54321".to_string(),
589 },
590 ];
591 insert_bulk(tx.as_mut(), later_registrations).await.unwrap();
592
593 let before_registrations =
594 get_by_completion_id_and_registrar_id(tx.as_mut(), completion.id, registrar_id)
595 .await
596 .unwrap();
597 assert_eq!(before_registrations.len(), 3);
598
599 let deleted_count =
600 delete_duplicates_for_specific_completions(tx.as_mut(), &[completion.id])
601 .await
602 .unwrap();
603 assert_eq!(deleted_count, 2); let after_registrations =
606 get_by_completion_id_and_registrar_id(tx.as_mut(), completion.id, registrar_id)
607 .await
608 .unwrap();
609 assert_eq!(after_registrations.len(), 1);
610
611 assert_eq!(after_registrations[0].id, first_id);
613 assert_eq!(after_registrations[0].real_student_number, "12345");
614 }
615
616 #[tokio::test]
617 async fn delete_duplicate_registrations_with_no_duplicates() {
618 insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module);
619
620 let registrar_id = crate::study_registry_registrars::insert(
621 tx.as_mut(),
622 PKeyPolicy::Generate,
623 "Test Registrar",
624 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
625 )
626 .await
627 .unwrap();
628
629 let completion1 = crate::course_module_completions::insert(
630 tx.as_mut(),
631 PKeyPolicy::Generate,
632 &crate::course_module_completions::NewCourseModuleCompletion {
633 course_id: course,
634 course_module_id: course_module.id,
635 user_id: user,
636 completion_date: Utc::now(),
637 completion_registration_attempt_date: None,
638 completion_language: "en-US".to_string(),
639 eligible_for_ects: true,
640 email: "test1@example.com".to_string(),
641 grade: Some(5),
642 passed: true,
643 },
644 CourseModuleCompletionGranter::User(user),
645 )
646 .await
647 .unwrap();
648
649 let completion2 = crate::course_module_completions::insert(
650 tx.as_mut(),
651 PKeyPolicy::Generate,
652 &crate::course_module_completions::NewCourseModuleCompletion {
653 course_id: course,
654 course_module_id: course_module.id,
655 user_id: user,
656 completion_date: Utc::now(),
657 completion_registration_attempt_date: None,
658 completion_language: "en-US".to_string(),
659 eligible_for_ects: true,
660 email: "test2@example.com".to_string(),
661 grade: Some(4),
662 passed: true,
663 },
664 CourseModuleCompletionGranter::User(user),
665 )
666 .await
667 .unwrap();
668
669 let registrations = vec![
671 NewCourseModuleCompletionRegisteredToStudyRegistry {
672 course_id: course,
673 course_module_completion_id: completion1.id,
674 course_module_id: course_module.id,
675 study_registry_registrar_id: registrar_id,
676 user_id: user,
677 real_student_number: "12345".to_string(),
678 },
679 NewCourseModuleCompletionRegisteredToStudyRegistry {
680 course_id: course,
681 course_module_completion_id: completion2.id,
682 course_module_id: course_module.id,
683 study_registry_registrar_id: registrar_id,
684 user_id: user,
685 real_student_number: "67890".to_string(),
686 },
687 ];
688 insert_bulk(tx.as_mut(), registrations).await.unwrap();
689
690 let before_reg1 =
692 get_by_completion_id_and_registrar_id(tx.as_mut(), completion1.id, registrar_id)
693 .await
694 .unwrap();
695 let before_reg2 =
696 get_by_completion_id_and_registrar_id(tx.as_mut(), completion2.id, registrar_id)
697 .await
698 .unwrap();
699 assert_eq!(before_reg1.len(), 1);
700 assert_eq!(before_reg2.len(), 1);
701
702 let deleted_count = delete_all_duplicates(tx.as_mut()).await.unwrap();
704 assert_eq!(deleted_count, 0); let after_reg1 =
708 get_by_completion_id_and_registrar_id(tx.as_mut(), completion1.id, registrar_id)
709 .await
710 .unwrap();
711 let after_reg2 =
712 get_by_completion_id_and_registrar_id(tx.as_mut(), completion2.id, registrar_id)
713 .await
714 .unwrap();
715 assert_eq!(after_reg1.len(), 1);
716 assert_eq!(after_reg2.len(), 1);
717 }
718
719 #[tokio::test]
720 async fn delete_duplicate_registrations_filters_by_completion_id() {
721 insert_data!(:tx, :user, :org, :course, instance: _instance, :course_module);
722
723 let registrar_id = crate::study_registry_registrars::insert(
724 tx.as_mut(),
725 PKeyPolicy::Generate,
726 "Test Registrar",
727 "test_123131231231231231231231231231238971283718927389172893718923712893129837189273891278317892378193971289",
728 )
729 .await
730 .unwrap();
731
732 let completion1 = crate::course_module_completions::insert(
734 tx.as_mut(),
735 PKeyPolicy::Generate,
736 &crate::course_module_completions::NewCourseModuleCompletion {
737 course_id: course,
738 course_module_id: course_module.id,
739 user_id: user,
740 completion_date: Utc::now(),
741 completion_registration_attempt_date: None,
742 completion_language: "en-US".to_string(),
743 eligible_for_ects: true,
744 email: "test1@example.com".to_string(),
745 grade: Some(5),
746 passed: true,
747 },
748 CourseModuleCompletionGranter::User(user),
749 )
750 .await
751 .unwrap();
752
753 let completion2 = crate::course_module_completions::insert(
754 tx.as_mut(),
755 PKeyPolicy::Generate,
756 &crate::course_module_completions::NewCourseModuleCompletion {
757 course_id: course,
758 course_module_id: course_module.id,
759 user_id: user,
760 completion_date: Utc::now(),
761 completion_registration_attempt_date: None,
762 completion_language: "en-US".to_string(),
763 eligible_for_ects: true,
764 email: "test2@example.com".to_string(),
765 grade: Some(5),
766 passed: true,
767 },
768 CourseModuleCompletionGranter::User(user),
769 )
770 .await
771 .unwrap();
772
773 let first_registrations = vec![
775 NewCourseModuleCompletionRegisteredToStudyRegistry {
776 course_id: course,
777 course_module_completion_id: completion1.id,
778 course_module_id: course_module.id,
779 study_registry_registrar_id: registrar_id,
780 user_id: user,
781 real_student_number: "12345-1".to_string(),
782 },
783 NewCourseModuleCompletionRegisteredToStudyRegistry {
784 course_id: course,
785 course_module_completion_id: completion2.id,
786 course_module_id: course_module.id,
787 study_registry_registrar_id: registrar_id,
788 user_id: user,
789 real_student_number: "12345-2".to_string(),
790 },
791 ];
792 insert_bulk(tx.as_mut(), first_registrations).await.unwrap();
793
794 let second_registrations = vec![
796 NewCourseModuleCompletionRegisteredToStudyRegistry {
797 course_id: course,
798 course_module_completion_id: completion1.id,
799 course_module_id: course_module.id,
800 study_registry_registrar_id: registrar_id,
801 user_id: user,
802 real_student_number: "67890-1".to_string(),
803 },
804 NewCourseModuleCompletionRegisteredToStudyRegistry {
805 course_id: course,
806 course_module_completion_id: completion2.id,
807 course_module_id: course_module.id,
808 study_registry_registrar_id: registrar_id,
809 user_id: user,
810 real_student_number: "67890-2".to_string(),
811 },
812 ];
813 insert_bulk(tx.as_mut(), second_registrations)
814 .await
815 .unwrap();
816
817 let before_reg1 =
819 get_by_completion_id_and_registrar_id(tx.as_mut(), completion1.id, registrar_id)
820 .await
821 .unwrap();
822 let before_reg2 =
823 get_by_completion_id_and_registrar_id(tx.as_mut(), completion2.id, registrar_id)
824 .await
825 .unwrap();
826 assert_eq!(before_reg1.len(), 2);
827 assert_eq!(before_reg2.len(), 2);
828
829 let deleted_count =
831 delete_duplicates_for_specific_completions(tx.as_mut(), &[completion1.id])
832 .await
833 .unwrap();
834 assert_eq!(deleted_count, 1); let after_reg1 =
838 get_by_completion_id_and_registrar_id(tx.as_mut(), completion1.id, registrar_id)
839 .await
840 .unwrap();
841 let after_reg2 =
842 get_by_completion_id_and_registrar_id(tx.as_mut(), completion2.id, registrar_id)
843 .await
844 .unwrap();
845 assert_eq!(after_reg1.len(), 1);
846 assert_eq!(after_reg2.len(), 2);
847
848 assert_eq!(after_reg1[0].real_student_number, "12345-1");
850 }
851}