diff --git a/src-tauri/tests/calendar_link_smoke_test.rs b/src-tauri/tests/calendar_link_smoke_test.rs new file mode 100644 index 0000000..8ad092c --- /dev/null +++ b/src-tauri/tests/calendar_link_smoke_test.rs @@ -0,0 +1,98 @@ +use whispassist_lib::models::{AttendeeInfo, CalendarEvent, ImportedEvent}; +use whispassist_lib::storage::{NewMeeting, SqliteStore, Store}; + +fn fresh_store_env() { + std::env::set_var( + "LOCALAPPDATA", + std::env::temp_dir().join(format!("wa-linktest-{}", uuid::Uuid::new_v4())), + ); +} + +#[tokio::test] +async fn attach_meeting_to_event_and_map_speaker_to_participant_round_trip() { + fresh_store_env(); + let store = SqliteStore::connect().await.unwrap(); + + // Import one event with one attendee (Alice). + let imported = store + .import_calendar_events(vec![ImportedEvent { + event: CalendarEvent { + id: uuid::Uuid::new_v4().to_string(), + source: "pst".to_string(), + subject: Some("Sprint planning".to_string()), + organizer: Some("Jordan Lee".to_string()), + starts_at: Some(1_782_896_400), + ends_at: Some(1_782_900_000), + description: None, + raw_uid: Some("evt-1".to_string()), + }, + attendees: vec![AttendeeInfo { + name: "Alice".to_string(), + email: Some("alice@example.com".to_string()), + role: Some("required".to_string()), + }], + }]) + .await + .unwrap(); + assert_eq!(imported, 1); + + // list_calendar_events surfaces it. + let events = store.list_calendar_events(None, None).await.unwrap(); + assert_eq!(events.len(), 1); + let event_id = events[0].id.clone(); + assert_eq!(events[0].subject.as_deref(), Some("Sprint planning")); + + // get_calendar_event returns the attendee. + let detail = store.get_calendar_event(&event_id).await.unwrap(); + assert_eq!(detail.participants.len(), 1); + assert_eq!(detail.participants[0].name, "Alice"); + let alice_id = detail.participants[0].id.clone(); + + // attach_meeting_to_event errs on an unknown event id. + let meeting_id = store + .create_meeting(NewMeeting { + title: "Test meeting".to_string(), + calendar_event_id: None, + }) + .await + .unwrap(); + assert!(store + .attach_meeting_to_event(&meeting_id, "no-such-event") + .await + .is_err()); + + // ... and succeeds + is reflected in get_meeting for a real one. + store + .attach_meeting_to_event(&meeting_id, &event_id) + .await + .unwrap(); + let meeting = store.get_meeting(&meeting_id).await.unwrap(); + assert_eq!( + meeting.calendar_event_id.as_deref(), + Some(event_id.as_str()) + ); + + // map_speaker_to_participant sets both display_name and participant_id. + store + .map_speaker_to_participant(&meeting_id, "S1", &alice_id) + .await + .unwrap(); + let meeting = store.get_meeting(&meeting_id).await.unwrap(); + let s1 = meeting.speakers.iter().find(|s| s.label == "S1").unwrap(); + assert_eq!(s1.display_name.as_deref(), Some("Alice")); + assert_eq!(s1.participant_id.as_deref(), Some(alice_id.as_str())); + + // A later free-text rename must NOT silently unlink the participant. + store + .rename_speaker(&meeting_id, "S1", "Bob") + .await + .unwrap(); + let meeting = store.get_meeting(&meeting_id).await.unwrap(); + let s1 = meeting.speakers.iter().find(|s| s.label == "S1").unwrap(); + assert_eq!(s1.display_name.as_deref(), Some("Bob")); + assert_eq!( + s1.participant_id.as_deref(), + Some(alice_id.as_str()), + "rename_speaker must preserve an existing participant_id link" + ); +}