#![cfg(test)]
use crate::parse_schema;
fn collision_error(source: &str) -> String {
parse_schema(source)
.expect_err("a procedure colliding with a generated CRUD handler must be rejected")
.to_string()
}
const ORDER_MODEL: &str = r#"
model Order {
id String @id
total Int
}
"#;
fn schema_with_procedure(signature: &str) -> String {
format!("{ORDER_MODEL}\nprocedure {signature}\n")
}
#[test]
fn rejects_get_procedure_colliding_with_the_model_get_handler() {
let message = collision_error(&schema_with_procedure("getOrder(orderId: String): Order"));
assert!(message.contains("getOrder"), "error: {message}");
assert!(message.contains("Order"), "error: {message}");
assert!(message.contains("handle_get_order"), "error: {message}");
assert!(message.contains("rename the procedure"), "error: {message}");
}
#[test]
fn rejects_update_and_delete_procedures_on_the_singular_stem() {
for signature in [
"updateOrder(orderId: String): Order",
"deleteOrder(orderId: String): Order",
] {
let message = collision_error(&schema_with_procedure(signature));
assert!(message.contains("Order"), "error: {message}");
}
}
#[test]
fn rejects_list_and_create_procedures_on_the_pluralized_stem() {
for (signature, ident) in [
("listOrders(limit: Int): Order", "handle_list_orders"),
("createOrders(total: Int): Order", "handle_create_orders"),
] {
let message = collision_error(&schema_with_procedure(signature));
assert!(message.contains(ident), "error: {message}");
}
}
#[test]
fn rejects_a_snake_case_spelled_procedure_identically() {
let message = collision_error(&schema_with_procedure("get_order(orderId: String): Order"));
assert!(message.contains("handle_get_order"), "error: {message}");
}
#[test]
fn rejects_a_procedure_colliding_with_the_dispatch_twin() {
let message = collision_error(&schema_with_procedure(
"getOrderDispatch(orderId: String): Order",
));
assert!(
message.contains("handle_get_order_dispatch"),
"error: {message}"
);
}
#[test]
fn rejects_the_collision_on_a_multi_word_model_name() {
let source = r#"
model SubOrder {
id String @id
total Int
}
procedure getSubOrder(subOrderId: String): SubOrder
"#;
let message = collision_error(source);
assert!(message.contains("handle_get_sub_order"), "error: {message}");
}
#[test]
fn internal_suppression_is_not_an_exemption() {
let source = r#"
model Order {
id String @id
total Int
@@internal("get")
}
procedure getOrder(orderId: String): Order
"#;
let message = collision_error(source);
assert!(message.contains("handle_get_order"), "error: {message}");
}
#[test]
fn accepts_a_procedure_that_shares_no_generated_ident() {
parse_schema(&schema_with_procedure(
"orderDetail(orderId: String): Order",
))
.expect("`orderDetail` shares no ident with any generated CRUD handler");
}
#[test]
fn accepts_a_verb_prefixed_procedure_naming_no_declared_model() {
parse_schema(&schema_with_procedure(
"getInvoice(invoiceId: String): Order",
))
.expect("there is no `model Invoice`, so `handle_get_invoice` is unclaimed");
}
#[test]
fn accepts_a_colliding_name_when_the_schema_declares_no_model() {
parse_schema(
r#"
datasource db {
provider = "none"
}
procedure getOrder(orderId: String): String
"#,
)
.expect("a procedures-only schema generates no CRUD handlers");
}