use std::collections::BTreeMap;
use std::fs;
use std::path::{Path, PathBuf};
use serde_json::Value;
use supercode_interchange::ontology::Fidelity;
use supercode_interchange::orchestration::codec::openclaw::{
from_openclaw, parse_openclaw_session_key, strip_json5, to_openclaw, OPENCLAW_CONFIG,
OPENCLAW_STATE_DB,
};
fn repo() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..")
}
fn fixture() -> PathBuf {
repo()
.join("crates/harness/tests/fixtures/openclaw_home")
.canonicalize()
.unwrap()
}
fn tmp(prefix: &str) -> PathBuf {
use std::time::{SystemTime, UNIX_EPOCH};
let dir = std::env::temp_dir().join(format!(
"{prefix}-{}-{}",
std::process::id(),
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos()
));
fs::create_dir_all(&dir).unwrap();
dir.canonicalize().unwrap()
}
fn files_under(root: &Path) -> BTreeMap<String, Vec<u8>> {
let mut out = BTreeMap::new();
fn walk(base: &Path, dir: &Path, out: &mut BTreeMap<String, Vec<u8>>) {
for entry in fs::read_dir(dir).unwrap().flatten() {
let p = entry.path();
if p.is_dir() {
walk(base, &p, out);
} else if p.is_file() {
out.insert(
p.strip_prefix(base).unwrap().to_string_lossy().into_owned(),
fs::read(&p).unwrap(),
);
}
}
}
walk(root, root, &mut out);
out
}
#[test]
fn json5_and_session_keys_are_openclaws_own() {
assert_eq!(
strip_json5("{ // c\n \"a\": [1, 2,], /* x */ \"b\": \"//not\", }"),
"{ \n \"a\": [1, 2], \"b\": \"//not\" }"
);
let p = parse_openclaw_session_key("agent:ops:main").unwrap();
assert_eq!(p.key.platform.as_deref(), Some("main"));
assert_eq!(p.residue.get("dm_collapse"), Some(&Value::Bool(true)));
let p = parse_openclaw_session_key("agent:ops:telegram:group:-1:topic:7").unwrap();
assert_eq!(p.key.thread_id.as_deref(), Some("7"));
assert_eq!(
p.residue.get("thread_word"),
Some(&Value::String("topic".into()))
);
let p = parse_openclaw_session_key("cron:abc:def").unwrap();
assert_eq!(
p.recurrence.as_ref().map(|r| r.job_id.as_str()),
Some("abc:def")
);
assert!(parse_openclaw_session_key("nonsense").is_none());
}
#[test]
fn from_openclaw_reads_the_fixture() {
let loaded = from_openclaw(&fixture()).unwrap();
let names: Vec<&String> = loaded.orchestration.profiles.keys().collect();
assert!(names.contains(&&"default".to_string()), "{names:?}");
let root = &loaded.orchestration.profiles["default"];
assert!(
root.channels.contains_key("slack/T0FIXTURE"),
"two accounts on one transport stay two records: {:?}",
root.channels.keys().collect::<Vec<_>>()
);
assert!(loaded
.vault
.values()
.all(|v| v.contains("FAKE") || !v.is_empty()));
assert!(
!serde_json::to_string(&loaded.orchestration)
.unwrap()
.contains("DO-NOT-EMIT"),
"no credential value in the orchestration"
);
let jobs: usize = loaded
.orchestration
.profiles
.values()
.map(|p| p.jobs.len())
.sum();
assert!(jobs >= 1, "jobs come from the store");
assert!(!root.routes.is_empty(), "bindings[] are routes");
assert!(!root.residue.files.is_empty());
}
#[test]
fn to_openclaw_of_from_openclaw_copies_the_store_and_the_config() {
let loaded = from_openclaw(&fixture()).unwrap();
let dest = tmp("orc-openclaw-back");
let report = to_openclaw(&loaded, &dest).unwrap();
assert!(report.refused.is_empty(), "{:?}", report.refused);
let before = files_under(&fixture());
let after = files_under(&dest);
for a in &report.written {
if a.fidelity == Fidelity::ByteLossless {
assert_eq!(
after.get(&a.path),
before.get(&a.path),
"{} byte for byte",
a.path
);
}
}
assert!(
report
.written
.iter()
.any(|a| a.path == OPENCLAW_STATE_DB && a.fidelity == Fidelity::ByteLossless),
"the unchanged store is copied"
);
assert!(
report
.written
.iter()
.any(|a| a.path == OPENCLAW_CONFIG && a.fidelity == Fidelity::ByteLossless),
"the unchanged config keeps its bytes"
);
assert_eq!(
before.len(),
after.len(),
"every file reproduced: {:?}",
before
.keys()
.filter(|k| !after.contains_key(*k))
.collect::<Vec<_>>()
);
}
#[test]
fn a_changed_job_re_emits_and_the_session_half_is_refused() {
let mut loaded = from_openclaw(&fixture()).unwrap();
let (owner, job_id) = loaded
.orchestration
.profiles
.iter()
.find_map(|(n, p)| p.jobs.keys().next().map(|j| (n.clone(), j.clone())))
.unwrap();
loaded
.orchestration
.profiles
.get_mut(&owner)
.unwrap()
.jobs
.get_mut(&job_id)
.unwrap()
.prompt = Some("changed by the test".into());
let planted = supercode_interchange::ontology::Binding {
key: supercode_interchange::ontology::SurfaceKey {
platform: Some("telegram".into()),
kind: Some("dm".into()),
chat_id: Some("1".into()),
..Default::default()
},
worker: supercode_interchange::ontology::Worker {
harness: supercode_interchange::ontology::HarnessId::new("codex"),
session_id: Some("s".into()),
locator: None,
},
..Default::default()
};
loaded
.orchestration
.profiles
.get_mut("default")
.unwrap()
.bindings
.insert("telegram|dm|1||".into(), planted);
let dest = tmp("orc-openclaw-changed");
let report = to_openclaw(&loaded, &dest).unwrap();
assert!(
report.rows_emitted >= 1 && report.rows_byte >= 1,
"changed rows re-encode, unchanged rows go back column for column: {:?}",
(report.rows_byte, report.rows_emitted)
);
assert!(
report
.refused
.iter()
.any(|r| r.file.contains("sessions/") && r.reason.contains("UNI-22")),
"{:?}",
report.refused
);
let again = from_openclaw(&dest).unwrap();
assert_eq!(
again.orchestration.profiles[&owner].jobs[&job_id]
.prompt
.as_deref(),
Some("changed by the test")
);
}