fraiseql-cli 2.17.1

CLI tools for FraiseQL v2 - Schema compilation and development utilities
Documentation
use super::*;

#[test]
fn test_rest_toml_defaults_match_core() {
    let toml_defaults = RestTomlConfig::default();
    // These must stay in sync with RestConfig::default() in fraiseql-core
    assert!(!toml_defaults.enabled);
    assert_eq!(toml_defaults.path, "/rest/v1");
    assert_eq!(toml_defaults.max_page_size, 1_000);
    assert_eq!(toml_defaults.default_page_size, 100);
    assert_eq!(toml_defaults.default_embed_page_size, 50);
    assert_eq!(
        toml_defaults.default_embed_page_size,
        RestConfig::default().default_embed_page_size
    );
    assert_eq!(toml_defaults.sse_heartbeat_seconds, 30);
    assert_eq!(toml_defaults.sse_max_replay_events, 10_000);
    assert!(toml_defaults.etag);
    assert_eq!(toml_defaults.idempotency_ttl_seconds, 300);
    assert!(!toml_defaults.require_auth);
}

#[test]
fn test_rest_toml_deserialize_minimal() {
    let toml_str = r"enabled = true";
    let config: RestTomlConfig = toml::from_str(toml_str).unwrap();
    assert!(config.enabled);
    assert_eq!(config.path, "/rest/v1"); // default preserved
}

#[test]
fn test_rest_toml_deserialize_full() {
    let toml_str = r#"
        enabled = true
        path = "/api/v2"
        max_page_size = 500
        delete_response = "entity"
        require_auth = true
        etag = false
    "#;
    let config: RestTomlConfig = toml::from_str(toml_str).unwrap();
    assert!(config.enabled);
    assert_eq!(config.path, "/api/v2");
    assert_eq!(config.max_page_size, 500);
    assert_eq!(config.delete_response, DeleteResponseToml::Entity);
    assert!(config.require_auth);
    assert!(!config.etag);
}

/// A `[rest]` key reaches the compiled schema only if the TOML type declares it *and*
/// the conversion carries it. A field added to one of the two is silently the default
/// everywhere (#1265's shape), and no defaults-comparison test can see it — this sets a
/// value no default equals and reads it out the far end.
#[test]
fn the_replay_bound_reaches_the_compiled_schema() {
    let config: RestTomlConfig = toml::from_str("sse_max_replay_events = 42").unwrap();
    assert_eq!(config.sse_max_replay_events, 42);

    let compiled: RestConfig = config.into();
    assert_eq!(
        compiled.sse_max_replay_events, 42,
        "the operator's bound must survive the conversion the compiler performs"
    );
}

/// The same, for the embed page: declared in `[rest]`, carried into the compiled schema.
#[test]
fn the_embed_page_default_reaches_the_compiled_schema() {
    let config: RestTomlConfig = toml::from_str("default_embed_page_size = 75").unwrap();
    let compiled: RestConfig = config.into();
    assert_eq!(compiled.default_embed_page_size, 75);
}

#[test]
fn test_delete_response_serde_roundtrip() {
    assert_eq!(
        serde_json::from_str::<DeleteResponseToml>(r#""no_content""#).unwrap(),
        DeleteResponseToml::NoContent,
    );
    assert_eq!(
        serde_json::from_str::<DeleteResponseToml>(r#""entity""#).unwrap(),
        DeleteResponseToml::Entity,
    );
}

/// A misspelled `[rest]` key is refused, not silently the default.
#[test]
fn an_unknown_rest_key_is_refused() {
    let err = toml::from_str::<RestTomlConfig>("max_pag_size = 10").unwrap_err();
    assert!(err.to_string().contains("max_pag_size"), "{err}");
}

/// The retired key parses only so that it can be refused with its replacement named.
#[test]
fn the_retired_embed_read_tally_is_refused_naming_its_replacement() {
    let config: RestTomlConfig = toml::from_str("max_embedded_reads = 10000").unwrap();
    let err = config.reject_retired_keys().unwrap_err().to_string();
    assert!(err.contains("max_embedded_reads was removed"), "{err}");
    assert!(err.contains("[security.cost_budget] per_request_max"), "{err}");

    assert!(RestTomlConfig::default().reject_retired_keys().is_ok());
}