use std::fs;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
use kcode_dev_tools::{
CALL_RUST_BIN_TOOL, CREATE_RUST_BIN_TOOL, CREATE_RUST_LIB_TOOL, CREATE_WEB_LIB_TOOL, Config,
DOCS_RUST_BIN_TOOL, DOCS_RUST_LIB_TOOL, DOCS_WEB_LIB_TOOL, ManagedSourceKind, Service,
WRITE_FILE_FREEFORM_RUST_BIN_TOOL, WRITE_FILE_FREEFORM_RUST_LIB_TOOL,
WRITE_FILE_FREEFORM_WEB_LIB_TOOL,
};
use serde_json::json;
static TEST_COUNTER: AtomicU64 = AtomicU64::new(0);
struct TestRoot(PathBuf);
impl TestRoot {
fn new() -> Self {
let counter = TEST_COUNTER.fetch_add(1, Ordering::Relaxed);
let path = std::env::temp_dir().join(format!(
"kcode-dev-tools-integration-{}-{counter}",
std::process::id()
));
fs::create_dir(&path).unwrap();
Self(path)
}
fn path(&self) -> &Path {
&self.0
}
}
impl Drop for TestRoot {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.0);
}
}
fn service(root: &Path) -> Service {
Service::open(Config {
rust_libraries_root: root.join("rust-libs"),
web_libraries_root: root.join("web-libs"),
web_publications_root: root.join("web-publications"),
rust_binaries_root: root.join("rust-bins"),
rust_binary_publications_root: root.join("rust-bin-publications"),
crates_io_registry_token: "test-token".to_owned(),
})
.unwrap()
}
#[tokio::test]
async fn one_service_manages_all_three_source_kinds_and_releases_together() {
let root = TestRoot::new();
let service = service(root.path());
assert_eq!(
service.web_publications_root(),
fs::canonicalize(root.path().join("web-publications")).unwrap()
);
let session = "conversation:test";
for (create, write, docs, name, path, kind) in [
(
CREATE_RUST_LIB_TOOL,
WRITE_FILE_FREEFORM_RUST_LIB_TOOL,
DOCS_RUST_LIB_TOOL,
"demo-lib",
"src/extra.rs",
ManagedSourceKind::RustLibrary,
),
(
CREATE_WEB_LIB_TOOL,
WRITE_FILE_FREEFORM_WEB_LIB_TOOL,
DOCS_WEB_LIB_TOOL,
"demo-web",
"extra.js",
ManagedSourceKind::WebLibrary,
),
(
CREATE_RUST_BIN_TOOL,
WRITE_FILE_FREEFORM_RUST_BIN_TOOL,
DOCS_RUST_BIN_TOOL,
"demo-bin",
"src/extra.rs",
ManagedSourceKind::RustBinary,
),
] {
let created = service
.execute(session, create, json!({"name":name}), Vec::new())
.await
.unwrap();
assert_eq!(created.snapshot.as_ref().unwrap().kind, kind);
let written = service
.execute(
session,
write,
json!({"name":name,"path":path,"contents":"// managed\n"}),
Vec::new(),
)
.await
.unwrap();
assert!(written.snapshot.unwrap().text.contains(path));
let documentation = service
.execute(session, docs, json!({"name":name}), Vec::new())
.await
.unwrap();
assert!(documentation.text.contains("Version: 0.1.0"));
}
assert_eq!(service.release(session).await.unwrap(), 3);
}
#[tokio::test]
async fn only_binary_calls_accept_one_resolved_object_per_object_id() {
let root = TestRoot::new();
let service = service(root.path());
let mismatch = service
.execute(
"call:test",
CALL_RUST_BIN_TOOL,
json!({
"name":"demo",
"version":"^1",
"objectIds":["pending:1"],
}),
Vec::new(),
)
.await
.unwrap_err();
assert_eq!(mismatch.code, "invalid_arguments");
assert_eq!(
mismatch.message,
"objectIds and resolved objects must have the same length."
);
let wrong_tool = service
.execute(
"call:test",
DOCS_RUST_LIB_TOOL,
json!({"name":"demo"}),
vec![vec![1, 2, 3]],
)
.await
.unwrap_err();
assert_eq!(wrong_tool.code, "invalid_arguments");
assert_eq!(
wrong_tool.message,
"Resolved objects are accepted only by the Rust-binary call tool."
);
}
#[test]
fn web_source_and_publication_roots_must_be_disjoint() {
let root = TestRoot::new();
let shared = root.path().join("web");
let error = match Service::open(Config {
rust_libraries_root: root.path().join("rust-libs"),
web_libraries_root: shared.clone(),
web_publications_root: shared,
rust_binaries_root: root.path().join("rust-bins"),
rust_binary_publications_root: root.path().join("rust-bin-publications"),
crates_io_registry_token: "test-token".to_owned(),
}) {
Ok(_) => panic!("overlapping Web roots were accepted"),
Err(error) => error,
};
assert_eq!(error.code, "dev_tools_invalid_root");
assert_eq!(
error.message,
"Managed Web source and publication roots must be disjoint."
);
}