Skip to main content

agentic_forge_core/engine/
write.rs

1//! WriteEngine — mutation operations for blueprints.
2
3use crate::engine::ForgeEngine;
4use crate::types::blueprint::*;
5use crate::types::ids::*;
6use crate::types::intent::*;
7use crate::types::{ForgeError, ForgeResult, MAX_DEPENDENCIES, MAX_ENTITIES, MAX_FILES};
8
9pub struct WriteEngine<'a> {
10    engine: &'a mut ForgeEngine,
11}
12
13impl<'a> WriteEngine<'a> {
14    pub fn new(engine: &'a mut ForgeEngine) -> Self {
15        Self { engine }
16    }
17
18    // Blueprint operations
19
20    pub fn rename_blueprint(
21        &mut self,
22        id: &BlueprintId,
23        name: impl Into<String>,
24    ) -> ForgeResult<()> {
25        let bp = self.engine.store.load_mut(id)?;
26        bp.name = name.into();
27        bp.touch();
28        self.engine.dirty = true;
29        Ok(())
30    }
31
32    pub fn set_description(
33        &mut self,
34        id: &BlueprintId,
35        desc: impl Into<String>,
36    ) -> ForgeResult<()> {
37        let bp = self.engine.store.load_mut(id)?;
38        bp.description = desc.into();
39        bp.touch();
40        self.engine.dirty = true;
41        Ok(())
42    }
43
44    pub fn set_status(&mut self, id: &BlueprintId, status: BlueprintStatus) -> ForgeResult<()> {
45        let bp = self.engine.store.load_mut(id)?;
46        bp.status = status;
47        bp.touch();
48        self.engine.dirty = true;
49        Ok(())
50    }
51
52    pub fn set_version(&mut self, id: &BlueprintId, version: impl Into<String>) -> ForgeResult<()> {
53        let bp = self.engine.store.load_mut(id)?;
54        bp.version = version.into();
55        bp.touch();
56        self.engine.dirty = true;
57        Ok(())
58    }
59
60    pub fn set_metadata(
61        &mut self,
62        id: &BlueprintId,
63        key: impl Into<String>,
64        value: impl Into<String>,
65    ) -> ForgeResult<()> {
66        let bp = self.engine.store.load_mut(id)?;
67        bp.metadata.insert(key.into(), value.into());
68        bp.touch();
69        self.engine.dirty = true;
70        Ok(())
71    }
72
73    pub fn delete_blueprint(&mut self, id: &BlueprintId) -> ForgeResult<Blueprint> {
74        let bp = self.engine.store.delete(id)?;
75        self.engine.indexes.remove_blueprint(id);
76        self.engine.dirty = true;
77        Ok(bp)
78    }
79
80    // Entity operations
81
82    pub fn add_entity(&mut self, bp_id: &BlueprintId, entity: Entity) -> ForgeResult<EntityId> {
83        let bp = self.engine.store.load_mut(bp_id)?;
84        if bp.entities.len() >= MAX_ENTITIES {
85            return Err(ForgeError::capacity("entities", MAX_ENTITIES));
86        }
87        if bp.entities.iter().any(|e| e.name == entity.name) {
88            return Err(ForgeError::DuplicateEntity(entity.name.clone()));
89        }
90        let id = entity.id;
91        bp.entities.push(entity);
92        bp.touch();
93        self.engine.dirty = true;
94        Ok(id)
95    }
96
97    pub fn add_entity_from_spec(
98        &mut self,
99        bp_id: &BlueprintId,
100        spec: &EntitySpec,
101    ) -> ForgeResult<EntityId> {
102        let mut entity = Entity::new(&spec.name, &spec.description);
103        entity.is_aggregate_root = spec.is_aggregate_root;
104        for field in &spec.fields {
105            entity.fields.push(EntityField {
106                name: field.name.clone(),
107                field_type: field.field_type.clone(),
108                required: field.required,
109                default_value: field.default_value.clone(),
110                description: String::new(),
111            });
112        }
113        for op in &spec.operations {
114            let mut eop = EntityOperation::new(&op.name, op.operation_type);
115            eop.description = op.description.clone();
116            eop.is_async = op.is_async;
117            eop.return_type = op.return_type.clone();
118            eop.error_types = op.error_cases.clone();
119            for p in &op.parameters {
120                eop.parameters.push(OperationParameter {
121                    name: p.name.clone(),
122                    param_type: p.param_type.clone(),
123                    required: p.required,
124                });
125            }
126            entity.operations.push(eop);
127        }
128        self.add_entity(bp_id, entity)
129    }
130
131    pub fn remove_entity(
132        &mut self,
133        bp_id: &BlueprintId,
134        entity_id: &EntityId,
135    ) -> ForgeResult<Entity> {
136        let bp = self.engine.store.load_mut(bp_id)?;
137        let pos = bp
138            .entities
139            .iter()
140            .position(|e| e.id == *entity_id)
141            .ok_or_else(|| ForgeError::EntityNotFound(entity_id.to_string()))?;
142        let entity = bp.entities.remove(pos);
143        bp.touch();
144        self.engine.dirty = true;
145        Ok(entity)
146    }
147
148    pub fn update_entity_name(
149        &mut self,
150        bp_id: &BlueprintId,
151        entity_id: &EntityId,
152        name: impl Into<String>,
153    ) -> ForgeResult<()> {
154        let bp = self.engine.store.load_mut(bp_id)?;
155        let entity = bp
156            .entities
157            .iter_mut()
158            .find(|e| e.id == *entity_id)
159            .ok_or_else(|| ForgeError::EntityNotFound(entity_id.to_string()))?;
160        entity.name = name.into();
161        bp.touch();
162        self.engine.dirty = true;
163        Ok(())
164    }
165
166    pub fn add_field_to_entity(
167        &mut self,
168        bp_id: &BlueprintId,
169        entity_id: &EntityId,
170        field: EntityField,
171    ) -> ForgeResult<()> {
172        let bp = self.engine.store.load_mut(bp_id)?;
173        let entity = bp
174            .entities
175            .iter_mut()
176            .find(|e| e.id == *entity_id)
177            .ok_or_else(|| ForgeError::EntityNotFound(entity_id.to_string()))?;
178        entity.fields.push(field);
179        bp.touch();
180        self.engine.dirty = true;
181        Ok(())
182    }
183
184    pub fn remove_field_from_entity(
185        &mut self,
186        bp_id: &BlueprintId,
187        entity_id: &EntityId,
188        field_name: &str,
189    ) -> ForgeResult<()> {
190        let bp = self.engine.store.load_mut(bp_id)?;
191        let entity = bp
192            .entities
193            .iter_mut()
194            .find(|e| e.id == *entity_id)
195            .ok_or_else(|| ForgeError::EntityNotFound(entity_id.to_string()))?;
196        let pos = entity
197            .fields
198            .iter()
199            .position(|f| f.name == field_name)
200            .ok_or_else(|| ForgeError::MissingField(field_name.to_string()))?;
201        entity.fields.remove(pos);
202        bp.touch();
203        self.engine.dirty = true;
204        Ok(())
205    }
206
207    pub fn add_operation_to_entity(
208        &mut self,
209        bp_id: &BlueprintId,
210        entity_id: &EntityId,
211        op: EntityOperation,
212    ) -> ForgeResult<OperationId> {
213        let bp = self.engine.store.load_mut(bp_id)?;
214        let entity = bp
215            .entities
216            .iter_mut()
217            .find(|e| e.id == *entity_id)
218            .ok_or_else(|| ForgeError::EntityNotFound(entity_id.to_string()))?;
219        let id = op.id;
220        entity.operations.push(op);
221        bp.touch();
222        self.engine.dirty = true;
223        Ok(id)
224    }
225
226    pub fn remove_operation_from_entity(
227        &mut self,
228        bp_id: &BlueprintId,
229        entity_id: &EntityId,
230        op_id: &OperationId,
231    ) -> ForgeResult<()> {
232        let bp = self.engine.store.load_mut(bp_id)?;
233        let entity = bp
234            .entities
235            .iter_mut()
236            .find(|e| e.id == *entity_id)
237            .ok_or_else(|| ForgeError::EntityNotFound(entity_id.to_string()))?;
238        let pos = entity
239            .operations
240            .iter()
241            .position(|o| o.id == *op_id)
242            .ok_or_else(|| ForgeError::OperationNotFound(op_id.to_string()))?;
243        entity.operations.remove(pos);
244        bp.touch();
245        self.engine.dirty = true;
246        Ok(())
247    }
248
249    pub fn add_relationship(
250        &mut self,
251        bp_id: &BlueprintId,
252        entity_id: &EntityId,
253        rel: Relationship,
254    ) -> ForgeResult<()> {
255        let bp = self.engine.store.load_mut(bp_id)?;
256        let entity = bp
257            .entities
258            .iter_mut()
259            .find(|e| e.id == *entity_id)
260            .ok_or_else(|| ForgeError::EntityNotFound(entity_id.to_string()))?;
261        entity.relationships.push(rel);
262        bp.touch();
263        self.engine.dirty = true;
264        Ok(())
265    }
266
267    pub fn add_validation_rule(
268        &mut self,
269        bp_id: &BlueprintId,
270        entity_id: &EntityId,
271        rule: ValidationRule,
272    ) -> ForgeResult<()> {
273        let bp = self.engine.store.load_mut(bp_id)?;
274        let entity = bp
275            .entities
276            .iter_mut()
277            .find(|e| e.id == *entity_id)
278            .ok_or_else(|| ForgeError::EntityNotFound(entity_id.to_string()))?;
279        entity.validation_rules.push(rule);
280        bp.touch();
281        self.engine.dirty = true;
282        Ok(())
283    }
284
285    // File operations
286
287    pub fn add_file(&mut self, bp_id: &BlueprintId, file: FileBlueprint) -> ForgeResult<FileId> {
288        let bp = self.engine.store.load_mut(bp_id)?;
289        if bp.files.len() >= MAX_FILES {
290            return Err(ForgeError::capacity("files", MAX_FILES));
291        }
292        let id = file.id;
293        bp.files.push(file);
294        bp.touch();
295        self.engine.dirty = true;
296        Ok(id)
297    }
298
299    pub fn remove_file(
300        &mut self,
301        bp_id: &BlueprintId,
302        file_id: &FileId,
303    ) -> ForgeResult<FileBlueprint> {
304        let bp = self.engine.store.load_mut(bp_id)?;
305        let pos = bp
306            .files
307            .iter()
308            .position(|f| f.id == *file_id)
309            .ok_or_else(|| ForgeError::FileNotFound(file_id.to_string()))?;
310        let file = bp.files.remove(pos);
311        bp.touch();
312        self.engine.dirty = true;
313        Ok(file)
314    }
315
316    pub fn update_file_imports(
317        &mut self,
318        bp_id: &BlueprintId,
319        file_id: &FileId,
320        imports: Vec<String>,
321    ) -> ForgeResult<()> {
322        let bp = self.engine.store.load_mut(bp_id)?;
323        let file = bp
324            .files
325            .iter_mut()
326            .find(|f| f.id == *file_id)
327            .ok_or_else(|| ForgeError::FileNotFound(file_id.to_string()))?;
328        file.imports = imports;
329        bp.touch();
330        self.engine.dirty = true;
331        Ok(())
332    }
333
334    pub fn update_file_exports(
335        &mut self,
336        bp_id: &BlueprintId,
337        file_id: &FileId,
338        exports: Vec<String>,
339    ) -> ForgeResult<()> {
340        let bp = self.engine.store.load_mut(bp_id)?;
341        let file = bp
342            .files
343            .iter_mut()
344            .find(|f| f.id == *file_id)
345            .ok_or_else(|| ForgeError::FileNotFound(file_id.to_string()))?;
346        file.exports = exports;
347        bp.touch();
348        self.engine.dirty = true;
349        Ok(())
350    }
351
352    // Dependency operations
353
354    pub fn add_dependency(
355        &mut self,
356        bp_id: &BlueprintId,
357        dep: Dependency,
358    ) -> ForgeResult<DependencyId> {
359        let bp = self.engine.store.load_mut(bp_id)?;
360        if bp.dependencies.len() >= MAX_DEPENDENCIES {
361            return Err(ForgeError::capacity("dependencies", MAX_DEPENDENCIES));
362        }
363        if bp.dependencies.iter().any(|d| d.name == dep.name) {
364            return Err(ForgeError::DuplicateDependency(dep.name.clone()));
365        }
366        let id = dep.id;
367        bp.dependencies.push(dep);
368        bp.touch();
369        self.engine.dirty = true;
370        Ok(id)
371    }
372
373    pub fn remove_dependency(
374        &mut self,
375        bp_id: &BlueprintId,
376        dep_id: &DependencyId,
377    ) -> ForgeResult<Dependency> {
378        let bp = self.engine.store.load_mut(bp_id)?;
379        let pos = bp
380            .dependencies
381            .iter()
382            .position(|d| d.id == *dep_id)
383            .ok_or_else(|| ForgeError::DependencyNotFound(dep_id.to_string()))?;
384        let dep = bp.dependencies.remove(pos);
385        bp.touch();
386        self.engine.dirty = true;
387        Ok(dep)
388    }
389
390    pub fn update_dependency_version(
391        &mut self,
392        bp_id: &BlueprintId,
393        dep_id: &DependencyId,
394        version: impl Into<String>,
395    ) -> ForgeResult<()> {
396        let bp = self.engine.store.load_mut(bp_id)?;
397        let dep = bp
398            .dependencies
399            .iter_mut()
400            .find(|d| d.id == *dep_id)
401            .ok_or_else(|| ForgeError::DependencyNotFound(dep_id.to_string()))?;
402        dep.version = version.into();
403        bp.touch();
404        self.engine.dirty = true;
405        Ok(())
406    }
407
408    // Test operations
409
410    pub fn add_test_case(&mut self, bp_id: &BlueprintId, tc: TestCase) -> ForgeResult<TestCaseId> {
411        let bp = self.engine.store.load_mut(bp_id)?;
412        let id = tc.id;
413        bp.test_cases.push(tc);
414        bp.touch();
415        self.engine.dirty = true;
416        Ok(id)
417    }
418
419    pub fn remove_test_case(
420        &mut self,
421        bp_id: &BlueprintId,
422        tc_id: &TestCaseId,
423    ) -> ForgeResult<TestCase> {
424        let bp = self.engine.store.load_mut(bp_id)?;
425        let pos = bp
426            .test_cases
427            .iter()
428            .position(|t| t.id == *tc_id)
429            .ok_or_else(|| ForgeError::TestCaseNotFound(tc_id.to_string()))?;
430        let tc = bp.test_cases.remove(pos);
431        bp.touch();
432        self.engine.dirty = true;
433        Ok(tc)
434    }
435
436    // Type definition operations
437
438    pub fn add_type_definition(
439        &mut self,
440        bp_id: &BlueprintId,
441        td: TypeDefinition,
442    ) -> ForgeResult<()> {
443        let bp = self.engine.store.load_mut(bp_id)?;
444        bp.type_definitions.push(td);
445        bp.touch();
446        self.engine.dirty = true;
447        Ok(())
448    }
449
450    pub fn remove_type_definition(&mut self, bp_id: &BlueprintId, name: &str) -> ForgeResult<()> {
451        let bp = self.engine.store.load_mut(bp_id)?;
452        let pos = bp
453            .type_definitions
454            .iter()
455            .position(|t| t.name == name)
456            .ok_or_else(|| ForgeError::MissingField(name.to_string()))?;
457        bp.type_definitions.remove(pos);
458        bp.touch();
459        self.engine.dirty = true;
460        Ok(())
461    }
462
463    // Function blueprint operations
464
465    pub fn add_function_blueprint(
466        &mut self,
467        bp_id: &BlueprintId,
468        fb: FunctionBlueprint,
469    ) -> ForgeResult<()> {
470        let bp = self.engine.store.load_mut(bp_id)?;
471        bp.function_blueprints.push(fb);
472        bp.touch();
473        self.engine.dirty = true;
474        Ok(())
475    }
476
477    // Architecture operations
478
479    pub fn add_layer(&mut self, bp_id: &BlueprintId, layer: ArchitectureLayer) -> ForgeResult<()> {
480        let bp = self.engine.store.load_mut(bp_id)?;
481        bp.layers.push(layer);
482        bp.touch();
483        self.engine.dirty = true;
484        Ok(())
485    }
486
487    pub fn add_concern(
488        &mut self,
489        bp_id: &BlueprintId,
490        concern: CrossCuttingConcern,
491    ) -> ForgeResult<()> {
492        let bp = self.engine.store.load_mut(bp_id)?;
493        bp.concerns.push(concern);
494        bp.touch();
495        self.engine.dirty = true;
496        Ok(())
497    }
498
499    // Wiring operations
500
501    pub fn add_wiring(&mut self, bp_id: &BlueprintId, wiring: ComponentWiring) -> ForgeResult<()> {
502        let bp = self.engine.store.load_mut(bp_id)?;
503        bp.wiring.push(wiring);
504        bp.touch();
505        self.engine.dirty = true;
506        Ok(())
507    }
508
509    pub fn add_data_flow(&mut self, bp_id: &BlueprintId, flow: DataFlow) -> ForgeResult<()> {
510        let bp = self.engine.store.load_mut(bp_id)?;
511        bp.data_flows.push(flow);
512        bp.touch();
513        self.engine.dirty = true;
514        Ok(())
515    }
516
517    pub fn add_import_edge(&mut self, bp_id: &BlueprintId, edge: ImportEdge) -> ForgeResult<()> {
518        let bp = self.engine.store.load_mut(bp_id)?;
519        bp.import_graph.push(edge);
520        bp.touch();
521        self.engine.dirty = true;
522        Ok(())
523    }
524
525    pub fn set_generation_order(
526        &mut self,
527        bp_id: &BlueprintId,
528        order: Vec<String>,
529    ) -> ForgeResult<()> {
530        let bp = self.engine.store.load_mut(bp_id)?;
531        bp.generation_order = order;
532        bp.touch();
533        self.engine.dirty = true;
534        Ok(())
535    }
536}
537
538#[cfg(test)]
539mod tests {
540    use super::*;
541    use crate::engine::ForgeEngine;
542
543    fn setup() -> (ForgeEngine, BlueprintId) {
544        let mut engine = ForgeEngine::new();
545        let id = engine
546            .create_blueprint("Test", "Test blueprint", Domain::Api)
547            .unwrap();
548        (engine, id)
549    }
550
551    #[test]
552    fn test_rename_blueprint() {
553        let (mut engine, id) = setup();
554        engine.writer().rename_blueprint(&id, "Renamed").unwrap();
555        assert_eq!(engine.store.load(&id).unwrap().name, "Renamed");
556    }
557
558    #[test]
559    fn test_set_description() {
560        let (mut engine, id) = setup();
561        engine.writer().set_description(&id, "New desc").unwrap();
562        assert_eq!(engine.store.load(&id).unwrap().description, "New desc");
563    }
564
565    #[test]
566    fn test_set_status() {
567        let (mut engine, id) = setup();
568        engine
569            .writer()
570            .set_status(&id, BlueprintStatus::Complete)
571            .unwrap();
572        assert_eq!(
573            engine.store.load(&id).unwrap().status,
574            BlueprintStatus::Complete
575        );
576    }
577
578    #[test]
579    fn test_set_version() {
580        let (mut engine, id) = setup();
581        engine.writer().set_version(&id, "1.0.0").unwrap();
582        assert_eq!(engine.store.load(&id).unwrap().version, "1.0.0");
583    }
584
585    #[test]
586    fn test_set_metadata() {
587        let (mut engine, id) = setup();
588        engine.writer().set_metadata(&id, "key", "value").unwrap();
589        assert_eq!(
590            engine.store.load(&id).unwrap().metadata.get("key").unwrap(),
591            "value"
592        );
593    }
594
595    #[test]
596    fn test_add_entity() {
597        let (mut engine, id) = setup();
598        let entity = Entity::new("User", "A user");
599        let eid = engine.writer().add_entity(&id, entity).unwrap();
600        let bp = engine.store.load(&id).unwrap();
601        assert_eq!(bp.entity_count(), 1);
602        assert!(bp.find_entity_by_id(&eid).is_some());
603    }
604
605    #[test]
606    fn test_add_duplicate_entity() {
607        let (mut engine, id) = setup();
608        engine
609            .writer()
610            .add_entity(&id, Entity::new("User", "A"))
611            .unwrap();
612        let result = engine.writer().add_entity(&id, Entity::new("User", "B"));
613        assert!(result.is_err());
614    }
615
616    #[test]
617    fn test_remove_entity() {
618        let (mut engine, id) = setup();
619        let eid = engine
620            .writer()
621            .add_entity(&id, Entity::new("User", "A"))
622            .unwrap();
623        engine.writer().remove_entity(&id, &eid).unwrap();
624        assert_eq!(engine.store.load(&id).unwrap().entity_count(), 0);
625    }
626
627    #[test]
628    fn test_update_entity_name() {
629        let (mut engine, id) = setup();
630        let eid = engine
631            .writer()
632            .add_entity(&id, Entity::new("User", "A"))
633            .unwrap();
634        engine
635            .writer()
636            .update_entity_name(&id, &eid, "Account")
637            .unwrap();
638        assert_eq!(
639            engine
640                .store
641                .load(&id)
642                .unwrap()
643                .find_entity_by_id(&eid)
644                .unwrap()
645                .name,
646            "Account"
647        );
648    }
649
650    #[test]
651    fn test_add_field_to_entity() {
652        let (mut engine, id) = setup();
653        let eid = engine
654            .writer()
655            .add_entity(&id, Entity::new("User", "A"))
656            .unwrap();
657        let field = EntityField::new("name", FieldType::String);
658        engine
659            .writer()
660            .add_field_to_entity(&id, &eid, field)
661            .unwrap();
662        let bp = engine.store.load(&id).unwrap();
663        assert_eq!(bp.find_entity_by_id(&eid).unwrap().fields.len(), 1);
664    }
665
666    #[test]
667    fn test_remove_field_from_entity() {
668        let (mut engine, id) = setup();
669        let eid = engine
670            .writer()
671            .add_entity(&id, Entity::new("User", "A"))
672            .unwrap();
673        let field = EntityField::new("name", FieldType::String);
674        engine
675            .writer()
676            .add_field_to_entity(&id, &eid, field)
677            .unwrap();
678        engine
679            .writer()
680            .remove_field_from_entity(&id, &eid, "name")
681            .unwrap();
682        assert_eq!(
683            engine
684                .store
685                .load(&id)
686                .unwrap()
687                .find_entity_by_id(&eid)
688                .unwrap()
689                .fields
690                .len(),
691            0
692        );
693    }
694
695    #[test]
696    fn test_add_operation_to_entity() {
697        let (mut engine, id) = setup();
698        let eid = engine
699            .writer()
700            .add_entity(&id, Entity::new("User", "A"))
701            .unwrap();
702        let op = EntityOperation::new("create", OperationType::Create);
703        engine
704            .writer()
705            .add_operation_to_entity(&id, &eid, op)
706            .unwrap();
707        assert_eq!(
708            engine
709                .store
710                .load(&id)
711                .unwrap()
712                .find_entity_by_id(&eid)
713                .unwrap()
714                .operations
715                .len(),
716            1
717        );
718    }
719
720    #[test]
721    fn test_add_file() {
722        let (mut engine, id) = setup();
723        let file = FileBlueprint::new("src/main.rs", FileType::Source);
724        engine.writer().add_file(&id, file).unwrap();
725        assert_eq!(engine.store.load(&id).unwrap().file_count(), 1);
726    }
727
728    #[test]
729    fn test_remove_file() {
730        let (mut engine, id) = setup();
731        let file = FileBlueprint::new("src/main.rs", FileType::Source);
732        let fid = engine.writer().add_file(&id, file).unwrap();
733        engine.writer().remove_file(&id, &fid).unwrap();
734        assert_eq!(engine.store.load(&id).unwrap().file_count(), 0);
735    }
736
737    #[test]
738    fn test_add_dependency() {
739        let (mut engine, id) = setup();
740        let dep = Dependency::new("serde", "1.0");
741        engine.writer().add_dependency(&id, dep).unwrap();
742        assert_eq!(engine.store.load(&id).unwrap().dependency_count(), 1);
743    }
744
745    #[test]
746    fn test_add_duplicate_dependency() {
747        let (mut engine, id) = setup();
748        engine
749            .writer()
750            .add_dependency(&id, Dependency::new("serde", "1.0"))
751            .unwrap();
752        let result = engine
753            .writer()
754            .add_dependency(&id, Dependency::new("serde", "2.0"));
755        assert!(result.is_err());
756    }
757
758    #[test]
759    fn test_remove_dependency() {
760        let (mut engine, id) = setup();
761        let did = engine
762            .writer()
763            .add_dependency(&id, Dependency::new("serde", "1.0"))
764            .unwrap();
765        engine.writer().remove_dependency(&id, &did).unwrap();
766        assert_eq!(engine.store.load(&id).unwrap().dependency_count(), 0);
767    }
768
769    #[test]
770    fn test_update_dependency_version() {
771        let (mut engine, id) = setup();
772        let did = engine
773            .writer()
774            .add_dependency(&id, Dependency::new("serde", "1.0"))
775            .unwrap();
776        engine
777            .writer()
778            .update_dependency_version(&id, &did, "2.0")
779            .unwrap();
780        assert_eq!(
781            engine
782                .store
783                .load(&id)
784                .unwrap()
785                .find_dependency("serde")
786                .unwrap()
787                .version,
788            "2.0"
789        );
790    }
791
792    #[test]
793    fn test_add_test_case() {
794        let (mut engine, id) = setup();
795        let tc = TestCase::new("test_create", TestType::Unit, "User::create");
796        engine.writer().add_test_case(&id, tc).unwrap();
797        assert_eq!(engine.store.load(&id).unwrap().test_count(), 1);
798    }
799
800    #[test]
801    fn test_add_type_definition() {
802        let (mut engine, id) = setup();
803        let td = TypeDefinition::new("User", TypeKind::Struct);
804        engine.writer().add_type_definition(&id, td).unwrap();
805        assert_eq!(engine.store.load(&id).unwrap().type_definitions.len(), 1);
806    }
807
808    #[test]
809    fn test_add_function_blueprint() {
810        let (mut engine, id) = setup();
811        let fb = FunctionBlueprint::new("create_user");
812        engine.writer().add_function_blueprint(&id, fb).unwrap();
813        assert_eq!(engine.store.load(&id).unwrap().function_blueprints.len(), 1);
814    }
815
816    #[test]
817    fn test_add_layer() {
818        let (mut engine, id) = setup();
819        let layer = ArchitectureLayer {
820            name: "domain".into(),
821            description: "Domain layer".into(),
822            modules: vec!["models".into()],
823            allowed_dependencies: vec![],
824        };
825        engine.writer().add_layer(&id, layer).unwrap();
826        assert_eq!(engine.store.load(&id).unwrap().layers.len(), 1);
827    }
828
829    #[test]
830    fn test_add_concern() {
831        let (mut engine, id) = setup();
832        let concern = CrossCuttingConcern {
833            name: "logging".into(),
834            concern_type: ConcernType::Logging,
835            affected_layers: vec!["all".into()],
836            implementation_strategy: "tracing".into(),
837        };
838        engine.writer().add_concern(&id, concern).unwrap();
839        assert_eq!(engine.store.load(&id).unwrap().concerns.len(), 1);
840    }
841
842    #[test]
843    fn test_add_wiring() {
844        let (mut engine, id) = setup();
845        let wiring = ComponentWiring {
846            source: "UserService".into(),
847            target: "UserRepository".into(),
848            wiring_type: WiringType::DependencyInjection,
849            description: "Service depends on repo".into(),
850        };
851        engine.writer().add_wiring(&id, wiring).unwrap();
852        assert_eq!(engine.store.load(&id).unwrap().wiring.len(), 1);
853    }
854
855    #[test]
856    fn test_add_data_flow() {
857        let (mut engine, id) = setup();
858        let flow = DataFlow {
859            source: "API".into(),
860            target: "Database".into(),
861            data_type: "User".into(),
862            direction: FlowDirection::Unidirectional,
863            is_async: true,
864        };
865        engine.writer().add_data_flow(&id, flow).unwrap();
866        assert_eq!(engine.store.load(&id).unwrap().data_flows.len(), 1);
867    }
868
869    #[test]
870    fn test_add_import_edge() {
871        let (mut engine, id) = setup();
872        let edge = ImportEdge {
873            from_file: "src/main.rs".into(),
874            to_file: "src/lib.rs".into(),
875            imported_symbols: vec!["App".into()],
876        };
877        engine.writer().add_import_edge(&id, edge).unwrap();
878        assert_eq!(engine.store.load(&id).unwrap().import_graph.len(), 1);
879    }
880
881    #[test]
882    fn test_set_generation_order() {
883        let (mut engine, id) = setup();
884        let order = vec!["types.rs".into(), "models.rs".into(), "main.rs".into()];
885        engine.writer().set_generation_order(&id, order).unwrap();
886        assert_eq!(engine.store.load(&id).unwrap().generation_order.len(), 3);
887    }
888
889    #[test]
890    fn test_delete_blueprint() {
891        let (mut engine, id) = setup();
892        engine.writer().delete_blueprint(&id).unwrap();
893        assert_eq!(engine.blueprint_count(), 0);
894    }
895
896    #[test]
897    fn test_add_entity_from_spec() {
898        let (mut engine, id) = setup();
899        let spec = EntitySpec::new("User", "A user")
900            .with_field(FieldSpec::new("name", FieldType::String))
901            .with_operation(OperationSpec::new("create", OperationType::Create).async_op());
902        engine.writer().add_entity_from_spec(&id, &spec).unwrap();
903        let bp = engine.store.load(&id).unwrap();
904        let entity = bp.find_entity("User").unwrap();
905        assert_eq!(entity.fields.len(), 1);
906        assert_eq!(entity.operations.len(), 1);
907        assert!(entity.operations[0].is_async);
908    }
909
910    #[test]
911    fn test_add_relationship() {
912        let (mut engine, id) = setup();
913        let eid = engine
914            .writer()
915            .add_entity(&id, Entity::new("User", "A"))
916            .unwrap();
917        let rel = Relationship {
918            target_entity: "Post".into(),
919            relationship_type: RelationshipType::HasMany,
920            cardinality: Cardinality::OneToMany,
921            description: "User has many posts".into(),
922        };
923        engine.writer().add_relationship(&id, &eid, rel).unwrap();
924        let entity = engine
925            .store
926            .load(&id)
927            .unwrap()
928            .find_entity_by_id(&eid)
929            .unwrap();
930        assert_eq!(entity.relationships.len(), 1);
931    }
932
933    #[test]
934    fn test_add_validation_rule() {
935        let (mut engine, id) = setup();
936        let eid = engine
937            .writer()
938            .add_entity(&id, Entity::new("User", "A"))
939            .unwrap();
940        let rule = ValidationRule {
941            field: "email".into(),
942            rule_type: "format".into(),
943            parameters: std::collections::HashMap::new(),
944            message: "Invalid email".into(),
945        };
946        engine
947            .writer()
948            .add_validation_rule(&id, &eid, rule)
949            .unwrap();
950    }
951
952    #[test]
953    fn test_update_file_imports() {
954        let (mut engine, id) = setup();
955        let fid = engine
956            .writer()
957            .add_file(&id, FileBlueprint::new("src/main.rs", FileType::Source))
958            .unwrap();
959        engine
960            .writer()
961            .update_file_imports(&id, &fid, vec!["std::io".into()])
962            .unwrap();
963        let file = engine
964            .store
965            .load(&id)
966            .unwrap()
967            .files
968            .iter()
969            .find(|f| f.id == fid)
970            .unwrap();
971        assert_eq!(file.imports.len(), 1);
972    }
973
974    #[test]
975    fn test_update_file_exports() {
976        let (mut engine, id) = setup();
977        let fid = engine
978            .writer()
979            .add_file(&id, FileBlueprint::new("src/lib.rs", FileType::Source))
980            .unwrap();
981        engine
982            .writer()
983            .update_file_exports(&id, &fid, vec!["App".into()])
984            .unwrap();
985        let file = engine
986            .store
987            .load(&id)
988            .unwrap()
989            .files
990            .iter()
991            .find(|f| f.id == fid)
992            .unwrap();
993        assert_eq!(file.exports.len(), 1);
994    }
995
996    #[test]
997    fn test_remove_operation_from_entity() {
998        let (mut engine, id) = setup();
999        let eid = engine
1000            .writer()
1001            .add_entity(&id, Entity::new("User", "A"))
1002            .unwrap();
1003        let op = EntityOperation::new("create", OperationType::Create);
1004        let oid = engine
1005            .writer()
1006            .add_operation_to_entity(&id, &eid, op)
1007            .unwrap();
1008        engine
1009            .writer()
1010            .remove_operation_from_entity(&id, &eid, &oid)
1011            .unwrap();
1012        assert_eq!(
1013            engine
1014                .store
1015                .load(&id)
1016                .unwrap()
1017                .find_entity_by_id(&eid)
1018                .unwrap()
1019                .operations
1020                .len(),
1021            0
1022        );
1023    }
1024
1025    #[test]
1026    fn test_remove_type_definition() {
1027        let (mut engine, id) = setup();
1028        engine
1029            .writer()
1030            .add_type_definition(&id, TypeDefinition::new("User", TypeKind::Struct))
1031            .unwrap();
1032        engine.writer().remove_type_definition(&id, "User").unwrap();
1033        assert_eq!(engine.store.load(&id).unwrap().type_definitions.len(), 0);
1034    }
1035
1036    #[test]
1037    fn test_remove_test_case() {
1038        let (mut engine, id) = setup();
1039        let tcid = engine
1040            .writer()
1041            .add_test_case(&id, TestCase::new("test_a", TestType::Unit, "A"))
1042            .unwrap();
1043        engine.writer().remove_test_case(&id, &tcid).unwrap();
1044        assert_eq!(engine.store.load(&id).unwrap().test_count(), 0);
1045    }
1046}