mirror of
https://github.com/Xevion/banner.git
synced 2026-01-31 06:23:37 -06:00
Replace is_creating mutex with atomic flag and RAII guard to ensure proper cleanup when acquire() futures are cancelled mid-creation, preventing permanent deadlock for subsequent callers.
104 lines
3.4 KiB
Rust
104 lines
3.4 KiB
Rust
#[allow(dead_code)]
|
|
mod helpers;
|
|
|
|
use banner::data::rmp::unmatch_instructor;
|
|
use sqlx::PgPool;
|
|
|
|
/// Test that unmatching an instructor resets accepted candidates back to pending.
|
|
///
|
|
/// When a user unmatches an instructor, accepted candidates should be reset to
|
|
/// 'pending' so they can be re-matched later. This prevents the bug where
|
|
/// candidates remain 'accepted' but have no corresponding link.
|
|
#[sqlx::test]
|
|
async fn unmatch_resets_accepted_candidates_to_pending(pool: PgPool) {
|
|
// ARRANGE: Create an instructor
|
|
let (instructor_id,): (i32,) = sqlx::query_as(
|
|
"INSERT INTO instructors (display_name, email)
|
|
VALUES ('Test, Instructor', 'test@utsa.edu')
|
|
RETURNING id",
|
|
)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.expect("failed to create instructor");
|
|
|
|
// ARRANGE: Create an RMP professor
|
|
let (rmp_legacy_id,): (i32,) = sqlx::query_as(
|
|
"INSERT INTO rmp_professors (legacy_id, graphql_id, first_name, last_name, num_ratings)
|
|
VALUES (9999999, 'test-graphql-id', 'Test', 'Professor', 10)
|
|
RETURNING legacy_id",
|
|
)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.expect("failed to create rmp professor");
|
|
|
|
// ARRANGE: Create a match candidate with 'accepted' status
|
|
sqlx::query(
|
|
"INSERT INTO rmp_match_candidates (instructor_id, rmp_legacy_id, score, status)
|
|
VALUES ($1, $2, 0.85, 'accepted')",
|
|
)
|
|
.bind(instructor_id)
|
|
.bind(rmp_legacy_id)
|
|
.execute(&pool)
|
|
.await
|
|
.expect("failed to create candidate");
|
|
|
|
// ARRANGE: Create a link in instructor_rmp_links
|
|
sqlx::query(
|
|
"INSERT INTO instructor_rmp_links (instructor_id, rmp_legacy_id, source)
|
|
VALUES ($1, $2, 'manual')",
|
|
)
|
|
.bind(instructor_id)
|
|
.bind(rmp_legacy_id)
|
|
.execute(&pool)
|
|
.await
|
|
.expect("failed to create link");
|
|
|
|
// ARRANGE: Update instructor status to 'confirmed'
|
|
sqlx::query("UPDATE instructors SET rmp_match_status = 'confirmed' WHERE id = $1")
|
|
.bind(instructor_id)
|
|
.execute(&pool)
|
|
.await
|
|
.expect("failed to update instructor status");
|
|
|
|
// ACT: Unmatch the specific RMP profile
|
|
unmatch_instructor(&pool, instructor_id, Some(rmp_legacy_id))
|
|
.await
|
|
.expect("unmatch should succeed");
|
|
|
|
// ASSERT: Candidate should be reset to pending
|
|
let (candidate_status,): (String,) = sqlx::query_as(
|
|
"SELECT status FROM rmp_match_candidates
|
|
WHERE instructor_id = $1 AND rmp_legacy_id = $2",
|
|
)
|
|
.bind(instructor_id)
|
|
.bind(rmp_legacy_id)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.expect("failed to fetch candidate status");
|
|
assert_eq!(
|
|
candidate_status, "pending",
|
|
"candidate should be reset to pending after unmatch"
|
|
);
|
|
|
|
// ASSERT: Link should be deleted
|
|
let (link_count,): (i64,) =
|
|
sqlx::query_as("SELECT COUNT(*) FROM instructor_rmp_links WHERE instructor_id = $1")
|
|
.bind(instructor_id)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.expect("failed to count links");
|
|
assert_eq!(link_count, 0, "link should be deleted");
|
|
|
|
// ASSERT: Instructor status should be unmatched
|
|
let (instructor_status,): (String,) =
|
|
sqlx::query_as("SELECT rmp_match_status FROM instructors WHERE id = $1")
|
|
.bind(instructor_id)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.expect("failed to fetch instructor status");
|
|
assert_eq!(
|
|
instructor_status, "unmatched",
|
|
"instructor should be unmatched"
|
|
);
|
|
}
|