use super::*;
const TWO_CAPABILITY_DISPATCH: &str = concat!(
"pub fn dispatch_audit_exports(harness: Harness, args: dict) -> dict {\n",
" const rows = harness.postgres.query(args.database_url, \"select 1\")\n",
" harness.net.post(args.sink_url, rows)\n",
" return {status: \"ok\"}\n",
"}\n",
);
fn migrate(source: &str) -> String {
let temp = tempfile::TempDir::new().unwrap();
let script = temp.path().join("audit_export.harn");
fs::write(&script, source).unwrap();
apply_repairs_with_options_at(
temp.path(),
RepairSafety::SurfaceChanging,
false,
super::FixOptions::capability_migrations(),
)
.unwrap();
fs::read_to_string(&script).unwrap()
}
#[test]
fn capability_migration_narrows_an_undeclared_two_capability_entry_point() {
let migrated = migrate(TWO_CAPABILITY_DISPATCH);
assert!(
migrated.contains("postgres: HarnessPostgres"),
"undeclared entry point should still be narrowed to a record: {migrated}"
);
assert!(
!migrated.contains("harness: Harness,"),
"undeclared entry point should no longer take root Harness: {migrated}"
);
}
#[test]
fn capability_migration_leaves_a_declared_host_entry_point_alone() {
let declared = format!("@host_entry\n{TWO_CAPABILITY_DISPATCH}");
let migrated = migrate(&declared);
assert_eq!(
migrated, declared,
"a host-entered signature is a contract with a caller no Harn source can see"
);
}
#[test]
fn capability_migration_never_proposes_a_record_carrier_for_a_host_entry() {
for extra in ["", " harness.fs.read_file(args.path)\n"] {
let source = format!(
"@host_entry\npub fn dispatch(harness: Harness, args: dict) -> dict {{\n\
\x20 const rows = harness.postgres.query(args.database_url, \"select 1\")\n\
\x20 harness.net.post(args.sink_url, rows)\n\
{extra}\
\x20 return {{status: \"ok\"}}\n\
}}\n"
);
let migrated = migrate(&source);
let signature = migrated
.split("-> dict {")
.next()
.expect("signature precedes the body")
.to_string();
assert!(
!signature.contains("HarnessFs")
&& !signature.contains("HarnessNet")
&& !signature.contains("HarnessPostgres"),
"expected no record carrier in the signature, got: {signature}"
);
assert!(
signature.contains("harness: Harness"),
"expected root Harness to survive, got: {signature}"
);
}
}
const AMBIENT_HOST_ENTRY: &str = concat!(
"@host_entry\n",
"pub fn dispatch(args: dict) -> string {\n",
" return read_text(args.path)\n",
"}\n",
"\n",
"pub fn summarize(path: string) -> string {\n",
" return read_text(path)\n",
"}\n",
);
fn write_and_migrate(source: &str) -> String {
let temp = tempfile::TempDir::new().unwrap();
let script = temp.path().join("main.harn");
fs::write(&script, source).unwrap();
apply_repairs_with_options_at(
temp.path(),
RepairSafety::SurfaceChanging,
false,
super::FixOptions::capability_migrations(),
)
.unwrap();
fs::read_to_string(&script).unwrap()
}
#[test]
fn ambient_migration_threads_an_undeclared_entry_point() {
let migrated = write_and_migrate(&AMBIENT_HOST_ENTRY.replace("@host_entry\n", ""));
assert!(
migrated.contains("pub fn dispatch(fs: HarnessFs, args: dict)"),
"an undeclared entry point should still be threaded: {migrated}"
);
}
#[test]
fn ambient_migration_leaves_a_declared_host_entry_signature_alone() {
let migrated = write_and_migrate(AMBIENT_HOST_ENTRY);
assert!(
migrated.contains("pub fn dispatch(args: dict) -> string"),
"a host-entered signature must not gain a parameter: {migrated}"
);
}
#[test]
fn ambient_migration_still_repairs_a_sibling_of_a_frozen_host_entry() {
let migrated = write_and_migrate(AMBIENT_HOST_ENTRY);
assert!(
migrated.contains("pub fn summarize(fs: HarnessFs, path: string)"),
"the sibling must still be migrated: {migrated}"
);
assert!(
migrated.contains("fs.read_text(path)"),
"the sibling's body must still be rewritten: {migrated}"
);
}
#[test]
fn a_declared_host_entry_does_not_get_a_parameter_propagated_into_it() {
const CALLS_A_HELPER: &str = concat!(
"@host_entry\n",
"pub fn dispatch(args: dict) -> string {\n",
" return summarize(args.path)\n",
"}\n",
"\n",
"fn summarize(path: string) -> string {\n",
" return read_text(path)\n",
"}\n",
);
let migrated = write_and_migrate(CALLS_A_HELPER);
assert!(
migrated.contains("pub fn dispatch(args: dict) -> string"),
"propagation must stop at the declared entry point: {migrated}"
);
assert!(
!migrated.contains("dispatch(harness"),
"no capability parameter may be propagated in: {migrated}"
);
}
#[test]
fn a_frozen_host_entry_is_reported_with_its_own_reason() {
let temp = tempfile::TempDir::new().unwrap();
fs::write(temp.path().join("main.harn"), AMBIENT_HOST_ENTRY).unwrap();
let plan = super::build_plan_with_options_at(
temp.path(),
None,
&super::FixOptions::capability_migrations(),
)
.expect("plan");
let frozen = plan
.frozen_callables
.iter()
.find(|frozen| frozen.name == "dispatch")
.unwrap_or_else(|| {
panic!(
"the frozen host entry must be named; got {:?}",
plan.frozen_callables
)
});
assert!(
frozen.reason.contains("`@host_entry`"),
"the reason must name the declaration: {}",
frozen.reason
);
assert!(
!frozen.reason.contains("first-class reference"),
"a host entry must not be explained as a value escape: {}",
frozen.reason
);
}