bunsane 0.3.1 → 0.4.0

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.
Files changed (224) hide show
  1. package/CHANGELOG.md +445 -318
  2. package/config/cache.config.ts +35 -1
  3. package/core/App.ts +24 -1064
  4. package/core/ArcheType.ts +78 -2110
  5. package/core/BatchLoader.ts +56 -32
  6. package/core/Entity.ts +85 -1043
  7. package/core/EntityHookManager.ts +52 -754
  8. package/core/Logger.ts +10 -0
  9. package/core/RequestContext.ts +64 -6
  10. package/core/RequestLoaders.ts +187 -36
  11. package/core/SchedulerManager.ts +28 -600
  12. package/core/app/bootstrap.ts +133 -0
  13. package/core/app/cors.ts +85 -0
  14. package/core/app/graphqlSetup.ts +56 -0
  15. package/core/app/healthEndpoints.ts +31 -0
  16. package/core/app/metricsCollector.ts +27 -0
  17. package/core/app/preparedStatementWarmup.ts +15 -0
  18. package/core/app/processHandlers.ts +43 -0
  19. package/core/app/requestRouter.ts +310 -0
  20. package/core/app/restRegistry.ts +80 -0
  21. package/core/app/shutdown.ts +97 -0
  22. package/core/app/studioRouter.ts +83 -0
  23. package/core/archetype/customTypes.ts +100 -0
  24. package/core/archetype/decorators.ts +171 -0
  25. package/core/archetype/fieldResolvers.ts +666 -0
  26. package/core/archetype/helpers.ts +29 -0
  27. package/core/archetype/relationLoader.ts +161 -0
  28. package/core/archetype/schemaBuilder.ts +141 -0
  29. package/core/archetype/weaver.ts +218 -0
  30. package/core/archetype/zodSchemaBuilder.ts +527 -0
  31. package/core/cache/CacheManager.ts +173 -267
  32. package/core/cache/CompressionUtils.ts +34 -3
  33. package/core/cache/MemoryCache.ts +40 -37
  34. package/core/cache/RedisCache.ts +4 -4
  35. package/core/cache/health.ts +30 -0
  36. package/core/cache/invalidation.ts +96 -0
  37. package/core/cache/strategies/writeInvalidate.ts +111 -0
  38. package/core/cache/strategies/writeThrough.ts +233 -0
  39. package/core/components/BaseComponent.ts +16 -8
  40. package/core/components/ComponentRegistry.ts +28 -0
  41. package/core/decorators/IndexedField.ts +1 -1
  42. package/core/entity/cacheStrategies.ts +97 -0
  43. package/core/entity/componentAccess.ts +364 -0
  44. package/core/entity/finders.ts +202 -0
  45. package/core/entity/pendingOps.ts +72 -0
  46. package/core/entity/saveEntity.ts +377 -0
  47. package/core/hooks/dispatcher.ts +439 -0
  48. package/core/hooks/guards.ts +155 -0
  49. package/core/hooks/registry.ts +247 -0
  50. package/core/metadata/definitions/Component.ts +1 -1
  51. package/core/metadata/index.ts +15 -4
  52. package/core/middleware/AccessLog.ts +8 -1
  53. package/core/middleware/RateLimit.ts +102 -105
  54. package/core/middleware/RequestId.ts +2 -9
  55. package/core/middleware/SecurityHeaders.ts +2 -11
  56. package/core/middleware/headers.ts +28 -0
  57. package/core/remote/OutboxWorker.ts +213 -183
  58. package/core/remote/RemoteManager.ts +401 -400
  59. package/core/remote/types.ts +153 -151
  60. package/core/requestScope.ts +34 -0
  61. package/core/scheduler/cronEvaluator.ts +174 -0
  62. package/core/scheduler/lifecycleHooks.ts +21 -0
  63. package/core/scheduler/lockCoordinator.ts +27 -0
  64. package/core/scheduler/metrics.ts +14 -0
  65. package/core/scheduler/taskRunner.ts +420 -0
  66. package/database/DatabaseHelper.ts +128 -101
  67. package/database/IndexingStrategy.ts +72 -2
  68. package/database/PreparedStatementCache.ts +20 -5
  69. package/database/cancellable.ts +35 -0
  70. package/database/index.ts +15 -3
  71. package/database/instrumentedDb.ts +141 -0
  72. package/endpoints/archetypes.ts +2 -8
  73. package/endpoints/tables.ts +6 -1
  74. package/gql/index.ts +1 -1
  75. package/gql/visitors/ResolverGeneratorVisitor.ts +25 -4
  76. package/package.json +22 -1
  77. package/query/CTENode.ts +5 -3
  78. package/query/ComponentInclusionNode.ts +240 -13
  79. package/query/OrNode.ts +6 -5
  80. package/query/Query.ts +203 -59
  81. package/query/QueryContext.ts +6 -0
  82. package/query/QueryDAG.ts +7 -2
  83. package/query/membershipSource.ts +66 -0
  84. package/storage/LocalStorageProvider.ts +8 -3
  85. package/studio/dist/assets/index-BMZ67Npg.js +254 -0
  86. package/studio/dist/assets/index-BpbuYz9g.css +1 -0
  87. package/studio/{index.html → dist/index.html} +3 -2
  88. package/swagger/generator.ts +11 -1
  89. package/upload/UploadManager.ts +8 -6
  90. package/utils/uuid.ts +40 -10
  91. package/.claude/settings.local.json +0 -47
  92. package/.prettierrc +0 -4
  93. package/.serena/memories/architectural-decision-no-dependency-injection.md +0 -76
  94. package/.serena/memories/architecture.md +0 -154
  95. package/.serena/memories/cache-interface-refactoring-2026-01-24.md +0 -165
  96. package/.serena/memories/code_style_and_conventions.md +0 -76
  97. package/.serena/memories/project_overview.md +0 -43
  98. package/.serena/memories/schema-dsl-plan.md +0 -107
  99. package/.serena/memories/suggested_commands.md +0 -80
  100. package/.serena/memories/typescript-compilation-status.md +0 -54
  101. package/.serena/project.yml +0 -114
  102. package/BunSane.jpg +0 -0
  103. package/CLAUDE.md +0 -198
  104. package/TODO.md +0 -2
  105. package/bun.lock +0 -302
  106. package/bunfig.toml +0 -10
  107. package/docs/SCALABILITY_PLAN.md +0 -175
  108. package/studio/bun.lock +0 -482
  109. package/studio/package.json +0 -39
  110. package/studio/postcss.config.js +0 -6
  111. package/studio/src/components/DataTable.tsx +0 -211
  112. package/studio/src/components/Layout.tsx +0 -13
  113. package/studio/src/components/PageContainer.tsx +0 -9
  114. package/studio/src/components/PageHeader.tsx +0 -13
  115. package/studio/src/components/SearchBar.tsx +0 -57
  116. package/studio/src/components/Sidebar.tsx +0 -294
  117. package/studio/src/components/ui/button.tsx +0 -56
  118. package/studio/src/components/ui/checkbox.tsx +0 -26
  119. package/studio/src/components/ui/input.tsx +0 -25
  120. package/studio/src/hooks/useDataTable.ts +0 -131
  121. package/studio/src/index.css +0 -36
  122. package/studio/src/lib/api.ts +0 -186
  123. package/studio/src/lib/utils.ts +0 -13
  124. package/studio/src/main.tsx +0 -17
  125. package/studio/src/pages/ArcheType.tsx +0 -239
  126. package/studio/src/pages/Components.tsx +0 -124
  127. package/studio/src/pages/EntityInspector.tsx +0 -302
  128. package/studio/src/pages/QueryRunner.tsx +0 -246
  129. package/studio/src/pages/Table.tsx +0 -94
  130. package/studio/src/pages/Welcome.tsx +0 -241
  131. package/studio/src/routes.tsx +0 -45
  132. package/studio/src/store/archeTypeSettings.ts +0 -30
  133. package/studio/src/store/studio.ts +0 -65
  134. package/studio/src/utils/columnHelpers.tsx +0 -114
  135. package/studio/studio-instructions.md +0 -81
  136. package/studio/tailwind.config.js +0 -77
  137. package/studio/utils.ts +0 -54
  138. package/studio/vite.config.js +0 -19
  139. package/tests/benchmark/BENCHMARK_DATABASES_PLAN.md +0 -338
  140. package/tests/benchmark/bunfig.toml +0 -9
  141. package/tests/benchmark/fixtures/EcommerceComponents.ts +0 -283
  142. package/tests/benchmark/fixtures/EcommerceDataGenerators.ts +0 -301
  143. package/tests/benchmark/fixtures/RelationTracker.ts +0 -159
  144. package/tests/benchmark/fixtures/index.ts +0 -6
  145. package/tests/benchmark/index.ts +0 -22
  146. package/tests/benchmark/noop-preload.ts +0 -3
  147. package/tests/benchmark/query-lateral-benchmark.test.ts +0 -372
  148. package/tests/benchmark/runners/BenchmarkLoader.ts +0 -132
  149. package/tests/benchmark/runners/index.ts +0 -4
  150. package/tests/benchmark/scenarios/query-benchmarks.test.ts +0 -465
  151. package/tests/benchmark/scripts/generate-db.ts +0 -344
  152. package/tests/benchmark/scripts/run-benchmarks.ts +0 -97
  153. package/tests/e2e/http.test.ts +0 -130
  154. package/tests/fixtures/archetypes/TestUserArchetype.ts +0 -21
  155. package/tests/fixtures/components/TestOrder.ts +0 -23
  156. package/tests/fixtures/components/TestProduct.ts +0 -23
  157. package/tests/fixtures/components/TestUser.ts +0 -20
  158. package/tests/fixtures/components/index.ts +0 -6
  159. package/tests/graphql/SchemaGeneration.test.ts +0 -90
  160. package/tests/graphql/builders/ResolverBuilder.test.ts +0 -223
  161. package/tests/graphql/builders/TypeDefBuilder.test.ts +0 -153
  162. package/tests/helpers/MockRedisClient.ts +0 -113
  163. package/tests/helpers/MockRedisStreamServer.ts +0 -448
  164. package/tests/integration/archetype/ArcheType.persistence.test.ts +0 -241
  165. package/tests/integration/cache/CacheInvalidation.test.ts +0 -259
  166. package/tests/integration/entity/Entity.persistence.test.ts +0 -333
  167. package/tests/integration/entity/Entity.saveTimeout.test.ts +0 -110
  168. package/tests/integration/query/Query.complexAnalysis.test.ts +0 -557
  169. package/tests/integration/query/Query.edgeCases.test.ts +0 -595
  170. package/tests/integration/query/Query.exec.test.ts +0 -576
  171. package/tests/integration/query/Query.explainAnalyze.test.ts +0 -233
  172. package/tests/integration/query/Query.jsonbArray.test.ts +0 -214
  173. package/tests/integration/remote/dlq.test.ts +0 -175
  174. package/tests/integration/remote/event-dispatch.test.ts +0 -114
  175. package/tests/integration/remote/outbox.test.ts +0 -130
  176. package/tests/integration/remote/rpc.test.ts +0 -177
  177. package/tests/pglite-setup.ts +0 -62
  178. package/tests/setup.ts +0 -164
  179. package/tests/stress/BenchmarkRunner.ts +0 -203
  180. package/tests/stress/DataSeeder.ts +0 -190
  181. package/tests/stress/StressTestReporter.ts +0 -229
  182. package/tests/stress/cursor-perf-test.ts +0 -171
  183. package/tests/stress/fixtures/RealisticComponents.ts +0 -235
  184. package/tests/stress/fixtures/StressTestComponents.ts +0 -58
  185. package/tests/stress/index.ts +0 -7
  186. package/tests/stress/scenarios/query-benchmarks.test.ts +0 -285
  187. package/tests/stress/scenarios/realistic-scenarios.test.ts +0 -1081
  188. package/tests/stress/scenarios/timeout-investigation.test.ts +0 -522
  189. package/tests/unit/BatchLoader.test.ts +0 -196
  190. package/tests/unit/archetype/ArcheType.test.ts +0 -107
  191. package/tests/unit/cache/CacheManager.test.ts +0 -367
  192. package/tests/unit/cache/MemoryCache.test.ts +0 -260
  193. package/tests/unit/cache/RedisCache.test.ts +0 -411
  194. package/tests/unit/entity/Entity.components.test.ts +0 -317
  195. package/tests/unit/entity/Entity.drainSideEffects.test.ts +0 -51
  196. package/tests/unit/entity/Entity.reload.test.ts +0 -63
  197. package/tests/unit/entity/Entity.requireComponents.test.ts +0 -72
  198. package/tests/unit/entity/Entity.test.ts +0 -345
  199. package/tests/unit/gql/depthLimit.test.ts +0 -203
  200. package/tests/unit/gql/operationMiddleware.test.ts +0 -293
  201. package/tests/unit/health/Health.test.ts +0 -129
  202. package/tests/unit/middleware/AccessLog.test.ts +0 -37
  203. package/tests/unit/middleware/Middleware.test.ts +0 -98
  204. package/tests/unit/middleware/RequestId.test.ts +0 -54
  205. package/tests/unit/middleware/SecurityHeaders.test.ts +0 -66
  206. package/tests/unit/query/FilterBuilder.test.ts +0 -111
  207. package/tests/unit/query/JsonbArrayBuilder.test.ts +0 -178
  208. package/tests/unit/query/Query.emptyString.test.ts +0 -69
  209. package/tests/unit/query/Query.test.ts +0 -310
  210. package/tests/unit/remote/CircuitBreaker.test.ts +0 -159
  211. package/tests/unit/remote/RemoteError.test.ts +0 -55
  212. package/tests/unit/remote/decorators.test.ts +0 -195
  213. package/tests/unit/remote/metrics.test.ts +0 -115
  214. package/tests/unit/remote/mockRedisStreamServer.test.ts +0 -104
  215. package/tests/unit/scheduler/DistributedLock.test.ts +0 -274
  216. package/tests/unit/scheduler/SchedulerManager.timeBased.test.ts +0 -95
  217. package/tests/unit/schema/schema-integration.test.ts +0 -426
  218. package/tests/unit/schema/schema.test.ts +0 -580
  219. package/tests/unit/storage/S3StorageProvider.test.ts +0 -567
  220. package/tests/unit/upload/RestUpload.test.ts +0 -267
  221. package/tests/unit/validateEnv.test.ts +0 -82
  222. package/tests/utils/entity-tracker.ts +0 -57
  223. package/tests/utils/index.ts +0 -13
  224. package/tests/utils/test-context.ts +0 -149
