use super::*;
#[test]
fn callable_param_insert_handles_dict_defaults_before_body() {
let source = "pub fn poll(check, options: dict = {}) -> any {\n harness.clock.now_ms()\n}\n";
let (offset, has_params) = super::signature_threading::callable_param_insert(
source,
harn_lexer::Span::with_offsets(0, source.len(), 1, 1),
)
.expect("callable header");
assert!(has_params);
assert_eq!(&source[offset..offset + 5], "check");
}
#[test]
fn missing_capability_argument_repair_uses_typed_root_field() {
let source = "fn main(harness: Harness) {\n helper(\"old\")\n}\n";
let program = harn_parser::parse_source(source).unwrap();
let span = source.find("\"old\"").unwrap();
let span = harn_lexer::Span::with_offsets(span, span + 5, 2, 10);
let (_, edits, _) = synthesize_missing_capability_argument_repair(
span,
&named_type("HarnessFs"),
&named_type("string"),
source,
&program,
)
.expect("capability migration repair");
assert_eq!(edits.len(), 1);
let insert_at = source.find("(\"old\")").unwrap() + 1;
assert_eq!(edits[0].span.start, insert_at);
assert_eq!(edits[0].span.end, insert_at);
assert_eq!(edits[0].replacement, "harness.fs, ");
}
#[test]
fn missing_capability_argument_is_inserted_at_the_diagnosed_position() {
let source = "fn main(harness: Harness) {\n helper(harness.fs, \"old\")\n}\n";
let program = harn_parser::parse_source(source).unwrap();
let start = source.find("\"old\"").unwrap();
let span = harn_lexer::Span::with_offsets(start, start + 5, 2, 22);
let (_, edits, _) = synthesize_missing_capability_argument_repair(
span,
&named_type("HarnessSystem"),
&named_type("string"),
source,
&program,
)
.expect("positioned capability repair");
assert_eq!(edits.len(), 1);
assert_eq!(edits[0].replacement, ", harness.system");
assert_eq!(
FixEdit::apply_all(source, &edits),
"fn main(harness: Harness) {\n helper(harness.fs, harness.system, \"old\")\n}\n"
);
}
#[test]
fn attenuated_capability_argument_repair_projects_existing_root_grant() {
let source = "fn main(harness: Harness) {\n helper(harness, \"old\")\n}\n";
let program = harn_parser::parse_source(source).unwrap();
let start = source.find("harness, \"").unwrap();
let span = harn_lexer::Span::with_offsets(start, start + "harness".len(), 2, 10);
let (repair, edits, _) = synthesize_missing_capability_argument_repair(
span,
&named_type("HarnessFs"),
&named_type("Harness"),
source,
&program,
)
.expect("attenuation repair");
assert_eq!(repair.id.as_str(), "bindings/attenuate-capability-argument");
assert_eq!(edits.len(), 1);
assert_eq!(edits[0].span, span);
assert_eq!(edits[0].replacement, "harness.fs");
}
#[test]
fn attenuated_capability_argument_repair_projects_a_root_grant_reached_through_a_field() {
let source = "fn main(request: Request) {\n helper(request.harness, \"old\")\n}\n";
let program = harn_parser::parse_source(source).unwrap();
let start = source.find("request.harness, \"").unwrap();
let span = harn_lexer::Span::with_offsets(start, start + "request.harness".len(), 2, 10);
let (repair, edits, _) = synthesize_missing_capability_argument_repair(
span,
&named_type("HarnessObs"),
&named_type("Harness"),
source,
&program,
)
.expect("attenuation repair for a field-reached root grant");
assert_eq!(repair.id.as_str(), "bindings/attenuate-capability-argument");
assert_eq!(edits.len(), 1);
assert_eq!(edits[0].span, span);
assert_eq!(edits[0].replacement, "request.harness.obs");
assert_eq!(
FixEdit::apply_all(source, &edits),
"fn main(request: Request) {\n helper(request.harness.obs, \"old\")\n}\n"
);
}
#[test]
fn attenuated_capability_argument_repair_declines_a_non_path_argument() {
let source = "fn main(harness: Harness) {\n helper(pick(harness), \"old\")\n}\n";
let program = harn_parser::parse_source(source).unwrap();
let start = source.find("pick(harness)").unwrap();
let span = harn_lexer::Span::with_offsets(start, start + "pick(harness)".len(), 2, 10);
assert!(
synthesize_missing_capability_argument_repair(
span,
&named_type("HarnessObs"),
&named_type("Harness"),
source,
&program,
)
.is_none(),
"a call expression must not be re-rooted"
);
}
#[test]
fn attenuated_capability_bundle_repair_projects_existing_root_grant() {
let source = "fn main(harness: Harness) {\n helper(harness, \"old\")\n}\n";
let program = harn_parser::parse_source(source).unwrap();
let start = source.find("harness, \"").unwrap();
let span = harn_lexer::Span::with_offsets(start, start + "harness".len(), 2, 10);
let (repair, edits, _) = synthesize_missing_capability_argument_repair(
span,
&capability_shape(&[("fs", "HarnessFs"), ("tools", "HarnessTools")]),
&named_type("Harness"),
source,
&program,
)
.expect("capability bundle repair");
assert_eq!(
repair.id.as_str(),
"bindings/attenuate-capability-bundle-argument"
);
assert_eq!(edits.len(), 1);
assert_eq!(edits[0].span, span);
assert_eq!(
edits[0].replacement,
"{fs: harness.fs, tools: harness.tools}"
);
}
#[test]
fn missing_capability_argument_repair_inserts_before_parenthesized_expression() {
let source = "fn main(harness: Harness) {\n helper((params ?? {}) + {path: \"src\"})\n}\n";
let program = harn_parser::parse_source(source).unwrap();
let mut span = None;
visit::walk_program(&program, &mut |node| {
if let Node::FunctionCall { name, args, .. } = &node.node {
if name == "helper" {
span = args.first().map(|arg| arg.span);
}
}
});
let span = span.expect("helper first argument");
let (_, edits, _) = synthesize_missing_capability_argument_repair(
span,
&named_type("HarnessFs"),
&named_type("dict"),
source,
&program,
)
.expect("capability migration repair");
let insert_at = source.find("((params").unwrap() + 1;
assert_eq!(edits[0].span.start, insert_at);
assert_eq!(edits[0].replacement, "harness.fs, ");
}
#[test]
fn missing_root_argument_threads_a_distinct_root_past_a_narrow_harness_binding() {
let source = "fn leaf(harness: HarnessFs, path: string) {\n needs_root(harness, path)\n}\n\nfn main(harness: Harness) {\n leaf(harness.fs, \"old\")\n}\n";
let program = harn_parser::parse_source(source).unwrap();
let start = source.find("harness, path").unwrap();
let span = harn_lexer::Span::with_offsets(start, start + "harness".len(), 2, 14);
let (_, edits, _) = synthesize_missing_root_argument_repair(
span,
source,
&program,
&BTreeSet::new(),
&AmbientRepairContext {
cross_module_importer_count: 0,
},
&mut ValueEscape {
referenced_by_value: &BTreeSet::new(),
frozen: &mut Vec::new(),
},
)
.expect("root threading repair");
let fixed = FixEdit::apply_all(source, &edits);
assert_eq!(
fixed,
"fn leaf(_harness: Harness, harness: HarnessFs, path: string) {\n needs_root(_harness, harness, path)\n}\n\nfn main(harness: Harness) {\n leaf(harness, harness.fs, \"old\")\n}\n"
);
}
#[test]
fn missing_capability_argument_repair_rejects_non_call_spans() {
let source = "import { helper } from \"./lib\"\n\nfn main(harness: Harness) {}\n";
let program = harn_parser::parse_source(source).unwrap();
let start = source.find("helper").unwrap();
let import_span = harn_lexer::Span::with_offsets(start, start + "helper".len(), 1, 10);
assert!(
synthesize_missing_capability_argument_repair(
import_span,
&named_type("HarnessAst"),
&named_type("string"),
source,
&program,
)
.is_none(),
"an imported-declaration diagnostic must never edit the importer"
);
}
#[test]
fn missing_root_argument_repair_preserves_parenthesized_first_argument() {
let source = "pipeline test(harness: Harness, params: dict) {\n host_search_request((params ?? {}) + {path: \"root\"})\n}\n";
let program = harn_parser::parse_source(source).expect("source parses");
let mut span = None;
visit::walk_program(&program, &mut |node| {
if let Node::FunctionCall { name, args, .. } = &node.node {
if name == "host_search_request" {
span = args.first().map(|arg| arg.span);
}
}
});
let span = span.expect("host_search_request first argument");
let (_, edits, _) = synthesize_missing_root_argument_repair(
span,
source,
&program,
&BTreeSet::new(),
&AmbientRepairContext {
cross_module_importer_count: 0,
},
&mut ValueEscape {
referenced_by_value: &BTreeSet::new(),
frozen: &mut Vec::new(),
},
)
.expect("root argument repair");
let applied = FixEdit::apply_all(source, &edits);
assert!(
applied.contains("host_search_request(harness, (params ?? {}) + {path: \"root\"})"),
"capability must be inserted at the call boundary: {applied}"
);
harn_parser::parse_source(&applied).expect("repair must remain parse-safe");
}
#[test]
fn value_references_skip_locally_bound_names() {
let source = concat!(
"fn main(harness: Harness) {\n",
" const repo_root = discover()\n",
" let shell_argv = false\n",
" const {alias: renamed} = config()\n",
" const [first] = items()\n",
" print(repo_root, shell_argv, renamed, first)\n",
"}\n",
);
let program = harn_parser::parse_source(source).unwrap();
let mut names = BTreeSet::new();
super::signature_threading::collect_value_references(&program, &mut names);
for local in ["repo_root", "shell_argv", "renamed", "first"] {
assert!(!names.contains(local), "`{local}` is a local binding");
}
}
#[test]
fn value_references_still_catch_a_bare_handler_reference() {
let source = concat!(
"fn handler(args: dict) -> string {\n",
" return \"\"\n",
"}\n",
"fn main(harness: Harness) {\n",
" const registry = {handler: handler}\n",
" use_it(registry)\n",
"}\n",
);
let program = harn_parser::parse_source(source).unwrap();
let mut names = BTreeSet::new();
super::signature_threading::collect_value_references(&program, &mut names);
assert!(
names.contains("handler"),
"a bare reference must still freeze"
);
}