hyperstack-macros 0.6.4

Proc-macros for defining HyperStack streams
Documentation
mod support;

use std::path::PathBuf;

use support::{cargo_toml, escape_path, macro_manifest_dir, TempCrate};

fn compile_failure_stderr(name: &str, source: &str) -> String {
    let manifest_dir = macro_manifest_dir();
    let temp_crate = TempCrate::new(
        "phase4-dynamic",
        name,
        cargo_toml(
            name,
            &[format!(
                "hyperstack-macros = {{ path = \"{}\" }}",
                escape_path(&manifest_dir)
            )],
        ),
        source,
        &[],
    );

    let output = temp_crate.cargo_check();

    assert!(
        !output.status.success(),
        "expected cargo check to fail for {name}"
    );

    String::from_utf8_lossy(&output.stderr).into_owned()
}

fn pump_idl_path() -> String {
    escape_path(
        &PathBuf::from(env!("CARGO_MANIFEST_DIR"))
            .parent()
            .expect("workspace root")
            .join("hyperstack-idl/tests/fixtures/pump.json"),
    )
}

#[test]
fn unknown_account_field_is_rejected() {
    let source = format!(
        r#"use hyperstack_macros::hyperstack;

#[hyperstack(idl = "{}")]
mod broken {{
    #[entity(name = "Thing")]
    struct Thing {{
        #[map(pump_sdk::accounts::BondingCurve::bogus, strategy = LastWrite)]
        value: u64,
    }}
}}

fn main() {{}}
"#,
        pump_idl_path()
    );

    let stderr = compile_failure_stderr("unknown_account_field_is_rejected", &source);
    assert!(stderr.contains("Not found: 'bogus' in account fields for 'BondingCurve'"));
}

#[test]
fn account_field_validation_is_case_sensitive() {
    let source = format!(
        r#"use hyperstack_macros::hyperstack;

#[hyperstack(idl = "{}")]
mod broken {{
    #[entity(name = "Thing")]
    struct Thing {{
        #[map(pump_sdk::accounts::BondingCurve::Complete, strategy = LastWrite)]
        value: bool,
    }}
}}

fn main() {{}}
"#,
        pump_idl_path()
    );

    let stderr = compile_failure_stderr("account_field_validation_is_case_sensitive", &source);
    assert!(stderr.contains("Not found: 'Complete' in account fields for 'BondingCurve'"));
}

#[test]
fn missing_computed_section_reference_is_rejected() {
    let source = r#"use hyperstack_macros::hyperstack;

#[hyperstack]
mod broken {
    #[entity(name = "Thing")]
    struct Thing {
        base: u64,
        #[computed(ghost.value + 1)]
        total: u64,
    }
}

fn main() {}
"#;

    let stderr = compile_failure_stderr("missing_computed_section_reference_is_rejected", source);
    assert!(stderr.contains("unknown computed field reference 'ghost.value' on entity 'Thing'"));
}

#[test]
fn invalid_resolver_input_field_is_rejected() {
    let source = r#"use hyperstack_macros::hyperstack;

#[hyperstack]
mod broken {
    #[entity(name = "Thing")]
    struct Thing {
        existing: String,
        #[resolve(from = "ghost.value", resolver = Token)]
        metadata: String,
    }
}

fn main() {}
"#;

    let stderr = compile_failure_stderr("invalid_resolver_input_field_is_rejected", source);
    assert!(stderr.contains("unknown resolver input field 'ghost.value' on entity 'Thing'"));
}

#[test]
fn invalid_resolver_condition_field_is_rejected() {
    let source = r#"use hyperstack_macros::hyperstack;

#[hyperstack]
mod broken {
    #[entity(name = "Thing")]
    struct Thing {
        existing: String,
        #[resolve(from = "existing", resolver = Token, condition = "ghost.value == pending")]
        metadata: String,
    }
}

fn main() {}
"#;

    let stderr = compile_failure_stderr("invalid_resolver_condition_field_is_rejected", source);
    assert!(stderr.contains("unknown resolver condition field 'ghost.value' on entity 'Thing'"));
}

#[test]
fn invalid_view_sort_by_is_rejected() {
    let source = r#"use hyperstack_macros::hyperstack;

#[hyperstack]
mod broken {
    #[entity(name = "Thing")]
    #[view(name = "latest", sort_by = "ghost.value")]
    struct Thing {
        base: u64,
    }
}

fn main() {}
"#;

    let stderr = compile_failure_stderr("invalid_view_sort_by_is_rejected", source);
    assert!(stderr.contains("unknown view field 'ghost.value' on entity 'Thing'"));
}

#[test]
fn computed_cycle_is_rejected() {
    let source = r#"use hyperstack_macros::hyperstack;

#[hyperstack]
mod broken {
    #[entity(name = "Thing")]
    struct Thing {
        #[computed(b)]
        a: u64,
        #[computed(a)]
        b: u64,
    }
}

fn main() {}
"#;

    let stderr = compile_failure_stderr("computed_cycle_is_rejected", source);
    assert!(stderr.contains("computed fields contain a dependency cycle"));
}

#[test]
fn validation_reports_multiple_errors() {
    let source = r#"use hyperstack_macros::hyperstack;

#[hyperstack]
mod broken {
    #[entity(name = "Thing")]
    #[view(name = "latest", sort_by = "ghost.value")]
    struct Thing {
        existing: String,
        #[resolve(from = "missing.field", resolver = Token)]
        metadata: String,
    }
}

fn main() {}
"#;

    let stderr = compile_failure_stderr("validation_reports_multiple_errors", source);
    assert!(stderr.contains("unknown view field 'ghost.value' on entity 'Thing'"));
    assert!(stderr.contains("unknown resolver input field 'missing.field' on entity 'Thing'"));
}