// 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 use full table names // for FK columns, so no collision occurs - this should succeed. 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 - should succeed with full table names let linking_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, linking_req).await; // Assert - should succeed let response = result.unwrap(); assert!(response.success); // Verify the SQL contains both full table name FK columns assert!(response.sql.contains("\"team1_users_id\""), "SQL should contain team1_users_id column"); assert!(response.sql.contains("\"team2_users_id\""), "SQL should contain team2_users_id column"); // Verify the references are correct assert!(response.sql.contains("REFERENCES \"default\".\"team1_users\"(id)")); assert!(response.sql.contains("REFERENCES \"default\".\"team2_users\"(id)")); // Verify one is NOT NULL and one is nullable assert!(response.sql.contains("\"team1_users_id\" BIGINT NOT NULL")); assert!(response.sql.contains("\"team2_users_id\" BIGINT REFERENCES")); } #[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'")); } }