use std::error::Error;
use std::io::Read;
use std::os::unix::net::UnixListener;
use std::path::Path;
use std::process::Command;
use serde_json::json;
use super::network::OpenServer;
use super::{Host, Ticket};
fn template(ready: &Path) -> Result<String, Box<dyn Error>> {
let exe = std::env::current_exe()?;
let args = serde_json::to_string(&[
"-c",
"export LIMINAL_LIFECYCLE_READY=\"$1\"; shift; exec \"$@\"",
"service-ready",
ready.to_str().ok_or("ready path is not Unicode")?,
exe.to_str().ok_or("executable path is not Unicode")?,
"--exact",
"lifecycle::service_process",
"--nocapture",
])?;
Ok(format!(
"[[templates]]\nname = 'echo'\ncommand = '/bin/sh'\nargs = {args}\nsubjects = ['echo']\nstop_timeout_ms = 2000\n"
))
}
pub fn seat_init(server: &str, name: Option<&str>, out: &Path) -> Result<(), Box<dyn Error>> {
let mut command = Command::new(env!("CARGO_BIN_EXE_liminal"));
command
.args(["seat", "init", "--open", server, "--out"])
.arg(out);
if let Some(name) = name {
command.args(["--name", name]);
}
let output = command.output()?;
if !output.status.success() {
return Err(format!(
"seat init exited {}: {}",
output.status,
String::from_utf8_lossy(&output.stderr)
)
.into());
}
Ok(())
}
#[test]
fn access012_an_open_exchange_runs_a_named_seats_services_through_the_binary()
-> Result<(), Box<dyn Error>> {
let dir = tempfile::tempdir()?;
let ready_path = dir.path().join("ready.sock");
let ready = UnixListener::bind(&ready_path)?;
let server = OpenServer::start_on_with(dir.path(), "127.0.0.1", &template(&ready_path)?)?;
let address = server.address.to_string();
let seat = dir.path().join("hermes.toml");
seat_init(&address, Some("hermes"), &seat)?;
let text = std::fs::read_to_string(&seat)?;
assert!(text.contains("[open]\nname = \"hermes\""), "{text}");
let mut host = Host::open(Ticket::spawn(&seat)?, true)?;
let listed = host.tool("services", &json!({}))?;
assert_eq!(listed["isError"], false, "{listed}");
assert_eq!(
listed["structuredContent"]["templates"][0]["name"], "echo",
"{listed}"
);
assert_eq!(
listed["structuredContent"]["services"],
json!([]),
"{listed}"
);
let made = host.tool("register", &json!({"template": "echo", "name": "one"}))?;
assert_eq!(made["isError"], false, "{made}");
let made = &made["structuredContent"];
assert_eq!(made["holder"], "open:hermes", "{made}");
assert_eq!(made["service"]["created_by"], "open:hermes", "{made}");
assert_eq!(made["service"]["state"], "asleep", "{made}");
assert_eq!(made["service"]["subjects"], json!(["echo.one"]), "{made}");
let id = made["service"]["id"]
.as_str()
.ok_or("the record has an id")?
.to_owned();
let started = host.tool("start", &json!({"id": id}))?;
assert_eq!(started["isError"], false, "{started}");
{
let (mut registered, _) = ready.accept()?;
let mut signal = [0];
registered.read_exact(&mut signal)?;
assert_eq!(&signal, b"R");
}
drop(ready);
let listed = host.tool("services", &json!({}))?;
assert_eq!(
listed["structuredContent"]["services"][0]["state"], "live",
"{listed}"
);
let reply = host.tool(
"call",
&json!({"subject": "echo.one", "input": {"text": "hi"}}),
)?;
assert_eq!(reply["isError"], false, "{reply}");
assert_eq!(
reply["structuredContent"],
json!({"echoed": {"text": "hi"}}),
"{reply}"
);
let held = host.tool("stop", &json!({"id": id}))?;
assert_eq!(held["isError"], false, "{held}");
assert_eq!(
held["structuredContent"]["service"]["state"], "held",
"{held}"
);
let anonymous = dir.path().join("anonymous.toml");
seat_init(&address, None, &anonymous)?;
let mut other = Host::open(Ticket::spawn(&anonymous)?, true)?;
let made = other.tool("register", &json!({"template": "echo", "name": "two"}))?;
assert_eq!(made["isError"], false, "{made}");
assert_eq!(
made["structuredContent"]["service"]["created_by"], "open:anonymous",
"{made}"
);
let (stderr, code) = other.ticket.close()?;
assert_eq!(code, 0, "{stderr}");
let listed = host.tool("services", &json!({}))?;
assert_eq!(
listed["structuredContent"]["services"]
.as_array()
.map(Vec::len),
Some(2),
"{listed}"
);
let retired = host.tool("retire", &json!({"id": id}))?;
assert_eq!(retired["isError"], false, "{retired}");
assert_eq!(
retired["structuredContent"]["service"]["state"], "retired",
"{retired}"
);
let (stderr, code) = host.ticket.close()?;
assert_eq!(code, 0, "{stderr}");
let log = server.log()?;
assert!(log.contains("mode=open"), "{log}");
Ok(())
}