shaperail-codegen 0.10.0

YAML parser, validator, and code generator for Shaperail
Documentation
use shaperail_codegen::parser::parse_resource;
use shaperail_codegen::validator::validate_resource;

// ── Valid resource snapshots (5) ──────────────────────────────────────────

#[test]
fn snapshot_valid_users() {
    let yaml = include_str!("fixtures/valid/users.yaml");
    let rd = parse_resource(yaml).unwrap();
    let errors = validate_resource(&rd);
    assert!(errors.is_empty());
    insta::assert_yaml_snapshot!("valid_users", rd);
}

#[test]
fn snapshot_valid_tags() {
    let yaml = include_str!("fixtures/valid/tags.yaml");
    let rd = parse_resource(yaml).unwrap();
    let errors = validate_resource(&rd);
    assert!(errors.is_empty());
    insta::assert_yaml_snapshot!("valid_tags", rd);
}

#[test]
fn snapshot_valid_orders() {
    let yaml = include_str!("fixtures/valid/orders.yaml");
    let rd = parse_resource(yaml).unwrap();
    let errors = validate_resource(&rd);
    assert!(errors.is_empty());
    insta::assert_yaml_snapshot!("valid_orders", rd);
}

#[test]
fn snapshot_valid_organizations() {
    let yaml = include_str!("fixtures/valid/organizations.yaml");
    let rd = parse_resource(yaml).unwrap();
    let errors = validate_resource(&rd);
    assert!(errors.is_empty());
    insta::assert_yaml_snapshot!("valid_organizations", rd);
}

#[test]
fn snapshot_valid_minimal() {
    let yaml = include_str!("fixtures/valid/minimal.yaml");
    let rd = parse_resource(yaml).unwrap();
    let errors = validate_resource(&rd);
    assert!(errors.is_empty());
    insta::assert_yaml_snapshot!("valid_minimal", rd);
}

// ── Invalid resource snapshots (10) ───────────────────────────────────────

#[test]
fn snapshot_invalid_missing_resource() {
    let yaml = include_str!("fixtures/invalid/missing_resource.yaml");
    let err = parse_resource(yaml).unwrap_err();
    insta::assert_snapshot!("invalid_missing_resource", err.to_string());
}

#[test]
fn snapshot_invalid_missing_version() {
    let yaml = include_str!("fixtures/invalid/missing_version.yaml");
    let err = parse_resource(yaml).unwrap_err();
    insta::assert_snapshot!("invalid_missing_version", err.to_string());
}

#[test]
fn snapshot_invalid_missing_schema() {
    let yaml = include_str!("fixtures/invalid/missing_schema.yaml");
    let err = parse_resource(yaml).unwrap_err();
    insta::assert_snapshot!("invalid_missing_schema", err.to_string());
}

#[test]
fn snapshot_invalid_enum_no_values() {
    let yaml = include_str!("fixtures/invalid/enum_no_values.yaml");
    let rd = parse_resource(yaml).unwrap();
    let errors = validate_resource(&rd);
    let messages: Vec<String> = errors.iter().map(|e| e.message.clone()).collect();
    insta::assert_yaml_snapshot!("invalid_enum_no_values", messages);
}

#[test]
fn snapshot_invalid_ref_not_uuid() {
    let yaml = include_str!("fixtures/invalid/ref_not_uuid.yaml");
    let rd = parse_resource(yaml).unwrap();
    let errors = validate_resource(&rd);
    let messages: Vec<String> = errors.iter().map(|e| e.message.clone()).collect();
    insta::assert_yaml_snapshot!("invalid_ref_not_uuid", messages);
}

#[test]
fn snapshot_invalid_no_primary_key() {
    let yaml = include_str!("fixtures/invalid/no_primary_key.yaml");
    let rd = parse_resource(yaml).unwrap();
    let errors = validate_resource(&rd);
    let messages: Vec<String> = errors.iter().map(|e| e.message.clone()).collect();
    insta::assert_yaml_snapshot!("invalid_no_primary_key", messages);
}

