@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.
- package/README.md +643 -0
- package/dist/__tests__/mock-server.test.d.ts +5 -0
- package/dist/__tests__/mock-server.test.d.ts.map +1 -0
- package/dist/__tests__/mock-server.test.js +599 -0
- package/dist/__tests__/mock-server.test.js.map +1 -0
- package/dist/admin/__tests__/admin-router.test.d.ts +2 -0
- package/dist/admin/__tests__/admin-router.test.d.ts.map +1 -0
- package/dist/admin/__tests__/admin-router.test.js +621 -0
- package/dist/admin/__tests__/admin-router.test.js.map +1 -0
- package/dist/admin/admin-router.d.ts +27 -0
- package/dist/admin/admin-router.d.ts.map +1 -0
- package/dist/admin/admin-router.js +616 -0
- package/dist/admin/admin-router.js.map +1 -0
- package/dist/admin/index.d.ts +4 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +10 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/types.d.ts +185 -0
- package/dist/admin/types.d.ts.map +1 -0
- package/dist/admin/types.js +33 -0
- package/dist/admin/types.js.map +1 -0
- package/dist/config/__tests__/config-discovery.test.d.ts +2 -0
- package/dist/config/__tests__/config-discovery.test.d.ts.map +1 -0
- package/dist/config/__tests__/config-discovery.test.js +52 -0
- package/dist/config/__tests__/config-discovery.test.js.map +1 -0
- package/dist/config/__tests__/config-loader.test.d.ts +2 -0
- package/dist/config/__tests__/config-loader.test.d.ts.map +1 -0
- package/dist/config/__tests__/config-loader.test.js +124 -0
- package/dist/config/__tests__/config-loader.test.js.map +1 -0
- package/dist/config/__tests__/file-loader.test.d.ts +2 -0
- package/dist/config/__tests__/file-loader.test.d.ts.map +1 -0
- package/dist/config/__tests__/file-loader.test.js +63 -0
- package/dist/config/__tests__/file-loader.test.js.map +1 -0
- package/dist/config/__tests__/fixtures/mock-config/endpoints/products.d.ts +25 -0
- package/dist/config/__tests__/fixtures/mock-config/endpoints/products.d.ts.map +1 -0
- package/dist/config/__tests__/fixtures/mock-config/endpoints/products.js +25 -0
- package/dist/config/__tests__/fixtures/mock-config/endpoints/products.js.map +1 -0
- package/dist/config/__tests__/fixtures/mock-config/flows/error-flow.d.ts +11 -0
- package/dist/config/__tests__/fixtures/mock-config/flows/error-flow.d.ts.map +1 -0
- package/dist/config/__tests__/fixtures/mock-config/flows/error-flow.js +11 -0
- package/dist/config/__tests__/fixtures/mock-config/flows/error-flow.js.map +1 -0
- package/dist/config/config-discovery.d.ts +23 -0
- package/dist/config/config-discovery.d.ts.map +1 -0
- package/dist/config/config-discovery.js +60 -0
- package/dist/config/config-discovery.js.map +1 -0
- package/dist/config/config-loader.d.ts +56 -0
- package/dist/config/config-loader.d.ts.map +1 -0
- package/dist/config/config-loader.js +162 -0
- package/dist/config/config-loader.js.map +1 -0
- package/dist/config/config-writer.d.ts +96 -0
- package/dist/config/config-writer.d.ts.map +1 -0
- package/dist/config/config-writer.js +248 -0
- package/dist/config/config-writer.js.map +1 -0
- package/dist/config/file-loader.d.ts +16 -0
- package/dist/config/file-loader.d.ts.map +1 -0
- package/dist/config/file-loader.js +90 -0
- package/dist/config/file-loader.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +23 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +47 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +7 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/mock-server.d.ts +243 -0
- package/dist/mock-server.d.ts.map +1 -0
- package/dist/mock-server.js +387 -0
- package/dist/mock-server.js.map +1 -0
- package/dist/resolver/__tests__/response-resolver.test.d.ts +2 -0
- package/dist/resolver/__tests__/response-resolver.test.d.ts.map +1 -0
- package/dist/resolver/__tests__/response-resolver.test.js +358 -0
- package/dist/resolver/__tests__/response-resolver.test.js.map +1 -0
- package/dist/resolver/index.d.ts +4 -0
- package/dist/resolver/index.d.ts.map +1 -0
- package/dist/resolver/index.js +9 -0
- package/dist/resolver/index.js.map +1 -0
- package/dist/resolver/response-resolver.d.ts +73 -0
- package/dist/resolver/response-resolver.d.ts.map +1 -0
- package/dist/resolver/response-resolver.js +167 -0
- package/dist/resolver/response-resolver.js.map +1 -0
- package/dist/resolver/types.d.ts +47 -0
- package/dist/resolver/types.d.ts.map +1 -0
- package/dist/resolver/types.js +9 -0
- package/dist/resolver/types.js.map +1 -0
- package/dist/schemas/__tests__/config.validator.test.d.ts +2 -0
- package/dist/schemas/__tests__/config.validator.test.d.ts.map +1 -0
- package/dist/schemas/__tests__/config.validator.test.js +66 -0
- package/dist/schemas/__tests__/config.validator.test.js.map +1 -0
- package/dist/schemas/__tests__/endpoint.schema.test.d.ts +2 -0
- package/dist/schemas/__tests__/endpoint.schema.test.d.ts.map +1 -0
- package/dist/schemas/__tests__/endpoint.schema.test.js +103 -0
- package/dist/schemas/__tests__/endpoint.schema.test.js.map +1 -0
- package/dist/schemas/__tests__/flow.schema.test.d.ts +2 -0
- package/dist/schemas/__tests__/flow.schema.test.d.ts.map +1 -0
- package/dist/schemas/__tests__/flow.schema.test.js +92 -0
- package/dist/schemas/__tests__/flow.schema.test.js.map +1 -0
- package/dist/schemas/config.validator.d.ts +39 -0
- package/dist/schemas/config.validator.d.ts.map +1 -0
- package/dist/schemas/config.validator.js +117 -0
- package/dist/schemas/config.validator.js.map +1 -0
- package/dist/schemas/endpoint.schema.d.ts +108 -0
- package/dist/schemas/endpoint.schema.d.ts.map +1 -0
- package/dist/schemas/endpoint.schema.js +87 -0
- package/dist/schemas/endpoint.schema.js.map +1 -0
- package/dist/schemas/flow.schema.d.ts +46 -0
- package/dist/schemas/flow.schema.d.ts.map +1 -0
- package/dist/schemas/flow.schema.js +73 -0
- package/dist/schemas/flow.schema.js.map +1 -0
- package/dist/schemas/http.schema.d.ts +15 -0
- package/dist/schemas/http.schema.d.ts.map +1 -0
- package/dist/schemas/http.schema.js +32 -0
- package/dist/schemas/http.schema.js.map +1 -0
- package/dist/schemas/index.d.ts +6 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +33 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/scenario.schema.d.ts +54 -0
- package/dist/schemas/scenario.schema.d.ts.map +1 -0
- package/dist/schemas/scenario.schema.js +75 -0
- package/dist/schemas/scenario.schema.js.map +1 -0
- package/dist/server/__tests__/mock-server-app.test.d.ts +2 -0
- package/dist/server/__tests__/mock-server-app.test.d.ts.map +1 -0
- package/dist/server/__tests__/mock-server-app.test.js +296 -0
- package/dist/server/__tests__/mock-server-app.test.js.map +1 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +10 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/mock-server-app.d.ts +119 -0
- package/dist/server/mock-server-app.d.ts.map +1 -0
- package/dist/server/mock-server-app.js +304 -0
- package/dist/server/mock-server-app.js.map +1 -0
- package/dist/server/types.d.ts +38 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +11 -0
- package/dist/server/types.js.map +1 -0
- package/dist/state/__tests__/state-manager.test.d.ts +2 -0
- package/dist/state/__tests__/state-manager.test.d.ts.map +1 -0
- package/dist/state/__tests__/state-manager.test.js +275 -0
- package/dist/state/__tests__/state-manager.test.js.map +1 -0
- package/dist/state/index.d.ts +3 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +7 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/state-manager.d.ts +92 -0
- package/dist/state/state-manager.d.ts.map +1 -0
- package/dist/state/state-manager.js +198 -0
- package/dist/state/state-manager.js.map +1 -0
- package/dist/state/types.d.ts +30 -0
- package/dist/state/types.d.ts.map +1 -0
- package/dist/state/types.js +2 -0
- package/dist/state/types.js.map +1 -0
- package/dist/ui/assets/index-SADMOgPE.js +10 -0
- package/dist/ui/assets/index-rj7m-XdG.css +1 -0
- package/dist/ui/index.html +29 -0
- package/dist/watcher/__tests__/config-watcher.test.d.ts +2 -0
- package/dist/watcher/__tests__/config-watcher.test.d.ts.map +1 -0
- package/dist/watcher/__tests__/config-watcher.test.js +372 -0
- package/dist/watcher/__tests__/config-watcher.test.js.map +1 -0
- package/dist/watcher/config-watcher.d.ts +100 -0
- package/dist/watcher/config-watcher.d.ts.map +1 -0
- package/dist/watcher/config-watcher.js +358 -0
- package/dist/watcher/config-watcher.js.map +1 -0
- package/dist/watcher/index.d.ts +7 -0
- package/dist/watcher/index.d.ts.map +1 -0
- package/dist/watcher/index.js +8 -0
- package/dist/watcher/index.js.map +1 -0
- package/dist/watcher/types.d.ts +64 -0
- package/dist/watcher/types.d.ts.map +1 -0
- package/dist/watcher/types.js +15 -0
- package/dist/watcher/types.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,304 @@
|
|
|
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
|
+
exports.MockServerApp = exports.ADMIN_UI_PATH = void 0;
|
|
7
|
+
exports.createMockServerApp = createMockServerApp;
|
|
8
|
+
const express_1 = __importDefault(require("express"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const index_js_1 = require("../state/index.js");
|
|
11
|
+
const index_js_2 = require("../resolver/index.js");
|
|
12
|
+
const index_js_3 = require("../admin/index.js");
|
|
13
|
+
const types_js_1 = require("./types.js");
|
|
14
|
+
// UI path constant
|
|
15
|
+
exports.ADMIN_UI_PATH = '/mock-admin';
|
|
16
|
+
/**
|
|
17
|
+
* Mock server that serves configured endpoints
|
|
18
|
+
*/
|
|
19
|
+
class MockServerApp {
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.server = null;
|
|
22
|
+
this.registeredRoutes = [];
|
|
23
|
+
this.endpoints = [];
|
|
24
|
+
this.flows = [];
|
|
25
|
+
this.options = { ...types_js_1.DEFAULT_SERVER_OPTIONS, ...options };
|
|
26
|
+
this.app = (0, express_1.default)();
|
|
27
|
+
this.stateManager = (0, index_js_1.createStateManager)();
|
|
28
|
+
this.resolver = (0, index_js_2.createResponseResolver)(this.stateManager);
|
|
29
|
+
this.mockRouter = express_1.default.Router();
|
|
30
|
+
this.setupMiddleware();
|
|
31
|
+
this.mountAdminRouter();
|
|
32
|
+
this.mountAdminUI();
|
|
33
|
+
this.mountMockRouter();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Setup Express middleware
|
|
37
|
+
*/
|
|
38
|
+
setupMiddleware() {
|
|
39
|
+
// Parse JSON bodies
|
|
40
|
+
this.app.use(express_1.default.json());
|
|
41
|
+
// Parse URL-encoded bodies
|
|
42
|
+
this.app.use(express_1.default.urlencoded({ extended: true }));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Mount the admin API router
|
|
46
|
+
*/
|
|
47
|
+
mountAdminRouter() {
|
|
48
|
+
const adminRouter = (0, index_js_3.createAdminRouter)({
|
|
49
|
+
getEndpoints: () => this.endpoints,
|
|
50
|
+
getFlows: () => this.flows,
|
|
51
|
+
stateManager: this.stateManager,
|
|
52
|
+
getConfigWriter: () => this.configWriter,
|
|
53
|
+
getOnConfigChange: () => this.onConfigChange,
|
|
54
|
+
});
|
|
55
|
+
this.app.use(index_js_3.ADMIN_API_PATH, adminRouter);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Mount the admin UI static files
|
|
59
|
+
* Serves the built React app at /mock-admin
|
|
60
|
+
*/
|
|
61
|
+
mountAdminUI() {
|
|
62
|
+
// Get the directory where this module is located
|
|
63
|
+
const currentDir = __dirname;
|
|
64
|
+
// UI is built to dist/ui relative to package root (2 levels up from src/server)
|
|
65
|
+
const uiDistPath = path_1.default.resolve(currentDir, '../../dist/ui');
|
|
66
|
+
// Serve static files
|
|
67
|
+
this.app.use(exports.ADMIN_UI_PATH, express_1.default.static(uiDistPath));
|
|
68
|
+
// SPA fallback: serve index.html for any non-file requests under /mock-admin
|
|
69
|
+
this.app.get(`${exports.ADMIN_UI_PATH}/*`, (req, res, next) => {
|
|
70
|
+
// Skip API routes
|
|
71
|
+
if (req.path.startsWith(`${exports.ADMIN_UI_PATH}/api`)) {
|
|
72
|
+
return next();
|
|
73
|
+
}
|
|
74
|
+
res.sendFile(path_1.default.join(uiDistPath, 'index.html'), (err) => {
|
|
75
|
+
if (err) {
|
|
76
|
+
// If UI is not built, return helpful message
|
|
77
|
+
res.status(404).send(`
|
|
78
|
+
<html>
|
|
79
|
+
<body style="font-family: sans-serif; padding: 40px;">
|
|
80
|
+
<h1>Admin UI not available</h1>
|
|
81
|
+
<p>The admin UI has not been built yet.</p>
|
|
82
|
+
<p>Run <code>npm run build:ui</code> to build the admin interface.</p>
|
|
83
|
+
<p>You can still use the <a href="${index_js_3.ADMIN_API_PATH}/config">Admin API</a> directly.</p>
|
|
84
|
+
</body>
|
|
85
|
+
</html>
|
|
86
|
+
`);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Mount the mock router at the configured prefix
|
|
93
|
+
*/
|
|
94
|
+
mountMockRouter() {
|
|
95
|
+
this.app.use(this.options.apiPrefix, this.mockRouter);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get the Express app instance
|
|
99
|
+
*/
|
|
100
|
+
getApp() {
|
|
101
|
+
return this.app;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get the state manager
|
|
105
|
+
*/
|
|
106
|
+
getStateManager() {
|
|
107
|
+
return this.stateManager;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get the response resolver
|
|
111
|
+
*/
|
|
112
|
+
getResolver() {
|
|
113
|
+
return this.resolver;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get server options
|
|
117
|
+
*/
|
|
118
|
+
getOptions() {
|
|
119
|
+
return { ...this.options };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get registered routes
|
|
123
|
+
*/
|
|
124
|
+
getRegisteredRoutes() {
|
|
125
|
+
return [...this.registeredRoutes];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Set the config writer for endpoint/flow management
|
|
129
|
+
*/
|
|
130
|
+
setConfigWriter(configWriter, onConfigChange) {
|
|
131
|
+
this.configWriter = configWriter;
|
|
132
|
+
this.onConfigChange = onConfigChange;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get current server state
|
|
136
|
+
*/
|
|
137
|
+
getState() {
|
|
138
|
+
return {
|
|
139
|
+
running: this.server !== null && this.server.listening,
|
|
140
|
+
port: this.server?.listening ? this.options.port : null,
|
|
141
|
+
endpointCount: this.endpoints.length,
|
|
142
|
+
flowCount: this.flows.length,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Register endpoints and flows
|
|
147
|
+
*/
|
|
148
|
+
registerConfig(endpoints, flows) {
|
|
149
|
+
this.endpoints = endpoints;
|
|
150
|
+
this.flows = flows;
|
|
151
|
+
// Update state manager
|
|
152
|
+
this.stateManager.setEndpoints(endpoints);
|
|
153
|
+
this.stateManager.setFlows(flows);
|
|
154
|
+
// Update resolver
|
|
155
|
+
this.resolver.setEndpoints(endpoints);
|
|
156
|
+
// Register routes
|
|
157
|
+
this.registerRoutes(endpoints);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Register routes for all endpoints
|
|
161
|
+
*/
|
|
162
|
+
registerRoutes(endpoints) {
|
|
163
|
+
// Clear existing routes by creating a new router
|
|
164
|
+
this.mockRouter = express_1.default.Router();
|
|
165
|
+
this.registeredRoutes = [];
|
|
166
|
+
// Re-mount the router
|
|
167
|
+
// Note: Express doesn't support unmounting, so we need to replace the handler
|
|
168
|
+
this.app._router = undefined;
|
|
169
|
+
this.setupMiddleware();
|
|
170
|
+
this.mountAdminRouter();
|
|
171
|
+
this.mountAdminUI();
|
|
172
|
+
this.app.use(this.options.apiPrefix, this.mockRouter);
|
|
173
|
+
// Register each endpoint
|
|
174
|
+
for (const endpoint of endpoints) {
|
|
175
|
+
this.registerEndpoint(endpoint);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Register a single endpoint
|
|
180
|
+
*/
|
|
181
|
+
registerEndpoint(endpoint) {
|
|
182
|
+
const method = endpoint.method.toLowerCase();
|
|
183
|
+
const handler = this.createEndpointHandler(endpoint);
|
|
184
|
+
// Register with Express router
|
|
185
|
+
this.mockRouter[method](endpoint.path, handler);
|
|
186
|
+
// Track registered route
|
|
187
|
+
this.registeredRoutes.push({
|
|
188
|
+
method: endpoint.method,
|
|
189
|
+
path: endpoint.path,
|
|
190
|
+
fullPath: `${this.options.apiPrefix}${endpoint.path}`,
|
|
191
|
+
endpointId: endpoint.id,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Create request handler for an endpoint
|
|
196
|
+
*/
|
|
197
|
+
createEndpointHandler(endpoint) {
|
|
198
|
+
return async (req, res, next) => {
|
|
199
|
+
try {
|
|
200
|
+
const handled = await this.resolver.handleRequest(endpoint.id, req, res);
|
|
201
|
+
if (!handled) {
|
|
202
|
+
// This shouldn't happen since we're routing to a known endpoint
|
|
203
|
+
res.status(500).json({ error: 'Failed to resolve response' });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
next(error);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Add custom middleware to the Express app
|
|
213
|
+
*/
|
|
214
|
+
use(...args) {
|
|
215
|
+
return this.app.use(...args);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Start the server
|
|
219
|
+
*/
|
|
220
|
+
async start(port) {
|
|
221
|
+
const listenPort = port ?? this.options.port;
|
|
222
|
+
return new Promise((resolve, reject) => {
|
|
223
|
+
try {
|
|
224
|
+
this.server = this.app.listen(listenPort, () => {
|
|
225
|
+
// Get actual port (important when port 0 is used)
|
|
226
|
+
const address = this.server?.address();
|
|
227
|
+
const actualPort = typeof address === 'object' && address !== null
|
|
228
|
+
? address.port
|
|
229
|
+
: listenPort;
|
|
230
|
+
this.options.port = actualPort;
|
|
231
|
+
resolve(actualPort);
|
|
232
|
+
});
|
|
233
|
+
this.server.on('error', (error) => {
|
|
234
|
+
reject(error);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
reject(error);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Start the server with a callback
|
|
244
|
+
*/
|
|
245
|
+
startWithCallback(callback, port) {
|
|
246
|
+
const listenPort = port ?? this.options.port;
|
|
247
|
+
this.server = this.app.listen(listenPort, () => {
|
|
248
|
+
// Get actual port (important when port 0 is used)
|
|
249
|
+
const address = this.server?.address();
|
|
250
|
+
const actualPort = typeof address === 'object' && address !== null
|
|
251
|
+
? address.port
|
|
252
|
+
: listenPort;
|
|
253
|
+
this.options.port = actualPort;
|
|
254
|
+
callback(actualPort);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Stop the server
|
|
259
|
+
*/
|
|
260
|
+
async stop() {
|
|
261
|
+
return new Promise((resolve, reject) => {
|
|
262
|
+
if (!this.server) {
|
|
263
|
+
resolve();
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
this.server.close((error) => {
|
|
267
|
+
if (error) {
|
|
268
|
+
reject(error);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
this.server = null;
|
|
272
|
+
resolve();
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Check if server is running
|
|
279
|
+
*/
|
|
280
|
+
isRunning() {
|
|
281
|
+
return this.server !== null && this.server.listening;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get the HTTP server instance
|
|
285
|
+
*/
|
|
286
|
+
getHttpServer() {
|
|
287
|
+
return this.server;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Reload configuration (for hot reload support)
|
|
291
|
+
*/
|
|
292
|
+
reloadConfig(endpoints, flows) {
|
|
293
|
+
// Reset state before reloading
|
|
294
|
+
this.stateManager.reset();
|
|
295
|
+
this.registerConfig(endpoints, flows);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
exports.MockServerApp = MockServerApp;
|
|
299
|
+
/**
|
|
300
|
+
* Create a new MockServerApp instance
|
|
301
|
+
*/
|
|
302
|
+
function createMockServerApp(options = {}) {
|
|
303
|
+
return new MockServerApp(options);
|
|
304
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-server-app.js","sourceRoot":"","sources":["../../src/server/mock-server-app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server configuration options
|
|
3
|
+
*/
|
|
4
|
+
export interface ServerOptions {
|
|
5
|
+
/** Port to run the server on (default: 3001) */
|
|
6
|
+
port: number;
|
|
7
|
+
/** API prefix for mock endpoints (default: '/api') */
|
|
8
|
+
apiPrefix: string;
|
|
9
|
+
/** Enable hot reload (default: true) */
|
|
10
|
+
hotReload: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Default server options
|
|
14
|
+
*/
|
|
15
|
+
export declare const DEFAULT_SERVER_OPTIONS: ServerOptions;
|
|
16
|
+
/**
|
|
17
|
+
* Server state
|
|
18
|
+
*/
|
|
19
|
+
export interface ServerState {
|
|
20
|
+
/** Whether the server is currently running */
|
|
21
|
+
running: boolean;
|
|
22
|
+
/** The port the server is listening on */
|
|
23
|
+
port: number | null;
|
|
24
|
+
/** Number of registered endpoints */
|
|
25
|
+
endpointCount: number;
|
|
26
|
+
/** Number of registered flows */
|
|
27
|
+
flowCount: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Route registration info
|
|
31
|
+
*/
|
|
32
|
+
export interface RegisteredRoute {
|
|
33
|
+
method: string;
|
|
34
|
+
path: string;
|
|
35
|
+
fullPath: string;
|
|
36
|
+
endpointId: string;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,aAIpC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,qCAAqC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":";;;AAgBA;;GAEG;AACU,QAAA,sBAAsB,GAAkB;IACnD,IAAI,EAAE,IAAI;IACV,SAAS,EAAE,MAAM;IACjB,SAAS,EAAE,IAAI;CAChB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-manager.test.d.ts","sourceRoot":"","sources":["../../../src/state/__tests__/state-manager.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const state_manager_js_1 = require("../state-manager.js");
|
|
5
|
+
// Test fixtures
|
|
6
|
+
const mockEndpoints = [
|
|
7
|
+
{
|
|
8
|
+
id: 'get-users',
|
|
9
|
+
path: '/users',
|
|
10
|
+
method: 'GET',
|
|
11
|
+
scenarios: [
|
|
12
|
+
{ id: 'success', name: 'Success', status: 200, body: [] },
|
|
13
|
+
{ id: 'error', name: 'Error', status: 500, body: { error: 'Failed' } },
|
|
14
|
+
{ id: 'empty', name: 'Empty', status: 200, body: [] },
|
|
15
|
+
],
|
|
16
|
+
defaultScenarioId: 'success',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 'get-products',
|
|
20
|
+
path: '/products',
|
|
21
|
+
method: 'GET',
|
|
22
|
+
scenarios: [
|
|
23
|
+
{ id: 'list', name: 'Product List', status: 200, body: [] },
|
|
24
|
+
{ id: 'not-found', name: 'Not Found', status: 404, body: null },
|
|
25
|
+
],
|
|
26
|
+
defaultScenarioId: 'list',
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
const mockFlows = [
|
|
30
|
+
{
|
|
31
|
+
id: 'admin-flow',
|
|
32
|
+
name: 'Admin Flow',
|
|
33
|
+
endpointScenarios: {
|
|
34
|
+
'get-users': 'success',
|
|
35
|
+
'get-products': 'list',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'error-flow',
|
|
40
|
+
name: 'Error Flow',
|
|
41
|
+
endpointScenarios: {
|
|
42
|
+
'get-users': 'error',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
(0, vitest_1.describe)('StateManager', () => {
|
|
47
|
+
let stateManager;
|
|
48
|
+
(0, vitest_1.beforeEach)(() => {
|
|
49
|
+
stateManager = new state_manager_js_1.StateManager();
|
|
50
|
+
stateManager.setEndpoints(mockEndpoints);
|
|
51
|
+
stateManager.setFlows(mockFlows);
|
|
52
|
+
});
|
|
53
|
+
(0, vitest_1.describe)('getState', () => {
|
|
54
|
+
(0, vitest_1.it)('should return initial state', () => {
|
|
55
|
+
const state = stateManager.getState();
|
|
56
|
+
(0, vitest_1.expect)(state.activeFlowId).toBeNull();
|
|
57
|
+
(0, vitest_1.expect)(state.activeScenarios).toEqual({});
|
|
58
|
+
});
|
|
59
|
+
(0, vitest_1.it)('should return state with active flow', () => {
|
|
60
|
+
stateManager.activateFlow('admin-flow');
|
|
61
|
+
const state = stateManager.getState();
|
|
62
|
+
(0, vitest_1.expect)(state.activeFlowId).toBe('admin-flow');
|
|
63
|
+
});
|
|
64
|
+
(0, vitest_1.it)('should return state with active scenarios', () => {
|
|
65
|
+
stateManager.activateScenario('get-users', 'error');
|
|
66
|
+
const state = stateManager.getState();
|
|
67
|
+
(0, vitest_1.expect)(state.activeScenarios).toEqual({ 'get-users': 'error' });
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
(0, vitest_1.describe)('activateScenario', () => {
|
|
71
|
+
(0, vitest_1.it)('should activate a valid scenario', () => {
|
|
72
|
+
stateManager.activateScenario('get-users', 'error');
|
|
73
|
+
(0, vitest_1.expect)(stateManager.getActiveScenarioId('get-users')).toBe('error');
|
|
74
|
+
});
|
|
75
|
+
(0, vitest_1.it)('should throw for non-existent endpoint', () => {
|
|
76
|
+
(0, vitest_1.expect)(() => {
|
|
77
|
+
stateManager.activateScenario('non-existent', 'success');
|
|
78
|
+
}).toThrow('Endpoint "non-existent" not found');
|
|
79
|
+
});
|
|
80
|
+
(0, vitest_1.it)('should throw for non-existent scenario', () => {
|
|
81
|
+
(0, vitest_1.expect)(() => {
|
|
82
|
+
stateManager.activateScenario('get-users', 'non-existent');
|
|
83
|
+
}).toThrow('Scenario "non-existent" not found');
|
|
84
|
+
});
|
|
85
|
+
(0, vitest_1.it)('should include available scenarios in error message', () => {
|
|
86
|
+
(0, vitest_1.expect)(() => {
|
|
87
|
+
stateManager.activateScenario('get-users', 'non-existent');
|
|
88
|
+
}).toThrow('Available: success, error, empty');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
(0, vitest_1.describe)('clearScenario', () => {
|
|
92
|
+
(0, vitest_1.it)('should clear an active scenario', () => {
|
|
93
|
+
stateManager.activateScenario('get-users', 'error');
|
|
94
|
+
stateManager.clearScenario('get-users');
|
|
95
|
+
(0, vitest_1.expect)(stateManager.getActiveScenarioId('get-users')).toBeUndefined();
|
|
96
|
+
});
|
|
97
|
+
(0, vitest_1.it)('should be safe to call on non-active scenario', () => {
|
|
98
|
+
(0, vitest_1.expect)(() => {
|
|
99
|
+
stateManager.clearScenario('get-users');
|
|
100
|
+
}).not.toThrow();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
(0, vitest_1.describe)('activateFlow', () => {
|
|
104
|
+
(0, vitest_1.it)('should activate a valid flow', () => {
|
|
105
|
+
stateManager.activateFlow('admin-flow');
|
|
106
|
+
(0, vitest_1.expect)(stateManager.getActiveFlowId()).toBe('admin-flow');
|
|
107
|
+
});
|
|
108
|
+
(0, vitest_1.it)('should return flow config', () => {
|
|
109
|
+
stateManager.activateFlow('admin-flow');
|
|
110
|
+
const flow = stateManager.getActiveFlow();
|
|
111
|
+
(0, vitest_1.expect)(flow?.id).toBe('admin-flow');
|
|
112
|
+
(0, vitest_1.expect)(flow?.name).toBe('Admin Flow');
|
|
113
|
+
});
|
|
114
|
+
(0, vitest_1.it)('should throw for non-existent flow', () => {
|
|
115
|
+
(0, vitest_1.expect)(() => {
|
|
116
|
+
stateManager.activateFlow('non-existent');
|
|
117
|
+
}).toThrow('Flow "non-existent" not found');
|
|
118
|
+
});
|
|
119
|
+
(0, vitest_1.it)('should replace previously active flow', () => {
|
|
120
|
+
stateManager.activateFlow('admin-flow');
|
|
121
|
+
stateManager.activateFlow('error-flow');
|
|
122
|
+
(0, vitest_1.expect)(stateManager.getActiveFlowId()).toBe('error-flow');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
(0, vitest_1.describe)('deactivateFlow', () => {
|
|
126
|
+
(0, vitest_1.it)('should deactivate the active flow', () => {
|
|
127
|
+
stateManager.activateFlow('admin-flow');
|
|
128
|
+
stateManager.deactivateFlow();
|
|
129
|
+
(0, vitest_1.expect)(stateManager.getActiveFlowId()).toBeNull();
|
|
130
|
+
(0, vitest_1.expect)(stateManager.getActiveFlow()).toBeNull();
|
|
131
|
+
});
|
|
132
|
+
(0, vitest_1.it)('should be safe to call when no flow is active', () => {
|
|
133
|
+
(0, vitest_1.expect)(() => {
|
|
134
|
+
stateManager.deactivateFlow();
|
|
135
|
+
}).not.toThrow();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
(0, vitest_1.describe)('resolveScenarioId', () => {
|
|
139
|
+
(0, vitest_1.it)('should return default scenario when nothing is active', () => {
|
|
140
|
+
const scenarioId = stateManager.resolveScenarioId('get-users');
|
|
141
|
+
(0, vitest_1.expect)(scenarioId).toBe('success');
|
|
142
|
+
});
|
|
143
|
+
(0, vitest_1.it)('should return explicit scenario over default', () => {
|
|
144
|
+
stateManager.activateScenario('get-users', 'error');
|
|
145
|
+
const scenarioId = stateManager.resolveScenarioId('get-users');
|
|
146
|
+
(0, vitest_1.expect)(scenarioId).toBe('error');
|
|
147
|
+
});
|
|
148
|
+
(0, vitest_1.it)('should return flow scenario over explicit and default', () => {
|
|
149
|
+
stateManager.activateScenario('get-users', 'empty');
|
|
150
|
+
stateManager.activateFlow('error-flow');
|
|
151
|
+
const scenarioId = stateManager.resolveScenarioId('get-users');
|
|
152
|
+
// Flow takes priority
|
|
153
|
+
(0, vitest_1.expect)(scenarioId).toBe('error');
|
|
154
|
+
});
|
|
155
|
+
(0, vitest_1.it)('should fall back to explicit when flow does not define endpoint', () => {
|
|
156
|
+
stateManager.activateScenario('get-products', 'not-found');
|
|
157
|
+
stateManager.activateFlow('error-flow'); // error-flow doesn't define get-products
|
|
158
|
+
const scenarioId = stateManager.resolveScenarioId('get-products');
|
|
159
|
+
(0, vitest_1.expect)(scenarioId).toBe('not-found');
|
|
160
|
+
});
|
|
161
|
+
(0, vitest_1.it)('should fall back to default when flow does not define endpoint and no explicit', () => {
|
|
162
|
+
stateManager.activateFlow('error-flow'); // error-flow doesn't define get-products
|
|
163
|
+
const scenarioId = stateManager.resolveScenarioId('get-products');
|
|
164
|
+
(0, vitest_1.expect)(scenarioId).toBe('list'); // default
|
|
165
|
+
});
|
|
166
|
+
(0, vitest_1.it)('should return null for non-existent endpoint', () => {
|
|
167
|
+
const scenarioId = stateManager.resolveScenarioId('non-existent');
|
|
168
|
+
(0, vitest_1.expect)(scenarioId).toBeNull();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
(0, vitest_1.describe)('resolveScenario', () => {
|
|
172
|
+
(0, vitest_1.it)('should return full scenario config', () => {
|
|
173
|
+
const scenario = stateManager.resolveScenario('get-users');
|
|
174
|
+
(0, vitest_1.expect)(scenario).toBeDefined();
|
|
175
|
+
(0, vitest_1.expect)(scenario?.id).toBe('success');
|
|
176
|
+
(0, vitest_1.expect)(scenario?.status).toBe(200);
|
|
177
|
+
});
|
|
178
|
+
(0, vitest_1.it)('should return null for non-existent endpoint', () => {
|
|
179
|
+
const scenario = stateManager.resolveScenario('non-existent');
|
|
180
|
+
(0, vitest_1.expect)(scenario).toBeNull();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
(0, vitest_1.describe)('reset', () => {
|
|
184
|
+
(0, vitest_1.it)('should clear active flow', () => {
|
|
185
|
+
stateManager.activateFlow('admin-flow');
|
|
186
|
+
stateManager.reset();
|
|
187
|
+
(0, vitest_1.expect)(stateManager.getActiveFlowId()).toBeNull();
|
|
188
|
+
});
|
|
189
|
+
(0, vitest_1.it)('should clear all active scenarios', () => {
|
|
190
|
+
stateManager.activateScenario('get-users', 'error');
|
|
191
|
+
stateManager.activateScenario('get-products', 'not-found');
|
|
192
|
+
stateManager.reset();
|
|
193
|
+
(0, vitest_1.expect)(stateManager.getActiveScenarioId('get-users')).toBeUndefined();
|
|
194
|
+
(0, vitest_1.expect)(stateManager.getActiveScenarioId('get-products')).toBeUndefined();
|
|
195
|
+
});
|
|
196
|
+
(0, vitest_1.it)('should reset to default scenarios', () => {
|
|
197
|
+
stateManager.activateScenario('get-users', 'error');
|
|
198
|
+
stateManager.activateFlow('admin-flow');
|
|
199
|
+
stateManager.reset();
|
|
200
|
+
(0, vitest_1.expect)(stateManager.resolveScenarioId('get-users')).toBe('success');
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
(0, vitest_1.describe)('state change listeners', () => {
|
|
204
|
+
(0, vitest_1.it)('should notify on scenario activation', () => {
|
|
205
|
+
const listener = vitest_1.vi.fn();
|
|
206
|
+
stateManager.addListener(listener);
|
|
207
|
+
stateManager.activateScenario('get-users', 'error');
|
|
208
|
+
(0, vitest_1.expect)(listener).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
|
|
209
|
+
type: 'scenario-activated',
|
|
210
|
+
details: { endpointId: 'get-users', scenarioId: 'error' },
|
|
211
|
+
}));
|
|
212
|
+
});
|
|
213
|
+
(0, vitest_1.it)('should notify on flow activation', () => {
|
|
214
|
+
const listener = vitest_1.vi.fn();
|
|
215
|
+
stateManager.addListener(listener);
|
|
216
|
+
stateManager.activateFlow('admin-flow');
|
|
217
|
+
(0, vitest_1.expect)(listener).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
|
|
218
|
+
type: 'flow-activated',
|
|
219
|
+
details: { flowId: 'admin-flow' },
|
|
220
|
+
}));
|
|
221
|
+
});
|
|
222
|
+
(0, vitest_1.it)('should notify on flow deactivation', () => {
|
|
223
|
+
const listener = vitest_1.vi.fn();
|
|
224
|
+
stateManager.activateFlow('admin-flow');
|
|
225
|
+
stateManager.addListener(listener);
|
|
226
|
+
stateManager.deactivateFlow();
|
|
227
|
+
(0, vitest_1.expect)(listener).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
|
|
228
|
+
type: 'flow-deactivated',
|
|
229
|
+
details: { flowId: 'admin-flow' },
|
|
230
|
+
}));
|
|
231
|
+
});
|
|
232
|
+
(0, vitest_1.it)('should notify on reset', () => {
|
|
233
|
+
const listener = vitest_1.vi.fn();
|
|
234
|
+
stateManager.addListener(listener);
|
|
235
|
+
stateManager.reset();
|
|
236
|
+
(0, vitest_1.expect)(listener).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
|
|
237
|
+
type: 'reset',
|
|
238
|
+
}));
|
|
239
|
+
});
|
|
240
|
+
(0, vitest_1.it)('should remove listener', () => {
|
|
241
|
+
const listener = vitest_1.vi.fn();
|
|
242
|
+
stateManager.addListener(listener);
|
|
243
|
+
stateManager.removeListener(listener);
|
|
244
|
+
stateManager.activateFlow('admin-flow');
|
|
245
|
+
(0, vitest_1.expect)(listener).not.toHaveBeenCalled();
|
|
246
|
+
});
|
|
247
|
+
(0, vitest_1.it)('should include timestamp in events', () => {
|
|
248
|
+
const listener = vitest_1.vi.fn();
|
|
249
|
+
stateManager.addListener(listener);
|
|
250
|
+
const before = Date.now();
|
|
251
|
+
stateManager.reset();
|
|
252
|
+
const after = Date.now();
|
|
253
|
+
const event = listener.mock.calls[0][0];
|
|
254
|
+
(0, vitest_1.expect)(event.timestamp).toBeGreaterThanOrEqual(before);
|
|
255
|
+
(0, vitest_1.expect)(event.timestamp).toBeLessThanOrEqual(after);
|
|
256
|
+
});
|
|
257
|
+
(0, vitest_1.it)('should continue notifying other listeners if one throws', () => {
|
|
258
|
+
const badListener = vitest_1.vi.fn(() => {
|
|
259
|
+
throw new Error('Listener error');
|
|
260
|
+
});
|
|
261
|
+
const goodListener = vitest_1.vi.fn();
|
|
262
|
+
stateManager.addListener(badListener);
|
|
263
|
+
stateManager.addListener(goodListener);
|
|
264
|
+
// Should not throw
|
|
265
|
+
(0, vitest_1.expect)(() => stateManager.reset()).not.toThrow();
|
|
266
|
+
(0, vitest_1.expect)(goodListener).toHaveBeenCalled();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
(0, vitest_1.describe)('createStateManager', () => {
|
|
271
|
+
(0, vitest_1.it)('should create a StateManager instance', () => {
|
|
272
|
+
const manager = (0, state_manager_js_1.createStateManager)();
|
|
273
|
+
(0, vitest_1.expect)(manager).toBeInstanceOf(state_manager_js_1.StateManager);
|
|
274
|
+
});
|
|
275
|
+
});
|