1use crate::entities::{Entity, EntityRegistry};
7use crate::schema::{CascadeAction, ManyToManyDefinition, VbrSchemaDefinition};
8use crate::{Error, Result};
9
10pub struct MigrationManager {
12 version: u64,
14}
15
16impl MigrationManager {
17 pub fn new() -> Self {
19 Self { version: 0 }
20 }
21
22 pub fn generate_create_table(&self, entity: &Entity) -> Result<String> {
24 let schema = &entity.schema;
25 let table_name = entity.table_name();
26
27 let mut sql = format!("CREATE TABLE IF NOT EXISTS {} (\n", table_name);
28 let mut columns = Vec::new();
29
30 for field in &schema.base.fields {
32 let column_def = self.field_to_column_definition(field, schema)?;
33 columns.push(column_def);
34 }
35
36 if !schema.primary_key.is_empty() {
38 let pk_fields = schema.primary_key.join(", ");
39 columns.push(format!("PRIMARY KEY ({})", pk_fields));
40 }
41
42 sql.push_str(&columns.join(",\n "));
43 sql.push_str("\n)");
44
45 Ok(sql)
46 }
47
48 pub fn generate_foreign_keys(&self, entity: &Entity) -> Vec<String> {
50 let mut fk_statements = Vec::new();
51 let table_name = entity.table_name();
52
53 for fk in &entity.schema.foreign_keys {
54 let on_delete = cascade_action_to_sql(fk.on_delete);
55 let on_update = cascade_action_to_sql(fk.on_update);
56
57 let fk_name = format!("fk_{}_{}", table_name, fk.field);
58 let statement = format!(
59 "ALTER TABLE {} ADD CONSTRAINT {} FOREIGN KEY ({}) REFERENCES {}({}) ON DELETE {} ON UPDATE {}",
60 table_name, fk_name, fk.field, fk.target_entity.to_lowercase() + "s", fk.target_field, on_delete, on_update
61 );
62 fk_statements.push(statement);
63 }
64
65 fk_statements
66 }
67
68 pub fn generate_indexes(&self, entity: &Entity) -> Vec<String> {
70 let mut index_statements = Vec::new();
71 let table_name = entity.table_name();
72
73 for index in &entity.schema.indexes {
74 let unique = if index.unique { "UNIQUE " } else { "" };
75 let fields = index.fields.join(", ");
76 let statement = format!(
77 "CREATE {}INDEX IF NOT EXISTS {} ON {} ({})",
78 unique, index.name, table_name, fields
79 );
80 index_statements.push(statement);
81 }
82
83 index_statements
84 }
85
86 pub fn generate_junction_table(&self, m2m: &ManyToManyDefinition) -> Result<String> {
88 let junction_table = m2m
89 .junction_table
90 .as_ref()
91 .ok_or_else(|| Error::generic("Junction table name is required".to_string()))?;
92
93 let table_a = m2m.entity_a.to_lowercase() + "s";
95 let table_b = m2m.entity_b.to_lowercase() + "s";
96
97 let on_delete_a = cascade_action_to_sql(m2m.on_delete_a);
98 let on_delete_b = cascade_action_to_sql(m2m.on_delete_b);
99
100 let sql = format!(
102 "CREATE TABLE IF NOT EXISTS {} (
103 {} TEXT NOT NULL,
104 {} TEXT NOT NULL,
105 PRIMARY KEY ({}, {}),
106 FOREIGN KEY ({}) REFERENCES {}(id) ON DELETE {},
107 FOREIGN KEY ({}) REFERENCES {}(id) ON DELETE {}
108)",
109 junction_table,
110 m2m.entity_a_field,
111 m2m.entity_b_field,
112 m2m.entity_a_field,
113 m2m.entity_b_field,
114 m2m.entity_a_field,
115 table_a,
116 on_delete_a,
117 m2m.entity_b_field,
118 table_b,
119 on_delete_b
120 );
121
122 Ok(sql)
123 }
124
125 pub fn generate_all_junction_tables(
127 &self,
128 registry: &EntityRegistry,
129 ) -> Result<Vec<(String, String)>> {
130 let mut junction_tables = Vec::new();
131 let mut processed = std::collections::HashSet::new();
132
133 for entity in registry.list() {
135 if let Some(entity_def) = registry.get(&entity) {
136 for m2m in &entity_def.schema.many_to_many {
137 let (a, b) = if m2m.entity_a < m2m.entity_b {
139 (m2m.entity_a.clone(), m2m.entity_b.clone())
140 } else {
141 (m2m.entity_b.clone(), m2m.entity_a.clone())
142 };
143 let key = format!("{}:{}", a, b);
144
145 if !processed.contains(&key) {
146 processed.insert(key);
147 let table_name = m2m
148 .junction_table
149 .as_ref()
150 .ok_or_else(|| {
151 Error::generic("Junction table name is required".to_string())
152 })?
153 .clone();
154 let create_sql = self.generate_junction_table(m2m)?;
155 junction_tables.push((table_name, create_sql));
156 }
157 }
158 }
159 }
160
161 Ok(junction_tables)
162 }
163
164 fn field_to_column_definition(
166 &self,
167 field: &mockforge_data::FieldDefinition,
168 schema: &VbrSchemaDefinition,
169 ) -> Result<String> {
170 let name = &field.name;
171 let sql_type = rust_type_to_sql_type(&field.field_type)?;
172 let mut parts = vec![format!("{} {}", name, sql_type)];
173
174 if field.required {
176 parts.push("NOT NULL".to_string());
177 }
178
179 if let Some(default) = &field.default {
181 let default_value = value_to_sql_default(default)?;
182 parts.push(format!("DEFAULT {}", default_value));
183 }
184
185 if let Some(rule) = schema.auto_generation.get(name) {
187 match rule {
188 crate::schema::AutoGenerationRule::AutoIncrement => {
189 parts.push("AUTOINCREMENT".to_string());
190 }
191 crate::schema::AutoGenerationRule::Uuid => {
192 }
194 crate::schema::AutoGenerationRule::Timestamp => {
195 parts.push("DEFAULT CURRENT_TIMESTAMP".to_string());
196 }
197 crate::schema::AutoGenerationRule::Date => {
198 parts.push("DEFAULT (date('now'))".to_string());
199 }
200 crate::schema::AutoGenerationRule::Custom(expr) => {
201 parts.push(format!("DEFAULT ({})", expr));
202 }
203 crate::schema::AutoGenerationRule::Pattern(_) => {
204 }
206 crate::schema::AutoGenerationRule::Realistic { .. } => {
207 }
209 }
210 }
211
212 Ok(parts.join(" "))
213 }
214
215 pub async fn migrate(
217 &self,
218 entities: &[Entity],
219 database: &mut dyn crate::database::VirtualDatabase,
220 ) -> Result<()> {
221 for entity in entities {
223 let create_table = self.generate_create_table(entity)?;
224 database.create_table(&create_table).await?;
225
226 for index_sql in self.generate_indexes(entity) {
228 database.execute(&index_sql, &[]).await?;
229 }
230
231 }
234
235 for entity in entities {
237 for fk_sql in self.generate_foreign_keys(entity) {
238 let _ = database.execute(&fk_sql, &[]).await;
240 }
241 }
242
243 Ok(())
244 }
245}
246
247impl Default for MigrationManager {
248 fn default() -> Self {
249 Self::new()
250 }
251}
252
253pub async fn create_table_for_entity(
262 database: &dyn crate::database::VirtualDatabase,
263 entity: &Entity,
264) -> Result<()> {
265 let manager = MigrationManager::new();
266
267 let create_table = manager.generate_create_table(entity)?;
269 database.create_table(&create_table).await?;
270
271 for index_sql in manager.generate_indexes(entity) {
273 database.execute(&index_sql, &[]).await?;
274 }
275
276 for fk_sql in manager.generate_foreign_keys(entity) {
278 let _ = database.execute(&fk_sql, &[]).await;
281 }
282
283 Ok(())
284}
285
286pub async fn create_junction_tables(
292 database: &dyn crate::database::VirtualDatabase,
293 registry: &EntityRegistry,
294) -> Result<()> {
295 let manager = MigrationManager::new();
296 let junction_tables = manager.generate_all_junction_tables(registry)?;
297
298 for (_table_name, create_sql) in junction_tables {
299 database.create_table(&create_sql).await?;
300 }
301
302 Ok(())
303}
304
305fn rust_type_to_sql_type(rust_type: &str) -> Result<&str> {
307 match rust_type.to_lowercase().as_str() {
308 "string" | "str" | "text" | "uuid" | "email" | "url" => Ok("TEXT"),
309 "integer" | "int" | "i32" | "i64" => Ok("INTEGER"),
310 "float" | "double" | "f32" | "f64" | "number" => Ok("REAL"),
311 "boolean" | "bool" => Ok("INTEGER"), "date" | "datetime" | "timestamp" => Ok("TEXT"), _ => Ok("TEXT"), }
315}
316
317fn cascade_action_to_sql(action: CascadeAction) -> &'static str {
319 match action {
320 CascadeAction::NoAction => "NO ACTION",
321 CascadeAction::Cascade => "CASCADE",
322 CascadeAction::SetNull => "SET NULL",
323 CascadeAction::SetDefault => "SET DEFAULT",
324 CascadeAction::Restrict => "RESTRICT",
325 }
326}
327
328fn value_to_sql_default(value: &serde_json::Value) -> Result<String> {
330 match value {
331 serde_json::Value::String(s) => Ok(format!("'{}'", s.replace("'", "''"))),
332 serde_json::Value::Number(n) => Ok(n.to_string()),
333 serde_json::Value::Bool(b) => Ok(if *b { "1" } else { "0" }.to_string()),
334 serde_json::Value::Null => Ok("NULL".to_string()),
335 _ => Err(Error::generic("Unsupported default value type".to_string())),
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342 use crate::database::{InMemoryDatabase, VirtualDatabase};
343 use crate::schema::{
344 AutoGenerationRule, CascadeAction, ForeignKeyDefinition, IndexDefinition,
345 ManyToManyDefinition,
346 };
347 use mockforge_data::{FieldDefinition, SchemaDefinition};
348
349 fn create_test_entity(name: &str) -> Entity {
350 let base_schema = SchemaDefinition::new(name.to_string())
351 .with_field(FieldDefinition::new("id".to_string(), "string".to_string()))
352 .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
353
354 let vbr_schema = VbrSchemaDefinition::new(base_schema);
355 Entity::new(name.to_string(), vbr_schema)
356 }
357
358 #[test]
360 fn test_migration_manager_new() {
361 let manager = MigrationManager::new();
362 assert_eq!(manager.version, 0);
363 }
364
365 #[test]
366 fn test_migration_manager_default() {
367 let manager = MigrationManager::default();
368 assert_eq!(manager.version, 0);
369 }
370
371 #[test]
372 fn test_generate_create_table() {
373 let manager = MigrationManager::new();
374 let entity = create_test_entity("User");
375
376 let result = manager.generate_create_table(&entity);
377 assert!(result.is_ok());
378
379 let sql = result.unwrap();
380 assert!(sql.contains("CREATE TABLE IF NOT EXISTS users"));
381 assert!(sql.contains("id TEXT"));
382 assert!(sql.contains("name TEXT"));
383 assert!(sql.contains("PRIMARY KEY (id)"));
384 }
385
386 #[test]
387 fn test_generate_create_table_with_multiple_fields() {
388 let manager = MigrationManager::new();
389
390 let base_schema = SchemaDefinition::new("Product".to_string())
391 .with_field(FieldDefinition::new("id".to_string(), "string".to_string()))
392 .with_field(FieldDefinition::new("name".to_string(), "string".to_string()))
393 .with_field(FieldDefinition::new("price".to_string(), "number".to_string()))
394 .with_field(FieldDefinition::new("in_stock".to_string(), "boolean".to_string()));
395
396 let vbr_schema = VbrSchemaDefinition::new(base_schema);
397 let entity = Entity::new("Product".to_string(), vbr_schema);
398
399 let sql = manager.generate_create_table(&entity).unwrap();
400 assert!(sql.contains("name TEXT"));
401 assert!(sql.contains("price REAL"));
402 assert!(sql.contains("in_stock INTEGER"));
403 }
404
405 #[test]
406 fn test_generate_foreign_keys() {
407 let manager = MigrationManager::new();
408
409 let base_schema = SchemaDefinition::new("Order".to_string())
410 .with_field(FieldDefinition::new("id".to_string(), "string".to_string()));
411
412 let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
413 vbr_schema.foreign_keys.push(ForeignKeyDefinition {
414 field: "user_id".to_string(),
415 target_entity: "User".to_string(),
416 target_field: "id".to_string(),
417 on_delete: CascadeAction::Cascade,
418 on_update: CascadeAction::NoAction,
419 });
420
421 let entity = Entity::new("Order".to_string(), vbr_schema);
422
423 let fk_statements = manager.generate_foreign_keys(&entity);
424 assert_eq!(fk_statements.len(), 1);
425 assert!(fk_statements[0].contains("FOREIGN KEY"));
426 assert!(fk_statements[0].contains("user_id"));
427 assert!(fk_statements[0].contains("CASCADE"));
428 }
429
430 #[test]
431 fn test_generate_indexes() {
432 let manager = MigrationManager::new();
433
434 let base_schema = SchemaDefinition::new("User".to_string());
435 let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
436
437 vbr_schema.indexes.push(IndexDefinition {
438 name: "idx_email".to_string(),
439 fields: vec!["email".to_string()],
440 unique: true,
441 });
442
443 let entity = Entity::new("User".to_string(), vbr_schema);
444
445 let index_statements = manager.generate_indexes(&entity);
446 assert_eq!(index_statements.len(), 1);
447 assert!(index_statements[0].contains("CREATE UNIQUE INDEX"));
448 assert!(index_statements[0].contains("idx_email"));
449 assert!(index_statements[0].contains("email"));
450 }
451
452 #[test]
453 fn test_generate_indexes_non_unique() {
454 let manager = MigrationManager::new();
455
456 let base_schema = SchemaDefinition::new("Product".to_string());
457 let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
458
459 vbr_schema.indexes.push(IndexDefinition {
460 name: "idx_category".to_string(),
461 fields: vec!["category".to_string()],
462 unique: false,
463 });
464
465 let entity = Entity::new("Product".to_string(), vbr_schema);
466
467 let index_statements = manager.generate_indexes(&entity);
468 assert_eq!(index_statements.len(), 1);
469 assert!(index_statements[0].contains("CREATE INDEX"));
470 assert!(!index_statements[0].contains("UNIQUE"));
471 }
472
473 #[test]
474 fn test_generate_junction_table() {
475 let manager = MigrationManager::new();
476
477 let m2m = ManyToManyDefinition::new("User".to_string(), "Role".to_string());
478
479 let result = manager.generate_junction_table(&m2m);
480 assert!(result.is_ok());
481
482 let sql = result.unwrap();
483 assert!(sql.contains("CREATE TABLE IF NOT EXISTS"));
484 assert!(sql.contains("user_id"));
485 assert!(sql.contains("role_id"));
486 assert!(sql.contains("PRIMARY KEY"));
487 assert!(sql.contains("FOREIGN KEY"));
488 }
489
490 #[test]
491 fn test_generate_junction_table_custom() {
492 let manager = MigrationManager::new();
493
494 let m2m = ManyToManyDefinition::new("User".to_string(), "Role".to_string())
495 .with_junction_table("user_role_mapping".to_string())
496 .with_fields("usr_id".to_string(), "rol_id".to_string());
497
498 let sql = manager.generate_junction_table(&m2m).unwrap();
499 assert!(sql.contains("user_role_mapping"));
500 assert!(sql.contains("usr_id"));
501 assert!(sql.contains("rol_id"));
502 }
503
504 #[test]
505 fn test_generate_all_junction_tables() {
506 let manager = MigrationManager::new();
507 let mut registry = EntityRegistry::new();
508
509 let base_schema1 = SchemaDefinition::new("User".to_string());
511 let mut vbr_schema1 = VbrSchemaDefinition::new(base_schema1);
512 vbr_schema1
513 .many_to_many
514 .push(ManyToManyDefinition::new("User".to_string(), "Role".to_string()));
515 let entity1 = Entity::new("User".to_string(), vbr_schema1);
516 registry.register(entity1).unwrap();
517
518 let base_schema2 = SchemaDefinition::new("Role".to_string());
519 let vbr_schema2 = VbrSchemaDefinition::new(base_schema2);
520 let entity2 = Entity::new("Role".to_string(), vbr_schema2);
521 registry.register(entity2).unwrap();
522
523 let result = manager.generate_all_junction_tables(®istry);
524 assert!(result.is_ok());
525
526 let junction_tables = result.unwrap();
527 assert_eq!(junction_tables.len(), 1);
528 }
529
530 #[test]
531 fn test_generate_all_junction_tables_no_duplicates() {
532 let manager = MigrationManager::new();
533 let mut registry = EntityRegistry::new();
534
535 let base_schema1 = SchemaDefinition::new("User".to_string());
537 let mut vbr_schema1 = VbrSchemaDefinition::new(base_schema1);
538 vbr_schema1
539 .many_to_many
540 .push(ManyToManyDefinition::new("User".to_string(), "Role".to_string()));
541 let entity1 = Entity::new("User".to_string(), vbr_schema1);
542 registry.register(entity1).unwrap();
543
544 let base_schema2 = SchemaDefinition::new("Role".to_string());
545 let mut vbr_schema2 = VbrSchemaDefinition::new(base_schema2);
546 vbr_schema2
547 .many_to_many
548 .push(ManyToManyDefinition::new("Role".to_string(), "User".to_string()));
549 let entity2 = Entity::new("Role".to_string(), vbr_schema2);
550 registry.register(entity2).unwrap();
551
552 let junction_tables = manager.generate_all_junction_tables(®istry).unwrap();
553 assert_eq!(junction_tables.len(), 1);
555 }
556
557 #[tokio::test]
558 async fn test_migrate() {
559 let manager = MigrationManager::new();
560 let mut database = InMemoryDatabase::new().await.unwrap();
561 database.initialize().await.unwrap();
562
563 let entity = create_test_entity("User");
564 let entities = vec![entity];
565
566 let result = manager.migrate(&entities, &mut database).await;
567 assert!(result.is_ok());
568
569 assert!(database.table_exists("users").await.unwrap());
571 }
572
573 #[tokio::test]
574 async fn test_create_table_for_entity() {
575 let mut database = InMemoryDatabase::new().await.unwrap();
576 database.initialize().await.unwrap();
577
578 let entity = create_test_entity("Product");
579
580 let result = create_table_for_entity(&database, &entity).await;
581 assert!(result.is_ok());
582
583 assert!(database.table_exists("products").await.unwrap());
585 }
586
587 #[tokio::test]
588 async fn test_create_junction_tables() {
589 let mut database = InMemoryDatabase::new().await.unwrap();
590 database.initialize().await.unwrap();
591
592 let mut registry = EntityRegistry::new();
593
594 let base_schema1 = SchemaDefinition::new("User".to_string());
596 let mut vbr_schema1 = VbrSchemaDefinition::new(base_schema1);
597 vbr_schema1
598 .many_to_many
599 .push(ManyToManyDefinition::new("User".to_string(), "Group".to_string()));
600 let entity1 = Entity::new("User".to_string(), vbr_schema1);
601 registry.register(entity1).unwrap();
602
603 let result = create_junction_tables(&database, ®istry).await;
604 assert!(result.is_ok());
605 }
606
607 #[test]
609 fn test_rust_type_to_sql_type() {
610 assert_eq!(rust_type_to_sql_type("string").unwrap(), "TEXT");
611 assert_eq!(rust_type_to_sql_type("String").unwrap(), "TEXT");
612 assert_eq!(rust_type_to_sql_type("integer").unwrap(), "INTEGER");
613 assert_eq!(rust_type_to_sql_type("int").unwrap(), "INTEGER");
614 assert_eq!(rust_type_to_sql_type("i32").unwrap(), "INTEGER");
615 assert_eq!(rust_type_to_sql_type("float").unwrap(), "REAL");
616 assert_eq!(rust_type_to_sql_type("f64").unwrap(), "REAL");
617 assert_eq!(rust_type_to_sql_type("boolean").unwrap(), "INTEGER");
618 assert_eq!(rust_type_to_sql_type("bool").unwrap(), "INTEGER");
619 assert_eq!(rust_type_to_sql_type("date").unwrap(), "TEXT");
620 assert_eq!(rust_type_to_sql_type("datetime").unwrap(), "TEXT");
621 assert_eq!(rust_type_to_sql_type("unknown_type").unwrap(), "TEXT");
622 }
623
624 #[test]
625 fn test_cascade_action_to_sql() {
626 assert_eq!(cascade_action_to_sql(CascadeAction::NoAction), "NO ACTION");
627 assert_eq!(cascade_action_to_sql(CascadeAction::Cascade), "CASCADE");
628 assert_eq!(cascade_action_to_sql(CascadeAction::SetNull), "SET NULL");
629 assert_eq!(cascade_action_to_sql(CascadeAction::SetDefault), "SET DEFAULT");
630 assert_eq!(cascade_action_to_sql(CascadeAction::Restrict), "RESTRICT");
631 }
632
633 #[test]
634 fn test_value_to_sql_default() {
635 let result = value_to_sql_default(&serde_json::json!("test")).unwrap();
637 assert_eq!(result, "'test'");
638
639 let result = value_to_sql_default(&serde_json::json!("it's")).unwrap();
641 assert_eq!(result, "'it''s'");
642
643 let result = value_to_sql_default(&serde_json::json!(42)).unwrap();
645 assert_eq!(result, "42");
646
647 let result = value_to_sql_default(&serde_json::json!(true)).unwrap();
649 assert_eq!(result, "1");
650
651 let result = value_to_sql_default(&serde_json::json!(false)).unwrap();
653 assert_eq!(result, "0");
654
655 let result = value_to_sql_default(&serde_json::json!(null)).unwrap();
657 assert_eq!(result, "NULL");
658
659 let result = value_to_sql_default(&serde_json::json!([]));
661 assert!(result.is_err());
662 }
663
664 #[test]
665 fn test_field_to_column_definition_with_auto_generation() {
666 let manager = MigrationManager::new();
667
668 let base_schema = SchemaDefinition::new("User".to_string())
669 .with_field(FieldDefinition::new("id".to_string(), "string".to_string()));
670
671 let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
672 vbr_schema.auto_generation.insert("id".to_string(), AutoGenerationRule::Uuid);
673
674 let field = &vbr_schema.base.fields[0];
676 let result = manager.field_to_column_definition(field, &vbr_schema);
677 assert!(result.is_ok());
678
679 let column_def = result.unwrap();
680 assert!(column_def.contains("id"));
681 assert!(column_def.contains("TEXT"));
682 }
683
684 #[test]
685 fn test_field_to_column_definition_with_default() {
686 let manager = MigrationManager::new();
687
688 let mut field = FieldDefinition::new("status".to_string(), "string".to_string());
689 field.default = Some(serde_json::json!("active"));
690 let base_schema = SchemaDefinition::new("User".to_string()).with_field(field);
691
692 let vbr_schema = VbrSchemaDefinition::new(base_schema);
693 let field_ref = &vbr_schema.base.fields[0];
694
695 let column_def = manager.field_to_column_definition(field_ref, &vbr_schema).unwrap();
696 assert!(column_def.contains("DEFAULT 'active'"));
697 }
698
699 #[test]
700 fn test_field_to_column_definition_required() {
701 let manager = MigrationManager::new();
702
703 let base_schema = SchemaDefinition::new("User".to_string())
704 .with_field(FieldDefinition::new("email".to_string(), "string".to_string()));
705
706 let vbr_schema = VbrSchemaDefinition::new(base_schema);
707 let field = &vbr_schema.base.fields[0];
708
709 let column_def = manager.field_to_column_definition(field, &vbr_schema).unwrap();
710 assert!(column_def.contains("NOT NULL"));
711 }
712
713 #[test]
714 fn test_generate_create_table_with_composite_primary_key() {
715 let manager = MigrationManager::new();
716
717 let base_schema = SchemaDefinition::new("UserRole".to_string());
718 let vbr_schema = VbrSchemaDefinition::new(base_schema)
719 .with_primary_key(vec!["user_id".to_string(), "role_id".to_string()]);
720
721 let entity = Entity::new("UserRole".to_string(), vbr_schema);
722
723 let sql = manager.generate_create_table(&entity).unwrap();
724 assert!(sql.contains("PRIMARY KEY (user_id, role_id)"));
725 }
726
727 #[test]
728 fn test_generate_create_table_with_auto_increment() {
729 let manager = MigrationManager::new();
730
731 let base_schema = SchemaDefinition::new("Counter".to_string())
732 .with_field(FieldDefinition::new("id".to_string(), "integer".to_string()));
733
734 let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
735 vbr_schema
736 .auto_generation
737 .insert("id".to_string(), AutoGenerationRule::AutoIncrement);
738
739 let entity = Entity::new("Counter".to_string(), vbr_schema);
740
741 let sql = manager.generate_create_table(&entity).unwrap();
742 assert!(sql.contains("AUTOINCREMENT"));
743 }
744}