mod common;
use std::process::{Command, Stdio};
const INSTRUCTION: &str = "Résumé the brief — 日本語で요약 ✅ (τέλος)";
fn mojibake(s: &str) -> String {
s.bytes().map(|b| b as char).collect()
}
fn write_file(tag: &str, ext: &str, body: &str) -> String {
let path = common::unique_path(tag, ext);
std::fs::write(&path, body).expect("write test file");
path
}
fn json_config(intel: &str) -> String {
format!(
r#"{{
"config_version": "2",
"agent": {{ "name": "unicode", "instruction": "{INSTRUCTION}" }},
"intelligence": {{ "endpoints": "{intel}", "model": "mock" }},
"observability": {{ "log_level": "error" }}
}}"#
)
}
fn jsonc_config(intel: &str) -> String {
format!(
r#"{{
// 設定 — the agent identity, commented in 日本語
"config_version": "2",
/* 註釋: a block comment carrying 4-byte text 🎌 before the key */
"agent": {{ "name": "unicode", "instruction": "{INSTRUCTION}"/* — glued to the quote */ }},
"intelligence": {{ "endpoints": "{intel}", "model": "mock" }}, // ✅ 日本語 trailing
"observability": {{ "log_level": "error" }}
}}"#
)
}
#[test]
fn json_and_jsonc_documents_round_trip_non_ascii_byte_for_byte() {
for (tag, ext, body) in [
("cfg-unicode", "json", json_config("https://intel.example")),
(
"cfg-unicode",
"jsonc",
jsonc_config("https://intel.example"),
),
] {
let path = write_file(tag, ext, &body);
let (doc, format) = agentd::config::file::read_document(&path).expect("config parses");
assert_eq!(format, agentd::config::file::Format::Json, "{ext} is json");
let got = doc["agent"]["instruction"]
.as_str()
.unwrap_or_else(|| panic!("{ext}: no agent.instruction in {doc}"));
assert_eq!(
got.as_bytes(),
INSTRUCTION.as_bytes(),
"{ext}: the instruction was corrupted in transit\n wrote: {INSTRUCTION}\n read : {got}"
);
assert_ne!(got, mojibake(INSTRUCTION), "{ext}: mojibake");
assert_eq!(doc["agent"]["name"], serde_json::json!("unicode"));
assert_eq!(doc["intelligence"]["model"], serde_json::json!("mock"));
assert_eq!(
doc["observability"]["log_level"],
serde_json::json!("error")
);
let _ = std::fs::remove_file(&path);
}
}
#[test]
fn a_multibyte_char_welded_to_a_comment_survives() {
let body = r#"{
"config_version": "2",
"agent": { "instruction": "—"/*—*/ },
"intelligence": { "endpoints": "https://intel.example", "model": "日本語"//—
}
}"#;
let path = write_file("cfg-welded", "jsonc", body);
let (doc, _) = agentd::config::file::read_document(&path).expect("config parses");
assert_eq!(doc["agent"]["instruction"], serde_json::json!("—"));
assert_eq!(doc["intelligence"]["model"], serde_json::json!("日本語"));
let _ = std::fs::remove_file(&path);
let body = r#"{ "config_version": "2",
"agent": { "instruction": "a\"—\\éé" } }"#;
let path = write_file("cfg-escape", "jsonc", body);
let (doc, _) = agentd::config::file::read_document(&path).expect("config parses");
assert_eq!(doc["agent"]["instruction"], serde_json::json!("a\"—\\éé"));
let _ = std::fs::remove_file(&path);
}
fn probe_playbook() -> serde_json::Value {
serde_json::json!({
"match": [
{"when_contains": mojibake(INSTRUCTION), "content": "CORRUPTED-INSTRUCTION"},
{"when_contains": INSTRUCTION, "content": "INTACT-INSTRUCTION"}
],
"turns": [{"content": "MISSING-INSTRUCTION"}]
})
}
struct MockLlm {
child: std::process::Child,
addr_file: String,
uri: String,
}
impl Drop for MockLlm {
fn drop(&mut self) {
let _ = self.child.kill();
let _ = self.child.wait();
let _ = std::fs::remove_file(&self.addr_file);
}
}
fn spawn_mock_llm(playbook: &serde_json::Value) -> MockLlm {
let pb = common::unique_path("playbook", "json");
std::fs::write(&pb, playbook.to_string()).unwrap();
let addr_file = common::unique_path("mock-llm", "addr");
let _ = std::fs::remove_file(&addr_file);
let child = Command::new(env!("CARGO_BIN_EXE_agentd"))
.args(["--internal-mock-llm", &addr_file, &format!("file:{pb}")])
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.expect("spawn mock llm");
let addr = common::read_addr_file(&addr_file);
MockLlm {
child,
addr_file,
uri: format!("http://{addr}"),
}
}
#[test]
fn the_daemon_sends_the_model_the_instruction_the_operator_wrote() {
let llm = spawn_mock_llm(&probe_playbook());
let cfg = write_file("cfg-unicode-e2e", "jsonc", &jsonc_config(&llm.uri));
let out = Command::new(env!("CARGO_BIN_EXE_agentd"))
.args(["--config", &cfg])
.stdin(Stdio::null())
.output()
.expect("run agentd");
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);
assert_eq!(out.status.code(), Some(0), "stderr:\n{stderr}");
assert!(
stdout.contains("INTACT-INSTRUCTION"),
"the model received a different instruction than the config carries.\nstdout:\n{stdout}\nstderr:\n{stderr}"
);
let _ = std::fs::remove_file(&cfg);
}
#[test]
fn the_yaml_reader_round_trips_non_ascii_too() {
let body = format!(
"config_version: \"2\"\n# 註釋 — a comment with 日本語\nagent:\n instruction: \"{INSTRUCTION}\" # ✅ trailing\n name: unicode\nintelligence:\n endpoints: https://intel.example\n model: 日本語\n"
);
let path = write_file("cfg-unicode", "yaml", &body);
let (doc, format) = agentd::config::file::read_document(&path).expect("yaml parses");
assert_eq!(format, agentd::config::file::Format::Yaml);
let got = doc["agent"]["instruction"]
.as_str()
.expect("an instruction");
assert_eq!(
got.as_bytes(),
INSTRUCTION.as_bytes(),
"yaml: the instruction was corrupted in transit\n wrote: {INSTRUCTION}\n read : {got}"
);
assert_eq!(doc["intelligence"]["model"], serde_json::json!("日本語"));
let _ = std::fs::remove_file(&path);
}