fusible 0.18.0

Electronic linter: a fuse that blows to block firmware code generation
use std::fs;
use std::path::PathBuf;

use serde_json::Value;

use super::assembly::{model_reply, report_json};
use crate::config::FusibleConfig;

fn manifest(assembly: bool) -> FusibleConfig {
    let tail = if assembly {
        "assembly: {parts: {main: {}}}\n"
    } else {
        ""
    };
    FusibleConfig::from_str(&format!("boards: {{main: {{}}}}\n{tail}")).unwrap()
}

fn project(name: &str) -> PathBuf {
    let dir = std::env::temp_dir().join(format!("fusible-asm-{name}-{}", std::process::id()));
    let _ = fs::remove_dir_all(&dir);
    fs::create_dir_all(&dir).unwrap();
    dir
}

fn parsed(json: String) -> Value {
    serde_json::from_str(&json).unwrap()
}

#[test]
fn undeclared_assembly_reports_nothing() {
    let dir = project("none");
    let body = parsed(report_json(Some(&manifest(false)), &dir));
    assert_eq!(
        body,
        serde_json::json!({"declared": false, "report": null, "model": null})
    );
    assert_eq!(model_reply(Some(&manifest(false)), &dir).0, 404);
    assert_eq!(model_reply(None, &dir).0, 404);
}

#[test]
fn declared_without_build_has_no_model() {
    let dir = project("unbuilt");
    let body = parsed(report_json(Some(&manifest(true)), &dir));
    assert_eq!(body["declared"], true);
    assert!(body["report"].is_null());
    assert!(body["model"].is_null());
    let (status, kind, payload) = model_reply(Some(&manifest(true)), &dir);
    assert_eq!((status, kind), (404, "application/json"));
    assert!(String::from_utf8(payload).unwrap().contains("error"));
}

#[test]
fn built_assembly_serves_report_and_model() {
    let dir = project("built");
    let parts = dir.join("dist/assembly");
    fs::create_dir_all(&parts).unwrap();
    fs::write(
        parts.join("check.json"),
        concat!(
            r#"{"version":1,"fuse":"blown","violations":["#,
            r#"{"rule":"body_interference","message":"m","parts":["case"],"collision":"collision:0"}]}"#
        ),
    )
    .unwrap();
    fs::write(parts.join("preview.glb"), b"glTF").unwrap();
    let body = parsed(report_json(Some(&manifest(true)), &dir));
    assert_eq!(body["report"]["fuse"], "blown");
    assert_eq!(body["report"]["violations"][0]["collision"], "collision:0");
    assert!(body["model"]
        .as_str()
        .unwrap()
        .starts_with("/api/assembly/model?rev="));
    let (status, kind, payload) = model_reply(Some(&manifest(true)), &dir);
    assert_eq!((status, kind), (200, "model/gltf-binary"));
    assert_eq!(payload, b"glTF");
}

#[test]
fn custom_out_dir_is_honoured() {
    let dir = project("outdir");
    let parts = dir.join("out/assembly");
    fs::create_dir_all(&parts).unwrap();
    fs::write(parts.join("preview.glb"), b"glTF").unwrap();
    let cfg = FusibleConfig::from_str(
        "boards: {main: {}}\nassembly: {out_dir: out, parts: {main: {}}}\n",
    )
    .unwrap();
    assert_eq!(model_reply(Some(&cfg), &dir).0, 200);
}

#[test]
fn a_corrupt_report_is_an_error_not_a_clean_model() {
    let dir = project("corrupt");
    let parts = dir.join("dist/assembly");
    fs::create_dir_all(&parts).unwrap();
    fs::write(parts.join("check.json"), b"{\"version\":1,\"fuse\":").unwrap();
    fs::write(parts.join("preview.glb"), b"glTF").unwrap();
    let body = parsed(report_json(Some(&manifest(true)), &dir));
    assert!(body["report"].is_null());
    assert!(body["report_error"]
        .as_str()
        .unwrap()
        .contains("does not parse"));
    assert!(body["model"].is_string());
    let clean = parsed(report_json(Some(&manifest(true)), &project("noreport")));
    assert!(clean["report_error"].is_null());
}