1//! A host's answers to the script, as text that two hosts can be compared on with `cmp`.
2
3use crate::host::{Host, Reply};
4use crate::script::{Step, script};
5
6/// One step's answer as a block: its name, the status, the content type, the body.
7pub fn block(step: &Step, reply: &Reply) -> String {
8    format!("### {}\n{} {}\n{}\n{}\n", step.name, reply.status, reply.content_type.as_deref().unwrap_or("-"), reply.body.len(), reply.body)
9}
10
11/// A step that could not be asked at all.
12pub fn failed(step: &Step, why: &str) -> String {
13    format!("### {}\nNO ANSWER {why}\n", step.name)
14}
15
16/// Runs the script against `host` in `household` and returns the transcript and the elapsed time of each step.
17pub fn run(host: &Host, household: &str) -> (String, Vec<(String, std::time::Duration)>) {
18    let mut text = String::new();
19    let mut times = Vec::new();
20    for step in script() {
21        let started = std::time::Instant::now();
22        let answer = host.send(step.method, step.path, household, &step.body);
23        times.push((step.name.to_owned(), started.elapsed()));
24        text.push_str(&match answer {
25            Ok(reply) => block(&step, &reply),
26            Err(e) => failed(&step, &e),
27        });
28    }
29    (text, times)
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35
36    #[test]
37    fn a_block_says_what_a_device_could_see() {
38        let step = Step { name: "n", method: "POST", path: "/p", body: String::new(), expect: 200 };
39        let reply = Reply { status: 200, content_type: Some("application/json".into()), body: "{}".into() };
40        assert_eq!(block(&step, &reply), "### n\n200 application/json\n2\n{}\n");
41        let none = Reply { status: 405, content_type: None, body: String::new() };
42        assert_eq!(block(&step, &none), "### n\n405 -\n0\n\n");
43    }
44}