#![cfg(test)]
use super::parse_schema;
#[test]
fn accepts_isolation_attribute_on_procedure() {
let schema = parse_schema(
r#"
type TransferInput {
from Int
to Int
}
mutation procedure transfer(args: TransferInput): TransferInput
@isolation("serializable")
"#,
)
.expect("procedure with @isolation should parse");
let attrs = &schema.procedures[0].attributes;
assert!(
attrs
.iter()
.any(|a| a.raw == "@isolation(\"serializable\")"),
"expected @isolation in attributes: {attrs:?}",
);
}
#[test]
fn accepts_isolation_repeatable_read() {
parse_schema(
r#"
type Ping {
nonce String
}
procedure read_only(args: Ping): Ping
@isolation("repeatable_read")
"#,
)
.expect("repeatable_read isolation should parse");
}
#[test]
fn rejects_invalid_isolation_level() {
let error = parse_schema(
r#"
type Ping {
nonce String
}
procedure broken(args: Ping): Ping
@isolation("snapshot")
"#,
)
.expect_err("unknown isolation level should fail");
assert!(
error
.to_string()
.contains("unknown transaction isolation level"),
"error: {error}",
);
}
#[test]
fn rejects_isolation_missing_argument() {
let error = parse_schema(
r#"
type Ping {
nonce String
}
procedure broken(args: Ping): Ping
@isolation
"#,
)
.expect_err("@isolation without args should fail");
assert!(
error
.to_string()
.contains("@isolation requires a quoted level argument"),
"error: {error}",
);
}
#[test]
fn accepts_api_version_and_deprecated_on_procedure() {
let schema = parse_schema(
r#"
type Ping {
nonce String
}
procedure healthcheck(args: Ping): Ping
@api_version("v1")
@deprecated("use healthcheck_v2")
"#,
)
.expect("procedure with @api_version + @deprecated should parse");
let attrs = &schema.procedures[0].attributes;
assert!(
attrs.iter().any(|a| a.raw == "@api_version(\"v1\")"),
"expected @api_version: {attrs:?}",
);
assert!(
attrs
.iter()
.any(|a| a.raw == "@deprecated(\"use healthcheck_v2\")"),
"expected @deprecated",
);
}
#[test]
fn rejects_empty_api_version() {
let error = parse_schema(
r#"
type Ping {
nonce String
}
procedure healthcheck(args: Ping): Ping
@api_version("")
"#,
)
.expect_err("empty @api_version should fail");
assert!(
error.to_string().contains("@api_version must not be empty"),
"error: {error}",
);
}
#[test]
fn rejects_api_version_with_invalid_characters() {
let error = parse_schema(
r#"
type Ping {
nonce String
}
procedure healthcheck(args: Ping): Ping
@api_version("v 1")
"#,
)
.expect_err("@api_version with space should fail");
assert!(
error.to_string().contains("must contain only alphanumeric"),
"error: {error}",
);
}
#[test]
fn parses_no_idempotency_attribute_on_procedure() {
let schema = parse_schema(
r#"
type Ping {
nonce String
}
mutation procedure healthcheck(args: Ping): Ping
@no_idempotency
"#,
)
.expect("procedure with @no_idempotency should parse");
let attrs = &schema.procedures[0].attributes;
assert!(
attrs.iter().any(|a| a.raw == "@no_idempotency"),
"procedure attributes should include @no_idempotency: {:?}",
attrs,
);
}
#[test]
fn accepts_no_rate_limit_attribute_when_extension_is_declared() {
let schema = parse_schema(
r#"
extension rate_limit {
}
type Ping {
nonce String
}
mutation procedure createPayment(args: Ping): Ping
@no_rate_limit
"#,
)
.expect("@no_rate_limit should parse when `extension rate_limit { }` is declared");
let attrs = &schema.procedures[0].attributes;
assert!(
attrs.iter().any(|a| a.raw == "@no_rate_limit"),
"procedure attributes should include @no_rate_limit: {:?}",
attrs,
);
}
#[test]
fn rejects_no_rate_limit_attribute_without_extension_declared() {
let error = parse_schema(
r#"
type Ping {
nonce String
}
mutation procedure createPayment(args: Ping): Ping
@no_rate_limit
"#,
)
.expect_err("@no_rate_limit without `extension rate_limit { }` should be a validation error");
let message = error.to_string();
assert!(
message.contains("@no_rate_limit"),
"error should name the offending attribute, got: {message}",
);
assert!(
message.contains("extension rate_limit"),
"error should point at the missing `extension rate_limit {{ }}` declaration, got: {message}",
);
}
#[test]
fn rejects_no_rate_limit_attribute_with_arguments() {
let error = parse_schema(
r#"
extension rate_limit {
}
type Ping {
nonce String
}
mutation procedure createPayment(args: Ping): Ping
@no_rate_limit("forever")
"#,
)
.expect_err("@no_rate_limit should not accept any arguments");
assert!(
error.to_string().contains("does not take any arguments"),
"error: {error}",
);
}
#[test]
fn rejects_duplicate_no_rate_limit_attribute() {
let error = parse_schema(
r#"
extension rate_limit {
}
type Ping {
nonce String
}
mutation procedure createPayment(args: Ping): Ping
@no_rate_limit
@no_rate_limit
"#,
)
.expect_err("more than one @no_rate_limit on the same procedure should be rejected");
assert!(
error.to_string().contains("more than one @no_rate_limit"),
"error: {error}",
);
}