@stacks/rendezvous 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/app.js CHANGED
@@ -13,7 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  return (mod && mod.__esModule) ? mod : { "default": mod };
14
14
  };
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.tryParseRemoteDataSettings = exports.getManifestFileName = exports.invalidRemoteDataWarningMessage = exports.noRemoteData = void 0;
16
+ exports.tryParseRemoteDataSettings = exports.getManifestFileName = exports.noRemoteData = void 0;
17
17
  exports.main = main;
18
18
  const path_1 = require("path");
19
19
  const events_1 = require("events");
@@ -35,16 +35,7 @@ const logger = (log, logLevel = "log") => {
35
35
  */
36
36
  exports.noRemoteData = {
37
37
  enabled: false,
38
- api_url: "",
39
- initial_height: 1,
40
38
  };
41
- exports.invalidRemoteDataWarningMessage = `\nRemote data settings existing in Clarinet.toml, but remote data feature will not be used! To use remote data, please make sure the following fields are set:
42
-
43
- - enabled = true
44
- - api_url = <stacks-api-url>
45
- - initial_height = <stacks-block-height-to-fork-from>
46
-
47
- under the "repl.remote_data" section in the Clarinet.toml file.`;
48
39
  /**
49
40
  * Gets the manifest file name for a Clarinet project.
50
41
  * If a custom manifest exists (`Clarinet-<contract-name>.toml`), it is used.
@@ -64,17 +55,14 @@ exports.getManifestFileName = getManifestFileName;
64
55
  const tryParseRemoteDataSettings = (manifestPath, radio) => {
65
56
  var _a, _b;
66
57
  const clarinetToml = toml_1.default.parse((0, fs_1.readFileSync)((0, path_1.resolve)(manifestPath), "utf-8"));
67
- const remoteDataUserSettings = (_b = (_a = clarinetToml.repl) === null || _a === void 0 ? void 0 : _a["remote_data"]) !== null && _b !== void 0 ? _b : undefined;
68
- const invalidRemoteDataSetup = !(remoteDataUserSettings === null || remoteDataUserSettings === void 0 ? void 0 : remoteDataUserSettings["api_url"]) ||
69
- !(remoteDataUserSettings === null || remoteDataUserSettings === void 0 ? void 0 : remoteDataUserSettings["enabled"]) ||
70
- !(remoteDataUserSettings === null || remoteDataUserSettings === void 0 ? void 0 : remoteDataUserSettings["initial_height"]);
71
- if (remoteDataUserSettings !== undefined && invalidRemoteDataSetup) {
72
- radio.emit("logMessage", (0, ansicolor_1.yellow)(exports.invalidRemoteDataWarningMessage));
73
- }
74
- else if (remoteDataUserSettings) {
58
+ const remoteDataUserSettings = (_b = (_a = clarinetToml.repl) === null || _a === void 0 ? void 0 : _a.remote_data) !== null && _b !== void 0 ? _b : undefined;
59
+ if (remoteDataUserSettings && (remoteDataUserSettings === null || remoteDataUserSettings === void 0 ? void 0 : remoteDataUserSettings.enabled) === true) {
75
60
  radio.emit("logMessage", (0, ansicolor_1.yellow)("\nUsing remote data. Setting up the environment can take up to a minute..."));
76
61
  }
77
- if (!remoteDataUserSettings || invalidRemoteDataSetup) {
62
+ // If no remote data settings are provided, we still need to return an object
63
+ // with the `enabled` property set to `false`. That is what simnet expects
64
+ // at least in order to initialize an empty simnet session.
65
+ if (!remoteDataUserSettings) {
78
66
  return exports.noRemoteData;
79
67
  }
80
68
  return remoteDataUserSettings;
package/dist/citizen.js CHANGED
@@ -50,7 +50,9 @@ const issueFirstClassCitizenship = (manifestDir, manifestPath, remoteDataSetting
50
50
  const balanceHex = simnet.runSnippet(`(stx-get-balance '${address})`);
51
51
  return [address, (0, transactions_1.cvToValue)((0, transactions_1.hexToCV)(balanceHex))];
52
52
  }));
53
- const sbtcBalancesMap = (0, exports.getSbtcBalancesFromSimnet)(simnet);
53
+ // If the sbtc-token contract is included in the deployment plan, we need to
54
+ // restore the sBTC balances. Otherwise, use an empty map.
55
+ const sbtcBalancesMap = (0, exports.getSbtcBalancesFromSimnet)(simnet, deploymentPlan, remoteDataSettings);
54
56
  yield simnet.initEmptySession(remoteDataSettings);
55
57
  simnetAddresses.forEach((address) => {
56
58
  simnet.mintSTX(address, stxBalancesMap.get(address));
@@ -323,30 +325,59 @@ function scheduleRendezvous(targetContractSource, tests) {
323
325
  (ok (map-set context function-name {called: called})))`;
324
326
  return `${targetContractSource}\n\n${context}\n\n${tests}`;
325
327
  }
328
+ /**
329
+ * Checks if a contract can be found in the deployment plan.
330
+ * @param deploymentPlan The parsed deployment plan.
331
+ * @param contractAddress The address of the contract.
332
+ * @param contractName The name of the contract.
333
+ * @returns True if the contract can be found in the deployment plan, false
334
+ * otherwise.
335
+ */
336
+ const isContractInDeploymentPlan = (deploymentPlan, contractAddress, contractName) => {
337
+ return deploymentPlan.plan.batches.some((batch) => batch.transactions.some((transaction) => {
338
+ var _a, _b;
339
+ return ((_a = transaction["emulated-contract-publish"]) === null || _a === void 0 ? void 0 : _a["contract-name"]) ===
340
+ contractName &&
341
+ ((_b = transaction["emulated-contract-publish"]) === null || _b === void 0 ? void 0 : _b["emulated-sender"]) ===
342
+ contractAddress;
343
+ }));
344
+ };
326
345
  /**
327
346
  * Maps the simnet accounts to their sBTC balances. The function tries to call
328
347
  * the `get-balance` function of the `sbtc-token` contract for each address. If
329
348
  * the call fails, it returns a balance of 0 for that address. The call fails
330
349
  * if the user is not working with sBTC.
331
350
  * @param simnet The simnet instance.
351
+ * @param deploymentPlan The parsed deployment plan.
352
+ * @param remoteDataSettings The remote data settings.
332
353
  * @returns A map of addresses to their sBTC balances.
333
354
  */
334
- const getSbtcBalancesFromSimnet = (simnet) => new Map([...simnet.getAccounts().values()].map((address) => {
335
- try {
336
- const { result: getBalanceResult } = simnet.callReadOnlyFn("SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token", "get-balance", [transactions_1.Cl.principal(address)], address);
337
- // If the previous read-only call works, the user is working with
338
- // sBTC. This means we can proceed with restoring sBTC balances.
339
- const sbtcBalanceJSON = (0, transactions_1.cvToJSON)(getBalanceResult);
340
- // The `get-balance` function returns a response containing the uint
341
- // balance of the address. In the JSON representation, the balance is
342
- // represented as a string. We need to parse it to an integer.
343
- const sbtcBalance = parseInt(sbtcBalanceJSON.value.value, 10);
344
- return [address, sbtcBalance];
345
- }
346
- catch (e) {
347
- return [address, 0];
355
+ const getSbtcBalancesFromSimnet = (simnet, deploymentPlan, remoteDataSettings) => {
356
+ // If the user is not using remote data and the deployment plan does not
357
+ // contain the `sbtc-token` contract, return a map with 0 balances for all
358
+ // addresses. When remote data is enabled, the sbtc-token contract will not
359
+ // necessarily be present in the deployment plan.
360
+ if (!remoteDataSettings.enabled &&
361
+ !isContractInDeploymentPlan(deploymentPlan, "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4", "sbtc-token")) {
362
+ return new Map([...simnet.getAccounts().values()].map((address) => [address, 0]));
348
363
  }
349
- }));
364
+ return new Map([...simnet.getAccounts().values()].map((address) => {
365
+ try {
366
+ const { result: getBalanceResult } = simnet.callReadOnlyFn("SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token", "get-balance", [transactions_1.Cl.principal(address)], address);
367
+ // If the previous read-only call works, the user is working with
368
+ // sBTC. This means we can proceed with restoring sBTC balances.
369
+ const sbtcBalanceJSON = (0, transactions_1.cvToJSON)(getBalanceResult);
370
+ // The `get-balance` function returns a response containing the uint
371
+ // balance of the address. In the JSON representation, the balance is
372
+ // represented as a string. We need to parse it to an integer.
373
+ const sbtcBalance = parseInt(sbtcBalanceJSON.value.value, 10);
374
+ return [address, sbtcBalance];
375
+ }
376
+ catch (e) {
377
+ return [address, 0];
378
+ }
379
+ }));
380
+ };
350
381
  exports.getSbtcBalancesFromSimnet = getSbtcBalancesFromSimnet;
351
382
  /**
352
383
  * Utility function that restores the test wallets' initial sBTC balances in
@@ -20,7 +20,7 @@ const shared_1 = require("./shared");
20
20
  * @param type The type of test that failed: invariant or property.
21
21
  * @returns void
22
22
  */
23
- function reporter(runDetails, radio, type) {
23
+ function reporter(runDetails, radio, type, statistics) {
24
24
  var _a, _b;
25
25
  if (runDetails.failed) {
26
26
  // Report general run data.
@@ -79,4 +79,115 @@ function reporter(runDetails, radio, type) {
79
79
  else {
80
80
  radio.emit("logMessage", (0, ansicolor_1.green)(`\nOK, ${type === "invariant" ? "invariants" : "properties"} passed after ${runDetails.numRuns} runs.\n`));
81
81
  }
82
+ reportStatistics(statistics, type, radio);
83
+ radio.emit("logMessage", "\n");
82
84
  }
85
+ const ARROW = "->";
86
+ const SUCCESS_SYMBOL = "+";
87
+ const FAIL_SYMBOL = "-";
88
+ const WARN_SYMBOL = "!";
89
+ /**
90
+ * Reports execution statistics in a tree-like format.
91
+ * @param statistics The statistics object containing test execution data.
92
+ * @param type The type of test being reported.
93
+ * @param radio The event emitter for logging messages.
94
+ */
95
+ function reportStatistics(statistics, type, radio) {
96
+ if ((type === "invariant" && (!statistics.invariant || !statistics.sut)) ||
97
+ (type === "test" && !statistics.test)) {
98
+ radio.emit("logMessage", "No statistics available for this run");
99
+ return;
100
+ }
101
+ radio.emit("logMessage", `\nEXECUTION STATISTICS\n`);
102
+ switch (type) {
103
+ case "invariant": {
104
+ radio.emit("logMessage", "│ PUBLIC FUNCTION CALLS");
105
+ radio.emit("logMessage", "│");
106
+ radio.emit("logMessage", `├─ ${SUCCESS_SYMBOL} SUCCESSFUL`);
107
+ logAsTree(Object.fromEntries(statistics.sut.successful), radio);
108
+ radio.emit("logMessage", "│");
109
+ radio.emit("logMessage", `├─ ${FAIL_SYMBOL} IGNORED`);
110
+ logAsTree(Object.fromEntries(statistics.sut.failed), radio);
111
+ radio.emit("logMessage", "│");
112
+ radio.emit("logMessage", "│ INVARIANT CHECKS");
113
+ radio.emit("logMessage", "│");
114
+ radio.emit("logMessage", `├─ ${SUCCESS_SYMBOL} PASSED`);
115
+ logAsTree(Object.fromEntries(statistics.invariant.successful), radio);
116
+ radio.emit("logMessage", "│");
117
+ radio.emit("logMessage", `└─ ${FAIL_SYMBOL} FAILED`);
118
+ logAsTree(Object.fromEntries(statistics.invariant.failed), radio, {
119
+ isLastSection: true,
120
+ });
121
+ radio.emit("logMessage", "\nLEGEND:\n");
122
+ radio.emit("logMessage", " SUCCESSFUL calls executed and advanced the test");
123
+ radio.emit("logMessage", " IGNORED calls failed but did not affect the test");
124
+ radio.emit("logMessage", " PASSED invariants maintained system integrity");
125
+ radio.emit("logMessage", " FAILED invariants indicate contract vulnerabilities");
126
+ if (computeTotalCount(statistics.invariant.failed) > 0) {
127
+ radio.emit("logFailure", "\n! FAILED invariants require immediate attention as they indicate that your contract can enter an invalid state under certain conditions.");
128
+ }
129
+ break;
130
+ }
131
+ case "test": {
132
+ radio.emit("logMessage", "│ PROPERTY TEST CALLS");
133
+ radio.emit("logMessage", "│");
134
+ radio.emit("logMessage", `├─ ${SUCCESS_SYMBOL} PASSED`);
135
+ logAsTree(Object.fromEntries(statistics.test.successful), radio);
136
+ radio.emit("logMessage", "│");
137
+ radio.emit("logMessage", `├─ ${WARN_SYMBOL} DISCARDED`);
138
+ logAsTree(Object.fromEntries(statistics.test.discarded), radio);
139
+ radio.emit("logMessage", "│");
140
+ radio.emit("logMessage", `└─ ${FAIL_SYMBOL} FAILED`);
141
+ logAsTree(Object.fromEntries(statistics.test.failed), radio, {
142
+ isLastSection: true,
143
+ });
144
+ radio.emit("logMessage", "\nLEGEND:\n");
145
+ radio.emit("logMessage", " PASSED properties verified for given inputs");
146
+ radio.emit("logMessage", " DISCARDED skipped due to invalid preconditions");
147
+ radio.emit("logMessage", " FAILED property violations or unexpected behavior");
148
+ if (computeTotalCount(statistics.test.failed) > 0) {
149
+ radio.emit("logFailure", "\n! FAILED tests indicate that your function properties don't hold for all inputs. Review the counterexamples above for debugging.");
150
+ }
151
+ break;
152
+ }
153
+ }
154
+ }
155
+ /**
156
+ * Displays a tree structure of data.
157
+ * @param tree The object to display as a tree.
158
+ * @param radio The event emitter for logging messages.
159
+ * @param options Configuration options for tree display.
160
+ */
161
+ function logAsTree(tree, radio, options = {}) {
162
+ const { isLastSection = false, baseIndent = " " } = options;
163
+ const printTree = (node, indent = baseIndent, isLastParent = true, radio) => {
164
+ const keys = Object.keys(node);
165
+ keys.forEach((key, index) => {
166
+ const isLast = index === keys.length - 1;
167
+ const connector = isLast ? "└─" : "├─";
168
+ const nextIndent = indent + (isLastParent ? " " : "│ ");
169
+ const leadingChar = isLastSection ? " " : "│";
170
+ if (typeof node[key] === "object" && node[key] !== null) {
171
+ radio.emit("logMessage", `${leadingChar} ${indent}${connector} ${ARROW} ${key}`);
172
+ printTree(node[key], nextIndent, isLast, radio);
173
+ }
174
+ else {
175
+ const count = node[key];
176
+ radio.emit("logMessage", `${leadingChar} ${indent}${connector} ${key}: x${count}`);
177
+ }
178
+ });
179
+ };
180
+ printTree(tree, baseIndent, true, radio);
181
+ }
182
+ /**
183
+ * Computes the total number of failures from a failure map.
184
+ * @param failedMap Map containing failure counts by test name
185
+ * @returns The sum of all failure counts
186
+ */
187
+ const computeTotalCount = (failedMap) => {
188
+ let totalFailures = 0;
189
+ for (const count of failedMap.values()) {
190
+ totalFailures += count;
191
+ }
192
+ return totalFailures;
193
+ };
package/dist/invariant.js CHANGED
@@ -38,17 +38,35 @@ const dialer_1 = require("./dialer");
38
38
  * @returns void
39
39
  */
40
40
  const checkInvariants = (simnet, targetContractName, rendezvousList, rendezvousAllFunctions, seed, path, runs, bail, dialerRegistry, radio) => __awaiter(void 0, void 0, void 0, function* () {
41
+ const statistics = {
42
+ sut: {
43
+ successful: new Map(),
44
+ failed: new Map(),
45
+ },
46
+ invariant: {
47
+ successful: new Map(),
48
+ failed: new Map(),
49
+ },
50
+ };
41
51
  // A map where the keys are the Rendezvous identifiers and the values are
42
52
  // arrays of their SUT (System Under Test) functions. This map will be used
43
53
  // to access the SUT functions for each Rendezvous contract afterwards.
44
54
  const rendezvousSutFunctions = filterSutFunctions(rendezvousAllFunctions);
55
+ // The Rendezvous identifier is the first one in the list. Only one contract
56
+ // can be fuzzed at a time.
57
+ const rendezvousContractId = rendezvousList[0];
58
+ for (const functionInterface of rendezvousSutFunctions.get(rendezvousContractId)) {
59
+ statistics.sut.successful.set(functionInterface.name, 0);
60
+ statistics.sut.failed.set(functionInterface.name, 0);
61
+ }
45
62
  // A map where the keys are the Rendezvous identifiers and the values are
46
63
  // arrays of their invariant functions. This map will be used to access the
47
64
  // invariant functions for each Rendezvous contract afterwards.
48
65
  const rendezvousInvariantFunctions = filterInvariantFunctions(rendezvousAllFunctions);
49
- // The Rendezvous identifier is the first one in the list. Only one contract
50
- // can be fuzzed at a time.
51
- const rendezvousContractId = rendezvousList[0];
66
+ for (const functionInterface of rendezvousInvariantFunctions.get(rendezvousContractId)) {
67
+ statistics.invariant.successful.set(functionInterface.name, 0);
68
+ statistics.invariant.failed.set(functionInterface.name, 0);
69
+ }
52
70
  const traitReferenceSutFunctions = rendezvousSutFunctions
53
71
  .get(rendezvousContractId)
54
72
  .filter((fn) => (0, traits_1.isTraitReferenceFunction)(fn));
@@ -95,7 +113,7 @@ const checkInvariants = (simnet, targetContractName, rendezvousList, rendezvousA
95
113
  return;
96
114
  }
97
115
  const radioReporter = (runDetails) => {
98
- (0, heatstroke_1.reporter)(runDetails, radio, "invariant");
116
+ (0, heatstroke_1.reporter)(runDetails, radio, "invariant", statistics);
99
117
  };
100
118
  yield fast_check_1.default.assert(fast_check_1.default.asyncProperty(fast_check_1.default
101
119
  .record({
@@ -178,6 +196,7 @@ const checkInvariants = (simnet, targetContractName, rendezvousList, rendezvousA
178
196
  // call during the run.
179
197
  const selectedFunctionClarityResult = (0, transactions_1.cvToString)(functionCall.result);
180
198
  if (functionCallResultJson.success) {
199
+ statistics.sut.successful.set(selectedFunction.name, statistics.sut.successful.get(selectedFunction.name) + 1);
181
200
  localContext[r.rendezvousContractId][selectedFunction.name]++;
182
201
  simnet.callPublicFn(r.rendezvousContractId, "update-context", [
183
202
  transactions_1.Cl.stringAscii(selectedFunction.name),
@@ -206,6 +225,7 @@ const checkInvariants = (simnet, targetContractName, rendezvousList, rendezvousA
206
225
  }
207
226
  else {
208
227
  // Function call failed.
228
+ statistics.sut.failed.set(selectedFunction.name, statistics.sut.failed.get(selectedFunction.name) + 1);
209
229
  radio.emit("logMessage", (0, ansicolor_1.dim)(`₿ ${simnet.burnBlockHeight.toString().padStart(8)} ` +
210
230
  `Ӿ ${simnet.blockHeight.toString().padStart(8)} ` +
211
231
  `${sutCallerWallet} ` +
@@ -257,6 +277,8 @@ const checkInvariants = (simnet, targetContractName, rendezvousList, rendezvousA
257
277
  const invariantCallResultJson = (0, transactions_1.cvToJSON)(invariantCallResult);
258
278
  const invariantCallClarityResult = (0, transactions_1.cvToString)(invariantCallResult);
259
279
  if (invariantCallResultJson.value === true) {
280
+ statistics.invariant.successful.set(r.selectedInvariant.name, statistics.invariant.successful.get(r.selectedInvariant.name) +
281
+ 1);
260
282
  radio.emit("logMessage", `₿ ${simnet.burnBlockHeight.toString().padStart(8)} ` +
261
283
  `Ӿ ${simnet.blockHeight.toString().padStart(8)} ` +
262
284
  `${(0, ansicolor_1.dim)(invariantCallerWallet)} ` +
@@ -267,6 +289,7 @@ const checkInvariants = (simnet, targetContractName, rendezvousList, rendezvousA
267
289
  (0, ansicolor_1.green)(invariantCallClarityResult));
268
290
  }
269
291
  else {
292
+ statistics.invariant.failed.set(r.selectedInvariant.name, statistics.invariant.failed.get(r.selectedInvariant.name) + 1);
270
293
  radio.emit("logMessage", (0, ansicolor_1.red)(`₿ ${simnet.burnBlockHeight.toString().padStart(8)} ` +
271
294
  `Ӿ ${simnet.blockHeight.toString().padStart(8)} ` +
272
295
  `${invariantCallerWallet} ` +
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stacks/rendezvous",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Meet your contract's vulnerabilities head-on.",
5
5
  "main": "app.js",
6
6
  "bin": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "build": "npx -p typescript tsc --project tsconfig.json && node -e \"if (process.platform !== 'win32') require('fs').chmodSync('./dist/app.js', 0o755);\"",
11
- "test": "npx jest",
11
+ "test": "npx jest --testTimeout=15000",
12
12
  "test:coverage": "npx tsc --project tsconfig.json && npx jest --coverage"
13
13
  },
14
14
  "keywords": [
@@ -30,15 +30,15 @@
30
30
  "url": "https://github.com/stacks-network/rendezvous.git"
31
31
  },
32
32
  "dependencies": {
33
- "@hirosystems/clarinet-sdk": "^3.0.1",
34
- "@stacks/transactions": "^7.0.6",
33
+ "@hirosystems/clarinet-sdk": "^3.7.0",
34
+ "@stacks/transactions": "^7.2.0",
35
35
  "ansicolor": "^2.0.3",
36
36
  "fast-check": "^3.20.0",
37
37
  "toml": "^3.0.0",
38
38
  "yaml": "^2.6.1"
39
39
  },
40
40
  "devDependencies": {
41
- "@hirosystems/clarinet-sdk-wasm": "^3.0.1",
41
+ "@hirosystems/clarinet-sdk-wasm": "^3.7.0",
42
42
  "@types/jest": "^29.5.12",
43
43
  "jest": "^29.7.0",
44
44
  "ts-jest": "^29.2.5",
package/dist/property.js CHANGED
@@ -27,11 +27,23 @@ const traits_1 = require("./traits");
27
27
  * @returns void
28
28
  */
29
29
  const checkProperties = (simnet, targetContractName, rendezvousList, rendezvousAllFunctions, seed, path, runs, bail, radio) => {
30
+ const statistics = {
31
+ test: {
32
+ successful: new Map(),
33
+ discarded: new Map(),
34
+ failed: new Map(),
35
+ },
36
+ };
30
37
  const testContractId = rendezvousList[0];
31
38
  // A map where the keys are the test contract identifiers and the values are
32
39
  // arrays of their test functions. This map will be used to access the test
33
40
  // functions for each test contract in the property-based testing routine.
34
41
  const testContractsTestFunctions = filterTestFunctions(rendezvousAllFunctions);
42
+ for (const functionInterface of testContractsTestFunctions.get(testContractId)) {
43
+ statistics.test.successful.set(functionInterface.name, 0);
44
+ statistics.test.discarded.set(functionInterface.name, 0);
45
+ statistics.test.failed.set(functionInterface.name, 0);
46
+ }
35
47
  const traitReferenceFunctions = testContractsTestFunctions
36
48
  .get(testContractId)
37
49
  .filter((fn) => (0, traits_1.isTraitReferenceFunction)(fn));
@@ -81,7 +93,7 @@ const checkProperties = (simnet, targetContractName, rendezvousList, rendezvousA
81
93
  return;
82
94
  }
83
95
  const radioReporter = (runDetails) => {
84
- (0, heatstroke_1.reporter)(runDetails, radio, "test");
96
+ (0, heatstroke_1.reporter)(runDetails, radio, "test", statistics);
85
97
  };
86
98
  fast_check_1.default.assert(fast_check_1.default.property(fast_check_1.default
87
99
  .record({
@@ -134,6 +146,7 @@ const checkProperties = (simnet, targetContractName, rendezvousList, rendezvousA
134
146
  .get(r.selectedTestFunction.name);
135
147
  const discarded = isTestDiscarded(discardFunctionName, selectedTestFunctionArgs, r.testContractId, simnet, testCallerAddress);
136
148
  if (discarded) {
149
+ statistics.test.discarded.set(r.selectedTestFunction.name, statistics.test.discarded.get(r.selectedTestFunction.name) + 1);
137
150
  radio.emit("logMessage", `₿ ${simnet.burnBlockHeight.toString().padStart(8)} ` +
138
151
  `Ӿ ${simnet.blockHeight.toString().padStart(8)} ` +
139
152
  `${(0, ansicolor_1.dim)(testCallerWallet)} ` +
@@ -151,6 +164,7 @@ const checkProperties = (simnet, targetContractName, rendezvousList, rendezvousA
151
164
  const discardedInPlace = (0, exports.isTestDiscardedInPlace)(testFunctionCallResultJson);
152
165
  const testFunctionCallClarityResult = (0, transactions_1.cvToString)(testFunctionCallResult);
153
166
  if (discardedInPlace) {
167
+ statistics.test.discarded.set(r.selectedTestFunction.name, statistics.test.discarded.get(r.selectedTestFunction.name) + 1);
154
168
  radio.emit("logMessage", `₿ ${simnet.burnBlockHeight.toString().padStart(8)} ` +
155
169
  `Ӿ ${simnet.blockHeight.toString().padStart(8)} ` +
156
170
  `${(0, ansicolor_1.dim)(testCallerWallet)} ` +
@@ -163,6 +177,8 @@ const checkProperties = (simnet, targetContractName, rendezvousList, rendezvousA
163
177
  else if (!discardedInPlace &&
164
178
  testFunctionCallResultJson.success &&
165
179
  testFunctionCallResultJson.value.value === true) {
180
+ statistics.test.successful.set(r.selectedTestFunction.name, statistics.test.successful.get(r.selectedTestFunction.name) +
181
+ 1);
166
182
  radio.emit("logMessage", `₿ ${simnet.burnBlockHeight.toString().padStart(8)} ` +
167
183
  `Ӿ ${simnet.blockHeight.toString().padStart(8)} ` +
168
184
  `${(0, ansicolor_1.dim)(testCallerWallet)} ` +
@@ -176,6 +192,7 @@ const checkProperties = (simnet, targetContractName, rendezvousList, rendezvousA
176
192
  }
177
193
  }
178
194
  else {
195
+ statistics.test.failed.set(r.selectedTestFunction.name, statistics.test.failed.get(r.selectedTestFunction.name) + 1);
179
196
  // The function call did not result in (ok true) or (ok false).
180
197
  // Either the test failed or the test function returned an
181
198
  // unexpected value i.e. `(ok 1)`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stacks/rendezvous",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Meet your contract's vulnerabilities head-on.",
5
5
  "main": "app.js",
6
6
  "bin": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "build": "npx -p typescript tsc --project tsconfig.json && node -e \"if (process.platform !== 'win32') require('fs').chmodSync('./dist/app.js', 0o755);\"",
11
- "test": "npx jest",
11
+ "test": "npx jest --testTimeout=15000",
12
12
  "test:coverage": "npx tsc --project tsconfig.json && npx jest --coverage"
13
13
  },
14
14
  "keywords": [
@@ -30,15 +30,15 @@
30
30
  "url": "https://github.com/stacks-network/rendezvous.git"
31
31
  },
32
32
  "dependencies": {
33
- "@hirosystems/clarinet-sdk": "^3.0.1",
34
- "@stacks/transactions": "^7.0.6",
33
+ "@hirosystems/clarinet-sdk": "^3.7.0",
34
+ "@stacks/transactions": "^7.2.0",
35
35
  "ansicolor": "^2.0.3",
36
36
  "fast-check": "^3.20.0",
37
37
  "toml": "^3.0.0",
38
38
  "yaml": "^2.6.1"
39
39
  },
40
40
  "devDependencies": {
41
- "@hirosystems/clarinet-sdk-wasm": "^3.0.1",
41
+ "@hirosystems/clarinet-sdk-wasm": "^3.7.0",
42
42
  "@types/jest": "^29.5.12",
43
43
  "jest": "^29.7.0",
44
44
  "ts-jest": "^29.2.5",