@auto-engineer/server-generator-apollo-emmett 0.11.13 → 0.11.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
 
2
- > @auto-engineer/server-generator-apollo-emmett@0.11.13 build /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
2
+ > @auto-engineer/server-generator-apollo-emmett@0.11.14 build /home/runner/work/auto-engineer/auto-engineer/packages/server-generator-apollo-emmett
3
3
  > tsc && tsx ../../scripts/fix-esm-imports.ts && rm -rf dist/src/codegen/templates && mkdir -p dist/src/codegen && cp -r src/codegen/templates dist/src/codegen/templates && cp src/server.ts dist/src && cp -r src/utils dist/src && cp -r src/domain dist/src
4
4
 
5
5
  Fixed ESM imports in dist/
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @auto-engineer/server-generator-apollo-emmett
2
2
 
3
+ ## 0.11.14
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies []:
8
+ - @auto-engineer/narrative@0.11.14
9
+ - @auto-engineer/message-bus@0.11.14
10
+
3
11
  ## 0.11.13
4
12
 
5
13
  ### Patch Changes
@@ -620,4 +620,372 @@ describe('projection.specs.ts.ejs', () => {
620
620
  // canHandle must include BOTH events
621
621
  expect(projectionFile?.contents).toContain("canHandle: ['QuestionnaireLinkSent', 'QuestionAnswered']");
622
622
  });
