Fixed some bot tests.
This commit is contained in:
@@ -1,21 +0,0 @@
|
|||||||
---
|
|
||||||
name: "Show server logs in web UI"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Story 292: Show server logs in web UI
|
|
||||||
|
|
||||||
## User Story
|
|
||||||
|
|
||||||
As a project owner using the web UI, I want to see live server logs in the interface, so that I can debug agent behavior and pipeline issues without needing terminal access.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ] Web UI has a server logs panel accessible from the main interface
|
|
||||||
- [ ] Logs stream in real-time via WebSocket or SSE
|
|
||||||
- [ ] Logs can be filtered by keyword (same as get_server_logs MCP tool's filter param)
|
|
||||||
- [ ] Log entries show timestamp and severity level
|
|
||||||
- [ ] Panel doesn't interfere with the existing pipeline board and work item views
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
|
|
||||||
- TBD
|
|
||||||
34
Cargo.lock
generated
34
Cargo.lock
generated
@@ -4026,7 +4026,7 @@ dependencies = [
|
|||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite 0.29.0",
|
"tokio-tungstenite 0.29.0",
|
||||||
"toml 1.0.6+spec-1.1.0",
|
"toml 1.0.7+spec-1.1.0",
|
||||||
"uuid",
|
"uuid",
|
||||||
"wait-timeout",
|
"wait-timeout",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
@@ -4367,22 +4367,22 @@ dependencies = [
|
|||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime 0.7.5+spec-1.1.0",
|
"toml_datetime 0.7.5+spec-1.1.0",
|
||||||
"toml_parser",
|
"toml_parser",
|
||||||
"winnow",
|
"winnow 0.7.14",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "1.0.6+spec-1.1.0"
|
version = "1.0.7+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "399b1124a3c9e16766831c6bba21e50192572cdd98706ea114f9502509686ffc"
|
checksum = "dd28d57d8a6f6e458bc0b8784f8fdcc4b99a437936056fa122cb234f18656a96"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime 1.0.0+spec-1.1.0",
|
"toml_datetime 1.0.1+spec-1.1.0",
|
||||||
"toml_parser",
|
"toml_parser",
|
||||||
"toml_writer",
|
"toml_writer",
|
||||||
"winnow",
|
"winnow 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4396,9 +4396,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "1.0.0+spec-1.1.0"
|
version = "1.0.1+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
|
checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
@@ -4412,23 +4412,23 @@ dependencies = [
|
|||||||
"indexmap",
|
"indexmap",
|
||||||
"toml_datetime 0.7.5+spec-1.1.0",
|
"toml_datetime 0.7.5+spec-1.1.0",
|
||||||
"toml_parser",
|
"toml_parser",
|
||||||
"winnow",
|
"winnow 0.7.14",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_parser"
|
name = "toml_parser"
|
||||||
version = "1.0.9+spec-1.1.0"
|
version = "1.0.10+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
|
checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winnow",
|
"winnow 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_writer"
|
name = "toml_writer"
|
||||||
version = "1.0.6+spec-1.1.0"
|
version = "1.0.7+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
@@ -5444,6 +5444,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
|
|||||||
@@ -103,11 +103,7 @@ pub fn load_history(project_root: &std::path::Path) -> HashMap<OwnedRoomId, Room
|
|||||||
persisted
|
persisted
|
||||||
.rooms
|
.rooms
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(k, v)| {
|
.filter_map(|(k, v)| k.parse::<OwnedRoomId>().ok().map(|room_id| (room_id, v)))
|
||||||
k.parse::<OwnedRoomId>()
|
|
||||||
.ok()
|
|
||||||
.map(|room_id| (room_id, v))
|
|
||||||
})
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,10 +233,7 @@ fn read_stage_items(
|
|||||||
project_root: &std::path::Path,
|
project_root: &std::path::Path,
|
||||||
stage_dir: &str,
|
stage_dir: &str,
|
||||||
) -> Vec<(String, Option<String>)> {
|
) -> Vec<(String, Option<String>)> {
|
||||||
let dir = project_root
|
let dir = project_root.join(".story_kit").join("work").join(stage_dir);
|
||||||
.join(".story_kit")
|
|
||||||
.join("work")
|
|
||||||
.join(stage_dir);
|
|
||||||
if !dir.exists() {
|
if !dir.exists() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
@@ -252,13 +245,11 @@ fn read_stage_items(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
|
if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
|
||||||
let name = std::fs::read_to_string(&path)
|
let name = std::fs::read_to_string(&path).ok().and_then(|contents| {
|
||||||
.ok()
|
crate::io::story_metadata::parse_front_matter(&contents)
|
||||||
.and_then(|contents| {
|
.ok()
|
||||||
crate::io::story_metadata::parse_front_matter(&contents)
|
.and_then(|m| m.name)
|
||||||
.ok()
|
});
|
||||||
.and_then(|m| m.name)
|
|
||||||
});
|
|
||||||
items.push((stem.to_string(), name));
|
items.push((stem.to_string(), name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -408,7 +399,10 @@ pub async fn run_bot(
|
|||||||
.ok_or_else(|| "No user ID after login".to_string())?
|
.ok_or_else(|| "No user ID after login".to_string())?
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
slog!("[matrix-bot] Logged in as {bot_user_id} (device: {})", login_response.device_id);
|
slog!(
|
||||||
|
"[matrix-bot] Logged in as {bot_user_id} (device: {})",
|
||||||
|
login_response.device_id
|
||||||
|
);
|
||||||
|
|
||||||
// Bootstrap cross-signing keys for E2EE verification support.
|
// Bootstrap cross-signing keys for E2EE verification support.
|
||||||
// Pass the bot's password for UIA (User-Interactive Authentication) —
|
// Pass the bot's password for UIA (User-Interactive Authentication) —
|
||||||
@@ -540,7 +534,9 @@ pub async fn run_bot(
|
|||||||
agents,
|
agents,
|
||||||
};
|
};
|
||||||
|
|
||||||
slog!("[matrix-bot] Cryptographic identity verification is always ON — commands from unencrypted rooms or unverified devices are rejected");
|
slog!(
|
||||||
|
"[matrix-bot] Cryptographic identity verification is always ON — commands from unencrypted rooms or unverified devices are rejected"
|
||||||
|
);
|
||||||
|
|
||||||
// Register event handlers and inject shared context.
|
// Register event handlers and inject shared context.
|
||||||
client.add_event_handler_context(ctx);
|
client.add_event_handler_context(ctx);
|
||||||
@@ -679,9 +675,7 @@ pub fn parse_ambient_command(
|
|||||||
// Strip a leading @mention (handles "@localpart" and "@localpart:server").
|
// Strip a leading @mention (handles "@localpart" and "@localpart:server").
|
||||||
let rest = if let Some(after_at) = lower.strip_prefix('@') {
|
let rest = if let Some(after_at) = lower.strip_prefix('@') {
|
||||||
// Skip everything up to the first whitespace (the full mention token).
|
// Skip everything up to the first whitespace (the full mention token).
|
||||||
let word_end = after_at
|
let word_end = after_at.find(char::is_whitespace).unwrap_or(after_at.len());
|
||||||
.find(char::is_whitespace)
|
|
||||||
.unwrap_or(after_at.len());
|
|
||||||
after_at[word_end..].trim()
|
after_at[word_end..].trim()
|
||||||
} else if let Some(after) = lower.strip_prefix(display_lower.as_str()) {
|
} else if let Some(after) = lower.strip_prefix(display_lower.as_str()) {
|
||||||
after.trim()
|
after.trim()
|
||||||
@@ -734,10 +728,7 @@ async fn is_reply_to_bot(
|
|||||||
/// is the correct trust model: a user is accepted when they have cross-signing
|
/// is the correct trust model: a user is accepted when they have cross-signing
|
||||||
/// configured, regardless of whether the bot has run an explicit verification
|
/// configured, regardless of whether the bot has run an explicit verification
|
||||||
/// ceremony with a specific device.
|
/// ceremony with a specific device.
|
||||||
async fn check_sender_verified(
|
async fn check_sender_verified(client: &Client, sender: &OwnedUserId) -> Result<bool, String> {
|
||||||
client: &Client,
|
|
||||||
sender: &OwnedUserId,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
let identity = client
|
let identity = client
|
||||||
.encryption()
|
.encryption()
|
||||||
.get_user_identity(sender)
|
.get_user_identity(sender)
|
||||||
@@ -803,8 +794,9 @@ async fn on_to_device_verification_request(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
VerificationRequestState::Done
|
VerificationRequestState::Done | VerificationRequestState::Cancelled(_) => {
|
||||||
| VerificationRequestState::Cancelled(_) => break,
|
break;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1022,11 +1014,9 @@ async fn on_room_message(
|
|||||||
slog!("[matrix-bot] Message from {sender}: {user_message}");
|
slog!("[matrix-bot] Message from {sender}: {user_message}");
|
||||||
|
|
||||||
// Check for bot-level commands (e.g. "help") before invoking the LLM.
|
// Check for bot-level commands (e.g. "help") before invoking the LLM.
|
||||||
if let Some(response) = super::commands::try_handle_command(
|
if let Some(response) =
|
||||||
&ctx.bot_name,
|
super::commands::try_handle_command(&ctx.bot_name, ctx.bot_user_id.as_str(), &user_message)
|
||||||
ctx.bot_user_id.as_str(),
|
{
|
||||||
&user_message,
|
|
||||||
) {
|
|
||||||
slog!("[matrix-bot] Handled bot command from {sender}");
|
slog!("[matrix-bot] Handled bot command from {sender}");
|
||||||
let html = markdown_to_html(&response);
|
let html = markdown_to_html(&response);
|
||||||
if let Ok(resp) = room
|
if let Ok(resp) = room
|
||||||
@@ -1083,9 +1073,7 @@ async fn handle_message(
|
|||||||
// flattening history into a text prefix.
|
// flattening history into a text prefix.
|
||||||
let resume_session_id: Option<String> = {
|
let resume_session_id: Option<String> = {
|
||||||
let guard = ctx.history.lock().await;
|
let guard = ctx.history.lock().await;
|
||||||
guard
|
guard.get(&room_id).and_then(|conv| conv.session_id.clone())
|
||||||
.get(&room_id)
|
|
||||||
.and_then(|conv| conv.session_id.clone())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// The prompt is just the current message with sender attribution.
|
// The prompt is just the current message with sender attribution.
|
||||||
@@ -1260,7 +1248,11 @@ async fn handle_message(
|
|||||||
let conv = guard.entry(room_id).or_default();
|
let conv = guard.entry(room_id).or_default();
|
||||||
|
|
||||||
// Store the session ID so the next turn uses --resume.
|
// Store the session ID so the next turn uses --resume.
|
||||||
slog!("[matrix-bot] storing session_id: {:?} (was: {:?})", new_session_id, conv.session_id);
|
slog!(
|
||||||
|
"[matrix-bot] storing session_id: {:?} (was: {:?})",
|
||||||
|
new_session_id,
|
||||||
|
conv.session_id
|
||||||
|
);
|
||||||
if new_session_id.is_some() {
|
if new_session_id.is_some() {
|
||||||
conv.session_id = new_session_id;
|
conv.session_id = new_session_id;
|
||||||
}
|
}
|
||||||
@@ -2017,7 +2009,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn startup_announcement_uses_configured_display_name_not_hardcoded() {
|
fn startup_announcement_uses_configured_display_name_not_hardcoded() {
|
||||||
assert_eq!(format_startup_announcement("HAL"), "HAL is online.");
|
assert_eq!(format_startup_announcement("HAL"), "HAL is online.");
|
||||||
assert_eq!(format_startup_announcement("Assistant"), "Assistant is online.");
|
assert_eq!(
|
||||||
|
format_startup_announcement("Assistant"),
|
||||||
|
"Assistant is online."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- extract_command (status trigger) ------------------------------------
|
// -- extract_command (status trigger) ------------------------------------
|
||||||
@@ -2072,7 +2067,7 @@ mod tests {
|
|||||||
let pool = AgentPool::new_test(3001);
|
let pool = AgentPool::new_test(3001);
|
||||||
let out = build_pipeline_status(dir.path(), &pool);
|
let out = build_pipeline_status(dir.path(), &pool);
|
||||||
|
|
||||||
assert!(out.contains("Upcoming"), "missing Upcoming: {out}");
|
assert!(out.contains("Backlog"), "missing Backlog: {out}");
|
||||||
assert!(out.contains("In Progress"), "missing In Progress: {out}");
|
assert!(out.contains("In Progress"), "missing In Progress: {out}");
|
||||||
assert!(out.contains("QA"), "missing QA: {out}");
|
assert!(out.contains("QA"), "missing QA: {out}");
|
||||||
assert!(out.contains("Merge"), "missing Merge: {out}");
|
assert!(out.contains("Merge"), "missing Merge: {out}");
|
||||||
@@ -2104,7 +2099,10 @@ mod tests {
|
|||||||
let pool = AgentPool::new_test(3001);
|
let pool = AgentPool::new_test(3001);
|
||||||
let out = build_pipeline_status(dir.path(), &pool);
|
let out = build_pipeline_status(dir.path(), &pool);
|
||||||
|
|
||||||
assert!(out.contains("Free Agents"), "missing Free Agents section: {out}");
|
assert!(
|
||||||
|
out.contains("Free Agents"),
|
||||||
|
"missing Free Agents section: {out}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2114,8 +2112,11 @@ mod tests {
|
|||||||
let out = build_pipeline_status(dir.path(), &pool);
|
let out = build_pipeline_status(dir.path(), &pool);
|
||||||
|
|
||||||
// Stages and headers should use markdown bold (**text**).
|
// Stages and headers should use markdown bold (**text**).
|
||||||
assert!(out.contains("**Pipeline Status**"), "missing bold title: {out}");
|
assert!(
|
||||||
assert!(out.contains("**Upcoming**"), "stage should use bold: {out}");
|
out.contains("**Pipeline Status**"),
|
||||||
|
"missing bold title: {out}"
|
||||||
|
);
|
||||||
|
assert!(out.contains("**Backlog**"), "stage should use bold: {out}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2157,13 +2158,19 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ambient_command_on_with_at_mention() {
|
fn ambient_command_on_with_at_mention() {
|
||||||
let uid = make_user_id("@timmy:homeserver.local");
|
let uid = make_user_id("@timmy:homeserver.local");
|
||||||
assert_eq!(parse_ambient_command("@timmy ambient on", &uid, "Timmy"), Some(true));
|
assert_eq!(
|
||||||
|
parse_ambient_command("@timmy ambient on", &uid, "Timmy"),
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ambient_command_off_with_at_mention() {
|
fn ambient_command_off_with_at_mention() {
|
||||||
let uid = make_user_id("@timmy:homeserver.local");
|
let uid = make_user_id("@timmy:homeserver.local");
|
||||||
assert_eq!(parse_ambient_command("@timmy ambient off", &uid, "Timmy"), Some(false));
|
assert_eq!(
|
||||||
|
parse_ambient_command("@timmy ambient off", &uid, "Timmy"),
|
||||||
|
Some(false)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2178,39 +2185,60 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ambient_command_on_with_display_name() {
|
fn ambient_command_on_with_display_name() {
|
||||||
let uid = make_user_id("@timmy:homeserver.local");
|
let uid = make_user_id("@timmy:homeserver.local");
|
||||||
assert_eq!(parse_ambient_command("timmy ambient on", &uid, "Timmy"), Some(true));
|
assert_eq!(
|
||||||
|
parse_ambient_command("timmy ambient on", &uid, "Timmy"),
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ambient_command_off_with_display_name() {
|
fn ambient_command_off_with_display_name() {
|
||||||
let uid = make_user_id("@timmy:homeserver.local");
|
let uid = make_user_id("@timmy:homeserver.local");
|
||||||
assert_eq!(parse_ambient_command("timmy ambient off", &uid, "Timmy"), Some(false));
|
assert_eq!(
|
||||||
|
parse_ambient_command("timmy ambient off", &uid, "Timmy"),
|
||||||
|
Some(false)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ambient_command_on_bare() {
|
fn ambient_command_on_bare() {
|
||||||
// "ambient on" without any bot mention is also recognised.
|
// "ambient on" without any bot mention is also recognised.
|
||||||
let uid = make_user_id("@timmy:homeserver.local");
|
let uid = make_user_id("@timmy:homeserver.local");
|
||||||
assert_eq!(parse_ambient_command("ambient on", &uid, "Timmy"), Some(true));
|
assert_eq!(
|
||||||
|
parse_ambient_command("ambient on", &uid, "Timmy"),
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ambient_command_off_bare() {
|
fn ambient_command_off_bare() {
|
||||||
let uid = make_user_id("@timmy:homeserver.local");
|
let uid = make_user_id("@timmy:homeserver.local");
|
||||||
assert_eq!(parse_ambient_command("ambient off", &uid, "Timmy"), Some(false));
|
assert_eq!(
|
||||||
|
parse_ambient_command("ambient off", &uid, "Timmy"),
|
||||||
|
Some(false)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ambient_command_case_insensitive() {
|
fn ambient_command_case_insensitive() {
|
||||||
let uid = make_user_id("@timmy:homeserver.local");
|
let uid = make_user_id("@timmy:homeserver.local");
|
||||||
assert_eq!(parse_ambient_command("@Timmy AMBIENT ON", &uid, "Timmy"), Some(true));
|
assert_eq!(
|
||||||
assert_eq!(parse_ambient_command("TIMMY AMBIENT OFF", &uid, "Timmy"), Some(false));
|
parse_ambient_command("@Timmy AMBIENT ON", &uid, "Timmy"),
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_ambient_command("TIMMY AMBIENT OFF", &uid, "Timmy"),
|
||||||
|
Some(false)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ambient_command_unrelated_message_returns_none() {
|
fn ambient_command_unrelated_message_returns_none() {
|
||||||
let uid = make_user_id("@timmy:homeserver.local");
|
let uid = make_user_id("@timmy:homeserver.local");
|
||||||
assert_eq!(parse_ambient_command("@timmy what is the status?", &uid, "Timmy"), None);
|
assert_eq!(
|
||||||
|
parse_ambient_command("@timmy what is the status?", &uid, "Timmy"),
|
||||||
|
None
|
||||||
|
);
|
||||||
assert_eq!(parse_ambient_command("hello there", &uid, "Timmy"), None);
|
assert_eq!(parse_ambient_command("hello there", &uid, "Timmy"), None);
|
||||||
assert_eq!(parse_ambient_command("ambient", &uid, "Timmy"), None);
|
assert_eq!(parse_ambient_command("ambient", &uid, "Timmy"), None);
|
||||||
}
|
}
|
||||||
@@ -2237,11 +2265,17 @@ mod tests {
|
|||||||
|
|
||||||
let guard = ambient_rooms.lock().await;
|
let guard = ambient_rooms.lock().await;
|
||||||
assert!(guard.contains(&room_a), "room_a should be in ambient mode");
|
assert!(guard.contains(&room_a), "room_a should be in ambient mode");
|
||||||
assert!(!guard.contains(&room_b), "room_b should NOT be in ambient mode");
|
assert!(
|
||||||
|
!guard.contains(&room_b),
|
||||||
|
"room_b should NOT be in ambient mode"
|
||||||
|
);
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
// Disable ambient mode for room_a.
|
// Disable ambient mode for room_a.
|
||||||
ambient_rooms.lock().await.remove(&room_a);
|
ambient_rooms.lock().await.remove(&room_a);
|
||||||
assert!(!ambient_rooms.lock().await.contains(&room_a), "room_a ambient mode should be off");
|
assert!(
|
||||||
|
!ambient_rooms.lock().await.contains(&room_a),
|
||||||
|
"room_a ambient mode should be off"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user