#![cfg(test)]
use super::parse_schema;
#[test]
fn rejects_self_as_a_model_field_name() {
let error = parse_schema(
r#"
model KwProbe {
id Int @id
self String
}
"#,
)
.expect_err("a field named `self` must be rejected");
let message = error.to_string();
assert!(message.contains("self"), "error: {message}");
assert!(message.contains("KwProbe"), "error: {message}");
assert!(message.contains("model"), "error: {message}");
}
#[test]
fn rejects_every_unrepresentable_keyword_as_a_model_field_name() {
for keyword in ["self", "Self", "super", "crate"] {
let source = format!(
r#"
model KwProbe {{
id Int @id
{keyword} String
}}
"#
);
parse_schema(&source).expect_err(&format!("field named `{keyword}` must be rejected"));
}
}
#[test]
fn rejects_self_as_a_type_block_field_name() {
let error = parse_schema(
r#"
type KwProbe {
self String
}
"#,
)
.expect_err("a `type` block field named `self` must be rejected");
let message = error.to_string();
assert!(message.contains("self"), "error: {message}");
assert!(message.contains("KwProbe"), "error: {message}");
assert!(message.contains("type"), "error: {message}");
}
#[test]
fn rejects_self_as_a_mixin_field_name() {
let error = parse_schema(
r#"
mixin KwProbe {
self String
}
"#,
)
.expect_err("a mixin field named `self` must be rejected");
assert!(error.to_string().contains("mixin"), "error: {error}");
}
#[test]
fn raw_escapable_keywords_parse_successfully_as_model_fields() {
for keyword in [
"match", "type", "ref", "move", "impl", "fn", "let", "loop", "box",
] {
let source = format!(
r#"
model KwProbe {{
id Int @id
{keyword} String
}}
"#
);
let schema = parse_schema(&source)
.unwrap_or_else(|error| panic!("`{keyword}` should parse fine: {error}"));
assert_eq!(schema.models[0].fields[1].name, keyword);
}
}
#[test]
fn rejects_self_as_an_enum_name() {
let error = parse_schema(
r#"
enum self {
active
inactive
}
"#,
)
.expect_err("an enum named `self` must be rejected");
let message = error.to_string();
assert!(message.contains("self"), "error: {message}");
assert!(message.contains("enum"), "error: {message}");
}
#[test]
fn rejects_self_as_an_enum_variant_name() {
let error = parse_schema(
r#"
enum Status {
self
active
}
"#,
)
.expect_err("an enum variant named `self` must be rejected");
let message = error.to_string();
assert!(message.contains("self"), "error: {message}");
assert!(message.contains("Status"), "error: {message}");
}
#[test]
fn rejects_self_as_a_top_level_model_name() {
let error = parse_schema(
r#"
model self {
id Int @id
}
"#,
)
.expect_err("a model named `self` must be rejected");
let message = error.to_string();
assert!(message.contains("self"), "error: {message}");
assert!(message.contains("model"), "error: {message}");
}
#[test]
fn rejects_self_as_a_top_level_mixin_name() {
let error = parse_schema(
r#"
mixin self {
createdAt DateTime
}
"#,
)
.expect_err("a mixin named `self` must be rejected");
assert!(error.to_string().contains("mixin"), "error: {error}");
}
#[test]
fn rejects_self_as_a_top_level_type_name() {
let error = parse_schema(
r#"
type self {
label String
}
"#,
)
.expect_err("a `type` block named `self` must be rejected");
assert!(error.to_string().contains("type"), "error: {error}");
}
#[test]
fn rejects_self_as_a_view_name() {
let error = parse_schema(
r#"
datasource db {
provider = "postgresql"
}
model Customer {
id Int @id
}
view self from Customer {
id Int @id @from(Customer.id)
@@server_sql("SELECT id FROM customer")
}
"#,
)
.expect_err("a view named `self` must be rejected");
assert!(error.to_string().contains("view"), "error: {error}");
}
#[test]
fn rejects_self_as_a_procedure_name() {
let error = parse_schema(
r#"
procedure self(): Int
"#,
)
.expect_err("a procedure named `self` must be rejected");
let message = error.to_string();
assert!(message.contains("self"), "error: {message}");
assert!(message.contains("procedure"), "error: {message}");
}
#[test]
fn rejects_every_unrepresentable_keyword_as_a_procedure_name() {
for keyword in ["self", "Self", "super", "crate"] {
let source = format!("procedure {keyword}(): Int\n");
parse_schema(&source).expect_err(&format!("procedure named `{keyword}` must be rejected"));
}
}
#[test]
fn rejects_self_as_a_procedure_argument_name() {
let error = parse_schema(
r#"
procedure getFeed(self: Int): Int
"#,
)
.expect_err("a procedure argument named `self` must be rejected");
let message = error.to_string();
assert!(message.contains("self"), "error: {message}");
assert!(message.contains("procedure argument"), "error: {message}");
assert!(message.contains("getFeed"), "error: {message}");
}
#[test]
fn rejects_every_unrepresentable_keyword_as_a_procedure_argument_name() {
for keyword in ["self", "Self", "super", "crate"] {
let source = format!("procedure getFeed({keyword}: Int): Int\n");
parse_schema(&source).expect_err(&format!(
"procedure argument named `{keyword}` must be rejected"
));
}
}
#[test]
fn substring_matches_of_reserved_keywords_are_not_rejected() {
let schema = parse_schema(
r#"
model Probe {
id Int @id
selfie String
crater String
}
"#,
)
.expect("`selfie`/`crater` fields are not reserved keywords and must parse");
assert_eq!(schema.models[0].fields[1].name, "selfie");
assert_eq!(schema.models[0].fields[2].name, "crater");
}
#[test]
fn ordinary_top_level_names_are_not_rejected() {
let schema = parse_schema(
r#"
enum Status {
active
inactive
}
model Widget {
id Int @id
status Status
}
mixin Timestamps {
createdAt DateTime
}
type Summary {
count Int
}
procedure getWidget(id: Int): Widget
"#,
)
.expect("an ordinary schema should parse and validate fine");
assert_eq!(schema.enums[0].name, "Status");
assert_eq!(schema.models[0].name, "Widget");
assert_eq!(schema.mixins[0].name, "Timestamps");
assert_eq!(schema.types[0].name, "Summary");
assert_eq!(schema.procedures[0].name, "getWidget");
}