use juiz_core::prelude::*;
const PORT: i64 = 8015;
const ALLOWED_ORIGIN: &str = "http://localhost:5173";
fn system_manifest() -> Value {
jvalue!({
"name": "viewer_api_test",
"option": {
"http_broker": {
"start": true,
"port": PORT,
"cors_allow_origins": [ALLOWED_ORIGIN]
}
},
"plugins": {
"process_factories": {
"increment_process": { "path": "../target/debug" }
},
"container_factories": {
"example_container": {
"path": "../target/debug",
"processes": {
"example_container_increment": { "path": "../target/debug" },
"example_container_get": { "path": "../target/debug" }
}
}
}
},
"processes": [
{ "type_name": "increment_process", "name": "pub0", "publishes": ["topic0"] },
{ "type_name": "increment_process", "name": "sub0",
"subscribes": { "arg1": "topic0" },
"p_apply": { "arg1": 5 } }
],
"containers": [
{ "type_name": "example_container", "name": "c0",
"processes": [
{ "type_name": "example_container_increment", "name": "c0inc" },
{ "type_name": "example_container_get", "name": "c0get" } ] }
]
})
}
struct Api {
client: reqwest::blocking::Client,
}
impl Api {
fn new() -> JuizResult<Self> {
Ok(Self {
client: reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()?,
})
}
fn url(path: &str) -> String {
format!("http://localhost:{PORT}/api/{path}")
}
fn get(&self, path: &str) -> JuizResult<Value> {
let res = self.client.get(Self::url(path)).send()?;
assert!(res.status().is_success(), "{path}: {:?}", res.text());
Ok(res.json()?)
}
fn profile_full(&self, class_name: &str, identifier: &str) -> JuizResult<Value> {
let res = self
.client
.get(Self::url(&format!("{class_name}/profile_full")))
.query(&[("identifier", identifier)])
.send()?;
assert!(
res.status().is_success(),
"{class_name} {identifier}: {:?}",
res.text()
);
Ok(res.json()?)
}
}
fn names(v: &Value) -> Vec<String> {
let mut names = v
.as_array()
.unwrap()
.iter()
.map(|p| p["name"].as_str().unwrap().to_owned())
.collect::<Vec<String>>();
names.sort();
names
}
fn check_profile_identifiers(api: &Api) -> JuizResult<()> {
let id = "core://core/process/sub0::increment_process";
let p = api.profile_full("process", id)?;
assert_eq!(p["identifier"], jvalue!(id));
assert_eq!(p["class_name"], jvalue!("process"));
let id = "core://core/container_process/c0get::example_container_get:c0";
let p = api.profile_full("container_process", id)?;
assert_eq!(p["identifier"], jvalue!(id));
assert_eq!(p["class_name"], jvalue!("container_process"));
let id = "core://core/container/c0::example_container";
let c = api.profile_full("container", id)?;
assert_eq!(c["identifier"], jvalue!(id));
assert_eq!(c["class_name"], jvalue!("container"));
assert_eq!(names(&c["processes"]), vec!["c0get", "c0inc"]);
for p in c["processes"].as_array().unwrap() {
let pid = p["identifier"].as_str().unwrap();
assert_eq!(
api.profile_full("container_process", pid)?["name"],
p["name"]
);
}
let sys = api.get("system/profile_full")?;
let store = &sys["core_store"];
for key in ["processes", "container_processes", "containers", "topics"] {
for p in store[key].as_array().unwrap() {
assert!(p["identifier"].is_string(), "{key}: {p}");
assert!(p["class_name"].is_string(), "{key}: {p}");
}
}
assert_eq!(
names(&store["containers"][0]["processes"]),
vec!["c0get", "c0inc"]
);
Ok(())
}
fn check_argument_values(api: &Api) -> JuizResult<()> {
let arg = |p: &Value| p["arguments"][0].clone();
let sub = api.profile_full("process", "core://core/process/sub0::increment_process")?;
assert_eq!(arg(&sub)["value"], jvalue!(5), "conf の p_apply");
let pub0 = api.profile_full("process", "core://core/process/pub0::increment_process")?;
assert!(arg(&pub0).get("value").is_none(), "{pub0}");
let id = "core://core/container_process/c0inc::example_container_increment:c0";
let res = api
.client
.patch(Api::url("container_process/p_apply"))
.query(&[("identifier", id)])
.json(&jvalue!({"arg_name": "arg1", "value": 7}))
.send()?;
assert!(res.status().is_success(), "{:?}", res.text());
assert_eq!(
arg(&api.profile_full("container_process", id)?)["value"],
jvalue!(7)
);
Ok(())
}
fn check_list_broker_names(api: &Api) -> JuizResult<()> {
for (path, count) in [
("process/list", 2),
("container/list", 1),
("container_process/list", 2),
("any_process/list", 4),
] {
let ids = api.get(path)?;
let ids = ids.as_array().unwrap();
assert_eq!(ids.len(), count, "{path}: {ids:?}");
for id in ids {
assert_eq!(id["broker_type_name"], jvalue!("http"), "{path}: {id}");
assert_eq!(
id["broker_name"],
jvalue!(format!("localhost:{PORT}")),
"{path}: {id}"
);
}
}
let id = format!("http://localhost:{PORT}/container_process/c0get::example_container_get:c0");
assert_eq!(
api.profile_full("any_process", &id)?["name"],
jvalue!("c0get")
);
let id = format!("http://localhost:{PORT}/process/pub0::increment_process");
assert_eq!(
api.profile_full("any_process", &id)?["name"],
jvalue!("pub0")
);
Ok(())
}
fn check_topic_connections(api: &Api) -> JuizResult<()> {
let ids = api.get("connection/list")?;
let ids = ids.as_array().unwrap();
assert_eq!(ids.len(), 2, "{ids:?}");
let mut arg_names = vec![];
for id in ids {
let c = api.profile_full("connection", id.as_str().unwrap())?;
arg_names.push(c["arg_name"].as_str().unwrap().to_owned());
}
arg_names.sort();
assert_eq!(arg_names, vec!["arg1", "input"]);
Ok(())
}
fn check_cors(api: &Api) -> JuizResult<()> {
let allow_origin = |origin: &str| -> JuizResult<Option<String>> {
let res = api
.client
.get(Api::url("process/list"))
.header("Origin", origin)
.send()?;
Ok(res
.headers()
.get("access-control-allow-origin")
.map(|v| v.to_str().unwrap().to_owned()))
};
assert_eq!(
allow_origin(ALLOWED_ORIGIN)?,
Some(ALLOWED_ORIGIN.to_owned())
);
assert_eq!(allow_origin("http://example.com")?, None);
let res = api
.client
.request(reqwest::Method::OPTIONS, Api::url("process/p_apply"))
.header("Origin", ALLOWED_ORIGIN)
.header("Access-Control-Request-Method", "PATCH")
.header("Access-Control-Request-Headers", "content-type")
.send()?;
assert!(res.status().is_success(), "{:?}", res.status());
assert_eq!(
res.headers().get("access-control-allow-origin").unwrap(),
ALLOWED_ORIGIN
);
Ok(())
}
#[test]
fn viewer_api_returns_identifiers_values_and_cors() -> JuizResult<()> {
let _ = env_logger::try_init();
System::new(system_manifest())?
.setup()?
.run_and_do_once(|_system| {
let api = Api::new()?;
check_profile_identifiers(&api)?;
check_argument_values(&api)?;
check_list_broker_names(&api)?;
check_topic_connections(&api)?;
check_cors(&api)?;
Ok(())
})
}