mod common;
#[allow(unused_imports)]
use common::*;
use serde_json::json;
use std::time::Duration;
const NS: &str = "strict-connect-mode-e2e";
fn wait_tick() {
std::thread::sleep(Duration::from_secs(3));
}
#[test]
fn standalone_mutations_ok() {
let engine = microresolve::MicroResolve::new(microresolve::MicroResolveConfig::default())
.expect("standalone engine");
let ns = engine.namespace("test");
ns.add_intent("greet", &["hello world"][..])
.expect("add_intent ok");
ns.add_phrase("greet", "hi there", "en")
.expect("add_phrase ok");
ns.remove_phrase("greet", "hi there")
.expect("remove_phrase ok");
ns.set_domain_description("greet-domain", "Greeting domain")
.expect("set_domain_description ok");
ns.remove_domain_description("greet-domain")
.expect("remove_domain_description ok");
ns.reinforce_tokens(&["hello"], "greet")
.expect("reinforce_tokens ok");
ns.apply_weight_updates(&[("hello".to_string(), "greet".to_string(), 0.9)])
.expect("apply_weight_updates ok");
ns.decay_for_intents(&["hello world".to_string()], &["greet".to_string()], 0.1)
.expect("decay_for_intents ok");
ns.index_phrase("greet", "howdy").expect("index_phrase ok");
ns.rebuild_caches().expect("rebuild_caches ok");
ns.rebuild_index().expect("rebuild_index ok");
ns.apply_review(&Default::default(), &[], &[], "hello", 0.1)
.expect("apply_review ok");
ns.remove_intent("greet").expect("remove_intent ok");
assert_eq!(ns.intent_count(), 0, "intent removed");
}
#[test]
fn connected_mutations_refused() {
let server = TestServer::spawn();
let c = server.client();
let b = format!("{}/api", server.url);
let h = vec![("X-Namespace-ID", NS)];
let (s, body) = post_json(
&c,
&format!("{}/namespaces", b),
&[],
&json!({"namespace_id": NS}),
);
assert!((200..300).contains(&s), "create namespace: {}", body);
post_json(
&c,
&format!("{}/intents", b),
&h,
&json!({"id": "greet", "phrases": ["hello", "hi"]}),
);
post_json(
&c,
&format!("{}/intents", b),
&h,
&json!({"id": "farewell", "phrases": ["goodbye", "bye"]}),
);
let engine = microresolve::MicroResolve::new(microresolve::MicroResolveConfig {
server: Some(microresolve::ServerConfig {
url: server.url.clone(),
api_key: Some(server.api_key.clone()),
subscribe: vec![NS.to_string()],
tick_interval_secs: 1,
log_buffer_max: 100,
}),
..Default::default()
})
.expect("connected engine");
let ns = engine.namespace(NS);
assert!(
ns.intent_count() >= 1,
"should have >=1 intent after initial sync"
);
macro_rules! assert_connect_mode {
($name:expr, $expr:expr) => {{
match $expr {
Err(microresolve::Error::ConnectMode) => {} Err(other) => panic!("{}: expected ConnectMode, got: {}", $name, other),
Ok(_) => panic!(
"{}: expected ConnectMode error, but mutation succeeded",
$name
),
}
}};
}
assert_connect_mode!(
"add_intent",
ns.add_intent("new-intent", &["some phrase"][..])
);
assert_connect_mode!("remove_intent", ns.remove_intent("greet"));
assert_connect_mode!("add_phrase", ns.add_phrase("greet", "howdy", "en"));
assert_connect_mode!("remove_phrase", ns.remove_phrase("greet", "hello"));
assert_connect_mode!(
"set_domain_description",
ns.set_domain_description("x", "desc")
);
assert_connect_mode!(
"remove_domain_description",
ns.remove_domain_description("x")
);
assert_connect_mode!("reinforce_tokens", ns.reinforce_tokens(&["hello"], "greet"));
assert_connect_mode!(
"apply_weight_updates",
ns.apply_weight_updates(&[("hello".to_string(), "greet".to_string(), 0.5)])
);
assert_connect_mode!(
"decay_for_intents",
ns.decay_for_intents(&["hello".to_string()], &["greet".to_string()], 0.1)
);
assert_connect_mode!("index_phrase", ns.index_phrase("greet", "hey there"));
assert_connect_mode!("rebuild_caches", ns.rebuild_caches());
assert_connect_mode!("rebuild_index", ns.rebuild_index());
assert_connect_mode!(
"apply_review",
ns.apply_review(&Default::default(), &[], &[], "q", 0.1)
);
assert_connect_mode!("correct", ns.correct("hello", "greet", "greet2"));
let (result, _) = ns.resolve_with_options("hello", Some(0.05), 1.5, 0.05, false);
assert!(
!result.intents.is_empty(),
"read methods must still work in connected mode; got: {:?}",
result.intents
);
let _ = ns.intent_ids();
let _ = ns.intent_count();
let _ = ns.version();
delete_json(
&c,
&format!("{}/namespaces", b),
&[],
&json!({"namespace_id": NS}),
);
}
#[test]
fn sync_thread_works_in_connect_mode() {
const NS2: &str = "strict-connect-sync-thread";
let server = TestServer::spawn();
let c = server.client();
let b = format!("{}/api", server.url);
let h = vec![("X-Namespace-ID", NS2)];
post_json(
&c,
&format!("{}/namespaces", b),
&[],
&json!({"namespace_id": NS2}),
);
post_json(
&c,
&format!("{}/intents", b),
&h,
&json!({"id": "greet", "phrases": ["hello", "hi"]}),
);
post_json(
&c,
&format!("{}/intents", b),
&h,
&json!({"id": "farewell", "phrases": ["goodbye", "bye"]}),
);
let engine = microresolve::MicroResolve::new(microresolve::MicroResolveConfig {
server: Some(microresolve::ServerConfig {
url: server.url.clone(),
api_key: Some(server.api_key.clone()),
subscribe: vec![NS2.to_string()],
tick_interval_secs: 1,
log_buffer_max: 100,
}),
..Default::default()
})
.expect("connected engine for sync thread test");
assert!(
engine.namespace(NS2).intent_count() >= 1,
"initial sync loaded at least one intent"
);
let (s, _) = post_json(
&c,
&format!("{}/intents/greet/phrases", b),
&h,
&json!({"phrase": "howdy partner", "lang": "en"}),
);
assert!((200..300).contains(&s), "server-side add phrase");
wait_tick();
let (result, _) =
engine
.namespace(NS2)
.resolve_with_options("howdy partner", Some(0.05), 1.5, 0.05, false);
assert!(
result.intents.iter().any(|m| m.id == "greet"),
"background sync applied the server-side phrase; got: {:?}",
result.intents
);
delete_json(
&c,
&format!("{}/namespaces", b),
&[],
&json!({"namespace_id": NS2}),
);
}