// 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_column_name_with_id_suffix_is_allowed(#[future] pool: PgPool) { // NOTE: This test confirms the CURRENT behavior of the code, which is that creating // a column with an `_id` suffix is allowed. The existing test // `test_fail_on_column_name_suffix_id` makes a contrary assumption. // If this behavior is undesirable, the `is_valid_identifier` function or its // usage in the handler should be updated to reject such names. let pool = pool.await; let request = PostTableDefinitionRequest { profile_name: "default".into(), table_name: "orders_with_custom_id".into(), columns: vec![ColumnDefinition { name: "legacy_order_id".into(), // A user-defined column ending in `_id` field_type: "integer".into(), }], ..Default::default() }; // Act let response = post_table_definition(&pool, request).await.unwrap(); // Assert assert!(response.success); assert!(response.sql.contains("\"legacy_order_id\" INTEGER")); // Verify in the actual database assert_table_structure_is_correct( &pool, "orders_with_custom_id", &[("legacy_order_id", "integer")], ) .await; } #[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))); assert_table_structure_is_correct(&pool, &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) { // Scenario: Use identifiers with characters that should be stripped by sanitization, // including multi-byte unicode (emoji) and a null byte. let pool = pool.await; let request = PostTableDefinitionRequest { profile_name: "default".into(), table_name: "produits_😂".into(), // Should become "produits_" columns: vec![ColumnDefinition { name: "col\0with_null".into(), // Should become "colwith_null" field_type: "text".into(), }], ..Default::default() }; // Act let response = post_table_definition(&pool, request).await.unwrap(); // Assert assert!(response.success); // Assert that the generated SQL contains the SANITIZED names assert!(response.sql.contains("CREATE TABLE gen.\"produits_\"")); assert!(response.sql.contains("\"colwith_null\" TEXT")); // Verify the actual structure in the database assert_table_structure_is_correct(&pool, "produits_", &[("colwith_null", "text")]).await; } #[rstest] #[tokio::test] async fn test_fail_gracefully_if_schema_is_missing(#[future] pool: PgPool) { // Scenario: The handler relies on the 'gen' schema existing. This test ensures // it fails gracefully if that assumption is broken. let pool = pool.await; // Arrange: Drop the schema that the handler needs sqlx::query("DROP SCHEMA gen CASCADE;") .execute(&pool) .await .expect("Failed to drop 'gen' schema for test setup"); let request = PostTableDefinitionRequest { profile_name: "default".into(), table_name: "this_will_fail".into(), ..Default::default() }; // Act let result = post_table_definition(&pool, request).await; // Assert let err = result.unwrap_err(); assert_eq!(err.code(), Code::Internal); // Check for the Postgres error message for a missing schema. assert!(err.message().to_lowercase().contains("schema \"gen\" does not exist")); }