200 lines
6.9 KiB
Rust
200 lines
6.9 KiB
Rust
// tests/table_definition/post_table_definition_test4.rs
|
|
|
|
// NOTE: All 'use' statements are inherited from the parent file that includes this one.
|
|
|
|
// ========= Category 5: Implementation-Specific Edge Cases =========
|
|
|
|
#[rstest]
|
|
#[tokio::test]
|
|
async fn test_fail_on_fk_base_name_collision(#[future] pool: PgPool) {
|
|
// Scenario: Link to two tables (`team1_users`, `team2_users`) that both have a
|
|
// base name of "users". This should cause a duplicate "users_id" column in the
|
|
// generated SQL.
|
|
let pool = pool.await;
|
|
|
|
// Arrange: Create the two prerequisite tables
|
|
let req1 = PostTableDefinitionRequest {
|
|
profile_name: "default".into(),
|
|
table_name: "team1_users".into(),
|
|
..Default::default()
|
|
};
|
|
post_table_definition(&pool, req1).await.unwrap();
|
|
|
|
let req2 = PostTableDefinitionRequest {
|
|
profile_name: "default".into(),
|
|
table_name: "team2_users".into(),
|
|
..Default::default()
|
|
};
|
|
post_table_definition(&pool, req2).await.unwrap();
|
|
|
|
// Arrange: A request that links to both, causing the collision
|
|
let colliding_req = PostTableDefinitionRequest {
|
|
profile_name: "default".into(),
|
|
table_name: "tasks".into(),
|
|
links: vec![
|
|
TableLink {
|
|
linked_table_name: "team1_users".into(),
|
|
required: true,
|
|
},
|
|
TableLink {
|
|
linked_table_name: "team2_users".into(),
|
|
required: false,
|
|
},
|
|
],
|
|
..Default::default()
|
|
};
|
|
|
|
// Act
|
|
let result = post_table_definition(&pool, colliding_req).await;
|
|
|
|
// Assert
|
|
let err = result.unwrap_err();
|
|
assert_eq!(
|
|
err.code(),
|
|
Code::Internal,
|
|
"Expected Internal error from duplicate column in CREATE TABLE"
|
|
);
|
|
}
|
|
|
|
|
|
#[rstest]
|
|
#[tokio::test]
|
|
async fn test_sql_reserved_keywords_as_identifiers_are_allowed(#[future] pool: PgPool) {
|
|
// NOTE: This test confirms that the system currently allows SQL reserved keywords
|
|
// as column names because they are correctly quoted. This is technically correct,
|
|
// but some systems add validation to block this as a policy to prevent user confusion.
|
|
let pool = pool.await;
|
|
let keywords = vec!["user", "select", "group", "order"];
|
|
|
|
for (i, keyword) in keywords.into_iter().enumerate() {
|
|
let table_name = format!("keyword_test_{}", i);
|
|
let request = PostTableDefinitionRequest {
|
|
profile_name: "default".into(),
|
|
table_name: table_name.clone(),
|
|
columns: vec![ColumnDefinition {
|
|
name: keyword.into(),
|
|
field_type: "text".into(),
|
|
}],
|
|
..Default::default()
|
|
};
|
|
|
|
// Act & Assert
|
|
let response = post_table_definition(&pool, request)
|
|
.await
|
|
.unwrap_or_else(|e| {
|
|
panic!(
|
|
"Failed to create table with reserved keyword '{}': {:?}",
|
|
keyword, e
|
|
)
|
|
});
|
|
|
|
assert!(response.success);
|
|
assert!(response.sql.contains(&format!("\"{}\" TEXT", keyword)));
|
|
|
|
// FIXED: Added schema parameter
|
|
assert_table_structure_is_correct(&pool, "default", &table_name, &[(keyword, "text")]).await;
|
|
}
|
|
}
|
|
|
|
// ========= Category 6: Environmental and Extreme Edge Cases =========
|
|
|
|
#[rstest]
|
|
#[tokio::test]
|
|
async fn test_sanitization_of_unicode_and_special_chars(#[future] pool: PgPool) {
|
|
let pool = pool.await;
|
|
let request = PostTableDefinitionRequest {
|
|
profile_name: "default".into(),
|
|
table_name: "produits_😂".into(), // Invalid unicode
|
|
columns: vec![ColumnDefinition {
|
|
name: "col_with_unicode".into(),
|
|
field_type: "text".into(),
|
|
}],
|
|
..Default::default()
|
|
};
|
|
|
|
// FIXED: Now expect error instead of success
|
|
let result = post_table_definition(&pool, request).await;
|
|
assert!(result.is_err());
|
|
if let Err(status) = result {
|
|
assert!(status.message().contains("Table name contains invalid characters"));
|
|
}
|
|
}
|
|
|
|
#[rstest]
|
|
#[tokio::test]
|
|
async fn test_fail_gracefully_if_schema_is_missing(#[future] pool: PgPool) {
|
|
// Scenario: The handler relies on schemas existing. This test ensures
|
|
// it fails gracefully if the schema creation fails.
|
|
let pool = pool.await;
|
|
|
|
// Arrange: Try to create a table with an invalid schema name that would cause issues
|
|
let request = PostTableDefinitionRequest {
|
|
profile_name: "invalid-schema-name!@#".into(), // This should be sanitized and potentially cause issues
|
|
table_name: "this_will_fail".into(),
|
|
..Default::default()
|
|
};
|
|
|
|
// Act
|
|
let result = post_table_definition(&pool, request).await;
|
|
|
|
// Assert - This should either succeed with sanitized name or fail gracefully
|
|
match result {
|
|
Ok(_) => {
|
|
// If it succeeds, the sanitization worked
|
|
// This is actually a valid outcome
|
|
},
|
|
Err(err) => {
|
|
// If it fails, it should be a clear error, not a panic
|
|
assert_eq!(err.code(), Code::InvalidArgument);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[rstest]
|
|
#[tokio::test]
|
|
async fn test_column_name_with_id_suffix_is_rejected(#[future] pool: PgPool) {
|
|
let pool = pool.await;
|
|
let request = PostTableDefinitionRequest {
|
|
profile_name: "default".into(),
|
|
table_name: "orders".into(),
|
|
columns: vec![ColumnDefinition {
|
|
name: "legacy_order_id".into(),
|
|
field_type: "integer".into(),
|
|
}],
|
|
..Default::default()
|
|
};
|
|
let result = post_table_definition(&pool, request).await;
|
|
assert!(result.is_err(), "Column names ending with '_id' should be rejected");
|
|
if let Err(status) = result {
|
|
assert_eq!(status.code(), tonic::Code::InvalidArgument);
|
|
// UPDATED: Match the new error message format
|
|
assert!(status.message().contains("Column name 'legacy_order_id' cannot be") &&
|
|
status.message().contains("end with '_id'"));
|
|
}
|
|
}
|
|
|
|
#[rstest]
|
|
#[tokio::test]
|
|
async fn test_table_name_with_id_suffix_is_rejected(#[future] pool: PgPool) {
|
|
// Test that table names ending with '_id' are properly rejected during input validation
|
|
let pool = pool.await;
|
|
|
|
let request = PostTableDefinitionRequest {
|
|
profile_name: "default".into(),
|
|
table_name: "orders_id".into(), // This should be rejected
|
|
columns: vec![ColumnDefinition {
|
|
name: "customer_name".into(), // Valid column name
|
|
field_type: "text".into(),
|
|
}],
|
|
..Default::default()
|
|
};
|
|
|
|
// Act & Assert - should fail validation
|
|
let result = post_table_definition(&pool, request).await;
|
|
assert!(result.is_err(), "Table names ending with '_id' should be rejected");
|
|
if let Err(status) = result {
|
|
assert_eq!(status.code(), tonic::Code::InvalidArgument);
|
|
assert!(status.message().contains("Table name cannot be 'id', 'deleted', 'created_at' or end with '_id'"));
|
|
}
|
|
}
|