use super::*;
use crate::registry::random::MockRandomSelector;
fn create_test_scene_info(id: usize, name: &str, fn_name: &str) -> SceneInfo {
SceneInfo {
id: SceneId(id),
name: name.to_string(),
scope: SceneScope::Global,
attributes: HashMap::new(),
fn_name: fn_name.to_string(),
parent: None,
}
}
#[test]
fn test_resolve_scene_id_basic() {
let selector = Box::new(MockRandomSelector::new(vec![0]));
let mut table = SceneTable {
labels: vec![create_test_scene_info(0, "test", "test_1::__start__")],
prefix_index: {
let mut map = RadixMap::new();
map.insert(b"test_1::__start__", vec![SceneId(0)]);
map
},
cache: HashMap::new(),
random_selector: selector,
shuffle_enabled: false,
};
let result = table.resolve_scene_id("test", &HashMap::new());
assert!(result.is_ok());
assert_eq!(result.unwrap(), SceneId(0));
}
fn create_test_local_scene_info(
id: usize,
name: &str,
fn_name: &str,
parent: &str,
) -> SceneInfo {
SceneInfo {
id: SceneId(id),
name: name.to_string(),
scope: SceneScope::Local,
attributes: HashMap::new(),
fn_name: fn_name.to_string(),
parent: Some(parent.to_string()),
}
}
#[test]
fn test_collect_scene_candidates_local_search() {
let selector = Box::new(MockRandomSelector::new(vec![]));
let mut table = SceneTable::new(selector);
table.labels.push(create_test_local_scene_info(
0,
"選択肢",
"会話_1::選択肢_1",
"会話_1",
));
table
.prefix_index
.insert(":会話_1:選択肢".as_bytes(), vec![SceneId(0)]);
let result = table.collect_scene_candidates("会話_1", "選択肢");
assert!(result.is_ok());
let candidates = result.unwrap();
assert_eq!(candidates.len(), 1);
assert_eq!(candidates[0], SceneId(0));
}
#[test]
fn test_collect_scene_candidates_global_search() {
let selector = Box::new(MockRandomSelector::new(vec![]));
let mut table = SceneTable::new(selector);
table.labels.push(create_test_scene_info(0, "挨拶", "挨拶"));
table
.prefix_index
.insert("挨拶".as_bytes(), vec![SceneId(0)]);
let result = table.collect_scene_candidates("", "挨拶");
assert!(result.is_ok());
let candidates = result.unwrap();
assert_eq!(candidates.len(), 1);
assert_eq!(candidates[0], SceneId(0));
}
#[test]
fn test_collect_scene_candidates_local_only() {
let selector = Box::new(MockRandomSelector::new(vec![]));
let mut table = SceneTable::new(selector);
table.labels.push(create_test_scene_info(0, "挨拶", "挨拶"));
table
.prefix_index
.insert("挨拶".as_bytes(), vec![SceneId(0)]);
table.labels.push(create_test_local_scene_info(
1,
"挨拶",
"会話_1::挨拶_1",
"会話_1",
));
table
.prefix_index
.insert(":会話_1:挨拶".as_bytes(), vec![SceneId(1)]);
let result = table.collect_scene_candidates("会話_1", "挨拶");
assert!(result.is_ok());
let candidates = result.unwrap();
assert_eq!(candidates.len(), 1);
assert!(candidates.contains(&SceneId(1))); assert!(!candidates.contains(&SceneId(0))); }
#[test]
fn test_collect_scene_candidates_local_not_found_no_fallback() {
let selector = Box::new(MockRandomSelector::new(vec![]));
let mut table = SceneTable::new(selector);
table.labels.push(create_test_scene_info(0, "挨拶", "挨拶"));
table
.prefix_index
.insert("挨拶".as_bytes(), vec![SceneId(0)]);
let result = table.collect_scene_candidates("会話_1", "挨拶");
assert!(result.is_err());
match result {
Err(SceneTableError::SceneNotFound { scene }) => assert_eq!(scene, "挨拶"),
_ => panic!("Expected SceneNotFound error"),
}
}
#[test]
fn test_collect_scene_candidates_global_via_empty_module_name() {
let selector = Box::new(MockRandomSelector::new(vec![]));
let mut table = SceneTable::new(selector);
table.labels.push(create_test_scene_info(0, "挨拶", "挨拶"));
table
.prefix_index
.insert("挨拶".as_bytes(), vec![SceneId(0)]);
let result = table.collect_scene_candidates("", "挨拶");
assert!(result.is_ok());
let candidates = result.unwrap();
assert_eq!(candidates.len(), 1);
assert!(candidates.contains(&SceneId(0)));
}
#[test]
fn test_collect_scene_candidates_prefix_match() {
let selector = Box::new(MockRandomSelector::new(vec![]));
let mut table = SceneTable::new(selector);
table
.labels
.push(create_test_scene_info(0, "挨拶_朝", "挨拶_朝"));
table
.labels
.push(create_test_scene_info(1, "挨拶_昼", "挨拶_昼"));
table
.labels
.push(create_test_scene_info(2, "挨拶_夜", "挨拶_夜"));
table
.prefix_index
.insert("挨拶_朝".as_bytes(), vec![SceneId(0)]);
table
.prefix_index
.insert("挨拶_昼".as_bytes(), vec![SceneId(1)]);
table
.prefix_index
.insert("挨拶_夜".as_bytes(), vec![SceneId(2)]);
let result = table.collect_scene_candidates("", "挨拶");
assert!(result.is_ok());
let candidates = result.unwrap();
assert_eq!(candidates.len(), 3);
}
#[test]
fn test_collect_scene_candidates_not_found() {
let selector = Box::new(MockRandomSelector::new(vec![]));
let table = SceneTable::new(selector);
let result = table.collect_scene_candidates("会話_1", "存在しないシーン");
assert!(result.is_err());
match result {
Err(SceneTableError::SceneNotFound { scene }) => {
assert_eq!(scene, "存在しないシーン");
}
_ => panic!("Expected SceneNotFound error"),
}
}
#[test]
fn test_collect_scene_candidates_empty_prefix_error() {
let selector = Box::new(MockRandomSelector::new(vec![]));
let table = SceneTable::new(selector);
let result = table.collect_scene_candidates("会話_1", "");
assert!(result.is_err());
match result {
Err(SceneTableError::InvalidScene { scene }) => {
assert_eq!(scene, "");
}
_ => panic!("Expected InvalidScene error"),
}
}
#[test]
fn test_collect_scene_candidates_exclude_local_from_global() {
let selector = Box::new(MockRandomSelector::new(vec![]));
let mut table = SceneTable::new(selector);
table.labels.push(create_test_local_scene_info(
0,
"選択肢",
"他モジュール::選択肢_1",
"他モジュール",
));
table
.prefix_index
.insert(":他モジュール:選択肢".as_bytes(), vec![SceneId(0)]);
let result = table.collect_scene_candidates("会話_1", "選択肢");
assert!(result.is_err()); }
#[test]
fn test_fn_name_to_search_key_local_scene() {
let result = SceneTable::fn_name_to_search_key("会話_1::選択肢_1", true);
assert_eq!(result, ":会話_1:選択肢_1");
}
#[test]
fn test_fn_name_to_search_key_global_scene() {
let result = SceneTable::fn_name_to_search_key("会話_1::__start__", false);
assert_eq!(result, "会話_1");
}
#[test]
fn test_fn_name_to_search_key_global_scene_no_suffix() {
let result = SceneTable::fn_name_to_search_key("挨拶", false);
assert_eq!(result, "挨拶");
}
#[test]
fn test_from_scene_registry_key_conversion() {
use crate::registry::SceneRegistry;
let mut registry = SceneRegistry::new();
let (_, counter) = registry.register_global("会話", HashMap::new());
registry.register_local("選択肢", "会話", counter, 1, HashMap::new());
let selector = Box::new(MockRandomSelector::new(vec![]));
let table = SceneTable::from_scene_registry(registry, selector).unwrap();
let global_result = table.collect_scene_candidates("", "会話");
assert!(global_result.is_ok());
assert_eq!(global_result.unwrap().len(), 1);
let local_result = table.collect_scene_candidates("会話_1", "選択肢");
assert!(local_result.is_ok());
assert_eq!(local_result.unwrap().len(), 1);
let cross_module_result = table.collect_scene_candidates("他のモジュール", "選択肢");
assert!(cross_module_result.is_err());
}
#[test]
fn test_resolve_scene_id_unified_local_scene() {
use crate::registry::SceneRegistry;
let mut registry = SceneRegistry::new();
let (_, counter) = registry.register_global("会話", HashMap::new());
registry.register_local("選択肢", "会話", counter, 1, HashMap::new());
let selector = Box::new(MockRandomSelector::new(vec![0]));
let mut table = SceneTable::from_scene_registry(registry, selector).unwrap();
table.set_shuffle_enabled(false);
let result = table.resolve_scene_id_unified("会話_1", "選択肢", &HashMap::new());
assert!(result.is_ok());
let scene_id = result.unwrap();
let scene = table.get_scene(scene_id).unwrap();
assert!(scene.name.contains("選択肢"));
}
#[test]
fn test_resolve_scene_id_unified_global_scene() {
use crate::registry::SceneRegistry;
let mut registry = SceneRegistry::new();
registry.register_global("挨拶", HashMap::new());
let selector = Box::new(MockRandomSelector::new(vec![0]));
let mut table = SceneTable::from_scene_registry(registry, selector).unwrap();
table.set_shuffle_enabled(false);
let result = table.resolve_scene_id_unified("", "挨拶", &HashMap::new());
assert!(result.is_ok());
}
#[test]
fn test_resolve_scene_id_unified_global_scene_no_fallback() {
use crate::registry::SceneRegistry;
let mut registry = SceneRegistry::new();
registry.register_global("挨拶", HashMap::new());
let selector = Box::new(MockRandomSelector::new(vec![0]));
let mut table = SceneTable::from_scene_registry(registry, selector).unwrap();
table.set_shuffle_enabled(false);
let result = table.resolve_scene_id_unified("任意のモジュール", "挨拶", &HashMap::new());
assert!(result.is_err());
}
#[test]
fn test_resolve_scene_id_unified_local_found() {
use crate::registry::SceneRegistry;
let mut registry = SceneRegistry::new();
registry.register_global("挨拶", HashMap::new());
let (_, counter) = registry.register_global("会話", HashMap::new());
registry.register_local("挨拶", "会話", counter, 1, HashMap::new());
let selector = Box::new(MockRandomSelector::new(vec![0]));
let mut table = SceneTable::from_scene_registry(registry, selector).unwrap();
table.set_shuffle_enabled(false);
let result = table.resolve_scene_id_unified("会話_1", "挨拶", &HashMap::new());
assert!(result.is_ok());
let scene_id = result.unwrap();
let scene = table.get_scene(scene_id).unwrap();
assert!(scene.parent.is_some());
let result2 = table.resolve_scene_id_unified("会話_1", "挨拶", &HashMap::new());
assert!(result2.is_ok()); let scene_id2 = result2.unwrap();
let scene2 = table.get_scene(scene_id2).unwrap();
assert!(scene2.parent.is_some()); }
#[test]
fn test_resolve_scene_id_cycling() {
let selector = Box::new(MockRandomSelector::new(vec![0]));
let mut table = SceneTable {
labels: vec![
create_test_scene_info(0, "OnTalk1", "OnTalk"),
create_test_scene_info(1, "OnTalk2", "OnTalk"),
create_test_scene_info(2, "OnTalk3", "OnTalk"),
],
prefix_index: {
let mut map = RadixMap::new();
map.insert(b"OnTalk", vec![SceneId(0), SceneId(1), SceneId(2)]);
map
},
cache: HashMap::new(),
random_selector: selector,
shuffle_enabled: false,
};
let r1 = table.resolve_scene_id("OnTalk", &HashMap::new());
assert!(r1.is_ok());
let r2 = table.resolve_scene_id("OnTalk", &HashMap::new());
assert!(r2.is_ok());
let r3 = table.resolve_scene_id("OnTalk", &HashMap::new());
assert!(r3.is_ok());
let r4 = table.resolve_scene_id("OnTalk", &HashMap::new());
assert!(
r4.is_ok(),
"4回目の呼び出しが失敗: 循環リセットが動作していない"
);
let id = r4.unwrap();
assert!(
id == SceneId(0) || id == SceneId(1) || id == SceneId(2),
"返却されたSceneId {:?} が候補に含まれていない",
id
);
for i in 5..=12 {
let r = table.resolve_scene_id("OnTalk", &HashMap::new());
assert!(r.is_ok(), "{}回目の呼び出しが失敗", i);
}
}
#[test]
fn test_resolve_scene_id_cycling_reshuffles() {
use crate::registry::random::DefaultRandomSelector;
let selector = Box::new(DefaultRandomSelector::with_seed(42));
let mut table = SceneTable {
labels: vec![
create_test_scene_info(0, "OnTalk1", "OnTalk"),
create_test_scene_info(1, "OnTalk2", "OnTalk"),
create_test_scene_info(2, "OnTalk3", "OnTalk"),
],
prefix_index: {
let mut map = RadixMap::new();
map.insert(b"OnTalk", vec![SceneId(0), SceneId(1), SceneId(2)]);
map
},
cache: HashMap::new(),
random_selector: selector,
shuffle_enabled: true,
};
let mut first_cycle = Vec::new();
for _ in 0..3 {
let r = table.resolve_scene_id("OnTalk", &HashMap::new());
first_cycle.push(r.unwrap());
}
let mut second_cycle = Vec::new();
for _ in 0..3 {
let r = table.resolve_scene_id("OnTalk", &HashMap::new());
assert!(r.is_ok());
second_cycle.push(r.unwrap());
}
let first_set: std::collections::HashSet<_> = first_cycle.iter().collect();
let second_set: std::collections::HashSet<_> = second_cycle.iter().collect();
assert_eq!(first_set, second_set, "候補セットが変化している");
}
#[test]
fn test_resolve_scene_id_cycling_preserves_candidates() {
let selector = Box::new(MockRandomSelector::new(vec![0]));
let mut table = SceneTable {
labels: vec![
create_test_scene_info(0, "OnTalk1", "OnTalk"),
create_test_scene_info(1, "OnTalk2", "OnTalk"),
create_test_scene_info(2, "OnTalk3", "OnTalk"),
],
prefix_index: {
let mut map = RadixMap::new();
map.insert(b"OnTalk", vec![SceneId(0), SceneId(1), SceneId(2)]);
map
},
cache: HashMap::new(),
random_selector: selector,
shuffle_enabled: false,
};
let mut first_cycle: std::collections::HashSet<SceneId> = std::collections::HashSet::new();
for _ in 0..3 {
first_cycle.insert(table.resolve_scene_id("OnTalk", &HashMap::new()).unwrap());
}
assert_eq!(first_cycle.len(), 3);
let mut second_cycle: std::collections::HashSet<SceneId> = std::collections::HashSet::new();
for _ in 0..3 {
second_cycle.insert(table.resolve_scene_id("OnTalk", &HashMap::new()).unwrap());
}
assert_eq!(second_cycle.len(), 3);
assert_eq!(
first_cycle, second_cycle,
"リセット後に候補リストが変化している"
);
}
#[test]
fn test_resolve_scene_id_unified_cycling() {
use crate::registry::SceneRegistry;
let mut registry = SceneRegistry::new();
let (_, counter) = registry.register_global("会話", HashMap::new());
registry.register_local("選択肢A", "会話", counter, 1, HashMap::new());
registry.register_local("選択肢B", "会話", counter, 2, HashMap::new());
let selector = Box::new(MockRandomSelector::new(vec![0]));
let mut table = SceneTable::from_scene_registry(registry, selector).unwrap();
table.set_shuffle_enabled(false);
let r1 = table.resolve_scene_id_unified("会話_1", "選択肢", &HashMap::new());
assert!(r1.is_ok());
let r2 = table.resolve_scene_id_unified("会話_1", "選択肢", &HashMap::new());
assert!(r2.is_ok());
let r3 = table.resolve_scene_id_unified("会話_1", "選択肢", &HashMap::new());
assert!(
r3.is_ok(),
"unified版で3回目の呼び出しが失敗: 循環リセットが動作していない"
);
let scene = table.get_scene(r3.unwrap()).unwrap();
assert!(scene.parent.is_some(), "返却されたシーンがローカルでない");
}
#[test]
fn test_resolve_scene_id_unified_cache_key_includes_module() {
use crate::registry::SceneRegistry;
let mut registry = SceneRegistry::new();
let (_, counter1) = registry.register_global("会話A", HashMap::new());
registry.register_local("選択肢", "会話A", counter1, 1, HashMap::new());
let (_, counter2) = registry.register_global("会話B", HashMap::new());
registry.register_local("選択肢", "会話B", counter2, 1, HashMap::new());
let selector = Box::new(MockRandomSelector::new(vec![0]));
let mut table = SceneTable::from_scene_registry(registry, selector).unwrap();
table.set_shuffle_enabled(false);
let result_a = table.resolve_scene_id_unified("会話A_1", "選択肢", &HashMap::new());
assert!(result_a.is_ok());
let result_b = table.resolve_scene_id_unified("会話B_1", "選択肢", &HashMap::new());
assert!(result_b.is_ok());
let scene_a = table.get_scene(result_a.unwrap()).unwrap();
let scene_b = table.get_scene(result_b.unwrap()).unwrap();
assert_ne!(scene_a.fn_name, scene_b.fn_name);
}