@zze/mock-server 0.2.4

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 (177) hide show
  1. package/README.md +643 -0
  2. package/dist/__tests__/mock-server.test.d.ts +5 -0
  3. package/dist/__tests__/mock-server.test.d.ts.map +1 -0
  4. package/dist/__tests__/mock-server.test.js +599 -0
  5. package/dist/__tests__/mock-server.test.js.map +1 -0
  6. package/dist/admin/__tests__/admin-router.test.d.ts +2 -0
  7. package/dist/admin/__tests__/admin-router.test.d.ts.map +1 -0
  8. package/dist/admin/__tests__/admin-router.test.js +621 -0
  9. package/dist/admin/__tests__/admin-router.test.js.map +1 -0
  10. package/dist/admin/admin-router.d.ts +27 -0
  11. package/dist/admin/admin-router.d.ts.map +1 -0
  12. package/dist/admin/admin-router.js +616 -0
  13. package/dist/admin/admin-router.js.map +1 -0
  14. package/dist/admin/index.d.ts +4 -0
  15. package/dist/admin/index.d.ts.map +1 -0
  16. package/dist/admin/index.js +10 -0
  17. package/dist/admin/index.js.map +1 -0
  18. package/dist/admin/types.d.ts +185 -0
  19. package/dist/admin/types.d.ts.map +1 -0
  20. package/dist/admin/types.js +33 -0
  21. package/dist/admin/types.js.map +1 -0
  22. package/dist/config/__tests__/config-discovery.test.d.ts +2 -0
  23. package/dist/config/__tests__/config-discovery.test.d.ts.map +1 -0
  24. package/dist/config/__tests__/config-discovery.test.js +52 -0
  25. package/dist/config/__tests__/config-discovery.test.js.map +1 -0
  26. package/dist/config/__tests__/config-loader.test.d.ts +2 -0
  27. package/dist/config/__tests__/config-loader.test.d.ts.map +1 -0
  28. package/dist/config/__tests__/config-loader.test.js +124 -0
  29. package/dist/config/__tests__/config-loader.test.js.map +1 -0
  30. package/dist/config/__tests__/file-loader.test.d.ts +2 -0
  31. package/dist/config/__tests__/file-loader.test.d.ts.map +1 -0
  32. package/dist/config/__tests__/file-loader.test.js +63 -0
  33. package/dist/config/__tests__/file-loader.test.js.map +1 -0
  34. package/dist/config/__tests__/fixtures/mock-config/endpoints/products.d.ts +25 -0
  35. package/dist/config/__tests__/fixtures/mock-config/endpoints/products.d.ts.map +1 -0
  36. package/dist/config/__tests__/fixtures/mock-config/endpoints/products.js +25 -0
  37. package/dist/config/__tests__/fixtures/mock-config/endpoints/products.js.map +1 -0
  38. package/dist/config/__tests__/fixtures/mock-config/flows/error-flow.d.ts +11 -0
  39. package/dist/config/__tests__/fixtures/mock-config/flows/error-flow.d.ts.map +1 -0
  40. package/dist/config/__tests__/fixtures/mock-config/flows/error-flow.js +11 -0
  41. package/dist/config/__tests__/fixtures/mock-config/flows/error-flow.js.map +1 -0
  42. package/dist/config/config-discovery.d.ts +23 -0
  43. package/dist/config/config-discovery.d.ts.map +1 -0
  44. package/dist/config/config-discovery.js +60 -0
  45. package/dist/config/config-discovery.js.map +1 -0
  46. package/dist/config/config-loader.d.ts +56 -0
  47. package/dist/config/config-loader.d.ts.map +1 -0
  48. package/dist/config/config-loader.js +162 -0
  49. package/dist/config/config-loader.js.map +1 -0
  50. package/dist/config/config-writer.d.ts +96 -0
  51. package/dist/config/config-writer.d.ts.map +1 -0
  52. package/dist/config/config-writer.js +248 -0
  53. package/dist/config/config-writer.js.map +1 -0
  54. package/dist/config/file-loader.d.ts +16 -0
  55. package/dist/config/file-loader.d.ts.map +1 -0
  56. package/dist/config/file-loader.js +90 -0
  57. package/dist/config/file-loader.js.map +1 -0
  58. package/dist/config/index.d.ts +7 -0
  59. package/dist/config/index.d.ts.map +1 -0
  60. package/dist/config/index.js +23 -0
  61. package/dist/config/index.js.map +1 -0
  62. package/dist/config/types.d.ts +47 -0
  63. package/dist/config/types.d.ts.map +1 -0
  64. package/dist/config/types.js +7 -0
  65. package/dist/config/types.js.map +1 -0
  66. package/dist/index.d.ts +28 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +31 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/mock-server.d.ts +243 -0
  71. package/dist/mock-server.d.ts.map +1 -0
  72. package/dist/mock-server.js +387 -0
  73. package/dist/mock-server.js.map +1 -0
  74. package/dist/resolver/__tests__/response-resolver.test.d.ts +2 -0
  75. package/dist/resolver/__tests__/response-resolver.test.d.ts.map +1 -0
  76. package/dist/resolver/__tests__/response-resolver.test.js +358 -0
  77. package/dist/resolver/__tests__/response-resolver.test.js.map +1 -0
  78. package/dist/resolver/index.d.ts +4 -0
  79. package/dist/resolver/index.d.ts.map +1 -0
  80. package/dist/resolver/index.js +9 -0
  81. package/dist/resolver/index.js.map +1 -0
  82. package/dist/resolver/response-resolver.d.ts +73 -0
  83. package/dist/resolver/response-resolver.d.ts.map +1 -0
  84. package/dist/resolver/response-resolver.js +167 -0
  85. package/dist/resolver/response-resolver.js.map +1 -0
  86. package/dist/resolver/types.d.ts +47 -0
  87. package/dist/resolver/types.d.ts.map +1 -0
  88. package/dist/resolver/types.js +9 -0
  89. package/dist/resolver/types.js.map +1 -0
  90. package/dist/schemas/__tests__/config.validator.test.d.ts +2 -0
  91. package/dist/schemas/__tests__/config.validator.test.d.ts.map +1 -0
  92. package/dist/schemas/__tests__/config.validator.test.js +66 -0
  93. package/dist/schemas/__tests__/config.validator.test.js.map +1 -0
  94. package/dist/schemas/__tests__/endpoint.schema.test.d.ts +2 -0
  95. package/dist/schemas/__tests__/endpoint.schema.test.d.ts.map +1 -0
  96. package/dist/schemas/__tests__/endpoint.schema.test.js +103 -0
  97. package/dist/schemas/__tests__/endpoint.schema.test.js.map +1 -0
  98. package/dist/schemas/__tests__/flow.schema.test.d.ts +2 -0
  99. package/dist/schemas/__tests__/flow.schema.test.d.ts.map +1 -0
  100. package/dist/schemas/__tests__/flow.schema.test.js +92 -0
  101. package/dist/schemas/__tests__/flow.schema.test.js.map +1 -0
  102. package/dist/schemas/config.validator.d.ts +39 -0
  103. package/dist/schemas/config.validator.d.ts.map +1 -0
  104. package/dist/schemas/config.validator.js +117 -0
  105. package/dist/schemas/config.validator.js.map +1 -0
  106. package/dist/schemas/endpoint.schema.d.ts +108 -0
  107. package/dist/schemas/endpoint.schema.d.ts.map +1 -0
  108. package/dist/schemas/endpoint.schema.js +87 -0
  109. package/dist/schemas/endpoint.schema.js.map +1 -0
  110. package/dist/schemas/flow.schema.d.ts +46 -0
  111. package/dist/schemas/flow.schema.d.ts.map +1 -0
  112. package/dist/schemas/flow.schema.js +73 -0
  113. package/dist/schemas/flow.schema.js.map +1 -0
  114. package/dist/schemas/http.schema.d.ts +15 -0
  115. package/dist/schemas/http.schema.d.ts.map +1 -0
  116. package/dist/schemas/http.schema.js +32 -0
  117. package/dist/schemas/http.schema.js.map +1 -0
  118. package/dist/schemas/index.d.ts +6 -0
  119. package/dist/schemas/index.d.ts.map +1 -0
  120. package/dist/schemas/index.js +33 -0
  121. package/dist/schemas/index.js.map +1 -0
  122. package/dist/schemas/scenario.schema.d.ts +54 -0
  123. package/dist/schemas/scenario.schema.d.ts.map +1 -0
  124. package/dist/schemas/scenario.schema.js +75 -0
  125. package/dist/schemas/scenario.schema.js.map +1 -0
  126. package/dist/server/__tests__/mock-server-app.test.d.ts +2 -0
  127. package/dist/server/__tests__/mock-server-app.test.d.ts.map +1 -0
  128. package/dist/server/__tests__/mock-server-app.test.js +296 -0
  129. package/dist/server/__tests__/mock-server-app.test.js.map +1 -0
  130. package/dist/server/index.d.ts +4 -0
  131. package/dist/server/index.d.ts.map +1 -0
  132. package/dist/server/index.js +10 -0
  133. package/dist/server/index.js.map +1 -0
  134. package/dist/server/mock-server-app.d.ts +119 -0
  135. package/dist/server/mock-server-app.d.ts.map +1 -0
  136. package/dist/server/mock-server-app.js +304 -0
  137. package/dist/server/mock-server-app.js.map +1 -0
  138. package/dist/server/types.d.ts +38 -0
  139. package/dist/server/types.d.ts.map +1 -0
  140. package/dist/server/types.js +11 -0
  141. package/dist/server/types.js.map +1 -0
  142. package/dist/state/__tests__/state-manager.test.d.ts +2 -0
  143. package/dist/state/__tests__/state-manager.test.d.ts.map +1 -0
  144. package/dist/state/__tests__/state-manager.test.js +275 -0
  145. package/dist/state/__tests__/state-manager.test.js.map +1 -0
  146. package/dist/state/index.d.ts +3 -0
  147. package/dist/state/index.d.ts.map +1 -0
  148. package/dist/state/index.js +7 -0
  149. package/dist/state/index.js.map +1 -0
  150. package/dist/state/state-manager.d.ts +92 -0
  151. package/dist/state/state-manager.d.ts.map +1 -0
  152. package/dist/state/state-manager.js +198 -0
  153. package/dist/state/state-manager.js.map +1 -0
  154. package/dist/state/types.d.ts +30 -0
  155. package/dist/state/types.d.ts.map +1 -0
  156. package/dist/state/types.js +2 -0
  157. package/dist/state/types.js.map +1 -0
  158. package/dist/ui/assets/index-SADMOgPE.js +10 -0
  159. package/dist/ui/assets/index-rj7m-XdG.css +1 -0
  160. package/dist/ui/index.html +29 -0
  161. package/dist/watcher/__tests__/config-watcher.test.d.ts +2 -0
  162. package/dist/watcher/__tests__/config-watcher.test.d.ts.map +1 -0
  163. package/dist/watcher/__tests__/config-watcher.test.js +372 -0
  164. package/dist/watcher/__tests__/config-watcher.test.js.map +1 -0
  165. package/dist/watcher/config-watcher.d.ts +100 -0
  166. package/dist/watcher/config-watcher.d.ts.map +1 -0
  167. package/dist/watcher/config-watcher.js +358 -0
  168. package/dist/watcher/config-watcher.js.map +1 -0
  169. package/dist/watcher/index.d.ts +7 -0
  170. package/dist/watcher/index.d.ts.map +1 -0
  171. package/dist/watcher/index.js +8 -0
  172. package/dist/watcher/index.js.map +1 -0
  173. package/dist/watcher/types.d.ts +64 -0
  174. package/dist/watcher/types.d.ts.map +1 -0
  175. package/dist/watcher/types.js +15 -0
  176. package/dist/watcher/types.js.map +1 -0
  177. package/package.json +58 -0