@@ -1,90 +0,0 @@
1
- /**
2
- * Tests for GraphQL Schema Generation
3
- * Tests the overall schema generation process
4
- */
5
- import { describe, test, expect, beforeAll } from 'bun:test';
6
- import { TestUser, TestProduct, TestOrder } from '../fixtures/components';
7
- import { TestUserArchetype } from '../fixtures/archetypes/TestUserArchetype';
8
- import { ensureComponentsRegistered } from '../utils';
9
-
10
- describe('GraphQL Schema Generation', () => {
11
- beforeAll(async () => {
12
- await ensureComponentsRegistered(TestUser, TestProduct, TestOrder);
13
- });
14
-
15
- describe('archetype schema generation', () => {
16
- test('archetype has zodObjectSchema method', () => {
17
- const archetype = new TestUserArchetype();
18
- expect(typeof archetype.getZodObjectSchema).toBe('function');
19
- });
20
-
21
- test('archetype has inputSchema method', () => {
22
- const archetype = new TestUserArchetype();
23
- expect(typeof archetype.getInputSchema).toBe('function');
24
- });
25
- });
26
-
27
- describe('component to GraphQL type mapping', () => {
28
- test('archetype schema is defined', () => {
29
- const archetype = new TestUserArchetype();
30
- const schema = archetype.getZodObjectSchema();
31
-
32
- expect(schema).toBeDefined();
33
- });
34
-
35
- test('input schema is defined', () => {
36
- const archetype = new TestUserArchetype();
37
- const inputSchema = archetype.getInputSchema();
38
-
39
- expect(inputSchema).toBeDefined();
40
- });
41
- });
42
-
43
- describe('input type generation', () => {
44
- test('generates input schema for archetype', () => {
45
- const archetype = new TestUserArchetype();
46
- const inputSchema = archetype.getInputSchema();
47
-
48
- expect(inputSchema).toBeDefined();
49
- expect(typeof inputSchema).toBe('object');
50
- });
51
-
52
- test('input schema validates valid data', () => {
53
- const archetype = new TestUserArchetype();
54
- const schema = archetype.withValidation({
55
- user: { name: 'Valid', email: 'valid@example.com', age: 25 }
56
- });
57
-
58
- expect(schema).toBeDefined();
59
- expect(schema.shape).toBeDefined();
60
- expect(typeof schema.safeParse).toBe('function');
61
- });
62
- });
63
-
64
- describe('schema consistency', () => {
65
- test('multiple calls return structurally equivalent schemas', () => {
66
- const archetype = new TestUserArchetype();
67
-
68
- const schema1 = archetype.getZodObjectSchema();
69
- const schema2 = archetype.getZodObjectSchema();
70
-
71
- // Both should have the same shape keys
72
- const keys1 = Object.keys(schema1.shape);
73
- const keys2 = Object.keys(schema2.shape);
74
- expect(keys1).toEqual(keys2);
75
- expect(keys1.length).toBeGreaterThan(0);
76
- });
77
- });
78
-
79
- describe('archetype with components', () => {
80
- test('archetype has componentMap', () => {
81
- const archetype = new TestUserArchetype();
82
- expect(archetype.componentMap).toBeDefined();
83
- });
84
-
85
- test('componentMap includes user component', () => {
86
- const archetype = new TestUserArchetype();
87
- expect(archetype.componentMap.user).toBeDefined();
88
- });
89
- });
90
- });
@@ -1,223 +0,0 @@
1
- /**
2
- * Tests for ResolverBuilder
3
- * Tests GraphQL resolver building
4
- */
5
- import { describe, test, expect, beforeEach } from 'bun:test';
6
- import { ResolverBuilder } from '../../../gql/builders/ResolverBuilder';
7
-
8
- describe('ResolverBuilder', () => {
9
- let builder: ResolverBuilder;
10
-
11
- beforeEach(() => {
12
- builder = new ResolverBuilder();
13
- });
14
-
15
- describe('addResolver()', () => {
16
- test('adds resolver to collection', () => {
17
- const mockService = { myMethod: () => 'result' };
18
- builder.addResolver({
19
- name: 'myMethod',
20
- propertyKey: 'myMethod',
21
- type: 'Query',
22
- service: mockService,
23
- hasInput: false
24
- });
25
-
26
- const stats = builder.getStats();
27
- expect(stats.queries).toBe(1);
28
- });
29
-
30
- test('categorizes Query resolvers', () => {
31
- const mockService = { getUser: () => {} };
32
- builder.addResolver({
33
- name: 'getUser',
34
- propertyKey: 'getUser',
35
- type: 'Query',
36
- service: mockService,
37
- hasInput: false
38
- });
39
-
40
- const stats = builder.getStats();
41
- expect(stats.queries).toBe(1);
42
- });
43
-
44
- test('categorizes Mutation resolvers', () => {
45
- const mockService = { createUser: () => {} };
46
- builder.addResolver({
47
- name: 'createUser',
48
- propertyKey: 'createUser',
49
- type: 'Mutation',
50
- service: mockService,
51
- hasInput: true
52
- });
53
-
54
- const stats = builder.getStats();
55
- expect(stats.mutations).toBe(1);
56
- });
57
-
58
- test('categorizes Subscription resolvers', () => {
59
- const mockService = { userCreated: () => {} };
60
- builder.addResolver({
61
- name: 'userCreated',
62
- propertyKey: 'userCreated',
63
- type: 'Subscription',
64
- service: mockService,
65
- hasInput: false
66
- });
67
-
68
- const stats = builder.getStats();
69
- expect(stats.subscriptions).toBe(1);
70
- });
71
- });
72
-
73
- describe('getResolvers()', () => {
74
- test('returns empty object when no resolvers', () => {
75
- const resolvers = builder.getResolvers();
76
- expect(resolvers.Query).toEqual({});
77
- expect(resolvers.Mutation).toEqual({});
78
- });
79
-
80
- test('returns Query resolvers', () => {
81
- const mockService = { getUser: () => 'user' };
82
- builder.addResolver({
83
- name: 'getUser',
84
- propertyKey: 'getUser',
85
- type: 'Query',
86
- service: mockService,
87
- hasInput: false
88
- });
89
-
90
- const resolvers = builder.getResolvers();
91
- expect(resolvers.Query!.getUser).toBeDefined();
92
- });
93
-
94
- test('returns Mutation resolvers', () => {
95
- const mockService = { createUser: () => 'user' };
96
- builder.addResolver({
97
- name: 'createUser',
98
- propertyKey: 'createUser',
99
- type: 'Mutation',
100
- service: mockService,
101
- hasInput: true
102
- });
103
-
104
- const resolvers = builder.getResolvers();
105
- expect(resolvers.Mutation!.createUser).toBeDefined();
106
- });
107
- });
108
-
109
- describe('getResolversForType()', () => {
110
- test('returns resolvers for specific type', () => {
111
- const mockService = { getUser: () => {}, getUsers: () => {} };
112
- builder.addResolver({
113
- name: 'getUser',
114
- propertyKey: 'getUser',
115
- type: 'Query',
116
- service: mockService,
117
- hasInput: false
118
- });
119
- builder.addResolver({
120
- name: 'getUsers',
121
- propertyKey: 'getUsers',
122
- type: 'Query',
123
- service: mockService,
124
- hasInput: false
125
- });
126
-
127
- const queryResolvers = builder.getResolversForType('Query');
128
- expect(Object.keys(queryResolvers).length).toBe(2);
129
- });
130
-
131
- test('returns empty object for type with no resolvers', () => {
132
- const resolvers = builder.getResolversForType('Subscription');
133
- expect(resolvers).toEqual({});
134
- });
135
- });
136
-
137
- describe('createResolverWithoutInput()', () => {
138
- test('creates resolver function', () => {
139
- const mockService = {
140
- getUser: async () => ({ id: '1', name: 'Test' })
141
- };
142
-
143
- const resolver = (builder as any).createResolverWithoutInput(mockService, 'getUser');
144
- expect(typeof resolver).toBe('function');
145
- });
146
-
147
- test('resolver calls service method', async () => {
148
- let called = false;
149
- const mockService = {
150
- getUser: async () => {
151
- called = true;
152
- return { id: '1' };
153
- }
154
- };
155
-
156
- const resolver = (builder as any).createResolverWithoutInput(mockService, 'getUser');
157
- await resolver({}, {}, {}, {} as any);
158
- expect(called).toBe(true);
159
- });
160
- });
161
-
162
- describe('createResolverWithInput()', () => {
163
- test('creates resolver function with input handling', () => {
164
- const mockService = {
165
- createUser: async (input: any) => ({ id: '1', ...input })
166
- };
167
-
168
- const resolver = (builder as any).createResolverWithInput(mockService, 'createUser');
169
- expect(typeof resolver).toBe('function');
170
- });
171
-
172
- test('resolver passes input to service', async () => {
173
- let receivedInput: any = null;
174
- const mockService = {
175
- createUser: async (input: any) => {
176
- receivedInput = input;
177
- return { id: '1', ...input };
178
- }
179
- };
180
-
181
- const resolver = (builder as any).createResolverWithInput(mockService, 'createUser');
182
- await resolver({}, { input: { name: 'Test' } }, {}, {} as any);
183
- expect(receivedInput).toEqual({ name: 'Test' });
184
- });
185
- });
186
-
187
- describe('clear()', () => {
188
- test('clears all resolvers', () => {
189
- const mockService = { getUser: () => {} };
190
- builder.addResolver({
191
- name: 'getUser',
192
- propertyKey: 'getUser',
193
- type: 'Query',
194
- service: mockService,
195
- hasInput: false
196
- });
197
-
198
- builder.clear();
199
-
200
- const stats = builder.getStats();
201
- expect(stats.queries).toBe(0);
202
- expect(stats.mutations).toBe(0);
203
- expect(stats.subscriptions).toBe(0);
204
- });
205
- });
206
-
207
- describe('getStats()', () => {
208
- test('returns accurate statistics', () => {
209
- const mockService = { m1: () => {}, m2: () => {}, m3: () => {} };
210
-
211
- builder.addResolver({ name: 'q1', propertyKey: 'q1', type: 'Query', service: mockService, hasInput: false });
212
- builder.addResolver({ name: 'q2', propertyKey: 'q2', type: 'Query', service: mockService, hasInput: false });
213
- builder.addResolver({ name: 'm1', propertyKey: 'm1', type: 'Mutation', service: mockService, hasInput: true });
214
- builder.addResolver({ name: 's1', propertyKey: 's1', type: 'Subscription', service: mockService, hasInput: false });
215
-
216
- const stats = builder.getStats();
217
-
218
- expect(stats.queries).toBe(2);
219
- expect(stats.mutations).toBe(1);
220
- expect(stats.subscriptions).toBe(1);
221
- });
222
- });
223
- });
@@ -1,153 +0,0 @@
1
- /**
2
- * Tests for TypeDefBuilder
3
- * Tests GraphQL type definition building
4
- */
5
- import { describe, test, expect, beforeEach } from 'bun:test';
6
- import { TypeDefBuilder } from '../../../gql/builders/TypeDefBuilder';
7
-
8
- describe('TypeDefBuilder', () => {
9
- let builder: TypeDefBuilder;
10
-
11
- beforeEach(() => {
12
- builder = new TypeDefBuilder();
13
- });
14
-
15
- describe('addQueryField()', () => {
16
- test('adds query field', () => {
17
- builder.addQueryField({ name: 'getUser', fieldDef: 'getUser(id: ID!): User' });
18
- const stats = builder.getStats();
19
- expect(stats.queries).toBe(1);
20
- });
21
-
22
- test('adds multiple query fields', () => {
23
- builder.addQueryField({ name: 'getUser', fieldDef: 'getUser(id: ID!): User' });
24
- builder.addQueryField({ name: 'getUsers', fieldDef: 'getUsers: [User!]!' });
25
- const stats = builder.getStats();
26
- expect(stats.queries).toBe(2);
27
- });
28
- });
29
-
30
- describe('addMutationField()', () => {
31
- test('adds mutation field', () => {
32
- builder.addMutationField({ name: 'createUser', fieldDef: 'createUser(input: CreateUserInput!): User!' });
33
- const stats = builder.getStats();
34
- expect(stats.mutations).toBe(1);
35
- });
36
- });
37
-
38
- describe('addSubscriptionField()', () => {
39
- test('adds subscription field', () => {
40
- builder.addSubscriptionField({ name: 'userCreated', fieldDef: 'userCreated: User!' });
41
- const stats = builder.getStats();
42
- expect(stats.subscriptions).toBe(1);
43
- });
44
- });
45
-
46
- describe('buildQueryType()', () => {
47
- test('returns empty string when no queries', () => {
48
- const result = builder.buildQueryType();
49
- expect(result).toBe('');
50
- });
51
-
52
- test('builds Query type with fields', () => {
53
- builder.addQueryField({ name: 'getUser', fieldDef: 'getUser(id: ID!): User' });
54
- const result = builder.buildQueryType();
55
-
56
- expect(result).toContain('type Query');
57
- expect(result).toContain('getUser(id: ID!): User');
58
- });
59
-
60
- test('sorts fields alphabetically', () => {
61
- builder.addQueryField({ name: 'zebra', fieldDef: 'zebra: String' });
62
- builder.addQueryField({ name: 'alpha', fieldDef: 'alpha: String' });
63
- const result = builder.buildQueryType();
64
-
65
- const alphaIndex = result.indexOf('alpha');
66
- const zebraIndex = result.indexOf('zebra');
67
- expect(alphaIndex).toBeLessThan(zebraIndex);
68
- });
69
- });
70
-
71
- describe('buildMutationType()', () => {
72
- test('returns empty string when no mutations', () => {
73
- const result = builder.buildMutationType();
74
- expect(result).toBe('');
75
- });
76
-
77
- test('builds Mutation type with fields', () => {
78
- builder.addMutationField({ name: 'createUser', fieldDef: 'createUser(input: CreateUserInput!): User!' });
79
- const result = builder.buildMutationType();
80
-
81
- expect(result).toContain('type Mutation');
82
- expect(result).toContain('createUser');
83
- });
84
- });
85
-
86
- describe('buildSubscriptionType()', () => {
87
- test('returns empty string when no subscriptions', () => {
88
- const result = builder.buildSubscriptionType();
89
- expect(result).toBe('');
90
- });
91
-
92
- test('builds Subscription type with fields', () => {
93
- builder.addSubscriptionField({ name: 'userCreated', fieldDef: 'userCreated: User!' });
94
- const result = builder.buildSubscriptionType();
95
-
96
- expect(result).toContain('type Subscription');
97
- expect(result).toContain('userCreated');
98
- });
99
- });
100
-
101
- describe('buildAllOperationTypes()', () => {
102
- test('builds all operation types', () => {
103
- builder.addQueryField({ name: 'getUser', fieldDef: 'getUser(id: ID!): User' });
104
- builder.addMutationField({ name: 'createUser', fieldDef: 'createUser(input: CreateUserInput!): User!' });
105
- builder.addSubscriptionField({ name: 'userCreated', fieldDef: 'userCreated: User!' });
106
-
107
- const result = builder.buildAllOperationTypes();
108
-
109
- expect(result).toContain('type Query');
110
- expect(result).toContain('type Mutation');
111
- expect(result).toContain('type Subscription');
112
- });
113
-
114
- test('returns only defined types', () => {
115
- builder.addQueryField({ name: 'getUser', fieldDef: 'getUser(id: ID!): User' });
116
-
117
- const result = builder.buildAllOperationTypes();
118
-
119
- expect(result).toContain('type Query');
120
- expect(result).not.toContain('type Mutation');
121
- expect(result).not.toContain('type Subscription');
122
- });
123
- });
124
-
125
- describe('clear()', () => {
126
- test('clears all fields', () => {
127
- builder.addQueryField({ name: 'getUser', fieldDef: 'getUser: User' });
128
- builder.addMutationField({ name: 'createUser', fieldDef: 'createUser: User' });
129
- builder.addSubscriptionField({ name: 'userCreated', fieldDef: 'userCreated: User' });
130
-
131
- builder.clear();
132
-
133
- const stats = builder.getStats();
134
- expect(stats.queries).toBe(0);
135
- expect(stats.mutations).toBe(0);
136
- expect(stats.subscriptions).toBe(0);
137
- });
138
- });
139
-
140
- describe('getStats()', () => {
141
- test('returns correct counts', () => {
142
- builder.addQueryField({ name: 'q1', fieldDef: 'q1: String' });
143
- builder.addQueryField({ name: 'q2', fieldDef: 'q2: String' });
144
- builder.addMutationField({ name: 'm1', fieldDef: 'm1: String' });
145
-
146
- const stats = builder.getStats();
147
-
148
- expect(stats.queries).toBe(2);
149
- expect(stats.mutations).toBe(1);
150
- expect(stats.subscriptions).toBe(0);
151
- });
152
- });
153
- });
@@ -1,113 +0,0 @@
1
- /**
2
- * ioredis-shaped client backed by a MockRedisStreamServer.
3
- *
4
- * Cast the returned instance to `Redis` (from "ioredis") when passing into
5
- * the remote subsystem via `redisFactory`. Only methods the remote layer
6
- * touches are implemented; others throw on use.
7
- */
8
-
9
- import type { MockRedisStreamServer } from "./MockRedisStreamServer";
10
-
11
- export class MockRedisClient {
12
- private server: MockRedisStreamServer;
13
- private connected = true;
14
- private listeners = new Map<string, Array<(...args: any[]) => void>>();
15
-
16
- constructor(server: MockRedisStreamServer) {
17
- this.server = server;
18
- }
19
-
20
- on(event: string, listener: (...args: any[]) => void): this {
21
- const arr = this.listeners.get(event) ?? [];
22
- arr.push(listener);
23
- this.listeners.set(event, arr);
24
- return this;
25
- }
26
-
27
- async xadd(key: string, ...args: any[]): Promise<string | null> {
28
- this.ensureConnected();
29
- try {
30
- return this.server.xadd(key, ...args);
31
- } catch (err: any) {
32
- throw err;
33
- }
34
- }
35
-
36
- async xgroup(...args: any[]): Promise<string> {
37
- this.ensureConnected();
38
- const [op, key, group, id, mk] = args;
39
- return this.server.xgroup(op, key, group, id, mk);
40
- }
41
-
42
- async xreadgroup(...args: any[]): Promise<any> {
43
- this.ensureConnected();
44
- return this.server.xreadgroup(...args);
45
- }
46
-
47
- async xread(...args: any[]): Promise<any> {
48
- this.ensureConnected();
49
- return this.server.xread(...args);
50
- }
51
-
52
- async xack(key: string, group: string, msgId: string): Promise<number> {
53
- this.ensureConnected();
54
- return this.server.xack(key, group, msgId);
55
- }
56
-
57
- async xpending(...args: any[]): Promise<any> {
58
- this.ensureConnected();
59
- const [key, group, ...rest] = args;
60
- return this.server.xpending(key, group, ...rest);
61
- }
62
-
63
- async xautoclaim(...args: any[]): Promise<any> {
64
- this.ensureConnected();
65
- return this.server.xautoclaim(
66
- args[0],
67
- args[1],
68
- args[2],
69
- Number(args[3]),
70
- args[4],
71
- ...args.slice(5)
72
- );
73
- }
74
-
75
- async xlen(key: string): Promise<number> {
76
- this.ensureConnected();
77
- return this.server.xlen(key);
78
- }
79
-
80
- async xrange(...args: any[]): Promise<any> {
81
- this.ensureConnected();
82
- return this.server.xrange(
83
- args[0],
84
- args[1],
85
- args[2],
86
- ...args.slice(3)
87
- );
88
- }
89
-
90
- async ping(): Promise<string> {
91
- this.ensureConnected();
92
- return this.server.ping();
93
- }
94
-
95
- disconnect(): void {
96
- this.connected = false;
97
- }
98
-
99
- async quit(): Promise<string> {
100
- this.connected = false;
101
- return "OK";
102
- }
103
-
104
- private ensureConnected(): void {
105
- if (!this.connected) {
106
- throw new Error("Connection is closed");
107
- }
108
- }
109
- }
110
-
111
- export function createMockRedisFactory(server: MockRedisStreamServer) {
112
- return (_blocking: boolean) => new MockRedisClient(server);
113
- }