use kcode_k1_transaction_id::TxId;
use kcode_k1_web_code_document::{CodeDocument, Language};
use kcode_k1_web_package::{AuthorityId, SourceFile, SourcePackage, WebFamily};
use semver::Version;
fn family() -> WebFamily {
WebFamily::new(AuthorityId::new(TxId::from_bytes([1; 12])), "alpha").unwrap()
}
fn version() -> Version {
Version::new(1, 2, 3)
}
fn docs(dependencies: &str) -> Vec<u8> {
format!("<!-- k1-web/v1\n{{\"dependencies\":{dependencies}}}\n-->\nBody\r\n").into_bytes()
}
fn document(language: Language, code: &str) -> CodeDocument {
CodeDocument::new(family(), docs("[]"), language, code.as_bytes().to_vec()).unwrap()
}
fn files(source: &SourcePackage) -> Vec<&str> {
source.files().iter().map(|file| file.path()).collect()
}
fn joined(value: &CodeDocument) -> Vec<u8> {
value
.chunk_ranges()
.into_iter()
.flat_map(|range| value.code()[range].iter().copied())
.collect()
}
#[test]
fn version_is_injected_only_into_runtime_projection() {
let value = document(Language::JavaScript, "export async function runTests(){}");
assert_eq!(value.family(), &family());
let source = value.to_source_package(version()).unwrap();
assert_eq!(source.id().version(), &version());
assert_eq!(CodeDocument::from_source_package(&source).unwrap(), value);
}
#[test]
fn exact_runtime_shapes() {
let cases = [
(
Language::JavaScript,
"export async function runTests(){}",
vec!["Code.js", "Documentation.md", "k1-web.json"],
"Code.js",
),
(
Language::Html,
"<!doctype html><script>globalThis.runTests=async()=>{};</script>",
vec![
"Code.html",
"Documentation.md",
"k1-entry.js",
"k1-tests.js",
"k1-web.json",
],
"k1-entry.js",
),
(
Language::Css,
"body{color:red}",
vec![
"Code.css",
"Documentation.md",
"k1-entry.js",
"k1-tests.js",
"k1-web.json",
],
"k1-entry.js",
),
];
for (language, code, paths, entry) in cases {
let value = document(language, code);
let source = value.to_source_package(version()).unwrap();
assert_eq!(files(&source), paths);
assert_eq!(source.entry(), entry);
assert_eq!(
source
.files()
.iter()
.find(|file| file.path() == language.code_path())
.unwrap()
.bytes(),
code.as_bytes()
);
}
}
#[test]
fn header_is_strict_and_source_validation_is_reused() {
let authority = AuthorityId::new(TxId::from_bytes([2; 12])).to_string();
let dependency = format!(r#"[{{"authority":"{authority}","name":"beta","selector":"1"}}]"#);
assert_eq!(
CodeDocument::new(family(), docs(&dependency), Language::Css, b"a{}".to_vec())
.unwrap()
.dependencies()[0]
.name(),
"beta"
);
for json in [
" {}",
"{\"dependencies\":[],\"extra\":0}",
"{\"dependencies\":[{\"authority\":\"\",\"name\":\"a\",\"selector\":\"*\"}]}",
"{\"dependencies\":[{\"authority\":\"bad\",\"name\":\"a\",\"selector\":\"*\"}]}",
] {
let text = format!("<!-- k1-web/v1\n{json}\n-->\n");
assert!(
CodeDocument::new(family(), text.into_bytes(), Language::Css, b"a{}".to_vec()).is_err(),
"{json}"
);
}
}
#[test]
fn recovery_rejects_extra_or_changed_projection() {
let value = document(Language::Html, "<p>ok</p>");
let source = value.to_source_package(version()).unwrap();
let mut extra = source.files().to_vec();
extra.push(SourceFile::new("extra.bin", vec![1]));
let extra = SourcePackage::new(source.id().clone(), extra).unwrap();
assert!(CodeDocument::from_source_package(&extra).is_err());
let changed = source
.files()
.iter()
.map(|file| {
if file.path() == "k1-entry.js" {
SourceFile::new(file.path(), b"export {};".to_vec())
} else {
file.clone()
}
})
.collect();
let changed = SourcePackage::new(source.id().clone(), changed).unwrap();
assert!(CodeDocument::from_source_package(&changed).is_err());
}
#[test]
fn chunks_preserve_bytes_and_safety() {
let code = format!(
"\u{feff}const a=`{}`;\r\nconst b=/{}/;\r\nconst c='{}';\r\n",
"x".repeat(850),
"y".repeat(850),
"z".repeat(100)
);
let value = document(Language::JavaScript, &code);
assert_eq!(joined(&value), code.as_bytes());
assert_eq!(value.chunk_ranges().len(), 2);
let js = "/** docs */\nexport class A{m(){const x=`a${/x[;}]/.test('x') ? `{}` : '//'};return x;} n=1;}\n".repeat(30);
let value = document(Language::JavaScript, &js);
assert_eq!(joined(&value), value.code());
assert!(value.chunk_ranges().len() > 1);
let css = "@media screen{.a{background:url(\"data:x;y\");/* } */color:red}.b{content:'}';}}\n"
.repeat(30);
let value = document(Language::Css, &css);
assert_eq!(joined(&value), value.code());
assert!(value.chunk_ranges().len() > 1);
let html = "<!--x--><section data-x='>&'><p>text</p><script>const x=`<b>${/x/.test('x')}</b>`;</script><style>.a{color:red}</style></section>".repeat(20);
let value = document(Language::Html, &html);
assert_eq!(joined(&value), value.code());
assert!(value.chunk_ranges().len() > 1);
assert_eq!(
document(Language::JavaScript, "const x='unterminated")
.chunk_ranges()
.len(),
1
);
assert_eq!(document(Language::Css, "").chunk_ranges()[0], 0..0);
}