bunsane 0.3.2 → 0.5.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 (220) hide show
  1. package/CHANGELOG.md +471 -370
  2. package/core/BatchLoader.ts +56 -32
  3. package/core/Entity.ts +93 -1020
  4. package/core/EntityHookManager.ts +52 -754
  5. package/core/Logger.ts +10 -0
  6. package/core/RequestContext.ts +94 -85
  7. package/core/RequestLoaders.ts +98 -5
  8. package/core/SchedulerManager.ts +28 -600
  9. package/core/app/cors.ts +2 -11
  10. package/core/app/preparedStatementWarmup.ts +9 -49
  11. package/core/app/requestRouter.ts +9 -8
  12. package/core/app/restRegistry.ts +8 -0
  13. package/core/archetype/fieldResolvers.ts +85 -40
  14. package/core/archetype/relationLoader.ts +135 -92
  15. package/core/cache/CacheManager.ts +91 -302
  16. package/core/cache/CompressionUtils.ts +34 -3
  17. package/core/cache/MemoryCache.ts +40 -37
  18. package/core/cache/RedisCache.ts +8 -7
  19. package/core/cache/health.ts +30 -0
  20. package/core/cache/invalidation.ts +96 -0
  21. package/core/cache/strategies/writeInvalidate.ts +111 -0
  22. package/core/cache/strategies/writeThrough.ts +233 -0
  23. package/core/components/BaseComponent.ts +25 -10
  24. package/core/components/ComponentRegistry.ts +28 -0
  25. package/core/decorators/IndexedField.ts +1 -1
  26. package/core/entity/cacheStrategies.ts +97 -0
  27. package/core/entity/componentAccess.ts +383 -0
  28. package/core/entity/finders.ts +202 -0
  29. package/core/entity/getCacheManager.ts +10 -0
  30. package/core/entity/pendingOps.ts +72 -0
  31. package/core/entity/saveEntity.ts +375 -0
  32. package/core/health.ts +93 -4
  33. package/core/hooks/dispatcher.ts +439 -0
  34. package/core/hooks/guards.ts +155 -0
  35. package/core/hooks/registry.ts +247 -0
  36. package/core/metadata/definitions/Component.ts +1 -1
  37. package/core/metadata/index.ts +15 -4
  38. package/core/middleware/RateLimit.ts +102 -105
  39. package/core/middleware/RequestId.ts +2 -9
  40. package/core/middleware/SecurityHeaders.ts +2 -11
  41. package/core/middleware/headers.ts +28 -0
  42. package/core/remote/OutboxWorker.ts +213 -183
  43. package/core/remote/RemoteManager.ts +401 -400
  44. package/core/remote/StreamConsumer.ts +535 -535
  45. package/core/remote/types.ts +153 -151
  46. package/core/requestScope.ts +34 -0
  47. package/core/scheduler/cronEvaluator.ts +174 -0
  48. package/core/scheduler/lifecycleHooks.ts +21 -0
  49. package/core/scheduler/lockCoordinator.ts +27 -0
  50. package/core/scheduler/metrics.ts +14 -0
  51. package/core/scheduler/taskRunner.ts +420 -0
  52. package/core/validateEnv.ts +10 -0
  53. package/database/DatabaseHelper.ts +128 -101
  54. package/database/IndexingStrategy.ts +72 -2
  55. package/database/PreparedStatementCache.ts +8 -2
  56. package/database/cancellable.ts +35 -22
  57. package/database/index.ts +29 -3
  58. package/database/instrumentedDb.ts +141 -141
  59. package/database/sqlHelpers.ts +3 -1
  60. package/endpoints/archetypes.ts +2 -8
  61. package/endpoints/tables.ts +6 -1
  62. package/gql/index.ts +1 -1
  63. package/gql/schema/index.ts +15 -4
  64. package/gql/visitors/ResolverGeneratorVisitor.ts +25 -4
  65. package/package.json +22 -1
  66. package/query/CTENode.ts +5 -3
  67. package/query/ComponentInclusionNode.ts +245 -14
  68. package/query/OrNode.ts +8 -19
  69. package/query/Query.ts +208 -79
  70. package/query/QueryContext.ts +6 -0
  71. package/query/QueryDAG.ts +7 -2
  72. package/query/membershipSource.ts +66 -0
  73. package/storage/LocalStorageProvider.ts +8 -3
  74. package/studio/dist/assets/index-BMZ67Npg.js +254 -0
  75. package/studio/dist/assets/index-BpbuYz9g.css +1 -0
  76. package/studio/{index.html → dist/index.html} +3 -2
  77. package/swagger/generator.ts +11 -1
  78. package/upload/UploadManager.ts +8 -6
  79. package/utils/uuid.ts +40 -10
  80. package/.claude/scheduled_tasks.lock +0 -1
  81. package/.claude/settings.local.json +0 -47
  82. package/.prettierrc +0 -4
  83. package/.serena/memories/architectural-decision-no-dependency-injection.md +0 -76
  84. package/.serena/memories/architecture.md +0 -154
  85. package/.serena/memories/cache-interface-refactoring-2026-01-24.md +0 -165
  86. package/.serena/memories/code_style_and_conventions.md +0 -76
  87. package/.serena/memories/project_overview.md +0 -43
  88. package/.serena/memories/schema-dsl-plan.md +0 -107
  89. package/.serena/memories/suggested_commands.md +0 -80
  90. package/.serena/memories/typescript-compilation-status.md +0 -54
  91. package/.serena/project.yml +0 -114
  92. package/BunSane.jpg +0 -0
  93. package/CLAUDE.md +0 -198
  94. package/TODO.md +0 -2
  95. package/bun.lock +0 -302
  96. package/bunfig.toml +0 -10
  97. package/docs/RFC_APP_REFACTOR.md +0 -248
  98. package/docs/RFC_REFACTOR_TARGETS.md +0 -251
  99. package/docs/SCALABILITY_PLAN.md +0 -175
  100. package/studio/bun.lock +0 -482
  101. package/studio/package.json +0 -39
  102. package/studio/postcss.config.js +0 -6
  103. package/studio/src/components/DataTable.tsx +0 -211
  104. package/studio/src/components/Layout.tsx +0 -13
  105. package/studio/src/components/PageContainer.tsx +0 -9
  106. package/studio/src/components/PageHeader.tsx +0 -13
  107. package/studio/src/components/SearchBar.tsx +0 -57
  108. package/studio/src/components/Sidebar.tsx +0 -294
  109. package/studio/src/components/ui/button.tsx +0 -56
  110. package/studio/src/components/ui/checkbox.tsx +0 -26
  111. package/studio/src/components/ui/input.tsx +0 -25
  112. package/studio/src/hooks/useDataTable.ts +0 -131
  113. package/studio/src/index.css +0 -36
  114. package/studio/src/lib/api.ts +0 -186
  115. package/studio/src/lib/utils.ts +0 -13
  116. package/studio/src/main.tsx +0 -17
  117. package/studio/src/pages/ArcheType.tsx +0 -239
  118. package/studio/src/pages/Components.tsx +0 -124
  119. package/studio/src/pages/EntityInspector.tsx +0 -302
  120. package/studio/src/pages/QueryRunner.tsx +0 -246
  121. package/studio/src/pages/Table.tsx +0 -94
  122. package/studio/src/pages/Welcome.tsx +0 -241
  123. package/studio/src/routes.tsx +0 -45
  124. package/studio/src/store/archeTypeSettings.ts +0 -30
  125. package/studio/src/store/studio.ts +0 -65
  126. package/studio/src/utils/columnHelpers.tsx +0 -114
  127. package/studio/studio-instructions.md +0 -81
  128. package/studio/tailwind.config.js +0 -77
  129. package/studio/utils.ts +0 -54
  130. package/studio/vite.config.js +0 -19
  131. package/tests/benchmark/BENCHMARK_DATABASES_PLAN.md +0 -338
  132. package/tests/benchmark/bunfig.toml +0 -9
  133. package/tests/benchmark/fixtures/EcommerceComponents.ts +0 -283
  134. package/tests/benchmark/fixtures/EcommerceDataGenerators.ts +0 -301
  135. package/tests/benchmark/fixtures/RelationTracker.ts +0 -159
  136. package/tests/benchmark/fixtures/index.ts +0 -6
  137. package/tests/benchmark/index.ts +0 -22
  138. package/tests/benchmark/noop-preload.ts +0 -3
  139. package/tests/benchmark/query-lateral-benchmark.test.ts +0 -372
  140. package/tests/benchmark/runners/BenchmarkLoader.ts +0 -132
  141. package/tests/benchmark/runners/index.ts +0 -4
  142. package/tests/benchmark/scenarios/query-benchmarks.test.ts +0 -465
  143. package/tests/benchmark/scripts/generate-db.ts +0 -344
  144. package/tests/benchmark/scripts/run-benchmarks.ts +0 -97
  145. package/tests/e2e/http.test.ts +0 -130
  146. package/tests/fixtures/archetypes/TestUserArchetype.ts +0 -21
  147. package/tests/fixtures/components/TestOrder.ts +0 -23
  148. package/tests/fixtures/components/TestProduct.ts +0 -23
  149. package/tests/fixtures/components/TestUser.ts +0 -20
  150. package/tests/fixtures/components/index.ts +0 -6
  151. package/tests/graphql/SchemaGeneration.test.ts +0 -90
  152. package/tests/graphql/builders/ResolverBuilder.test.ts +0 -223
  153. package/tests/graphql/builders/TypeDefBuilder.test.ts +0 -153
  154. package/tests/helpers/MockRedisClient.ts +0 -113
  155. package/tests/helpers/MockRedisStreamServer.ts +0 -448
  156. package/tests/integration/archetype/ArcheType.persistence.test.ts +0 -241
  157. package/tests/integration/cache/CacheInvalidation.test.ts +0 -259
  158. package/tests/integration/entity/Entity.persistence.test.ts +0 -333
  159. package/tests/integration/entity/Entity.saveTimeout.test.ts +0 -110
  160. package/tests/integration/loaders/RequestLoaders.abort.test.ts +0 -82
  161. package/tests/integration/query/Query.abort.test.ts +0 -66
  162. package/tests/integration/query/Query.complexAnalysis.test.ts +0 -557
  163. package/tests/integration/query/Query.edgeCases.test.ts +0 -595
  164. package/tests/integration/query/Query.exec.test.ts +0 -576
  165. package/tests/integration/query/Query.explainAnalyze.test.ts +0 -233
  166. package/tests/integration/query/Query.jsonbArray.test.ts +0 -214
  167. package/tests/integration/remote/dlq.test.ts +0 -175
  168. package/tests/integration/remote/event-dispatch.test.ts +0 -114
  169. package/tests/integration/remote/outbox.test.ts +0 -130
  170. package/tests/integration/remote/rpc.test.ts +0 -177
  171. package/tests/pglite-setup.ts +0 -62
  172. package/tests/setup.ts +0 -164
  173. package/tests/stress/BenchmarkRunner.ts +0 -203
  174. package/tests/stress/DataSeeder.ts +0 -190
  175. package/tests/stress/StressTestReporter.ts +0 -229
  176. package/tests/stress/cursor-perf-test.ts +0 -171
  177. package/tests/stress/fixtures/RealisticComponents.ts +0 -235
  178. package/tests/stress/fixtures/StressTestComponents.ts +0 -58
  179. package/tests/stress/index.ts +0 -7
  180. package/tests/stress/scenarios/query-benchmarks.test.ts +0 -285
  181. package/tests/stress/scenarios/realistic-scenarios.test.ts +0 -1081
  182. package/tests/stress/scenarios/timeout-investigation.test.ts +0 -522
  183. package/tests/unit/BatchLoader.test.ts +0 -196
  184. package/tests/unit/archetype/ArcheType.test.ts +0 -107
  185. package/tests/unit/cache/CacheManager.test.ts +0 -498
  186. package/tests/unit/cache/MemoryCache.test.ts +0 -260
  187. package/tests/unit/cache/RedisCache.test.ts +0 -411
  188. package/tests/unit/database/cancellable.test.ts +0 -81
  189. package/tests/unit/database/instrumentedDb.test.ts +0 -160
  190. package/tests/unit/entity/Entity.components.test.ts +0 -317
  191. package/tests/unit/entity/Entity.drainSideEffects.test.ts +0 -51
  192. package/tests/unit/entity/Entity.reload.test.ts +0 -63
  193. package/tests/unit/entity/Entity.requireComponents.test.ts +0 -72
  194. package/tests/unit/entity/Entity.test.ts +0 -345
  195. package/tests/unit/gql/depthLimit.test.ts +0 -203
  196. package/tests/unit/gql/operationMiddleware.test.ts +0 -293
  197. package/tests/unit/health/Health.test.ts +0 -129
  198. package/tests/unit/middleware/AccessLog.test.ts +0 -37
  199. package/tests/unit/middleware/Middleware.test.ts +0 -98
  200. package/tests/unit/middleware/RequestId.test.ts +0 -54
  201. package/tests/unit/middleware/SecurityHeaders.test.ts +0 -66
  202. package/tests/unit/query/FilterBuilder.test.ts +0 -111
  203. package/tests/unit/query/JsonbArrayBuilder.test.ts +0 -178
  204. package/tests/unit/query/Query.emptyString.test.ts +0 -69
  205. package/tests/unit/query/Query.test.ts +0 -310
  206. package/tests/unit/remote/CircuitBreaker.test.ts +0 -159
  207. package/tests/unit/remote/RemoteError.test.ts +0 -55
  208. package/tests/unit/remote/decorators.test.ts +0 -195
  209. package/tests/unit/remote/metrics.test.ts +0 -115
  210. package/tests/unit/remote/mockRedisStreamServer.test.ts +0 -104
  211. package/tests/unit/scheduler/DistributedLock.test.ts +0 -274
  212. package/tests/unit/scheduler/SchedulerManager.timeBased.test.ts +0 -95
  213. package/tests/unit/schema/schema-integration.test.ts +0 -426
  214. package/tests/unit/schema/schema.test.ts +0 -580
  215. package/tests/unit/storage/S3StorageProvider.test.ts +0 -567
  216. package/tests/unit/upload/RestUpload.test.ts +0 -267
  217. package/tests/unit/validateEnv.test.ts +0 -82
  218. package/tests/utils/entity-tracker.ts +0 -57
  219. package/tests/utils/index.ts +0 -13
  220. 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
- }