fraiseql-server 2.8.0

HTTP server for FraiseQL v2 GraphQL engine
Documentation
use super::*;

#[test]
fn accepts_postgresql_scheme() {
    assert_eq!(
        parse_database_url("postgresql://user@localhost/db").expect("accepted"),
        DatabaseScheme::Postgres,
    );
}

#[test]
fn accepts_postgres_alias() {
    assert_eq!(
        parse_database_url("postgres://user@localhost/db").expect("accepted"),
        DatabaseScheme::Postgres,
    );
}

#[test]
fn accepts_postgresql_with_query_string() {
    assert_eq!(
        parse_database_url("postgresql://user:pw@host:5432/db?sslmode=require")
            .expect("query-string parameters must not affect scheme parsing"),
        DatabaseScheme::Postgres,
    );
}

#[test]
fn accepts_mysql_scheme() {
    assert_eq!(
        parse_database_url("mysql://localhost:3306/mydb").expect("accepted"),
        DatabaseScheme::MySql,
    );
}

#[test]
fn accepts_sqlite_scheme() {
    assert_eq!(
        parse_database_url("sqlite://./mydb.db").expect("accepted"),
        DatabaseScheme::Sqlite,
    );
}

#[test]
fn accepts_sqlserver_scheme() {
    assert_eq!(
        parse_database_url("sqlserver://localhost:1433").expect("accepted"),
        DatabaseScheme::SqlServer,
    );
}

#[test]
fn rejects_unknown_scheme_with_clear_message() {
    let err = parse_database_url("redis://localhost:6379")
        .expect_err("redis:// is not a supported database scheme")
        .to_string();
    assert!(
        err.starts_with(GUARD_MESSAGE_PREFIX),
        "diagnostic must start with the operator-facing prefix: {err}"
    );
    assert!(err.contains("\"redis\""), "missing observed-scheme reproduction: {err}");
}

#[test]
fn rejects_empty_string() {
    let err = parse_database_url("").expect_err("empty URL must be rejected").to_string();
    assert!(err.starts_with(GUARD_MESSAGE_PREFIX), "{err}");
}

#[test]
fn rejects_url_without_scheme() {
    let err = parse_database_url("localhost:5432")
        .expect_err("URL without a scheme must be rejected")
        .to_string();
    assert!(err.contains("\"localhost:5432\""), "{err}");
}

#[test]
fn required_feature_matrix() {
    assert_eq!(DatabaseScheme::Postgres.required_feature(), None);
    assert_eq!(DatabaseScheme::MySql.required_feature(), Some("mysql"));
    assert_eq!(DatabaseScheme::Sqlite.required_feature(), Some("sqlite"));
    assert_eq!(DatabaseScheme::SqlServer.required_feature(), Some("sqlserver"));
}

mod sqlite_guard {
    use fraiseql_core::schema::{CompiledSchema, MutationDefinition};

    use super::super::guard_sqlite_mutations;

    fn mutation(name: &str) -> MutationDefinition {
        MutationDefinition::new(name, "MutationResponse")
    }

    #[test]
    fn accepts_schema_with_no_mutations() {
        let schema = CompiledSchema::default();
        guard_sqlite_mutations(&schema).expect("read-only schema must be allowed on SQLite");
    }

    #[test]
    fn rejects_schema_with_mutations_and_names_them() {
        let mut schema = CompiledSchema::default();
        schema.mutations.push(mutation("createUser"));
        schema.mutations.push(mutation("deletePost"));

        let err = guard_sqlite_mutations(&schema)
            .expect_err("SQLite + mutations must be rejected at startup")
            .to_string();

        assert!(err.contains("SQLite is a read-only"), "missing read-only callout: {err}");
        assert!(err.contains("createUser"), "missing first mutation name: {err}");
        assert!(err.contains("deletePost"), "missing second mutation name: {err}");
        assert!(err.contains("2 mutation"), "missing mutation count: {err}");
    }

    #[test]
    fn truncates_long_mutation_lists_with_suffix() {
        let mut schema = CompiledSchema::default();
        for i in 0..5 {
            schema.mutations.push(mutation(&format!("m{i}")));
        }

        let err = guard_sqlite_mutations(&schema)
            .expect_err("any mutations must be rejected on SQLite")
            .to_string();

        assert!(err.contains("m0"), "missing first sample: {err}");
        assert!(err.contains("m2"), "missing third sample: {err}");
        assert!(!err.contains("m4"), "must truncate beyond sample window: {err}");
        assert!(err.contains("+2 more"), "missing overflow suffix: {err}");
    }
}