zeph_commands/handlers/
experiment.rs1use std::future::Future;
7use std::pin::Pin;
8
9use crate::context::CommandContext;
10use crate::{CommandError, CommandHandler, CommandOutput, SlashCategory};
11
12pub struct ExperimentCommand;
18
19impl CommandHandler<CommandContext<'_>> for ExperimentCommand {
20 fn name(&self) -> &'static str {
21 "/experiment"
22 }
23
24 fn description(&self) -> &'static str {
25 "Experimental features"
26 }
27
28 fn args_hint(&self) -> &'static str {
29 "[subcommand]"
30 }
31
32 fn category(&self) -> SlashCategory {
33 SlashCategory::Advanced
34 }
35
36 fn feature_gate(&self) -> Option<&'static str> {
37 Some("experiments")
38 }
39
40 fn requires_auth(&self) -> bool {
41 true
42 }
43
44 fn handle<'a>(
45 &'a self,
46 ctx: &'a mut CommandContext<'_>,
47 args: &'a str,
48 ) -> Pin<Box<dyn Future<Output = Result<CommandOutput, CommandError>> + Send + 'a>> {
49 use tracing::Instrument as _;
50 let span = tracing::info_span!("commands.experiment.handle");
51 Box::pin(
52 async move {
53 let input = if args.is_empty() {
54 "/experiment".to_owned()
55 } else {
56 format!("/experiment {args}")
57 };
58 let result = ctx.agent.handle_experiment(&input).await?;
59 Ok(CommandOutput::message_or_silent(result))
60 }
61 .instrument(span),
62 )
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::handlers::test_helpers::{MockDebug, MockMessages, MockSession, make_ctx};
70 use crate::sink::NullSink;
71 use std::assert_matches;
72
73 #[test]
74 fn experiment_name_and_description() {
75 assert_eq!(ExperimentCommand.name(), "/experiment");
76 assert!(!ExperimentCommand.description().is_empty());
77 }
78
79 #[tokio::test]
80 async fn experiment_no_args_returns_silent_when_agent_returns_empty() {
81 let mut sink = NullSink;
83 let mut debug = MockDebug;
84 let mut messages = MockMessages;
85 let session = MockSession;
86 let mut agent = crate::NullAgent;
87 let mut ctx = make_ctx(&mut sink, &mut debug, &mut messages, &session, &mut agent);
88 let out = ExperimentCommand.handle(&mut ctx, "").await.unwrap();
89 assert_matches!(out, CommandOutput::Silent);
90 }
91
92 #[tokio::test]
93 async fn experiment_with_args_returns_silent_when_agent_returns_empty() {
94 let mut sink = NullSink;
95 let mut debug = MockDebug;
96 let mut messages = MockMessages;
97 let session = MockSession;
98 let mut agent = crate::NullAgent;
99 let mut ctx = make_ctx(&mut sink, &mut debug, &mut messages, &session, &mut agent);
100 let out = ExperimentCommand.handle(&mut ctx, "list").await.unwrap();
101 assert_matches!(out, CommandOutput::Silent);
102 }
103}