harn-parser 0.10.53

Parser, AST, and type checker for the Harn programming language
Documentation
use super::*;
use crate::DiagnosticDetails;

#[test]
fn parameterized_test_and_fixture_attributes_are_structurally_validated() {
    let valid = warnings(
        r#"
@test_fixture(scope: file)
fn shared() -> int { return 1 }

@test(cases: [{name: "one", args: [1]}], fixture: shared)
pipeline test_value(_fixture: int, value: int) {}
"#,
    );
    assert!(
        valid.iter().all(|warning| {
            !warning.contains("unknown attribute")
                && !warning.contains("InvalidAttribute")
                && !warning.contains("only applies")
                && !warning.contains("@test")
        }),
        "valid fixture and row metadata should typecheck cleanly: {valid:?}"
    );

    let invalid = warnings(
        r"
@test_fixture(scope: suite, extra: true)
pipeline wrong_target() {}

@test(cases: 1, fixture: 42, typo: [])
fn also_wrong() -> int { return 1 }
",
    );
    assert!(
        invalid
            .iter()
            .any(|warning| warning.contains("only applies to function")),
        "fixture target should be structural: {invalid:?}"
    );
    assert!(
        invalid
            .iter()
            .any(|warning| warning.contains("only applies to pipeline")),
        "test target should be structural: {invalid:?}"
    );
    assert!(
        invalid
            .iter()
            .any(|warning| warning.contains("must be one of [\"file\", \"case\"]")),
        "fixture scope should be an enum-like contract: {invalid:?}"
    );
    assert!(
        invalid
            .iter()
            .any(|warning| warning.contains("unknown `@test` argument `typo`")),
        "test metadata should reject unknown fields: {invalid:?}"
    );
}

#[test]
fn flow_invariant_accepts_only_the_injected_ast_capability() {
    let diagnostics = diagnostics_with_code(
        r#"
@invariant
@deterministic
@archivist(evidence: ["https://example.com/a", "https://example.org/b"], confidence: 0.9, source_date: "2026-08-01")
fn inspect(ast: HarnessAst, slice, _ctx, _repo) -> bool { return true }
"#,
        Code::FlowInvariantAttributeInvalid,
        DiagnosticSeverity::Error,
    );
    assert!(diagnostics.is_empty(), "{diagnostics:#?}");
}

#[test]
fn flow_invariant_rejects_typed_authority_outside_the_runtime_contract() {
    let diagnostics = diagnostics_with_code(
        r#"
type Network = HarnessNet
type AstAlias = HarnessAst

@invariant
@deterministic
@archivist(evidence: ["https://example.com/a", "https://example.org/b"], confidence: 0.9, source_date: "2026-08-01")
fn inspect(
  ast_alias: AstAlias,
  bundle: {ast: HarnessAst, fs: HarnessFs},
  network: Network,
  root: Harness,
  slice,
  _ctx,
  _repo,
) -> bool { return true }
"#,
        Code::FlowInvariantAttributeInvalid,
        DiagnosticSeverity::Error,
    );
    let contracts = diagnostics
        .iter()
        .map(|diagnostic| match diagnostic.details.as_ref() {
            Some(DiagnosticDetails::FlowCapabilityBoundary {
                parameter,
                capabilities,
                allowed,
            }) => (parameter.clone(), capabilities.clone(), allowed.clone()),
            details => panic!("expected typed Flow capability detail, got {details:?}"),
        })
        .collect::<Vec<_>>();
    assert_eq!(
        contracts,
        [
            (
                "ast_alias".to_string(),
                vec!["HarnessAst".to_string()],
                vec!["HarnessAst".to_string()],
            ),
            (
                "bundle".to_string(),
                vec!["HarnessAst".to_string(), "HarnessFs".to_string()],
                vec!["HarnessAst".to_string()],
            ),
            (
                "network".to_string(),
                vec!["HarnessNet".to_string()],
                vec!["HarnessAst".to_string()],
            ),
            (
                "root".to_string(),
                vec!["Harness".to_string()],
                vec!["HarnessAst".to_string()],
            ),
        ]
    );
}

#[test]
fn flow_invariant_boundary_matches_runtime_discovery_kind() {
    let source = r"
@invariant
tool inspect_tool(fs: HarnessFs, slice, _ctx, _repo) -> bool { return true }

@invariant
pipeline inspect_pipeline(network: HarnessNet, slice, _ctx, _repo) {}
";
    let diagnostics = diagnostics_with_code(
        source,
        Code::FlowInvariantAttributeInvalid,
        DiagnosticSeverity::Error,
    );
    assert!(diagnostics.is_empty(), "{diagnostics:#?}");
}