helm-schema-k8s 0.0.4

Generate an accurate JSON schema for any helm chart
Documentation
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

use serde_json::json;
use test_util::prelude::sim_assert_eq;

use super::*;
use crate::inference::{ApiVersionCandidate, InferenceSource};

#[derive(Debug)]
struct CountingProvider {
    calls: Arc<AtomicUsize>,
}

impl K8sSchemaProvider for CountingProvider {
    fn origin(&self) -> ProviderOrigin {
        ProviderOrigin::KubernetesOpenApi
    }

    fn has_resource(&self, _resource: &ResourceRef) -> bool {
        true
    }

    fn lookup(&self, _resource: &ResourceRef, _path: &YamlPath) -> ProviderLookupResult {
        self.calls.fetch_add(1, Ordering::SeqCst);
        ProviderLookupResult::Found {
            schema: ProviderSchemaFragment::new(json!({"type": "string"})),
            resolved_k8s_version: None,
        }
    }
}

#[test]
fn repeated_chain_lookup_uses_cached_provider_result() {
    let calls = Arc::new(AtomicUsize::new(0));
    let chain = Chain::new(vec![Box::new(CountingProvider {
        calls: Arc::clone(&calls),
    })]);
    let resource = ResourceRef::concrete("v1".to_string(), "ConfigMap".to_string());
    let path = YamlPath(vec!["metadata".to_string(), "name".to_string()]);

    let first = chain.resolve_against_chain(&resource, &path);
    let second = chain.resolve_against_chain(&resource, &path);

    assert!(matches!(
        (first, second),
        (
            ChainLookupOutcome::Resolved(Some(_)),
            ChainLookupOutcome::Resolved(Some(_)),
        )
    ));
    sim_assert_eq!(have: calls.load(Ordering::SeqCst), want: 1);
}

#[derive(Debug)]
struct CountingInferenceProvider {
    calls: Arc<AtomicUsize>,
}

impl K8sSchemaProvider for CountingInferenceProvider {
    fn origin(&self) -> ProviderOrigin {
        ProviderOrigin::KubernetesOpenApi
    }

    fn lookup(&self, _resource: &ResourceRef, _path: &YamlPath) -> ProviderLookupResult {
        ProviderLookupResult::NotOwned
    }

    fn has_resource(&self, _resource: &ResourceRef) -> bool {
        false
    }

    fn infer_api_version_candidates(&self, _kind: &str) -> Vec<ApiVersionCandidate> {
        self.calls.fetch_add(1, Ordering::SeqCst);
        vec![ApiVersionCandidate {
            api_version: "example.com/v1".to_string(),
            source: InferenceSource::OnlineProbe,
            origin: ProviderOrigin::KubernetesOpenApi,
        }]
    }
}

#[test]
fn repeated_inference_uses_cached_outcome() {
    let calls = Arc::new(AtomicUsize::new(0));
    let chain = Chain::new(vec![Box::new(CountingInferenceProvider {
        calls: Arc::clone(&calls),
    })])
    .with_inference_enabled(true);
    let resource = ResourceRef::concrete(String::new(), "Widget".to_string());
    let path = YamlPath(Vec::new());

    let _ = chain.schema_fragment_for_resource_needing_inference(&resource, &path);
    let _ = chain.schema_fragment_for_resource_needing_inference(&resource, &path);

    sim_assert_eq!(have: calls.load(Ordering::SeqCst), want: 1);
}