#[test]
fn snapshot_invalid_soft_delete_no_deleted_at() {
    let yaml = include_str!("fixtures/invalid/soft_delete_no_deleted_at.yaml");
    let rd = parse_resource(yaml).unwrap();
    let errors = validate_resource(&rd);
    let messages: Vec<String> = errors.iter().map(|e| e.message.clone()).collect();
    insta::assert_yaml_snapshot!("invalid_soft_delete_no_deleted_at", messages);
}

#[test]
fn snapshot_invalid_input_field_missing() {
    let yaml = include_str!("fixtures/invalid/input_field_missing.yaml");
    let rd = parse_resource(yaml).unwrap();
    let errors = validate_resource(&rd);
    let messages: Vec<String> = errors.iter().map(|e| e.message.clone()).collect();
    insta::assert_yaml_snapshot!("invalid_input_field_missing", messages);
}

#[test]
fn snapshot_invalid_belongs_to_no_key() {
    let yaml = include_str!("fixtures/invalid/belongs_to_no_key.yaml");
    let rd = parse_resource(yaml).unwrap();
    let errors = validate_resource(&rd);
    let messages: Vec<String> = errors.iter().map(|e| e.message.clone()).collect();
    insta::assert_yaml_snapshot!("invalid_belongs_to_no_key", messages);
}

#[test]
fn snapshot_invalid_yaml_syntax() {
    let yaml = include_str!("fixtures/invalid/invalid_yaml_syntax.yaml");
    let err = parse_resource(yaml).unwrap_err();
    insta::assert_snapshot!("invalid_yaml_syntax", err.to_string());
}

#[test]
fn controller_map_populated_when_resource_has_controller() {
    let yaml = include_str!("fixtures/valid/users.yaml");
    let rd = shaperail_codegen::parser::parse_resource(yaml).unwrap();
    let project = shaperail_codegen::rust::generate_project(&[rd]).unwrap();
    assert!(
        project.mod_rs.contains("users_controller"),
        "expected #[path] module for users_controller in mod_rs:\n{}",
        project.mod_rs
    );
    assert!(
        project
            .mod_rs
            .contains(r#"map.register("users", "validate_org""#),
        "expected map.register call in mod_rs:\n{}",
        project.mod_rs
    );
}

#[test]
fn controller_map_empty_when_no_controllers() {
    let yaml = include_str!("fixtures/valid/minimal.yaml");
    let rd = shaperail_codegen::parser::parse_resource(yaml).unwrap();
    let project = shaperail_codegen::rust::generate_project(&[rd]).unwrap();
    assert!(
        !project.mod_rs.contains("mod settings_controller"),
        "expected no controller modules in mod_rs:\n{}",
        project.mod_rs
    );
    assert!(
        project.mod_rs.contains("ControllerMap::new()"),
        "expected empty ControllerMap::new() in mod_rs:\n{}",
        project.mod_rs
    );
}

#[test]
fn job_registry_populated_when_resource_has_jobs() {
    let yaml = include_str!("fixtures/valid/users.yaml");
    let rd = shaperail_codegen::parser::parse_resource(yaml).unwrap();
    let project = shaperail_codegen::rust::generate_project(&[rd]).unwrap();
    assert!(
        project.mod_rs.contains("build_job_registry"),
        "expected build_job_registry fn in mod_rs:\n{}",
        project.mod_rs
    );
    assert!(
        project.mod_rs.contains("send_welcome_email"),
        "expected send_welcome_email in mod_rs:\n{}",
        project.mod_rs
    );
}

#[test]
fn job_registry_empty_when_no_jobs() {
    let yaml = include_str!("fixtures/valid/minimal.yaml");
    let rd = shaperail_codegen::parser::parse_resource(yaml).unwrap();
    let project = shaperail_codegen::rust::generate_project(&[rd]).unwrap();
    assert!(
        project.mod_rs.contains("JobRegistry::new()"),
        "expected empty JobRegistry::new() in mod_rs:\n{}",
        project.mod_rs
    );
}