use kcode_web_libs::{File, Lib, create, docs, open};
use std::fs;
use std::path::PathBuf;
use tempfile::TempDir;
fn source_root(root: &TempDir) -> PathBuf {
root.path().join("source")
}
fn publications_root(root: &TempDir) -> PathBuf {
root.path().join("publications")
}
fn contents_mut<'a>(library: &'a mut Lib, path: &str) -> &'a mut String {
&mut library
.files
.iter_mut()
.find(|file| file.path == path)
.unwrap()
.contents
}
#[test]
fn create_open_and_docs_return_the_complete_sorted_source() {
let root = TempDir::new().unwrap();
let created = create(
source_root(&root),
publications_root(&root),
"Example_lib-2",
)
.unwrap();
assert_eq!(
created
.files
.iter()
.map(|file| file.path.as_str())
.collect::<Vec<_>>(),
["Documentation.md", "index.js", "kcode-web.json", "tests.js"]
);
let opened = open(
source_root(&root),
publications_root(&root),
"Example_lib-2",
)
.unwrap();
assert_eq!(opened.files, created.files);
let (version, documentation) = docs(source_root(&root), "Example_lib-2").unwrap();
assert_eq!(version, "0.1.0");
assert!(documentation.starts_with("# Example_lib-2\n"));
}
#[test]
fn source_and_publication_roots_must_be_disjoint() {
let root = TempDir::new().unwrap();
let source = source_root(&root);
let nested_publications = source.join(".published");
let error = create(&source, &nested_publications, "nested").unwrap_err();
assert_eq!(
error.to_string(),
"invalid_publication_storage: source and publication roots must be disjoint"
);
let shared = root.path().join("shared");
let error = create(&shared, &shared, "shared").unwrap_err();
assert_eq!(
error.to_string(),
"invalid_publication_storage: source and publication roots must be disjoint"
);
}
#[test]
fn complete_replacement_removes_omitted_files_and_old_generations() {
let root = TempDir::new().unwrap();
let mut library = create(source_root(&root), publications_root(&root), "demo").unwrap();
library.files.push(File {
path: "temporary.js".to_owned(),
contents: "export {};".to_owned(),
});
library.write().unwrap();
library.files.retain(|file| file.path != "temporary.js");
*contents_mut(&mut library, "Documentation.md") = "second write\n".to_owned();
library.write().unwrap();
let reopened = open(source_root(&root), publications_root(&root), "demo").unwrap();
assert!(
reopened
.files
.iter()
.all(|file| file.path != "temporary.js")
);
assert_eq!(
reopened
.files
.iter()
.find(|file| file.path == "Documentation.md")
.unwrap()
.contents,
"second write\n"
);
assert_eq!(
fs::read_dir(source_root(&root).join("demo/generations"))
.unwrap()
.count(),
1
);
}
#[test]
fn two_writers_from_one_snapshot_cannot_both_commit() {
let root = TempDir::new().unwrap();
create(source_root(&root), publications_root(&root), "demo").unwrap();
let mut first = open(source_root(&root), publications_root(&root), "demo").unwrap();
let mut second = open(source_root(&root), publications_root(&root), "demo").unwrap();
*contents_mut(&mut first, "Documentation.md") = "first\n".to_owned();
*contents_mut(&mut second, "Documentation.md") = "second\n".to_owned();
first.write().unwrap();
let error = second.write().unwrap_err();
assert!(error.to_string().starts_with("stale_snapshot:"));
assert_eq!(docs(source_root(&root), "demo").unwrap().1, "first\n");
}
#[test]
fn invalid_paths_and_metadata_fail_before_commit() {
let root = TempDir::new().unwrap();
let mut library = create(source_root(&root), publications_root(&root), "demo").unwrap();
let original = library.files.clone();
library.files.push(File {
path: "../escape".to_owned(),
contents: "bad".to_owned(),
});
assert!(library.write().is_err());
library.files = original;
*contents_mut(&mut library, "kcode-web.json") =
r#"{"name":"demo","version":"1.2.3-beta","entry":"index.js","tests":"tests.js"}"#
.to_owned();
assert!(library.write().is_err());
assert_eq!(docs(source_root(&root), "demo").unwrap().0, "0.1.0");
}
#[test]
fn check_uses_current_in_memory_source_without_requiring_a_write() {
let root = TempDir::new().unwrap();
let mut library = create(source_root(&root), publications_root(&root), "demo").unwrap();
library.files.push(File {
path: "../not-committed".to_owned(),
contents: "bad".to_owned(),
});
let error = library.check().unwrap_err();
assert!(error.to_string().starts_with("invalid_source:"));
}
#[test]
fn publish_rechecks_the_current_in_memory_source() {
let root = TempDir::new().unwrap();
let mut library = create(source_root(&root), publications_root(&root), "demo").unwrap();
*contents_mut(&mut library, "kcode-web.json") =
r#"{"name":"demo","version":"1.2.3-beta","entry":"index.js","tests":"tests.js"}"#
.to_owned();
let error = library.publish().unwrap_err();
assert!(error.to_string().starts_with("invalid_source:"));
assert!(!publications_root(&root).join("module").exists());
}
#[test]
#[ignore = "temporary diagnostic isolation"]
fn chromium_smoke_check_and_publish_a_browser_module() {
let root = TempDir::new().unwrap();
let mut library = create(
source_root(&root),
publications_root(&root),
"browser-smoke",
)
.unwrap();
*contents_mut(&mut library, "index.js") = r#"
export function mount(root) {
const heading = document.createElement("h1");
heading.textContent = "Hello, world!";
root.replaceChildren(heading);
}
"#
.trim_start()
.to_owned();
*contents_mut(&mut library, "tests.js") = r#"
import { mount } from "./index.js";
export async function runTests() {
await Promise.resolve();
const root = document.createElement("main");
mount(root);
if (root.textContent !== "Hello, world!") {
throw new Error(`unexpected rendered text: ${root.textContent}`);
}
}
"#
.trim_start()
.to_owned();
let expected_index = contents_mut(&mut library, "index.js").clone();
let expected_tests = contents_mut(&mut library, "tests.js").clone();
library.publish().unwrap();
let published = publications_root(&root).join("module/browser-smoke/v0.1.0");
assert_eq!(
fs::read_to_string(published.join("index.js")).unwrap(),
expected_index
);
assert_eq!(
fs::read_to_string(published.join("tests.js")).unwrap(),
expected_tests
);
}
#[test]
#[ignore = "temporary diagnostic isolation"]
fn chromium_smoke_reports_a_browser_test_failure() {
let root = TempDir::new().unwrap();
let mut library = create(
source_root(&root),
publications_root(&root),
"failing-browser-smoke",
)
.unwrap();
*contents_mut(&mut library, "tests.js") = r#"
export function runTests() {
throw new Error("deliberate Chromium smoke failure");
}
"#
.trim_start()
.to_owned();
let error = library.check().unwrap_err().to_string();
assert!(error.starts_with("check.test:"));
assert!(error.contains("deliberate Chromium smoke failure"));
}
#[cfg(unix)]
#[test]
fn docs_does_not_traverse_unrelated_source_but_open_rejects_symlinks() {
use std::os::unix::fs::symlink;
let root = TempDir::new().unwrap();
create(source_root(&root), publications_root(&root), "demo").unwrap();
let repository = source_root(&root).join("demo");
let generation = fs::read_to_string(repository.join("HEAD"))
.unwrap()
.trim()
.to_owned();
symlink(
root.path(),
repository
.join("generations")
.join(generation)
.join("unrelated-link"),
)
.unwrap();
assert_eq!(docs(source_root(&root), "demo").unwrap().0, "0.1.0");
assert!(open(source_root(&root), publications_root(&root), "demo").is_err());
}