fusible 0.20.0

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

use super::git_resolve::Keep;
use super::git_route::Route;
use super::git_support::{ask, save, sh, Pair};

const PCB: &str = "board.kicad_pcb";

fn edit(dir: &Path, middle: &str) {
    std::fs::write(
        dir.join(PCB),
        format!("(pcb\n  {middle}\n  (b 2)\n  (c 3)\n)\n"),
    )
    .unwrap();
}

fn text(dir: &Path) -> String {
    std::fs::read_to_string(dir.join(PCB)).unwrap()
}

fn stuck() -> (Pair, super::git_route::Reply) {
    let pair = Pair::new();
    edit(&pair.alice, "(a 10)");
    assert_eq!(save(&pair.alice, "Alice moves a").code, 200);
    edit(&pair.bob, "(a 20)");
    let reply = save(&pair.bob, "Bob moves a");
    (pair, reply)
}

fn resolve(dir: &Path, keep: Keep) -> super::git_route::Reply {
    ask(
        dir,
        Route::Resolve {
            path: PCB.into(),
            keep,
        },
    )
}

#[test]
fn a_same_file_edit_waits_for_a_decision() {
    let (pair, reply) = stuck();
    assert_eq!(reply.code, 200, "{}", reply.body);
    let status = &reply.body["status"];
    assert_eq!(status["conflicts"][0], PCB);
    assert_eq!(status["conflict_authors"][PCB], "alice");
    assert_eq!(status["branch"], "main");
    let blocked = save(&pair.bob, "again");
    assert_eq!(blocked.code, 409);
}

#[test]
fn keep_mine_puts_my_version_on_top_and_shares_it() {
    let (pair, _) = stuck();
    let reply = resolve(&pair.bob, Keep::Mine);
    assert_eq!(reply.code, 200, "{}", reply.body);
    assert_eq!(
        reply.body["status"]["conflicts"].as_array().unwrap().len(),
        0
    );
    assert!(text(&pair.bob).contains("(a 20)"));
    assert!(!text(&pair.bob).contains("<<<<"));
    let shared = save(&pair.bob, "");
    assert_eq!(shared.body["message"], "Saved and shared.");
    sh(&pair.alice, &["pull", "-q"]);
    assert!(text(&pair.alice).contains("(a 20)"));
}

#[test]
fn keep_theirs_takes_the_other_persons_version() {
    let (pair, _) = stuck();
    let reply = resolve(&pair.bob, Keep::Theirs);
    assert_eq!(reply.code, 200, "{}", reply.body);
    assert!(text(&pair.bob).contains("(a 10)"));
    assert!(!text(&pair.bob).contains("<<<<"));
    assert_eq!(
        reply.body["status"]["conflicts"].as_array().unwrap().len(),
        0
    );
    let status = ask(&pair.bob, Route::Status { check: false });
    assert_eq!(status.body["branch"], "main");
    assert_eq!(status.body["ahead"], 0);
}

#[test]
fn resolving_a_file_that_is_not_stuck_is_refused() {
    let pair = Pair::new();
    let reply = ask(
        &pair.alice,
        Route::Resolve {
            path: "../../etc/passwd".into(),
            keep: Keep::Mine,
        },
    );
    assert_eq!(reply.code, 400);
}