623
+
624
+ it('should generate a valid test spec for singleton projection', async () => {
625
+ const spec: SpecsSchema = {
626
+ variant: 'specs',
627
+ narratives: [
628
+ {
629
+ name: 'todo-flow',
630
+ slices: [
631
+ {
632
+ type: 'command',
633
+ name: 'manage-todo',
634
+ stream: 'todo-${todoId}',
635
+ client: { description: '' },
636
+ server: {
637
+ description: '',
638
+ specs: {
639
+ name: 'Manage todo command',
640
+ rules: [
641
+ {
642
+ description: 'Should handle todo operations',
643
+ examples: [
644
+ {
645
+ description: 'User adds todo',
646
+ when: {
647
+ commandRef: 'AddTodo',
648
+ exampleData: {
649
+ todoId: 'todo_123',
650
+ title: 'Buy milk',
651
+ },
652
+ },
653
+ then: [
654
+ {
655
+ eventRef: 'TodoAdded',
656
+ exampleData: {
657
+ todoId: 'todo_123',
658
+ title: 'Buy milk',
659
+ },
660
+ },
661
+ ],
662
+ },
663
+ ],
664
+ },
665
+ ],
666
+ },
667
+ },
668
+ },
669
+ {
670
+ type: 'query',
671
+ name: 'view-summary',
672
+ stream: 'todos',
673
+ client: { description: '' },
674
+ server: {
675
+ description: '',
676
+ data: [
677
+ {
678
+ target: {
679
+ type: 'State',
680
+ name: 'TodoSummary',
681
+ },
682
+ origin: {
683
+ type: 'projection',
684
+ name: 'TodoSummaryProjection',
685
+ singleton: true,
686
+ },
687
+ },
688
+ ],
689
+ specs: {
690
+ name: 'View summary query',
691
+ rules: [
692
+ {
693
+ description: 'Should aggregate todo counts',
694
+ examples: [
695
+ {
696
+ description: 'Todo added updates count',
697
+ when: [
698
+ {
699
+ eventRef: 'TodoAdded',
700
+ exampleData: {
701
+ todoId: 'todo_123',
702
+ title: 'Buy milk',
703
+ },
704
+ },
705
+ ],
706
+ then: [
707
+ {
708
+ stateRef: 'TodoSummary',
709
+ exampleData: {
710
+ totalCount: 1,
711
+ },
712
+ },
713
+ ],
714
+ },
715
+ ],
716
+ },
717
+ ],
718
+ },
719
+ },
720
+ },
721
+ ],
722
+ },
723
+ ],
724
+ messages: [
725
+ {
726
+ type: 'command',
727
+ name: 'AddTodo',
728
+ fields: [
729
+ { name: 'todoId', type: 'string', required: true },
730
+ { name: 'title', type: 'string', required: true },
731
+ ],
732
+ },
733
+ {
734
+ type: 'event',
735
+ name: 'TodoAdded',
736
+ source: 'internal',
737
+ fields: [
738
+ { name: 'todoId', type: 'string', required: true },
739
+ { name: 'title', type: 'string', required: true },
740
+ ],
741
+ },
742
+ {
743
+ type: 'state',
744
+ name: 'TodoSummary',
745
+ fields: [{ name: 'totalCount', type: 'number', required: true }],
746
+ },
747
+ ],
748
+ } as SpecsSchema;
749
+
750
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
751
+ const specFile = plans.find((p) => p.outputPath.endsWith('view-summary/projection.specs.ts'));
752
+
753
+ expect(specFile?.contents).toMatchInlineSnapshot(`
754
+ "import { describe, it, beforeEach, expect } from 'vitest';
755
+ import { InMemoryProjectionSpec } from '@event-driven-io/emmett';
756
+ import { projection } from './projection';
757
+ import type { TodoAdded } from '../manage-todo/events';
758
+ import { TodoSummary } from './state';
759
+
760
+ type ProjectionEvent = TodoAdded;
761
+
762
+ describe('Should aggregate todo counts', () => {
763
+ let given: InMemoryProjectionSpec<ProjectionEvent>;
764
+
765
+ beforeEach(() => {
766
+ given = InMemoryProjectionSpec.for({ projection });
767
+ });
768
+
769
+ it('Todo added updates count', () =>
770
+ given([])
771
+ .when([
772
+ {
773
+ type: 'TodoAdded',
774
+ data: {
775
+ todoId: 'todo_123',
776
+ title: 'Buy milk',
777
+ },
778
+ metadata: {
779
+ streamName: 'todos',
780
+ streamPosition: 1n,
781
+ globalPosition: 1n,
782
+ },
783
+ },
784
+ ])
785
+ .then(async (state) => {
786
+ const document = await state.database
787
+ .collection<TodoSummary>('TodoSummaryProjection')
788
+ .findOne((doc) => doc.id === 'test-id');
789
+
790
+ const expected: TodoSummary = {
791
+ totalCount: 1,
792
+ };
793
+
794
+ expect(document).toMatchObject(expected);
795
+ }));
796
+ });
797
+ "
798
+ `);
799
+ });
800
+
801
+ it('should generate a valid test spec for composite key projection', async () => {
802
+ const spec: SpecsSchema = {
803
+ variant: 'specs',
804
+ narratives: [
805
+ {
806
+ name: 'user-project-flow',
807
+ slices: [
808
+ {
809
+ type: 'command',
810
+ name: 'manage-user-project',
811
+ stream: 'user-project-${userId}-${projectId}',
812
+ client: { description: '' },
813
+ server: {
814
+ description: '',
815
+ specs: {
816
+ name: 'Manage user project command',
817
+ rules: [
818
+ {
819
+ description: 'Should handle user project operations',
820
+ examples: [
821
+ {
822
+ description: 'User joins project',
823
+ when: {
824
+ commandRef: 'JoinProject',
825
+ exampleData: {
826
+ userId: 'user_123',
827
+ projectId: 'proj_456',
828
+ role: 'developer',
829
+ },
830
+ },
831
+ then: [
832
+ {
833
+ eventRef: 'UserJoinedProject',
834
+ exampleData: {
835
+ userId: 'user_123',
836
+ projectId: 'proj_456',
837
+ role: 'developer',
838
+ },
839
+ },
840
+ ],
841
+ },
842
+ ],
843
+ },
844
+ ],
845
+ },
846
+ },
847
+ },
848
+ {
849
+ type: 'query',
850
+ name: 'view-user-projects',
851
+ stream: 'user-projects',
852
+ client: { description: '' },
853
+ server: {
854
+ description: '',
855
+ data: [
856
+ {
857
+ target: {
858
+ type: 'State',
859
+ name: 'UserProject',
860
+ },
861
+ origin: {
862
+ type: 'projection',
863
+ name: 'UserProjectsProjection',
864
+ idField: ['userId', 'projectId'],
865
+ },
866
+ },
867
+ ],
868
+ specs: {
869
+ name: 'View user projects query',
870
+ rules: [
871
+ {
872
+ description: 'Should track user project memberships',
873
+ examples: [
874
+ {
875
+ description: 'User joins project',
876
+ when: [
877
+ {
878
+ eventRef: 'UserJoinedProject',
879
+ exampleData: {
880
+ userId: 'user_123',
881
+ projectId: 'proj_456',
882
+ role: 'developer',
883
+ },
884
+ },
885
+ ],
886
+ then: [
887
+ {
888
+ stateRef: 'UserProject',
889
+ exampleData: {
890
+ userId: 'user_123',
891
+ projectId: 'proj_456',
892
+ role: 'developer',
893
+ },
894
+ },
895
+ ],
896
+ },
897
+ ],
898
+ },
899
+ ],
900
+ },
901
+ },
902
+ },
903
+ ],
904
+ },
905
+ ],
906
+ messages: [
907
+ {
908
+ type: 'command',
909
+ name: 'JoinProject',
910
+ fields: [
911
+ { name: 'userId', type: 'string', required: true },
912
+ { name: 'projectId', type: 'string', required: true },
913
+ { name: 'role', type: 'string', required: true },
914
+ ],
915
+ },
916
+ {
917
+ type: 'event',
918
+ name: 'UserJoinedProject',
919
+ source: 'internal',
920
+ fields: [
921
+ { name: 'userId', type: 'string', required: true },
922
+ { name: 'projectId', type: 'string', required: true },
923
+ { name: 'role', type: 'string', required: true },
924
+ ],
925
+ },
926
+ {
927
+ type: 'state',
928
+ name: 'UserProject',
929
+ fields: [
930
+ { name: 'userId', type: 'string', required: true },
931
+ { name: 'projectId', type: 'string', required: true },
932
+ { name: 'role', type: 'string', required: true },
933
+ ],
934
+ },
935
+ ],
936
+ } as SpecsSchema;
937
+
938
+ const plans = await generateScaffoldFilePlans(spec.narratives, spec.messages, undefined, 'src/domain/flows');
939
+ const specFile = plans.find((p) => p.outputPath.endsWith('view-user-projects/projection.specs.ts'));
940
+
941
+ expect(specFile?.contents).toMatchInlineSnapshot(`
942
+ "import { describe, it, beforeEach, expect } from 'vitest';
943
+ import { InMemoryProjectionSpec } from '@event-driven-io/emmett';
944
+ import { projection } from './projection';
945
+ import type { UserJoinedProject } from '../manage-user-project/events';
946
+ import { UserProject } from './state';
947
+
948
+ type ProjectionEvent = UserJoinedProject;
949
+
950
+ describe('Should track user project memberships', () => {
951
+ let given: InMemoryProjectionSpec<ProjectionEvent>;
952
+
953
+ beforeEach(() => {
954
+ given = InMemoryProjectionSpec.for({ projection });
955
+ });
956
+
957
+ it('User joins project', () =>
958
+ given([])
959
+ .when([
960
+ {
961
+ type: 'UserJoinedProject',
962
+ data: {
963
+ userId: 'user_123',
964
+ projectId: 'proj_456',
965
+ role: 'developer',
966
+ },
967
+ metadata: {
968
+ streamName: 'user-projects',
969
+ streamPosition: 1n,
970
+ globalPosition: 1n,
971
+ },
972
+ },
973
+ ])
974
+ .then(async (state) => {
975
+ const document = await state.database
976
+ .collection<UserProject>('UserProjectsProjection')
977
+ .findOne((doc) => doc.id === 'test-id');
978
+
979
+ const expected: UserProject = {
980
+ userId: 'user_123',
981
+ projectId: 'proj_456',
982
+ role: 'developer',
983
+ };
984
+
985
+ expect(document).toMatchObject(expected);
986
+ }));
987
+ });
988
+ "
989
+ `);
990
+ });
623
991
  });