helm-schema 0.0.7

Generate an accurate JSON schema for any helm chart
Documentation
use color_eyre::eyre;
use serde_json::{Value, json};
use test_util::prelude::sim_assert_eq;

use crate::generation::SchemaProfile;
use crate::output_pipeline::{
    EmitRequest, FinalOutputPolicy, OutputPipelineOptions, PreparedEmitRequest, ReferencePolicy,
    apply_schema_output_pipeline,
};

fn request(reference_policy: ReferencePolicy) -> PreparedEmitRequest {
    PreparedEmitRequest::empty(EmitRequest {
        reference_policy,
        output: OutputPipelineOptions {
            strip_descriptions: false,
            minimize: false,
        },
    })
}

fn emit(
    schema: Value,
    profile: SchemaProfile,
    reference_policy: ReferencePolicy,
) -> eyre::Result<Value> {
    Ok(apply_schema_output_pipeline(
        schema,
        request(reference_policy),
        std::path::Path::new("/does/not/matter"),
        FinalOutputPolicy::for_profile(profile, false),
    )?)
}

#[test]
fn final_policy_annotation_is_deterministic_and_overwrites_caller_key() -> eyre::Result<()> {
    let schema = json!({
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type": "object",
        "x-helm-schema-generated": false,
        "x-helm-schema-policy": "caller value"
    });

    let first = emit(
        schema.clone(),
        SchemaProfile::Lean,
        ReferencePolicy::PreserveRefs,
    )?;
    let second = emit(schema, SchemaProfile::Lean, ReferencePolicy::PreserveRefs)?;

    sim_assert_eq!(have: &first, want: &second);
    sim_assert_eq!(
        have: first["x-helm-schema-generated"].clone(),
        want: json!(true)
    );
    sim_assert_eq!(
        have: first["x-helm-schema-policy"]["requested-profile"].clone(),
        want: json!("lean")
    );
    sim_assert_eq!(
        have: first["x-helm-schema-policy"]["resolved"].clone(),
        want: json!({
            "kind-partitions": false,
            "local-conditionals": true,
            "root-anchored-conditionals": false,
            "terminal-clauses": false
        })
    );
    Ok(())
}

#[test]
fn boolean_roots_are_wrapped_without_changing_acceptance() -> eyre::Result<()> {
    for boolean in [false, true] {
        let output = emit(
            Value::Bool(boolean),
            SchemaProfile::Full,
            ReferencePolicy::PreserveRefs,
        )?;
        sim_assert_eq!(
            have: output["$schema"].clone(),
            want: json!("http://json-schema.org/draft-07/schema#")
        );
        sim_assert_eq!(have: output["allOf"].clone(), want: json!([boolean]));
        let validator = jsonschema::validator_for(&output)?;
        sim_assert_eq!(have: validator.is_valid(&json!({})), want: boolean);
    }
    Ok(())
}

#[test]
fn non_schema_roots_cannot_escape_annotation() {
    for root in [json!(null), json!(3), json!("schema"), json!([])] {
        let result = apply_schema_output_pipeline(
            root,
            request(ReferencePolicy::PreserveRefs),
            std::path::Path::new("/does/not/matter"),
            FinalOutputPolicy::for_profile(SchemaProfile::Full, false),
        );
        sim_assert_eq!(have: result.is_err(), want: true);
    }
}

#[test]
fn reference_modifier_changes_the_policy_fingerprint() -> eyre::Result<()> {
    let schema = json!({
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type": "object"
    });
    let bundled = emit(
        schema.clone(),
        SchemaProfile::Full,
        ReferencePolicy::SelfContained,
    )?;
    let preserved = emit(schema, SchemaProfile::Full, ReferencePolicy::PreserveRefs)?;

    let bundled_fingerprint = bundled
        .pointer("/x-helm-schema-policy/policy-fingerprint")
        .and_then(Value::as_str);
    let preserved_fingerprint = preserved
        .pointer("/x-helm-schema-policy/policy-fingerprint")
        .and_then(Value::as_str);
    sim_assert_eq!(
        have: bundled_fingerprint == preserved_fingerprint,
        want: false
    );
    Ok(())
}