@@ -0,0 +1,621 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const vitest_1 = require("vitest");
7
+ const supertest_1 = __importDefault(require("supertest"));
8
+ const mock_server_app_js_1 = require("../../server/mock-server-app.js");
9
+ // Test fixtures
10
+ const mockEndpoints = [
11
+ {
12
+ id: 'get-users',
13
+ path: '/users',
14
+ method: 'GET',
15
+ scenarios: [
16
+ { id: 'success', name: 'Success', status: 200, body: [] },
17
+ { id: 'error', name: 'Error', status: 500, body: { error: 'Failed' } },
18
+ ],
19
+ defaultScenarioId: 'success',
20
+ },
21
+ {
22
+ id: 'get-products',
23
+ path: '/products',
24
+ method: 'GET',
25
+ scenarios: [
26
+ { id: 'list', name: 'Product List', status: 200, body: [] },
27
+ { id: 'empty', name: 'Empty', status: 200, body: [] },
28
+ ],
29
+ defaultScenarioId: 'list',
30
+ },
31
+ ];
32
+ const mockFlows = [
33
+ {
34
+ id: 'admin-flow',
35
+ name: 'Admin Flow',
36
+ description: 'Flow for admin users',
37
+ endpointScenarios: {
38
+ 'get-users': 'success',
39
+ 'get-products': 'list',
40
+ },
41
+ },
42
+ {
43
+ id: 'error-flow',
44
+ name: 'Error Flow',
45
+ endpointScenarios: {
46
+ 'get-users': 'error',
47
+ },
48
+ },
49
+ ];
50
+ (0, vitest_1.describe)('Admin Router', () => {
51
+ let serverApp;
52
+ (0, vitest_1.beforeEach)(() => {
53
+ serverApp = new mock_server_app_js_1.MockServerApp({ port: 0 });
54
+ serverApp.registerConfig(mockEndpoints, mockFlows);
55
+ });
56
+ (0, vitest_1.afterEach)(async () => {
57
+ if (serverApp.isRunning()) {
58
+ await serverApp.stop();
59
+ }
60
+ });
61
+ (0, vitest_1.describe)('GET /mock-admin/api/config', () => {
62
+ (0, vitest_1.it)('should return all endpoints and flows', async () => {
63
+ const res = await (0, supertest_1.default)(serverApp.getApp())
64
+ .get('/mock-admin/api/config')
65
+ .expect(200);
66
+ (0, vitest_1.expect)(res.body.endpoints).toHaveLength(2);
67
+ (0, vitest_1.expect)(res.body.flows).toHaveLength(2);
68
+ });
69
+ (0, vitest_1.it)('should return endpoint details', async () => {
70
+ const res = await (0, supertest_1.default)(serverApp.getApp())
71
+ .get('/mock-admin/api/config')
72
+ .expect(200);
73
+ const usersEndpoint = res.body.endpoints.find((e) => e.id === 'get-users');
74
+ (0, vitest_1.expect)(usersEndpoint).toBeDefined();
75
+ (0, vitest_1.expect)(usersEndpoint.path).toBe('/users');
76
+ (0, vitest_1.expect)(usersEndpoint.method).toBe('GET');
77
+ (0, vitest_1.expect)(usersEndpoint.scenarios).toHaveLength(2);
78
+ (0, vitest_1.expect)(usersEndpoint.defaultScenarioId).toBe('success');
79
+ });
80
+ (0, vitest_1.it)('should return scenario summary without body', async () => {
81
+ const res = await (0, supertest_1.default)(serverApp.getApp())
82
+ .get('/mock-admin/api/config')
83
+ .expect(200);
84
+ const usersEndpoint = res.body.endpoints.find((e) => e.id === 'get-users');
85
+ const successScenario = usersEndpoint.scenarios.find((s) => s.id === 'success');
86
+ (0, vitest_1.expect)(successScenario.id).toBe('success');
87
+ (0, vitest_1.expect)(successScenario.name).toBe('Success');
88
+ (0, vitest_1.expect)(successScenario.status).toBe(200);
89
+ (0, vitest_1.expect)(successScenario.body).toBeUndefined(); // Body not included
90
+ });
91
+ (0, vitest_1.it)('should return flow details', async () => {
92
+ const res = await (0, supertest_1.default)(serverApp.getApp())
93
+ .get('/mock-admin/api/config')
94
+ .expect(200);
95
+ const adminFlow = res.body.flows.find((f) => f.id === 'admin-flow');
96
+ (0, vitest_1.expect)(adminFlow).toBeDefined();
97
+ (0, vitest_1.expect)(adminFlow.name).toBe('Admin Flow');
98
+ (0, vitest_1.expect)(adminFlow.description).toBe('Flow for admin users');
99
+ (0, vitest_1.expect)(adminFlow.endpointScenarios).toEqual({
100
+ 'get-users': 'success',
101
+ 'get-products': 'list',
102
+ });
103
+ });
104
+ });
105
+ (0, vitest_1.describe)('GET /mock-admin/api/state', () => {
106
+ (0, vitest_1.it)('should return initial state', async () => {
107
+ const res = await (0, supertest_1.default)(serverApp.getApp())
108
+ .get('/mock-admin/api/state')
109
+ .expect(200);
110
+ (0, vitest_1.expect)(res.body.activeFlowId).toBeNull();
111
+ (0, vitest_1.expect)(res.body.activeScenarios).toEqual({});
112
+ });
113
+ (0, vitest_1.it)('should return state with active scenario', async () => {
114
+ serverApp.getStateManager().activateScenario('get-users', 'error');
115
+ const res = await (0, supertest_1.default)(serverApp.getApp())
116
+ .get('/mock-admin/api/state')
117
+ .expect(200);
118
+ (0, vitest_1.expect)(res.body.activeScenarios).toEqual({ 'get-users': 'error' });
119
+ });
120
+ (0, vitest_1.it)('should return state with active flow', async () => {
121
+ serverApp.getStateManager().activateFlow('admin-flow');
122
+ const res = await (0, supertest_1.default)(serverApp.getApp())
123
+ .get('/mock-admin/api/state')
124
+ .expect(200);
125
+ (0, vitest_1.expect)(res.body.activeFlowId).toBe('admin-flow');
126
+ });
127
+ });
128
+ (0, vitest_1.describe)('POST /mock-admin/api/scenarios/activate', () => {
129
+ (0, vitest_1.it)('should activate a scenario', async () => {
130
+ await (0, supertest_1.default)(serverApp.getApp())
131
+ .post('/mock-admin/api/scenarios/activate')
132
+ .send({ endpointId: 'get-users', scenarioId: 'error' })
133
+ .expect(204);
134
+ const state = serverApp.getStateManager().getState();
135
+ (0, vitest_1.expect)(state.activeScenarios['get-users']).toBe('error');
136
+ });
137
+ (0, vitest_1.it)('should affect mock API responses', async () => {
138
+ await (0, supertest_1.default)(serverApp.getApp())
139
+ .post('/mock-admin/api/scenarios/activate')
140
+ .send({ endpointId: 'get-users', scenarioId: 'error' })
141
+ .expect(204);
142
+ await (0, supertest_1.default)(serverApp.getApp())
143
+ .get('/api/users')
144
+ .expect(500);
145
+ });
146
+ (0, vitest_1.it)('should return 400 for invalid endpoint', async () => {
147
+ const res = await (0, supertest_1.default)(serverApp.getApp())
148
+ .post('/mock-admin/api/scenarios/activate')
149
+ .send({ endpointId: 'nonexistent', scenarioId: 'success' })
150
+ .expect(400);
151
+ (0, vitest_1.expect)(res.body.error).toContain('not found');
152
+ });
153
+ (0, vitest_1.it)('should return 400 for invalid scenario', async () => {
154
+ const res = await (0, supertest_1.default)(serverApp.getApp())
155
+ .post('/mock-admin/api/scenarios/activate')
156
+ .send({ endpointId: 'get-users', scenarioId: 'nonexistent' })
157
+ .expect(400);
158
+ (0, vitest_1.expect)(res.body.error).toContain('not found');
159
+ });
160
+ (0, vitest_1.it)('should return 400 for missing endpointId', async () => {
161
+ const res = await (0, supertest_1.default)(serverApp.getApp())
162
+ .post('/mock-admin/api/scenarios/activate')
163
+ .send({ scenarioId: 'success' })
164
+ .expect(400);
165
+ (0, vitest_1.expect)(res.body.error).toBe('Invalid request body');
166
+ });
167
+ (0, vitest_1.it)('should return 400 for missing scenarioId', async () => {
168
+ const res = await (0, supertest_1.default)(serverApp.getApp())
169
+ .post('/mock-admin/api/scenarios/activate')
170
+ .send({ endpointId: 'get-users' })
171
+ .expect(400);
172
+ (0, vitest_1.expect)(res.body.error).toBe('Invalid request body');
173
+ });
174
+ });
175
+ (0, vitest_1.describe)('POST /mock-admin/api/flows/activate', () => {
176
+ (0, vitest_1.it)('should activate a flow', async () => {
177
+ await (0, supertest_1.default)(serverApp.getApp())
178
+ .post('/mock-admin/api/flows/activate')
179
+ .send({ flowId: 'admin-flow' })
180
+ .expect(204);
181
+ const state = serverApp.getStateManager().getState();
182
+ (0, vitest_1.expect)(state.activeFlowId).toBe('admin-flow');
183
+ });
184
+ (0, vitest_1.it)('should deactivate flow with null', async () => {
185
+ serverApp.getStateManager().activateFlow('admin-flow');
186
+ await (0, supertest_1.default)(serverApp.getApp())
187
+ .post('/mock-admin/api/flows/activate')
188
+ .send({ flowId: null })
189
+ .expect(204);
190
+ const state = serverApp.getStateManager().getState();
191
+ (0, vitest_1.expect)(state.activeFlowId).toBeNull();
192
+ });
193
+ (0, vitest_1.it)('should affect mock API responses', async () => {
194
+ await (0, supertest_1.default)(serverApp.getApp())
195
+ .post('/mock-admin/api/flows/activate')
196
+ .send({ flowId: 'error-flow' })
197
+ .expect(204);
198
+ await (0, supertest_1.default)(serverApp.getApp())
199
+ .get('/api/users')
200
+ .expect(500);
201
+ });
202
+ (0, vitest_1.it)('should return 400 for invalid flow', async () => {
203
+ const res = await (0, supertest_1.default)(serverApp.getApp())
204
+ .post('/mock-admin/api/flows/activate')
205
+ .send({ flowId: 'nonexistent' })
206
+ .expect(400);
207
+ (0, vitest_1.expect)(res.body.error).toContain('not found');
208
+ });
209
+ (0, vitest_1.it)('should return 400 for missing flowId', async () => {
210
+ const res = await (0, supertest_1.default)(serverApp.getApp())
211
+ .post('/mock-admin/api/flows/activate')
212
+ .send({})
213
+ .expect(400);
214
+ (0, vitest_1.expect)(res.body.error).toBe('Invalid request body');
215
+ });
216
+ });
217
+ (0, vitest_1.describe)('POST /mock-admin/api/reset', () => {
218
+ (0, vitest_1.it)('should reset all state', async () => {
219
+ serverApp.getStateManager().activateFlow('admin-flow');
220
+ serverApp.getStateManager().activateScenario('get-users', 'error');
221
+ await (0, supertest_1.default)(serverApp.getApp())
222
+ .post('/mock-admin/api/reset')
223
+ .expect(204);
224
+ const state = serverApp.getStateManager().getState();
225
+ (0, vitest_1.expect)(state.activeFlowId).toBeNull();
226
+ (0, vitest_1.expect)(state.activeScenarios).toEqual({});
227
+ });
228
+ (0, vitest_1.it)('should restore default scenarios', async () => {
229
+ serverApp.getStateManager().activateScenario('get-users', 'error');
230
+ await (0, supertest_1.default)(serverApp.getApp())
231
+ .post('/mock-admin/api/reset')
232
+ .expect(204);
233
+ // Should now return success (default)
234
+ await (0, supertest_1.default)(serverApp.getApp())
235
+ .get('/api/users')
236
+ .expect(200);
237
+ });
238
+ });
239
+ (0, vitest_1.describe)('integration with mock API', () => {
240
+ (0, vitest_1.it)('should allow full workflow: activate scenario, test, reset', async () => {
241
+ // Initial state - success
242
+ await (0, supertest_1.default)(serverApp.getApp())
243
+ .get('/api/users')
244
+ .expect(200);
245
+ // Activate error scenario
246
+ await (0, supertest_1.default)(serverApp.getApp())
247
+ .post('/mock-admin/api/scenarios/activate')
248
+ .send({ endpointId: 'get-users', scenarioId: 'error' })
249
+ .expect(204);
250
+ // Now returns error
251
+ await (0, supertest_1.default)(serverApp.getApp())
252
+ .get('/api/users')
253
+ .expect(500);
254
+ // Reset
255
+ await (0, supertest_1.default)(serverApp.getApp())
256
+ .post('/mock-admin/api/reset')
257
+ .expect(204);
258
+ // Back to success
259
+ await (0, supertest_1.default)(serverApp.getApp())
260
+ .get('/api/users')
261
+ .expect(200);
262
+ });
263
+ (0, vitest_1.it)('should allow full workflow: activate flow, test, deactivate', async () => {
264
+ // Activate error flow
265
+ await (0, supertest_1.default)(serverApp.getApp())
266
+ .post('/mock-admin/api/flows/activate')
267
+ .send({ flowId: 'error-flow' })
268
+ .expect(204);
269
+ // Now returns error
270
+ await (0, supertest_1.default)(serverApp.getApp())
271
+ .get('/api/users')
272
+ .expect(500);
273
+ // Deactivate flow
274
+ await (0, supertest_1.default)(serverApp.getApp())
275
+ .post('/mock-admin/api/flows/activate')
276
+ .send({ flowId: null })
277
+ .expect(204);
278
+ // Back to success
279
+ await (0, supertest_1.default)(serverApp.getApp())
280
+ .get('/api/users')
281
+ .expect(200);
282
+ });
283
+ });
284
+ (0, vitest_1.describe)('Endpoint CRUD operations', () => {
285
+ let mockConfigWriter;
286
+ let onConfigChangeCalled;
287
+ (0, vitest_1.beforeEach)(() => {
288
+ onConfigChangeCalled = false;
289
+ mockConfigWriter = {
290
+ writeEndpoint: vitest_1.vi.fn().mockResolvedValue({ success: true, filePath: '/test/endpoint.json' }),
291
+ writeFlow: vitest_1.vi.fn().mockResolvedValue({ success: true, filePath: '/test/flow.json' }),
292
+ deleteEndpoint: vitest_1.vi.fn().mockResolvedValue({ success: true }),
293
+ deleteFlow: vitest_1.vi.fn().mockResolvedValue({ success: true }),
294
+ getEndpointFilePath: vitest_1.vi.fn().mockReturnValue('/test/endpoint.json'),
295
+ getFlowFilePath: vitest_1.vi.fn().mockReturnValue('/test/flow.json'),
296
+ findEndpointFile: vitest_1.vi.fn().mockResolvedValue(null),
297
+ findFlowFile: vitest_1.vi.fn().mockResolvedValue(null),
298
+ };
299
+ serverApp.setConfigWriter(mockConfigWriter, async () => {
300
+ onConfigChangeCalled = true;
301
+ });
302
+ });
303
+ (0, vitest_1.describe)('POST /mock-admin/api/endpoints', () => {
304
+ (0, vitest_1.it)('should create a new endpoint', async () => {
305
+ const newEndpoint = {
306
+ id: 'new-endpoint',
307
+ path: '/new',
308
+ method: 'POST',
309
+ scenarios: [
310
+ { id: 'success', name: 'Success', status: 201, body: { created: true } },
311
+ ],
312
+ defaultScenarioId: 'success',
313
+ };
314
+ const res = await (0, supertest_1.default)(serverApp.getApp())
315
+ .post('/mock-admin/api/endpoints')
316
+ .send(newEndpoint)
317
+ .expect(201);
318
+ (0, vitest_1.expect)(res.body.id).toBe('new-endpoint');
319
+ (0, vitest_1.expect)(res.body.filePath).toBeDefined();
320
+ (0, vitest_1.expect)(mockConfigWriter.writeEndpoint).toHaveBeenCalledWith(newEndpoint);
321
+ (0, vitest_1.expect)(onConfigChangeCalled).toBe(true);
322
+ });
323
+ (0, vitest_1.it)('should return 409 if endpoint already exists', async () => {
324
+ const existingEndpoint = {
325
+ id: 'get-users',
326
+ path: '/users',
327
+ method: 'GET',
328
+ scenarios: [{ id: 's', name: 'S', status: 200, body: {} }],
329
+ defaultScenarioId: 's',
330
+ };
331
+ const res = await (0, supertest_1.default)(serverApp.getApp())
332
+ .post('/mock-admin/api/endpoints')
333
+ .send(existingEndpoint)
334
+ .expect(409);
335
+ (0, vitest_1.expect)(res.body.error).toContain('already exists');
336
+ });
337
+ (0, vitest_1.it)('should return 400 for invalid endpoint', async () => {
338
+ const res = await (0, supertest_1.default)(serverApp.getApp())
339
+ .post('/mock-admin/api/endpoints')
340
+ .send({ id: 'test' }) // Missing required fields
341
+ .expect(400);
342
+ (0, vitest_1.expect)(res.body.error).toBe('Invalid request body');
343
+ });
344
+ });
345
+ (0, vitest_1.describe)('PUT /mock-admin/api/endpoints/:id', () => {
346
+ (0, vitest_1.it)('should update an existing endpoint', async () => {
347
+ const updatedEndpoint = {
348
+ id: 'get-users',
349
+ path: '/users/updated',
350
+ method: 'GET',
351
+ scenarios: [
352
+ { id: 'success', name: 'Success', status: 200, body: { updated: true } },
353
+ ],
354
+ defaultScenarioId: 'success',
355
+ };
356
+ const res = await (0, supertest_1.default)(serverApp.getApp())
357
+ .put('/mock-admin/api/endpoints/get-users')
358
+ .send(updatedEndpoint)
359
+ .expect(200);
360
+ (0, vitest_1.expect)(res.body.id).toBe('get-users');
361
+ (0, vitest_1.expect)(mockConfigWriter.writeEndpoint).toHaveBeenCalledWith(updatedEndpoint);
362
+ (0, vitest_1.expect)(onConfigChangeCalled).toBe(true);
363
+ });
364
+ (0, vitest_1.it)('should return 404 if endpoint not found', async () => {
365
+ const endpoint = {
366
+ id: 'nonexistent',
367
+ path: '/test',
368
+ method: 'GET',
369
+ scenarios: [{ id: 's', name: 'S', status: 200, body: {} }],
370
+ defaultScenarioId: 's',
371
+ };
372
+ const res = await (0, supertest_1.default)(serverApp.getApp())
373
+ .put('/mock-admin/api/endpoints/nonexistent')
374
+ .send(endpoint)
375
+ .expect(404);
376
+ (0, vitest_1.expect)(res.body.error).toContain('not found');
377
+ });
378
+ (0, vitest_1.it)('should return 400 if ID in body does not match URL', async () => {
379
+ const endpoint = {
380
+ id: 'different-id',
381
+ path: '/users',
382
+ method: 'GET',
383
+ scenarios: [{ id: 's', name: 'S', status: 200, body: {} }],
384
+ defaultScenarioId: 's',
385
+ };
386
+ const res = await (0, supertest_1.default)(serverApp.getApp())
387
+ .put('/mock-admin/api/endpoints/get-users')
388
+ .send(endpoint)
389
+ .expect(400);
390
+ (0, vitest_1.expect)(res.body.error).toContain('must match');
391
+ });
392
+ });
393
+ (0, vitest_1.describe)('DELETE /mock-admin/api/endpoints/:id', () => {
394
+ (0, vitest_1.it)('should delete an existing endpoint', async () => {
395
+ await (0, supertest_1.default)(serverApp.getApp())
396
+ .delete('/mock-admin/api/endpoints/get-users')
397
+ .expect(204);
398
+ (0, vitest_1.expect)(mockConfigWriter.deleteEndpoint).toHaveBeenCalledWith('get-users');
399
+ (0, vitest_1.expect)(onConfigChangeCalled).toBe(true);
400
+ });
401
+ (0, vitest_1.it)('should return 404 if endpoint not found', async () => {
402
+ const res = await (0, supertest_1.default)(serverApp.getApp())
403
+ .delete('/mock-admin/api/endpoints/nonexistent')
404
+ .expect(404);
405
+ (0, vitest_1.expect)(res.body.error).toContain('not found');
406
+ });
407
+ });
408
+ });
409
+ (0, vitest_1.describe)('Flow CRUD operations', () => {
410
+ let mockConfigWriter;
411
+ let onConfigChangeCalled;
412
+ (0, vitest_1.beforeEach)(() => {
413
+ onConfigChangeCalled = false;
414
+ mockConfigWriter = {
415
+ writeEndpoint: vitest_1.vi.fn().mockResolvedValue({ success: true, filePath: '/test/endpoint.json' }),
416
+ writeFlow: vitest_1.vi.fn().mockResolvedValue({ success: true, filePath: '/test/flow.json' }),
417
+ deleteEndpoint: vitest_1.vi.fn().mockResolvedValue({ success: true }),
418
+ deleteFlow: vitest_1.vi.fn().mockResolvedValue({ success: true }),
419
+ getEndpointFilePath: vitest_1.vi.fn().mockReturnValue('/test/endpoint.json'),
420
+ getFlowFilePath: vitest_1.vi.fn().mockReturnValue('/test/flow.json'),
421
+ findEndpointFile: vitest_1.vi.fn().mockResolvedValue(null),
422
+ findFlowFile: vitest_1.vi.fn().mockResolvedValue(null),
423
+ };
424
+ serverApp.setConfigWriter(mockConfigWriter, async () => {
425
+ onConfigChangeCalled = true;
426
+ });
427
+ });
428
+ (0, vitest_1.describe)('POST /mock-admin/api/flows', () => {
429
+ (0, vitest_1.it)('should create a new flow', async () => {
430
+ const newFlow = {
431
+ id: 'new-flow',
432
+ name: 'New Flow',
433
+ endpointScenarios: { 'get-users': 'success' },
434
+ };
435
+ const res = await (0, supertest_1.default)(serverApp.getApp())
436
+ .post('/mock-admin/api/flows')
437
+ .send(newFlow)
438
+ .expect(201);
439
+ (0, vitest_1.expect)(res.body.id).toBe('new-flow');
440
+ (0, vitest_1.expect)(res.body.filePath).toBeDefined();
441
+ (0, vitest_1.expect)(mockConfigWriter.writeFlow).toHaveBeenCalledWith(newFlow);
442
+ (0, vitest_1.expect)(onConfigChangeCalled).toBe(true);
443
+ });
444
+ (0, vitest_1.it)('should return 409 if flow already exists', async () => {
445
+ const existingFlow = {
446
+ id: 'admin-flow',
447
+ name: 'Admin Flow',
448
+ endpointScenarios: {},
449
+ };
450
+ const res = await (0, supertest_1.default)(serverApp.getApp())
451
+ .post('/mock-admin/api/flows')
452
+ .send(existingFlow)
453
+ .expect(409);
454
+ (0, vitest_1.expect)(res.body.error).toContain('already exists');
455
+ });
456
+ });
457
+ (0, vitest_1.describe)('PUT /mock-admin/api/flows/:id', () => {
458
+ (0, vitest_1.it)('should update an existing flow', async () => {
459
+ const updatedFlow = {
460
+ id: 'admin-flow',
461
+ name: 'Updated Admin Flow',
462
+ endpointScenarios: { 'get-users': 'error' },
463
+ };
464
+ const res = await (0, supertest_1.default)(serverApp.getApp())
465
+ .put('/mock-admin/api/flows/admin-flow')
466
+ .send(updatedFlow)
467
+ .expect(200);
468
+ (0, vitest_1.expect)(res.body.id).toBe('admin-flow');
469
+ (0, vitest_1.expect)(mockConfigWriter.writeFlow).toHaveBeenCalledWith(updatedFlow);
470
+ (0, vitest_1.expect)(onConfigChangeCalled).toBe(true);
471
+ });
472
+ (0, vitest_1.it)('should return 404 if flow not found', async () => {
473
+ const flow = {
474
+ id: 'nonexistent',
475
+ name: 'Test',
476
+ endpointScenarios: {},
477
+ };
478
+ const res = await (0, supertest_1.default)(serverApp.getApp())
479
+ .put('/mock-admin/api/flows/nonexistent')
480
+ .send(flow)
481
+ .expect(404);
482
+ (0, vitest_1.expect)(res.body.error).toContain('not found');
483
+ });
484
+ });
485
+ (0, vitest_1.describe)('DELETE /mock-admin/api/flows/:id', () => {
486
+ (0, vitest_1.it)('should delete an existing flow', async () => {
487
+ await (0, supertest_1.default)(serverApp.getApp())
488
+ .delete('/mock-admin/api/flows/admin-flow')
489
+ .expect(204);
490
+ (0, vitest_1.expect)(mockConfigWriter.deleteFlow).toHaveBeenCalledWith('admin-flow');
491
+ (0, vitest_1.expect)(onConfigChangeCalled).toBe(true);
492
+ });
493
+ (0, vitest_1.it)('should return 404 if flow not found', async () => {
494
+ const res = await (0, supertest_1.default)(serverApp.getApp())
495
+ .delete('/mock-admin/api/flows/nonexistent')
496
+ .expect(404);
497
+ (0, vitest_1.expect)(res.body.error).toContain('not found');
498
+ });
499
+ (0, vitest_1.it)('should deactivate flow if it was active', async () => {
500
+ // Activate the flow first
501
+ await (0, supertest_1.default)(serverApp.getApp())
502
+ .post('/mock-admin/api/flows/activate')
503
+ .send({ flowId: 'admin-flow' })
504
+ .expect(204);
505
+ // Verify it's active
506
+ const stateBefore = await (0, supertest_1.default)(serverApp.getApp())
507
+ .get('/mock-admin/api/state')
508
+ .expect(200);
509
+ (0, vitest_1.expect)(stateBefore.body.activeFlowId).toBe('admin-flow');
510
+ // Delete the flow
511
+ await (0, supertest_1.default)(serverApp.getApp())
512
+ .delete('/mock-admin/api/flows/admin-flow')
513
+ .expect(204);
514
+ // Verify it's deactivated
515
+ const stateAfter = await (0, supertest_1.default)(serverApp.getApp())
516
+ .get('/mock-admin/api/state')
517
+ .expect(200);
518
+ (0, vitest_1.expect)(stateAfter.body.activeFlowId).toBeNull();
519
+ });
520
+ });
521
+ (0, vitest_1.describe)('POST /mock-admin/api/flows/:id/duplicate', () => {
522
+ (0, vitest_1.it)('should duplicate an existing flow', async () => {
523
+ const res = await (0, supertest_1.default)(serverApp.getApp())
524
+ .post('/mock-admin/api/flows/admin-flow/duplicate')
525
+ .send({ newId: 'admin-flow-copy' })
526
+ .expect(201);
527
+ (0, vitest_1.expect)(res.body.id).toBe('admin-flow-copy');
528
+ (0, vitest_1.expect)(res.body.filePath).toBeDefined();
529
+ (0, vitest_1.expect)(mockConfigWriter.writeFlow).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
530
+ id: 'admin-flow-copy',
531
+ name: 'Admin Flow (Copy)',
532
+ endpointScenarios: { 'get-users': 'success', 'get-products': 'list' },
533
+ }));
534
+ (0, vitest_1.expect)(onConfigChangeCalled).toBe(true);
535
+ });
536
+ (0, vitest_1.it)('should allow custom name for duplicate', async () => {
537
+ const res = await (0, supertest_1.default)(serverApp.getApp())
538
+ .post('/mock-admin/api/flows/admin-flow/duplicate')
539
+ .send({ newId: 'admin-flow-v2', newName: 'Admin Flow V2' })
540
+ .expect(201);
541
+ (0, vitest_1.expect)(res.body.id).toBe('admin-flow-v2');
542
+ (0, vitest_1.expect)(mockConfigWriter.writeFlow).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
543
+ id: 'admin-flow-v2',
544
+ name: 'Admin Flow V2',
545
+ }));
546
+ });
547
+ (0, vitest_1.it)('should return 404 if source flow not found', async () => {
548
+ const res = await (0, supertest_1.default)(serverApp.getApp())
549
+ .post('/mock-admin/api/flows/nonexistent/duplicate')
550
+ .send({ newId: 'new-flow' })
551
+ .expect(404);
552
+ (0, vitest_1.expect)(res.body.error).toContain('not found');
553
+ });
554
+ (0, vitest_1.it)('should return 409 if new ID already exists', async () => {
555
+ const res = await (0, supertest_1.default)(serverApp.getApp())
556
+ .post('/mock-admin/api/flows/admin-flow/duplicate')
557
+ .send({ newId: 'error-flow' }) // Already exists
558
+ .expect(409);
559
+ (0, vitest_1.expect)(res.body.error).toContain('already exists');
560
+ });
561
+ });
562
+ });
563
+ (0, vitest_1.describe)('CRUD operations without ConfigWriter', () => {
564
+ (0, vitest_1.it)('should return 501 for endpoint CRUD when ConfigWriter not configured', async () => {
565
+ const endpoint = {
566
+ id: 'test',
567
+ path: '/test',
568
+ method: 'GET',
569
+ scenarios: [{ id: 's', name: 'S', status: 200, body: {} }],
570
+ defaultScenarioId: 's',
571
+ };
572
+ // POST
573
+ let res = await (0, supertest_1.default)(serverApp.getApp())
574
+ .post('/mock-admin/api/endpoints')
575
+ .send(endpoint)
576
+ .expect(501);
577
+ (0, vitest_1.expect)(res.body.error).toContain('not available');
578
+ // PUT
579
+ res = await (0, supertest_1.default)(serverApp.getApp())
580
+ .put('/mock-admin/api/endpoints/get-users')
581
+ .send({ ...endpoint, id: 'get-users' })
582
+ .expect(501);
583
+ (0, vitest_1.expect)(res.body.error).toContain('not available');
584
+ // DELETE
585
+ res = await (0, supertest_1.default)(serverApp.getApp())
586
+ .delete('/mock-admin/api/endpoints/get-users')
587
+ .expect(501);
588
+ (0, vitest_1.expect)(res.body.error).toContain('not available');
589
+ });
590
+ (0, vitest_1.it)('should return 501 for flow CRUD when ConfigWriter not configured', async () => {
591
+ const flow = {
592
+ id: 'test',
593
+ name: 'Test',
594
+ endpointScenarios: {},
595
+ };
596
+ // POST
597
+ let res = await (0, supertest_1.default)(serverApp.getApp())
598
+ .post('/mock-admin/api/flows')
599
+ .send(flow)
600
+ .expect(501);
601
+ (0, vitest_1.expect)(res.body.error).toContain('not available');
602
+ // PUT
603
+ res = await (0, supertest_1.default)(serverApp.getApp())
604
+ .put('/mock-admin/api/flows/admin-flow')
605
+ .send({ ...flow, id: 'admin-flow' })
606
+ .expect(501);
607
+ (0, vitest_1.expect)(res.body.error).toContain('not available');
608
+ // DELETE
609
+ res = await (0, supertest_1.default)(serverApp.getApp())
610
+ .delete('/mock-admin/api/flows/admin-flow')
611
+ .expect(501);
612
+ (0, vitest_1.expect)(res.body.error).toContain('not available');
613
+ // Duplicate
614
+ res = await (0, supertest_1.default)(serverApp.getApp())
615
+ .post('/mock-admin/api/flows/admin-flow/duplicate')
616
+ .send({ newId: 'new' })
617
+ .expect(501);
618
+ (0, vitest_1.expect)(res.body.error).toContain('not available');
619
+ });
620
+ });
621
+ });