use super::*;
use axum::body::Body;
use axum::http::Request;
use tower::ServiceExt;
fn git(dir: &std::path::Path, args: &[&str]) {
let output = wsb::git_backend::git_command()
.arg("-C")
.arg(dir)
.args(args)
.output()
.expect("git runs");
assert!(output.status.success(), "git {args:?}: {}", String::from_utf8_lossy(&output.stderr));
}
async fn call(
router: &Router,
method: &str,
uri: &str,
body: Option<serde_json::Value>,
) -> (StatusCode, serde_json::Value) {
let request = Request::builder()
.method(method)
.uri(uri)
.header("content-type", "application/json")
.body(body.map_or_else(Body::empty, |body| Body::from(body.to_string())))
.expect("a request");
let response = router.clone().oneshot(request).await.expect("the router answers");
let status = response.status();
let bytes = hyper::body::to_bytes(response.into_body()).await.expect("a body");
(status, serde_json::from_slice(&bytes).unwrap_or(serde_json::Value::Null))
}
#[tokio::test]
async fn test20122_notes_and_links_are_written_where_the_page_says_and_read_by_ref() {
let prj = tempfile::tempdir().expect("a workshop");
let machine_dir = tempfile::tempdir().expect("a machine folder");
let root = wsb::platform::canonicalize(prj.path()).expect("resolves");
let _env = wsb::test_environment::Environment::set(&[
("PRJ", &root.to_string_lossy()),
(wsb::daemon::MACHINE_DIR_ENV, &machine_dir.path().to_string_lossy()),
]);
let main = root.join("names/main-worktree");
std::fs::create_dir_all(main.join("src")).expect("a worktree");
git(&main, &["init", "--quiet"]);
git(&main, &["remote", "add", "origin", "git@github.com:owner/names.git"]);
wsb::settings::RepoSettings {
name: "names".to_string(),
alias: "names".to_string(),
templates_root: ".".to_string(),
version: wsb::settings::VersionSettings::at_major(0),
}
.save(&main)
.expect("managed");
let machine = crate::machine::MachineState::new(
machine_dir.path().to_path_buf(),
&machine_dir.path().join("wsb.db"),
std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
0,
std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
0,
)
.expect("a machine");
machine.identity.rename(machine_dir.path(), "bench").expect("named");
let router = routes().with_state(machine);
let file = "path:owner/names/src/lib.rs";
let (status, made) = call(
&router,
"POST",
"/api/v1/annotations/notes",
Some(json!({ "worktree": "names:main", "title": "Lazy parser", "body": "Because.", "about": [file] })),
)
.await;
assert_eq!(status, StatusCode::CREATED, "{made}");
let id = made["note"]["id"].as_str().expect("an id").to_string();
assert_eq!(made["note"]["machine"], "bench", "the note says which machine wrote it");
assert!(main.join(format!(".wsb/notes/{id}.md")).is_file(), "the note is in the worktree it names");
let (status, about) = call(&router, "GET", &format!("/api/v1/annotations?ref={file}"), None).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(about["notes"].as_array().map(Vec::len), Some(1), "{about}");
assert_eq!(about["notes"][0]["id"], id.as_str());
let (_, unrelated) = call(&router, "GET", "/api/v1/annotations?ref=path:owner/names/README.md", None).await;
assert_eq!(unrelated["notes"].as_array().map(Vec::len), Some(0), "{unrelated}");
let link = json!({ "worktree": "names:main", "from": "project:owner/names", "to": "url:https://example.com/spec", "label": "spec" });
let (status, linked) = call(&router, "POST", "/api/v1/annotations/links", Some(link.clone())).await;
assert_eq!(status, StatusCode::CREATED, "{linked}");
let (_, at) = call(&router, "GET", "/api/v1/annotations?ref=project:owner/names", None).await;
assert_eq!(at["links"].as_array().map(Vec::len), Some(1), "{at}");
let (status, _) = call(&router, "DELETE", "/api/v1/annotations/links", Some(link)).await;
assert_eq!(status, StatusCode::OK);
let (status, removed) =
call(&router, "DELETE", &format!("/api/v1/annotations/notes/{id}?worktree=names:main"), None).await;
assert_eq!(status, StatusCode::OK, "{removed}");
assert_eq!(removed["removed"].as_array().map(Vec::len), Some(2), "the note and its link: {removed}");
let (_, everything) = call(&router, "GET", "/api/v1/annotations", None).await;
assert_eq!(everything["notes"].as_array().map(Vec::len), Some(0), "{everything}");
assert_eq!(everything["links"].as_array().map(Vec::len), Some(0), "{everything}");
let note = |worktree: &str, about: &str| {
Some(json!({ "worktree": worktree, "title": "t", "about": [about] }))
};
let (status, _) = call(&router, "POST", "/api/v1/annotations/notes", note("names:fix", file)).await;
assert_eq!(status, StatusCode::NOT_FOUND, "a worktree that is not there");
let (status, _) = call(&router, "POST", "/api/v1/annotations/notes", note("names:main", "nonsense")).await;
assert_eq!(status, StatusCode::BAD_REQUEST, "a ref that does not parse");
let (status, _) = call(
&router,
"POST",
"/api/v1/annotations/notes",
Some(json!({ "worktree": "names:main", "title": "t", "pinned": true })),
)
.await;
assert_eq!(status, StatusCode::UNPROCESSABLE_ENTITY, "a field the route does not take");
assert!(!main.join(".wsb/notes").read_dir().map(|mut d| d.next().is_some()).unwrap_or(false), "a refused note was written");
}