@forgehive/task 0.1.6 → 0.1.8
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/dist/index.d.ts +39 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +145 -77
- package/dist/index.js.map +1 -1
- package/dist/test/safe-replay-complex-boundary.test.d.ts +2 -0
- package/dist/test/safe-replay-complex-boundary.test.d.ts.map +1 -0
- package/dist/test/safe-replay-complex-boundary.test.js +314 -0
- package/dist/test/safe-replay-complex-boundary.test.js.map +1 -0
- package/dist/test/safe-replay.test.d.ts +2 -0
- package/dist/test/safe-replay.test.d.ts.map +1 -0
- package/dist/test/safe-replay.test.js +159 -0
- package/dist/test/safe-replay.test.js.map +1 -0
- package/dist/test/safe-run.test.js +55 -29
- package/dist/test/safe-run.test.js.map +1 -1
- package/dist/test/task-with-boundaries.test.js.map +1 -1
- package/dist/utils/boundary.d.ts +24 -5
- package/dist/utils/boundary.d.ts.map +1 -1
- package/dist/utils/boundary.js +16 -10
- package/dist/utils/boundary.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +258 -96
- package/src/test/safe-replay-complex-boundary.test.ts +385 -0
- package/src/test/safe-replay.test.ts +189 -0
- package/src/test/safe-run.test.ts +58 -31
- package/src/test/task-with-boundaries.test.ts +2 -2
- package/src/utils/boundary.ts +44 -17
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const schema_1 = require("@forgehive/schema");
|
|
4
|
+
const index_1 = require("../index");
|
|
5
|
+
describe('Complex boundary replay tests', () => {
|
|
6
|
+
// Test data
|
|
7
|
+
let priceData;
|
|
8
|
+
let portfolioData;
|
|
9
|
+
// Boundaries for the portfolio task
|
|
10
|
+
let boundaries;
|
|
11
|
+
// The task - using eslint-disable to allow any type for this test
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
let calculatePortfolioValue;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
// Setup mock data
|
|
16
|
+
priceData = {
|
|
17
|
+
'AAPL': 182.63,
|
|
18
|
+
'MSFT': 421.90,
|
|
19
|
+
'GOOGL': 171.04,
|
|
20
|
+
'AMZN': 184.72
|
|
21
|
+
};
|
|
22
|
+
portfolioData = {
|
|
23
|
+
'user1': {
|
|
24
|
+
id: 'portfolio1',
|
|
25
|
+
userId: 'user1',
|
|
26
|
+
stocks: [
|
|
27
|
+
{ ticker: 'AAPL', quantity: 10 },
|
|
28
|
+
{ ticker: 'MSFT', quantity: 5 },
|
|
29
|
+
{ ticker: 'GOOGL', quantity: 8 }
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
'user2': {
|
|
33
|
+
id: 'portfolio2',
|
|
34
|
+
userId: 'user2',
|
|
35
|
+
stocks: [
|
|
36
|
+
{ ticker: 'AMZN', quantity: 15 },
|
|
37
|
+
{ ticker: 'MSFT', quantity: 3 }
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
// Define boundaries with realistic functions
|
|
42
|
+
boundaries = {
|
|
43
|
+
fetchPrice: async (ticker) => {
|
|
44
|
+
// Check if we have a price for this ticker
|
|
45
|
+
if (!priceData[ticker]) {
|
|
46
|
+
throw new Error(`Price data not available for ticker: ${ticker}`);
|
|
47
|
+
}
|
|
48
|
+
return priceData[ticker];
|
|
49
|
+
},
|
|
50
|
+
fetchPortfolio: async (userId) => {
|
|
51
|
+
// Check if we have a portfolio for this user
|
|
52
|
+
if (!portfolioData[userId]) {
|
|
53
|
+
throw new Error(`Portfolio not found for user: ${userId}`);
|
|
54
|
+
}
|
|
55
|
+
return portfolioData[userId];
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
// Create a schema for the task
|
|
59
|
+
const schema = new schema_1.Schema({
|
|
60
|
+
userId: schema_1.Schema.string()
|
|
61
|
+
});
|
|
62
|
+
// Create the portfolio value calculation task
|
|
63
|
+
calculatePortfolioValue = (0, index_1.createTask)(schema, boundaries, async ({ userId }, { fetchPortfolio, fetchPrice }) => {
|
|
64
|
+
// First fetch the portfolio for the user
|
|
65
|
+
const portfolio = await fetchPortfolio(userId);
|
|
66
|
+
// Then calculate the value of each stock and the total portfolio value
|
|
67
|
+
const stocksWithValue = await Promise.all(portfolio.stocks.map(async (stock) => {
|
|
68
|
+
const price = await fetchPrice(stock.ticker);
|
|
69
|
+
const value = price * stock.quantity;
|
|
70
|
+
return {
|
|
71
|
+
ticker: stock.ticker,
|
|
72
|
+
quantity: stock.quantity,
|
|
73
|
+
price,
|
|
74
|
+
value
|
|
75
|
+
};
|
|
76
|
+
}));
|
|
77
|
+
// Calculate total value
|
|
78
|
+
const totalValue = stocksWithValue.reduce((sum, stock) => sum + stock.value, 0);
|
|
79
|
+
// Return portfolio value information
|
|
80
|
+
return {
|
|
81
|
+
id: portfolio.id,
|
|
82
|
+
userId: portfolio.userId,
|
|
83
|
+
totalValue,
|
|
84
|
+
stocks: stocksWithValue
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
it('Should calculate portfolio value using multiple boundaries', async () => {
|
|
89
|
+
// Run the task for user1
|
|
90
|
+
const result = await calculatePortfolioValue.run({ userId: 'user1' });
|
|
91
|
+
// Verify the structure of the result
|
|
92
|
+
expect(result).toHaveProperty('id', 'portfolio1');
|
|
93
|
+
expect(result).toHaveProperty('userId', 'user1');
|
|
94
|
+
expect(result).toHaveProperty('totalValue');
|
|
95
|
+
expect(result).toHaveProperty('stocks');
|
|
96
|
+
// Verify portfolio calculations
|
|
97
|
+
expect(result.stocks).toHaveLength(3);
|
|
98
|
+
// Calculate expected values
|
|
99
|
+
const expectedTotalValue = (priceData['AAPL'] * 10) +
|
|
100
|
+
(priceData['MSFT'] * 5) +
|
|
101
|
+
(priceData['GOOGL'] * 8);
|
|
102
|
+
// Verify total value matches expected
|
|
103
|
+
expect(result.totalValue).toBeCloseTo(expectedTotalValue);
|
|
104
|
+
// Verify each stock's value
|
|
105
|
+
const applStock = result.stocks.find((s) => s.ticker === 'AAPL');
|
|
106
|
+
expect(applStock).toBeDefined();
|
|
107
|
+
expect(applStock === null || applStock === void 0 ? void 0 : applStock.price).toBe(priceData['AAPL']);
|
|
108
|
+
expect(applStock === null || applStock === void 0 ? void 0 : applStock.value).toBeCloseTo(priceData['AAPL'] * 10);
|
|
109
|
+
const msftStock = result.stocks.find((s) => s.ticker === 'MSFT');
|
|
110
|
+
expect(msftStock).toBeDefined();
|
|
111
|
+
expect(msftStock === null || msftStock === void 0 ? void 0 : msftStock.price).toBe(priceData['MSFT']);
|
|
112
|
+
expect(msftStock === null || msftStock === void 0 ? void 0 : msftStock.value).toBeCloseTo(priceData['MSFT'] * 5);
|
|
113
|
+
const googlStock = result.stocks.find((s) => s.ticker === 'GOOGL');
|
|
114
|
+
expect(googlStock).toBeDefined();
|
|
115
|
+
expect(googlStock === null || googlStock === void 0 ? void 0 : googlStock.price).toBe(priceData['GOOGL']);
|
|
116
|
+
expect(googlStock === null || googlStock === void 0 ? void 0 : googlStock.value).toBeCloseTo(priceData['GOOGL'] * 8);
|
|
117
|
+
});
|
|
118
|
+
it('Should replay portfolio calculation from an execution log', async () => {
|
|
119
|
+
// First create a portfolio value calculation execution log
|
|
120
|
+
const executionLog = {
|
|
121
|
+
input: { userId: 'user1' },
|
|
122
|
+
output: {
|
|
123
|
+
id: 'portfolio1',
|
|
124
|
+
userId: 'user1',
|
|
125
|
+
totalValue: 4363.02,
|
|
126
|
+
stocks: [
|
|
127
|
+
{ ticker: 'AAPL', quantity: 10, price: 190.50, value: 1905.00 },
|
|
128
|
+
{ ticker: 'MSFT', quantity: 5, price: 405.75, value: 2028.75 },
|
|
129
|
+
{ ticker: 'GOOGL', quantity: 8, price: 161.16, value: 429.27 }
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
boundaries: {
|
|
133
|
+
fetchPortfolio: [
|
|
134
|
+
{
|
|
135
|
+
input: ['user1'],
|
|
136
|
+
output: {
|
|
137
|
+
id: 'portfolio1',
|
|
138
|
+
userId: 'user1',
|
|
139
|
+
stocks: [
|
|
140
|
+
{ ticker: 'AAPL', quantity: 10 },
|
|
141
|
+
{ ticker: 'MSFT', quantity: 5 },
|
|
142
|
+
{ ticker: 'GOOGL', quantity: 8 }
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
fetchPrice: [
|
|
148
|
+
{
|
|
149
|
+
input: ['AAPL'],
|
|
150
|
+
output: 190.50
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
input: ['MSFT'],
|
|
154
|
+
output: 405.75
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
input: ['GOOGL'],
|
|
158
|
+
output: 161.16
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
// Use replay mode for all boundaries
|
|
164
|
+
const [replayResult, replayError, replayLog] = await calculatePortfolioValue.safeReplay(executionLog, {
|
|
165
|
+
boundaries: {
|
|
166
|
+
fetchPortfolio: 'replay',
|
|
167
|
+
fetchPrice: 'replay'
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
// Verify there was no error
|
|
171
|
+
expect(replayError).toBeNull();
|
|
172
|
+
// Verify the replayed result
|
|
173
|
+
expect(replayResult).toHaveProperty('id', 'portfolio1');
|
|
174
|
+
expect(replayResult).toHaveProperty('userId', 'user1');
|
|
175
|
+
expect(replayResult).toHaveProperty('totalValue', 5223.03);
|
|
176
|
+
expect(replayResult).toHaveProperty('stocks');
|
|
177
|
+
expect(replayResult.stocks).toHaveLength(3);
|
|
178
|
+
// Check values match the execution log exactly
|
|
179
|
+
const applStock = replayResult.stocks.find((s) => s.ticker === 'AAPL');
|
|
180
|
+
expect(applStock === null || applStock === void 0 ? void 0 : applStock.price).toBe(190.50);
|
|
181
|
+
expect(applStock === null || applStock === void 0 ? void 0 : applStock.value).toBe(1905.00);
|
|
182
|
+
// Verify that boundary calls were properly replayed
|
|
183
|
+
expect(replayLog.boundaries.fetchPortfolio).toHaveLength(1);
|
|
184
|
+
expect(replayLog.boundaries.fetchPrice).toHaveLength(3);
|
|
185
|
+
});
|
|
186
|
+
it('Should handle errors during replay', async () => {
|
|
187
|
+
// Create an execution log with an error in one of the price fetches
|
|
188
|
+
const executionLog = {
|
|
189
|
+
input: { userId: 'user1' },
|
|
190
|
+
error: 'Price data not available for ticker: GOOGL',
|
|
191
|
+
boundaries: {
|
|
192
|
+
fetchPortfolio: [
|
|
193
|
+
{
|
|
194
|
+
input: ['user1'],
|
|
195
|
+
output: {
|
|
196
|
+
id: 'portfolio1',
|
|
197
|
+
userId: 'user1',
|
|
198
|
+
stocks: [
|
|
199
|
+
{ ticker: 'AAPL', quantity: 10 },
|
|
200
|
+
{ ticker: 'MSFT', quantity: 5 },
|
|
201
|
+
{ ticker: 'GOOGL', quantity: 8 }
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
],
|
|
206
|
+
fetchPrice: [
|
|
207
|
+
{
|
|
208
|
+
input: ['AAPL'],
|
|
209
|
+
output: 190.50
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
input: ['MSFT'],
|
|
213
|
+
output: 405.75
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
input: ['GOOGL'],
|
|
217
|
+
error: 'Price data not available for ticker: GOOGL'
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
// Use replay mode for all boundaries
|
|
223
|
+
const [replayResult, replayError, replayLog] = await calculatePortfolioValue.safeReplay(executionLog, {
|
|
224
|
+
boundaries: {
|
|
225
|
+
fetchPortfolio: 'replay',
|
|
226
|
+
fetchPrice: 'replay'
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
// Verify error was properly replayed
|
|
230
|
+
expect(replayResult).toBeNull();
|
|
231
|
+
expect(replayError).not.toBeNull();
|
|
232
|
+
expect(replayError === null || replayError === void 0 ? void 0 : replayError.message).toBe('Price data not available for ticker: GOOGL');
|
|
233
|
+
// Verify that boundary calls were properly replayed up to the error
|
|
234
|
+
expect(replayLog.boundaries.fetchPortfolio).toHaveLength(1);
|
|
235
|
+
expect(replayLog.boundaries.fetchPrice).toHaveLength(3);
|
|
236
|
+
// Check the error in the boundary
|
|
237
|
+
const googPriceCall = replayLog.boundaries.fetchPrice.find((call) => call.input && call.input[0] === 'GOOGL');
|
|
238
|
+
expect(googPriceCall).toBeDefined();
|
|
239
|
+
expect(googPriceCall === null || googPriceCall === void 0 ? void 0 : googPriceCall.error).toBe('Price data not available for ticker: GOOGL');
|
|
240
|
+
});
|
|
241
|
+
it('Should support mixed replay with some boundaries in replay mode and others in proxy mode', async () => {
|
|
242
|
+
// Update prices for the test
|
|
243
|
+
priceData['AAPL'] = 195.00; // Different from replay data
|
|
244
|
+
// Create an execution log with historical data
|
|
245
|
+
const executionLog = {
|
|
246
|
+
input: { userId: 'user1' },
|
|
247
|
+
output: {
|
|
248
|
+
id: 'portfolio1',
|
|
249
|
+
userId: 'user1',
|
|
250
|
+
totalValue: 4363.02,
|
|
251
|
+
stocks: [
|
|
252
|
+
{ ticker: 'AAPL', quantity: 10, price: 190.50, value: 1905.00 },
|
|
253
|
+
{ ticker: 'MSFT', quantity: 5, price: 405.75, value: 2028.75 },
|
|
254
|
+
{ ticker: 'GOOGL', quantity: 8, price: 161.16, value: 429.27 }
|
|
255
|
+
]
|
|
256
|
+
},
|
|
257
|
+
boundaries: {
|
|
258
|
+
fetchPortfolio: [
|
|
259
|
+
{
|
|
260
|
+
input: ['user1'],
|
|
261
|
+
output: {
|
|
262
|
+
id: 'portfolio1',
|
|
263
|
+
userId: 'user1',
|
|
264
|
+
stocks: [
|
|
265
|
+
{ ticker: 'AAPL', quantity: 10 },
|
|
266
|
+
{ ticker: 'MSFT', quantity: 5 },
|
|
267
|
+
{ ticker: 'GOOGL', quantity: 8 }
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
],
|
|
272
|
+
fetchPrice: [
|
|
273
|
+
{
|
|
274
|
+
input: ['AAPL'],
|
|
275
|
+
output: 190.50
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
input: ['MSFT'],
|
|
279
|
+
output: 405.75
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
input: ['GOOGL'],
|
|
283
|
+
output: 161.16
|
|
284
|
+
}
|
|
285
|
+
]
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
// Use replay mode for portfolio but proxy mode for prices
|
|
289
|
+
const [replayResult, replayError, replayLog] = await calculatePortfolioValue.safeReplay(executionLog, {
|
|
290
|
+
boundaries: {
|
|
291
|
+
fetchPortfolio: 'replay', // Use replay for portfolio
|
|
292
|
+
fetchPrice: 'proxy' // Use proxy (real execution) for prices
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
// Verify there was no error
|
|
296
|
+
expect(replayError).toBeNull();
|
|
297
|
+
// The result should reflect the updated AAPL price of 195.00
|
|
298
|
+
expect(replayResult).toHaveProperty('id', 'portfolio1');
|
|
299
|
+
expect(replayResult).toHaveProperty('userId', 'user1');
|
|
300
|
+
// Calculate expected value with current prices
|
|
301
|
+
const expectedValue = (priceData['AAPL'] * 10) + // AAPL: 195.00 * 10
|
|
302
|
+
(priceData['MSFT'] * 5) + // MSFT: 421.90 * 5
|
|
303
|
+
(priceData['GOOGL'] * 8); // GOOGL: 171.04 * 8
|
|
304
|
+
expect(replayResult.totalValue).toBeCloseTo(expectedValue);
|
|
305
|
+
// Check AAPL price reflects the current price, not the replay data
|
|
306
|
+
const applStock = replayResult.stocks.find((s) => s.ticker === 'AAPL');
|
|
307
|
+
expect(applStock === null || applStock === void 0 ? void 0 : applStock.price).toBe(195.00);
|
|
308
|
+
expect(applStock === null || applStock === void 0 ? void 0 : applStock.value).toBeCloseTo(195.00 * 10);
|
|
309
|
+
// Verify the log shows the mixed sources correctly
|
|
310
|
+
expect(replayLog.boundaries.fetchPortfolio).toHaveLength(1);
|
|
311
|
+
expect(replayLog.boundaries.fetchPrice).toHaveLength(3);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
//# sourceMappingURL=safe-replay-complex-boundary.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-replay-complex-boundary.test.js","sourceRoot":"","sources":["../../src/test/safe-replay-complex-boundary.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-replay.test.d.ts","sourceRoot":"","sources":["../../src/test/safe-replay.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const schema_1 = require("@forgehive/schema");
|
|
4
|
+
const index_1 = require("../index");
|
|
5
|
+
describe('safeReplay functionality tests', () => {
|
|
6
|
+
// Common variables
|
|
7
|
+
let prices;
|
|
8
|
+
let boundaries;
|
|
9
|
+
// ToDo: Add correct type for schema and getTickerPrice
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
let schema;
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
let getTickerPrice; // Using any temporarily until we implement safeReplay
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
// Create a schema for the task
|
|
16
|
+
schema = new schema_1.Schema({
|
|
17
|
+
ticker: schema_1.Schema.string()
|
|
18
|
+
});
|
|
19
|
+
// Mock price data
|
|
20
|
+
prices = {
|
|
21
|
+
'AAPL': 150.23
|
|
22
|
+
};
|
|
23
|
+
// Define the boundaries
|
|
24
|
+
boundaries = {
|
|
25
|
+
fetchData: async (ticker) => {
|
|
26
|
+
// check if the ticker is in the prices object
|
|
27
|
+
if (!prices[ticker]) {
|
|
28
|
+
throw new Error(`Ticker ${ticker} not found in prices`);
|
|
29
|
+
}
|
|
30
|
+
return prices[ticker];
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
// Create the task using createTask
|
|
34
|
+
getTickerPrice = (0, index_1.createTask)(schema, boundaries, async ({ ticker }, { fetchData }) => {
|
|
35
|
+
const price = await fetchData(ticker);
|
|
36
|
+
return {
|
|
37
|
+
ticker,
|
|
38
|
+
price
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
it('Should replay a previous execution using the execution log and replay the fetchData boundary', async () => {
|
|
43
|
+
// Create a manual execution log
|
|
44
|
+
const executionLog = {
|
|
45
|
+
input: { ticker: 'AAPL' },
|
|
46
|
+
output: {
|
|
47
|
+
ticker: 'AAPL',
|
|
48
|
+
price: 160.23
|
|
49
|
+
},
|
|
50
|
+
boundaries: {
|
|
51
|
+
fetchData: [
|
|
52
|
+
{
|
|
53
|
+
input: ['AAPL'],
|
|
54
|
+
output: 160.23
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
// No safeReplay method yet, this will be implemented later
|
|
60
|
+
// This will be our test for that functionality
|
|
61
|
+
const [replayResult, replayError, replayLog] = await getTickerPrice.safeReplay(executionLog);
|
|
62
|
+
// Verify the replay execution
|
|
63
|
+
expect(replayError).toBeNull();
|
|
64
|
+
expect(replayResult).toMatchObject({
|
|
65
|
+
ticker: 'AAPL',
|
|
66
|
+
price: 150.23
|
|
67
|
+
});
|
|
68
|
+
expect(replayLog).toMatchObject({
|
|
69
|
+
input: { ticker: 'AAPL' },
|
|
70
|
+
output: {
|
|
71
|
+
ticker: 'AAPL',
|
|
72
|
+
price: 150.23
|
|
73
|
+
},
|
|
74
|
+
boundaries: {
|
|
75
|
+
fetchData: [
|
|
76
|
+
{
|
|
77
|
+
input: ['AAPL'],
|
|
78
|
+
output: 150.23,
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
it('Should execute with mixed boundaries modes', async () => {
|
|
85
|
+
// Create a manual execution log for testing
|
|
86
|
+
const executionLog = {
|
|
87
|
+
input: { ticker: 'AAPL' },
|
|
88
|
+
output: {
|
|
89
|
+
ticker: 'AAPL',
|
|
90
|
+
price: 160.23
|
|
91
|
+
},
|
|
92
|
+
boundaries: {
|
|
93
|
+
fetchData: [
|
|
94
|
+
{
|
|
95
|
+
input: ['AAPL'],
|
|
96
|
+
output: 160.23
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
// Use mixed mode - replay for fetchData but execute logAccess
|
|
102
|
+
const [replayResult, replayError, replayLog] = await getTickerPrice.safeReplay(executionLog, {
|
|
103
|
+
boundaries: {
|
|
104
|
+
fetchData: 'replay',
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// Verify the replay execution
|
|
108
|
+
expect(replayError).toBeNull();
|
|
109
|
+
expect(replayResult).toMatchObject({
|
|
110
|
+
ticker: 'AAPL',
|
|
111
|
+
price: 160.23
|
|
112
|
+
});
|
|
113
|
+
expect(replayLog).toMatchObject({
|
|
114
|
+
input: { ticker: 'AAPL' },
|
|
115
|
+
output: {
|
|
116
|
+
ticker: 'AAPL',
|
|
117
|
+
price: 160.23
|
|
118
|
+
},
|
|
119
|
+
boundaries: {
|
|
120
|
+
fetchData: [
|
|
121
|
+
{
|
|
122
|
+
input: ['AAPL'],
|
|
123
|
+
output: 160.23
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
it('Should properly handle errors in boundary replay mode', async () => {
|
|
130
|
+
// Create a manual execution log with an error in the boundary
|
|
131
|
+
const executionLog = {
|
|
132
|
+
input: { ticker: 'AAPL' },
|
|
133
|
+
output: null,
|
|
134
|
+
error: 'API error: Rate limit exceeded',
|
|
135
|
+
boundaries: {
|
|
136
|
+
fetchData: [
|
|
137
|
+
{
|
|
138
|
+
input: ['AAPL'],
|
|
139
|
+
error: 'API error: Rate limit exceeded'
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
// Use replay mode for fetchData
|
|
145
|
+
const [replayResult, replayError, replayLog] = await getTickerPrice.safeReplay(executionLog, {
|
|
146
|
+
boundaries: {
|
|
147
|
+
fetchData: 'replay',
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
// Verify the replay execution - should have an error
|
|
151
|
+
expect(replayResult).toBeNull();
|
|
152
|
+
expect(replayError).not.toBeNull();
|
|
153
|
+
expect(replayError === null || replayError === void 0 ? void 0 : replayError.message).toBe('API error: Rate limit exceeded');
|
|
154
|
+
// The log should contain the error from the boundary
|
|
155
|
+
expect(replayLog.error).toBeDefined();
|
|
156
|
+
expect(replayLog.boundaries.fetchData[0].error).toBe('API error: Rate limit exceeded');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
//# sourceMappingURL=safe-replay.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-replay.test.js","sourceRoot":"","sources":["../../src/test/safe-replay.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const index_1 = require("../index");
|
|
4
4
|
describe('Task safeRun tests', () => {
|
|
5
|
-
it('returns [
|
|
5
|
+
it('returns [result, null, record] on successful execution', async () => {
|
|
6
6
|
// Create a simple schema
|
|
7
7
|
const schema = new index_1.Schema({
|
|
8
8
|
value: index_1.Schema.number()
|
|
@@ -19,14 +19,20 @@ describe('Task safeRun tests', () => {
|
|
|
19
19
|
return { result, success: true };
|
|
20
20
|
});
|
|
21
21
|
// Call safeRun with valid input
|
|
22
|
-
const [
|
|
22
|
+
const [result, error, record] = await successTask.safeRun({ value: 5 });
|
|
23
23
|
// Verify success case
|
|
24
24
|
expect(error).toBeNull();
|
|
25
25
|
expect(result).toEqual({ result: 10, success: true });
|
|
26
|
-
expect(
|
|
27
|
-
expect(
|
|
26
|
+
expect(record).not.toBeNull();
|
|
27
|
+
expect(record).toHaveProperty('boundaries.fetchData');
|
|
28
|
+
expect(record.boundaries.fetchData).toHaveLength(1);
|
|
29
|
+
// useful to check types on record
|
|
30
|
+
const data = record.boundaries.fetchData[0];
|
|
31
|
+
expect(data.input).toEqual([5]);
|
|
32
|
+
expect(data.output).toEqual(10);
|
|
33
|
+
expect(data.error).toBeUndefined();
|
|
28
34
|
});
|
|
29
|
-
it('returns [
|
|
35
|
+
it('returns [null, error, record] on failed execution', async () => {
|
|
30
36
|
// Create a simple schema
|
|
31
37
|
const schema = new index_1.Schema({
|
|
32
38
|
value: index_1.Schema.number()
|
|
@@ -46,15 +52,23 @@ describe('Task safeRun tests', () => {
|
|
|
46
52
|
return { result, success: true };
|
|
47
53
|
});
|
|
48
54
|
// Call safeRun with problematic input that will cause an error
|
|
49
|
-
const [
|
|
55
|
+
const [result, error, record] = await errorTask.safeRun({ value: -5 });
|
|
50
56
|
// Verify error case
|
|
51
|
-
expect(error).
|
|
52
|
-
expect(error
|
|
57
|
+
expect(error).not.toBeNull();
|
|
58
|
+
expect(error instanceof Error).toBe(true);
|
|
59
|
+
if (error instanceof Error) {
|
|
60
|
+
expect(error.message).toContain('Value cannot be negative');
|
|
61
|
+
}
|
|
53
62
|
expect(result).toBeNull();
|
|
54
|
-
expect(
|
|
55
|
-
expect(
|
|
63
|
+
expect(record).not.toBeNull();
|
|
64
|
+
expect(record).toHaveProperty('boundaries.fetchData');
|
|
65
|
+
expect(record.boundaries.fetchData).toHaveLength(1);
|
|
66
|
+
const data = record.boundaries.fetchData[0];
|
|
67
|
+
expect(data.input).toEqual([-5]);
|
|
68
|
+
expect(data.error).toContain('Value cannot be negative');
|
|
69
|
+
expect(data.output).toBeUndefined();
|
|
56
70
|
});
|
|
57
|
-
it('returns [
|
|
71
|
+
it('returns [null, error, record] on schema validation failure', async () => {
|
|
58
72
|
// Create a schema that requires a positive number
|
|
59
73
|
const schema = new index_1.Schema({
|
|
60
74
|
value: index_1.Schema.number().min(1, 'Value must be positive')
|
|
@@ -71,12 +85,20 @@ describe('Task safeRun tests', () => {
|
|
|
71
85
|
return { result, success: true };
|
|
72
86
|
});
|
|
73
87
|
// Call safeRun with invalid input that will fail schema validation
|
|
74
|
-
const [
|
|
88
|
+
const [result, error, record] = await validationTask.safeRun({ value: 0 });
|
|
75
89
|
// Verify validation error case
|
|
76
90
|
expect(error).toBeInstanceOf(Error);
|
|
77
|
-
expect(error
|
|
91
|
+
expect(error instanceof Error).toBe(true);
|
|
92
|
+
if (error instanceof Error) {
|
|
93
|
+
expect(error.message).toContain('Value must be positive');
|
|
94
|
+
}
|
|
78
95
|
expect(result).toBeNull();
|
|
79
|
-
expect(
|
|
96
|
+
expect(record).not.toBeNull();
|
|
97
|
+
expect(record.input).toEqual({ value: 0 });
|
|
98
|
+
expect(record.error).toContain('Value must be positive');
|
|
99
|
+
expect(record.boundaries).toEqual({
|
|
100
|
+
fetchData: []
|
|
101
|
+
});
|
|
80
102
|
});
|
|
81
103
|
it('properly calls the listener with safeRun and run', async () => {
|
|
82
104
|
// Create a schema
|
|
@@ -106,12 +128,18 @@ describe('Task safeRun tests', () => {
|
|
|
106
128
|
// First call should be for safeRun with value 10
|
|
107
129
|
expect(originalListener).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
108
130
|
input: { value: 10 },
|
|
109
|
-
output: 20
|
|
131
|
+
output: 20,
|
|
132
|
+
boundaries: {
|
|
133
|
+
fetchData: expect.any(Array)
|
|
134
|
+
}
|
|
110
135
|
}));
|
|
111
136
|
// Second call should be for run with value 20
|
|
112
137
|
expect(originalListener).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
|
113
138
|
input: { value: 20 },
|
|
114
|
-
output: 40
|
|
139
|
+
output: 40,
|
|
140
|
+
boundaries: {
|
|
141
|
+
fetchData: expect.any(Array)
|
|
142
|
+
}
|
|
115
143
|
}));
|
|
116
144
|
});
|
|
117
145
|
it('handles multiple boundary calls correctly', async () => {
|
|
@@ -135,25 +163,23 @@ describe('Task safeRun tests', () => {
|
|
|
135
163
|
return { doubled, total };
|
|
136
164
|
});
|
|
137
165
|
// Call safeRun
|
|
138
|
-
const [
|
|
166
|
+
const [result, error, record] = await multiBoundaryTask.safeRun({ values: [1, 2, 3] });
|
|
139
167
|
// Verify success
|
|
140
168
|
expect(error).toBeNull();
|
|
141
169
|
expect(result).toEqual({
|
|
142
170
|
doubled: [2, 4, 6],
|
|
143
171
|
total: 12
|
|
144
172
|
});
|
|
145
|
-
// Verify
|
|
146
|
-
expect(
|
|
147
|
-
expect(
|
|
148
|
-
expect(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
expect(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
expect(
|
|
155
|
-
// @ts-expect-error - we know the boundaryLogs is not null here
|
|
156
|
-
expect(boundaryLogs.sumValues[0].input).toEqual([[2, 4, 6]]);
|
|
173
|
+
// Verify record structure
|
|
174
|
+
expect(record).not.toBeNull();
|
|
175
|
+
expect(record).toHaveProperty('boundaries.doubleValue');
|
|
176
|
+
expect(record).toHaveProperty('boundaries.sumValues');
|
|
177
|
+
expect(record.boundaries.doubleValue).toHaveLength(3);
|
|
178
|
+
expect(record.boundaries.sumValues).toHaveLength(1);
|
|
179
|
+
expect(record.boundaries.doubleValue[0]).toEqual({ input: [1], output: 2 });
|
|
180
|
+
expect(record.boundaries.doubleValue[1]).toEqual({ input: [2], output: 4 });
|
|
181
|
+
expect(record.boundaries.doubleValue[2]).toEqual({ input: [3], output: 6 });
|
|
182
|
+
expect(record.boundaries.sumValues[0]).toEqual({ input: [[2, 4, 6]], output: 12 });
|
|
157
183
|
});
|
|
158
184
|
});
|
|
159
185
|
//# sourceMappingURL=safe-run.test.js.map
|