use kcode_k1_transaction_id::TxId;
use kcode_k1_web_code_session::{DocumentPart, SessionError, SessionScope};
use kcode_k1_web_package::{AuthorityId, SourceFile, SourcePackage, WebFamily, WebId};
use semver::Version;
use std::sync::Arc;
const USER: [u8; 12] = [0x21; 12];
const OTHER_USER: [u8; 12] = [0x34; 12];
const DOCUMENTATION: &str = concat!(
"<!-- k1-web/v1\n",
"{\"dependencies\":[]}\n",
"-->\n",
"\u{feff}Original documentation\r\n",
"with exact bytes.\n",
);
const REPLACEMENT_DOCUMENTATION: &str = concat!(
"<!-- k1-web/v1\n",
"{\"dependencies\":[]}\n",
"-->\n",
"\u{feff}Replacement documentation\r\n",
"without normalization.\n",
);
const CODE: &str = "export async function runTests() {\r\n return true;\r\n}\n";
fn source(documentation: &str, code: &str) -> SourcePackage {
let authority = AuthorityId::new(TxId::from_bytes([0x13; 12]));
let family = WebFamily::new(authority, "contract-fixture").unwrap();
let id = WebId::new(family, Version::new(1, 2, 3)).unwrap();
let manifest = concat!(
"{\"name\":\"contract-fixture\",\"version\":\"1.2.3\",",
"\"entry\":\"Code.js\",\"tests\":\"Code.js\",\"dependencies\":[]}"
);
SourcePackage::new(
id,
vec![
SourceFile::new("Documentation.md", documentation.as_bytes().to_vec()),
SourceFile::new("k1-web.json", manifest.as_bytes().to_vec()),
SourceFile::new("Code.js", code.as_bytes().to_vec()),
],
)
.unwrap()
}
#[test]
fn recovers_and_validates_only_the_canonical_session_source() {
let scope = SessionScope::new();
let current = Arc::new(source(DOCUMENTATION, CODE));
let opened = scope.recover(USER, Arc::clone(¤t)).unwrap();
assert_eq!(opened.document().documentation(), DOCUMENTATION.as_bytes());
assert_eq!(opened.document().code(), CODE.as_bytes());
assert_eq!(Arc::strong_count(¤t), 2);
assert_eq!(scope.validate(USER, &opened, current.as_ref()), Ok(()));
let foreign_scope = SessionScope::new();
assert_eq!(
foreign_scope.validate(USER, &opened, current.as_ref()),
Err(SessionError::ForeignHandle)
);
assert_eq!(
scope.validate(OTHER_USER, &opened, current.as_ref()),
Err(SessionError::ForeignHandle)
);
let stale = source(REPLACEMENT_DOCUMENTATION, CODE);
assert_eq!(
scope.validate(USER, &opened, &stale),
Err(SessionError::StaleSource)
);
}
#[test]
fn documentation_replacement_preserves_code_and_refreshes_exactly() {
let scope = SessionScope::new();
let current = Arc::new(source(DOCUMENTATION, CODE));
let opened = scope.recover(USER, Arc::clone(¤t)).unwrap();
let original_id = opened.document().id().clone();
let replaced = scope
.replacement(
USER,
&opened,
current.as_ref(),
DocumentPart::Documentation,
REPLACEMENT_DOCUMENTATION.to_owned(),
)
.unwrap();
let replaced = Arc::new(replaced);
let refreshed = scope.refresh(USER, Arc::clone(&replaced)).unwrap();
assert_eq!(refreshed.document().id(), &original_id);
assert_eq!(
refreshed.document().documentation(),
REPLACEMENT_DOCUMENTATION.as_bytes()
);
assert_eq!(refreshed.document().code(), CODE.as_bytes());
assert_eq!(
scope.validate(USER, &opened, replaced.as_ref()),
Err(SessionError::StaleSource)
);
assert_eq!(scope.validate(USER, &refreshed, replaced.as_ref()), Ok(()));
}
#[test]
fn code_replacement_preserves_documentation_and_refreshes_exactly() {
let scope = SessionScope::new();
let current = Arc::new(source(DOCUMENTATION, CODE));
let opened = scope.recover(USER, Arc::clone(¤t)).unwrap();
let original_id = opened.document().id().clone();
let replacement = "export async function runTests() {\n\treturn \"replacement\";\n}\n";
let replaced = scope
.replacement(
USER,
&opened,
current.as_ref(),
DocumentPart::Code,
replacement.to_owned(),
)
.unwrap();
let replaced = Arc::new(replaced);
let refreshed = scope.refresh(USER, Arc::clone(&replaced)).unwrap();
assert_eq!(refreshed.document().id(), &original_id);
assert_eq!(
refreshed.document().documentation(),
DOCUMENTATION.as_bytes()
);
assert_eq!(refreshed.document().code(), replacement.as_bytes());
assert_eq!(scope.validate(USER, &refreshed, replaced.as_ref()), Ok(()));
}
#[test]
fn evidence_matches_current_source_and_is_invalidated_by_replacement() {
let scope = SessionScope::new();
let current = Arc::new(source(DOCUMENTATION, CODE));
let opened = scope.recover(USER, Arc::clone(¤t)).unwrap();
let evidence = scope
.mint_evidence(USER, &opened, current.as_ref())
.unwrap();
assert_eq!(
scope.evidence_matches(USER, &opened, current.as_ref(), &evidence),
Ok(true)
);
let replaced = scope
.replacement(
USER,
&opened,
current.as_ref(),
DocumentPart::Code,
"export async function runTests() { return false; }\n".to_owned(),
)
.unwrap();
let replaced = Arc::new(replaced);
let refreshed = scope.refresh(USER, Arc::clone(&replaced)).unwrap();
assert_eq!(
scope.evidence_matches(USER, &refreshed, replaced.as_ref(), &evidence),
Ok(false)
);
}