//! Tests for entity note injection: the notes of the entities the //! previous turn named, appended to the next turn's system prompt. use super::tests::{dm_for, dm_with_campaign, fake_server, sent_messages, sse_response}; use crate::bus::Speaker; use tempfile::TempDir; /// The preamble's opening words, distinctive enough to prove the whole /// entity notes section is present or absent. const PREAMBLE_MARKER: &str = "the session's notes for the entities"; /// The system prompt of the next request the fake server captured. fn next_system_prompt( requests: &std::sync::mpsc::Receiver, ) -> String { sent_messages(&requests.recv().unwrap())[0]["content"] .as_str() .unwrap() .to_string() } #[test] fn a_second_turns_prompt_carries_the_note_of_an_entity_the_first_turn_named() { let world = TempDir::new().unwrap(); let first_reply = "data: {\"choices\":[{\"delta\":{\"content\":\"You meet [[Vera Blackwater]] at the bar.\"},\"finish_reason\":null}]}\n\n\ data: [DONE]\n\n"; let (url, requests, server) = fake_server(vec![ sse_response(first_reply), sse_response("data: [DONE]\n\n"), ]); let mut dm = dm_with_campaign(url, &world); dm.turn(Speaker::Player, "I enter the tavern.").unwrap(); dm.turn(Speaker::Player, "I greet her.").unwrap(); server.join().unwrap(); requests.recv().unwrap(); let prompt = next_system_prompt(&requests); assert!(prompt.contains("### Vera Blackwater")); assert!(prompt.contains("First seen")); assert!(prompt.contains(PREAMBLE_MARKER)); } #[test] fn two_named_entities_notes_both_reach_the_next_prompt() { let world = TempDir::new().unwrap(); let first_reply = "data: {\"choices\":[{\"delta\":{\"content\":\"[[Vera Blackwater]] nods to [[people/Joseph Black]].\"},\"finish_reason\":null}]}\n\n\ data: [DONE]\n\n"; let (url, requests, server) = fake_server(vec![ sse_response(first_reply), sse_response("data: [DONE]\n\n"), ]); let mut dm = dm_with_campaign(url, &world); dm.turn(Speaker::Player, "I enter the tavern.").unwrap(); dm.turn(Speaker::Player, "I watch them.").unwrap(); server.join().unwrap(); requests.recv().unwrap(); let prompt = next_system_prompt(&requests); assert!(prompt.contains("### Vera Blackwater")); assert!(prompt.contains("### people/Joseph Black")); } #[test] fn a_note_edited_on_disk_between_turns_reaches_the_next_prompt() { let world = TempDir::new().unwrap(); let first_reply = "data: {\"choices\":[{\"delta\":{\"content\":\"You meet [[Vera Blackwater]] at the bar.\"},\"finish_reason\":null}]}\n\n\ data: [DONE]\n\n"; let (url, requests, server) = fake_server(vec![ sse_response(first_reply), sse_response("data: [DONE]\n\n"), ]); let mut dm = dm_with_campaign(url, &world); dm.turn(Speaker::Player, "I enter the tavern.").unwrap(); let stub = world.path().join("entities/Vera Blackwater.md"); std::fs::write( &stub, "---\nkind: person\naliases: []\n---\n\nVera runs the tavern now.\n", ) .unwrap(); dm.turn(Speaker::Player, "I greet her.").unwrap(); server.join().unwrap(); requests.recv().unwrap(); let prompt = next_system_prompt(&requests); assert!(prompt.contains("Vera runs the tavern now.")); assert!(!prompt.contains("First seen")); } #[test] fn an_entity_whose_note_file_is_gone_injects_nothing() { let world = TempDir::new().unwrap(); let first_reply = "data: {\"choices\":[{\"delta\":{\"content\":\"You meet [[Vera Blackwater]] at the bar.\"},\"finish_reason\":null}]}\n\n\ data: [DONE]\n\n"; let (url, requests, server) = fake_server(vec![ sse_response(first_reply), sse_response("data: [DONE]\n\n"), ]); let mut dm = dm_with_campaign(url, &world); dm.turn(Speaker::Player, "I enter the tavern.").unwrap(); std::fs::remove_file(world.path().join("entities/Vera Blackwater.md")).unwrap(); dm.turn(Speaker::Player, "I greet her.").unwrap(); server.join().unwrap(); requests.recv().unwrap(); let prompt = next_system_prompt(&requests); assert!(!prompt.contains("### Vera Blackwater")); assert!(!prompt.contains(PREAMBLE_MARKER)); } #[test] fn a_dm_with_no_campaign_injects_nothing_and_still_turns() { let first_reply = "data: {\"choices\":[{\"delta\":{\"content\":\"You meet [[Vera Blackwater]] at the bar.\"},\"finish_reason\":null}]}\n\n\ data: [DONE]\n\n"; let (url, requests, server) = fake_server(vec![ sse_response(first_reply), sse_response("data: [DONE]\n\n"), ]); let mut dm = dm_for(url); dm.turn(Speaker::Player, "I enter the tavern.").unwrap(); dm.turn(Speaker::Player, "I greet her.").unwrap(); server.join().unwrap(); requests.recv().unwrap(); let prompt = next_system_prompt(&requests); assert!(!prompt.contains("### Vera Blackwater")); assert!(!prompt.contains(PREAMBLE_MARKER)); } #[test] fn a_turn_that_names_nothing_injects_no_preamble() { let world = TempDir::new().unwrap(); let first_reply = "data: {\"choices\":[{\"delta\":{\"content\":\"You walk down the road.\"},\"finish_reason\":null}]}\n\n\ data: [DONE]\n\n"; let (url, requests, server) = fake_server(vec![ sse_response(first_reply), sse_response("data: [DONE]\n\n"), ]); let mut dm = dm_with_campaign(url, &world); dm.turn(Speaker::Player, "I walk.").unwrap(); dm.turn(Speaker::Player, "I keep walking.").unwrap(); server.join().unwrap(); requests.recv().unwrap(); let prompt = next_system_prompt(&requests); assert!(!prompt.contains(PREAMBLE_MARKER)); }