@stacks/rendezvous 0.1.3 → 0.3.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/README.md +2 -23
- package/dist/citizen.js +34 -33
- package/dist/heatstroke.js +35 -32
- package/dist/heatstroke.types.js +2 -0
- package/dist/invariant.js +120 -80
- package/dist/package.json +7 -3
- package/dist/property.js +55 -31
- package/dist/shared.js +87 -105
- package/dist/traits.js +389 -0
- package/dist/traits.types.js +2 -0
- package/package.json +7 -3
package/dist/property.js
CHANGED
|
@@ -9,12 +9,42 @@ const transactions_1 = require("@stacks/transactions");
|
|
|
9
9
|
const heatstroke_1 = require("./heatstroke");
|
|
10
10
|
const shared_1 = require("./shared");
|
|
11
11
|
const ansicolor_1 = require("ansicolor");
|
|
12
|
-
const
|
|
12
|
+
const traits_1 = require("./traits");
|
|
13
|
+
/**
|
|
14
|
+
* Runs property-based tests on the target contract and logs the progress.
|
|
15
|
+
* Reports the test results through a custom reporter.
|
|
16
|
+
* @param simnet The simnet instance.
|
|
17
|
+
* @param targetContractName The name of the target contract.
|
|
18
|
+
* @param rendezvousList The list of contract IDs for each target contract.
|
|
19
|
+
* @param rendezvousAllFunctions A map of all target contract IDs to their
|
|
20
|
+
* function interfaces.
|
|
21
|
+
* @param seed The seed for reproducible property-based tests.
|
|
22
|
+
* @param path The path for reproducible property-based tests.
|
|
23
|
+
* @param runs The number of test runs.
|
|
24
|
+
* @param radio The custom logging event emitter.
|
|
25
|
+
* @returns void
|
|
26
|
+
*/
|
|
27
|
+
const checkProperties = (simnet, targetContractName, rendezvousList, rendezvousAllFunctions, seed, path, runs, radio) => {
|
|
28
|
+
const testContractId = rendezvousList[0];
|
|
13
29
|
// A map where the keys are the test contract identifiers and the values are
|
|
14
30
|
// arrays of their test functions. This map will be used to access the test
|
|
15
31
|
// functions for each test contract in the property-based testing routine.
|
|
16
32
|
const testContractsTestFunctions = filterTestFunctions(rendezvousAllFunctions);
|
|
17
|
-
|
|
33
|
+
const traitReferenceFunctions = testContractsTestFunctions
|
|
34
|
+
.get(testContractId)
|
|
35
|
+
.filter((fn) => (0, traits_1.isTraitReferenceFunction)(fn));
|
|
36
|
+
const projectTraitImplementations = (0, traits_1.extractProjectTraitImplementations)(simnet);
|
|
37
|
+
if (Object.entries(projectTraitImplementations).length === 0 &&
|
|
38
|
+
traitReferenceFunctions.length > 0) {
|
|
39
|
+
radio.emit("logMessage", (0, ansicolor_1.red)(`\nFound test functions referencing traits, but no trait implementations were found in the project.
|
|
40
|
+
\nNote: You can add contracts implementing traits either as project contracts or as Clarinet requirements. For more details, visit: https://www.hiro.so/clarinet/.
|
|
41
|
+
\n`));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const enrichedTestFunctionsInterfaces = traitReferenceFunctions.length > 0
|
|
45
|
+
? (0, traits_1.enrichInterfaceWithTraitData)(simnet.getContractAST(targetContractName), (0, traits_1.buildTraitReferenceMap)(testContractsTestFunctions.get(testContractId)), testContractsTestFunctions.get(testContractId), testContractId)
|
|
46
|
+
: testContractsTestFunctions;
|
|
47
|
+
radio.emit("logMessage", `\nStarting property testing type for the ${targetContractName} contract...\n`);
|
|
18
48
|
// Search for discard functions, for each test function. This map will
|
|
19
49
|
// be used to pair the test functions with their corresponding discard
|
|
20
50
|
// functions.
|
|
@@ -43,15 +73,9 @@ const checkProperties = (simnet, sutContractName, rendezvousList, rendezvousAllF
|
|
|
43
73
|
const simnetAccounts = simnet.getAccounts();
|
|
44
74
|
const eligibleAccounts = new Map([...simnetAccounts].filter(([key]) => key !== "faucet"));
|
|
45
75
|
const simnetAddresses = Array.from(simnetAccounts.values());
|
|
46
|
-
const
|
|
47
|
-
const testFunctions = (0, shared_1.getFunctionsListForContract)(testContractsTestFunctions, testContractId);
|
|
76
|
+
const testFunctions = (0, shared_1.getFunctionsListForContract)(enrichedTestFunctionsInterfaces, testContractId);
|
|
48
77
|
if ((testFunctions === null || testFunctions === void 0 ? void 0 : testFunctions.length) === 0) {
|
|
49
|
-
radio.emit("logMessage", (0, ansicolor_1.red)(`No test functions found for the "${
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const eligibleTestFunctions = testFunctions.filter((fn) => !(0, shared_1.isTraitReferenceFunction)(fn));
|
|
53
|
-
if (eligibleTestFunctions.length === 0) {
|
|
54
|
-
radio.emit("logMessage", (0, ansicolor_1.red)(`No eligible test functions found for the "${sutContractName}" contract. Note: trait references are not supported.\n`));
|
|
78
|
+
radio.emit("logMessage", (0, ansicolor_1.red)(`No test functions found for the "${targetContractName}" contract.\n`));
|
|
55
79
|
return;
|
|
56
80
|
}
|
|
57
81
|
const radioReporter = (runDetails) => {
|
|
@@ -65,12 +89,12 @@ const checkProperties = (simnet, sutContractName, rendezvousList, rendezvousAllF
|
|
|
65
89
|
})
|
|
66
90
|
.chain((r) => fast_check_1.default
|
|
67
91
|
.record({
|
|
68
|
-
selectedTestFunction: fast_check_1.default.constantFrom(...
|
|
92
|
+
selectedTestFunction: fast_check_1.default.constantFrom(...testFunctions),
|
|
69
93
|
})
|
|
70
94
|
.map((selectedTestFunction) => (Object.assign(Object.assign({}, r), selectedTestFunction))))
|
|
71
95
|
.chain((r) => fast_check_1.default
|
|
72
96
|
.record({
|
|
73
|
-
|
|
97
|
+
functionArgs: fast_check_1.default.tuple(...(0, shared_1.functionToArbitrary)(r.selectedTestFunction, simnetAddresses, projectTraitImplementations)),
|
|
74
98
|
})
|
|
75
99
|
.map((args) => (Object.assign(Object.assign({}, r), args))))
|
|
76
100
|
.chain((r) => fast_check_1.default
|
|
@@ -89,8 +113,8 @@ const checkProperties = (simnet, sutContractName, rendezvousList, rendezvousAllF
|
|
|
89
113
|
: fast_check_1.default.constant(0),
|
|
90
114
|
})
|
|
91
115
|
.map((burnBlocks) => (Object.assign(Object.assign({}, r), burnBlocks)))), (r) => {
|
|
92
|
-
const selectedTestFunctionArgs = (0, shared_1.argsToCV)(r.selectedTestFunction, r.
|
|
93
|
-
const printedTestFunctionArgs = r.
|
|
116
|
+
const selectedTestFunctionArgs = (0, shared_1.argsToCV)(r.selectedTestFunction, r.functionArgs);
|
|
117
|
+
const printedTestFunctionArgs = r.functionArgs
|
|
94
118
|
.map((arg) => {
|
|
95
119
|
try {
|
|
96
120
|
return typeof arg === "object"
|
|
@@ -112,7 +136,7 @@ const checkProperties = (simnet, sutContractName, rendezvousList, rendezvousAllF
|
|
|
112
136
|
`Ӿ ${simnet.blockHeight.toString().padStart(8)} ` +
|
|
113
137
|
`${(0, ansicolor_1.dim)(testCallerWallet)} ` +
|
|
114
138
|
`${(0, ansicolor_1.yellow)("[WARN]")} ` +
|
|
115
|
-
`${
|
|
139
|
+
`${targetContractName} ` +
|
|
116
140
|
`${(0, ansicolor_1.underline)(r.selectedTestFunction.name)} ` +
|
|
117
141
|
(0, ansicolor_1.dim)(printedTestFunctionArgs));
|
|
118
142
|
}
|
|
@@ -128,7 +152,7 @@ const checkProperties = (simnet, sutContractName, rendezvousList, rendezvousAllF
|
|
|
128
152
|
`Ӿ ${simnet.blockHeight.toString().padStart(8)} ` +
|
|
129
153
|
`${(0, ansicolor_1.dim)(testCallerWallet)} ` +
|
|
130
154
|
`${(0, ansicolor_1.yellow)("[WARN]")} ` +
|
|
131
|
-
`${
|
|
155
|
+
`${targetContractName} ` +
|
|
132
156
|
`${(0, ansicolor_1.underline)(r.selectedTestFunction.name)} ` +
|
|
133
157
|
(0, ansicolor_1.dim)(printedTestFunctionArgs));
|
|
134
158
|
}
|
|
@@ -139,7 +163,7 @@ const checkProperties = (simnet, sutContractName, rendezvousList, rendezvousAllF
|
|
|
139
163
|
`Ӿ ${simnet.blockHeight.toString().padStart(8)} ` +
|
|
140
164
|
`${(0, ansicolor_1.dim)(testCallerWallet)} ` +
|
|
141
165
|
`${(0, ansicolor_1.green)("[PASS]")} ` +
|
|
142
|
-
`${
|
|
166
|
+
`${targetContractName} ` +
|
|
143
167
|
`${(0, ansicolor_1.underline)(r.selectedTestFunction.name)} ` +
|
|
144
168
|
printedTestFunctionArgs);
|
|
145
169
|
if (r.canMineBlocks) {
|
|
@@ -147,7 +171,7 @@ const checkProperties = (simnet, sutContractName, rendezvousList, rendezvousAllF
|
|
|
147
171
|
}
|
|
148
172
|
}
|
|
149
173
|
else {
|
|
150
|
-
throw new Error(`Test failed for ${
|
|
174
|
+
throw new Error(`Test failed for ${targetContractName} contract: "${r.selectedTestFunction.name}" returned ${testFunctionCallResultJson.value.value}`);
|
|
151
175
|
}
|
|
152
176
|
}
|
|
153
177
|
catch (error) {
|
|
@@ -156,7 +180,7 @@ const checkProperties = (simnet, sutContractName, rendezvousList, rendezvousAllF
|
|
|
156
180
|
`Ӿ ${simnet.blockHeight.toString().padStart(8)} ` +
|
|
157
181
|
`${testCallerWallet} ` +
|
|
158
182
|
`[FAIL] ` +
|
|
159
|
-
`${
|
|
183
|
+
`${targetContractName} ` +
|
|
160
184
|
`${(0, ansicolor_1.underline)(r.selectedTestFunction.name)} ` +
|
|
161
185
|
printedTestFunctionArgs));
|
|
162
186
|
// Re-throw the error for fast-check to catch and process.
|
|
@@ -180,12 +204,12 @@ const isTestDiscardedInPlace = (testFunctionCallResultJson) => testFunctionCallR
|
|
|
180
204
|
testFunctionCallResultJson.value.value === false;
|
|
181
205
|
exports.isTestDiscardedInPlace = isTestDiscardedInPlace;
|
|
182
206
|
/**
|
|
183
|
-
*
|
|
207
|
+
* Checks if the test function has to be discarded.
|
|
184
208
|
* @param discardFunctionName The discard function name.
|
|
185
209
|
* @param selectedTestFunctionArgs The generated test function arguments.
|
|
186
210
|
* @param contractId The contract identifier.
|
|
187
211
|
* @param simnet The simnet instance.
|
|
188
|
-
* @param selectedCaller The selected caller.
|
|
212
|
+
* @param selectedCaller The selected caller address.
|
|
189
213
|
* @returns A boolean indicating if the test function has to be discarded.
|
|
190
214
|
*/
|
|
191
215
|
const isTestDiscarded = (discardFunctionName, selectedTestFunctionArgs, contractId, simnet, selectedCaller) => {
|
|
@@ -196,7 +220,7 @@ const isTestDiscarded = (discardFunctionName, selectedTestFunctionArgs, contract
|
|
|
196
220
|
return jsonDiscardFunctionCallResult.value === false;
|
|
197
221
|
};
|
|
198
222
|
/**
|
|
199
|
-
*
|
|
223
|
+
* Validates a discard function, ensuring that its parameters match the test
|
|
200
224
|
* function's parameters and that its return type is boolean.
|
|
201
225
|
* @param contractId The contract identifier.
|
|
202
226
|
* @param discardFunctionName The discard function name.
|
|
@@ -225,23 +249,23 @@ const validateDiscardFunction = (contractId, discardFunctionName, testFunctionNa
|
|
|
225
249
|
return true;
|
|
226
250
|
};
|
|
227
251
|
/**
|
|
228
|
-
*
|
|
252
|
+
* Checks if the test function parameters match the discard function
|
|
229
253
|
* parameters.
|
|
230
|
-
* @param
|
|
231
|
-
* @param
|
|
254
|
+
* @param testFunctionInterface The test function's interface.
|
|
255
|
+
* @param discardFunctionInterface The discard function's interface.
|
|
232
256
|
* @returns A boolean indicating if the parameters match.
|
|
233
257
|
*/
|
|
234
|
-
const isParamsMatch = (
|
|
235
|
-
const sortedTestFunctionArgs = [...
|
|
236
|
-
const sortedDiscardFunctionArgs = [...
|
|
258
|
+
const isParamsMatch = (testFunctionInterface, discardFunctionInterface) => {
|
|
259
|
+
const sortedTestFunctionArgs = [...testFunctionInterface.args].sort((a, b) => a.name.localeCompare(b.name));
|
|
260
|
+
const sortedDiscardFunctionArgs = [...discardFunctionInterface.args].sort((a, b) => a.name.localeCompare(b.name));
|
|
237
261
|
return (JSON.stringify(sortedTestFunctionArgs) ===
|
|
238
262
|
JSON.stringify(sortedDiscardFunctionArgs));
|
|
239
263
|
};
|
|
240
264
|
exports.isParamsMatch = isParamsMatch;
|
|
241
265
|
/**
|
|
242
|
-
*
|
|
243
|
-
* @param
|
|
266
|
+
* Checks if the discard function's return type is boolean.
|
|
267
|
+
* @param discardFunctionInterface The discard function's interface.
|
|
244
268
|
* @returns A boolean indicating if the return type is boolean.
|
|
245
269
|
*/
|
|
246
|
-
const isReturnTypeBoolean = (
|
|
270
|
+
const isReturnTypeBoolean = (discardFunctionInterface) => discardFunctionInterface.outputs.type === "bool";
|
|
247
271
|
exports.isReturnTypeBoolean = isReturnTypeBoolean;
|
package/dist/shared.js
CHANGED
|
@@ -3,42 +3,50 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getContractNameFromContractId = exports.
|
|
6
|
+
exports.getContractNameFromContractId = exports.argsToCV = exports.hexaString = exports.functionToArbitrary = exports.getFunctionsListForContract = exports.getFunctionsFromContractInterfaces = exports.getSimnetDeployerContractsInterfaces = void 0;
|
|
7
7
|
const fast_check_1 = __importDefault(require("fast-check"));
|
|
8
8
|
const transactions_1 = require("@stacks/transactions");
|
|
9
|
+
const traits_1 = require("./traits");
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
* simnet.
|
|
11
|
+
* Retrieves the contract interfaces of the contracts deployed by a specific
|
|
12
|
+
* deployer from the simnet instance.
|
|
12
13
|
* @param simnet The simnet instance.
|
|
13
|
-
* @
|
|
14
|
-
* @returns The contracts interfaces.
|
|
14
|
+
* @returns The contract IDs mapped to their interfaces.
|
|
15
15
|
*/
|
|
16
16
|
const getSimnetDeployerContractsInterfaces = (simnet) => new Map(Array.from(simnet.getContractsInterfaces()).filter(([key]) => key.split(".")[0] === simnet.deployer));
|
|
17
17
|
exports.getSimnetDeployerContractsInterfaces = getSimnetDeployerContractsInterfaces;
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* @
|
|
19
|
+
* Retrieves the function interfaces from the contract interfaces. Filters out
|
|
20
|
+
* other contract interface data such as data maps, variables, and constants.
|
|
21
|
+
* @param contractInterfaces The smart contract interfaces map.
|
|
22
|
+
* @returns The contract IDs mapped to their function interfaces.
|
|
22
23
|
*/
|
|
23
|
-
const getFunctionsFromContractInterfaces = (
|
|
24
|
+
const getFunctionsFromContractInterfaces = (contractInterfaces) => new Map(Array.from(contractInterfaces, ([contractId, contractInterface]) => [
|
|
24
25
|
contractId,
|
|
25
26
|
contractInterface.functions,
|
|
26
27
|
]));
|
|
27
28
|
exports.getFunctionsFromContractInterfaces = getFunctionsFromContractInterfaces;
|
|
28
29
|
const getFunctionsListForContract = (functionsMap, contractId) => functionsMap.get(contractId) || [];
|
|
29
30
|
exports.getFunctionsListForContract = getFunctionsListForContract;
|
|
30
|
-
/**
|
|
31
|
-
*
|
|
31
|
+
/** Dynamically generates fast-check arbitraries for a given function
|
|
32
|
+
* interface.
|
|
33
|
+
* @param functionInterface The "enriched" function interface.
|
|
34
|
+
* @param addresses The array of addresses to use for principal types.
|
|
35
|
+
* @param projectTraitImplementations The contract IDs mapped to the traits
|
|
36
|
+
* they implement.
|
|
32
37
|
* @returns Array of fast-check arbitraries.
|
|
33
38
|
*/
|
|
34
|
-
const functionToArbitrary = (
|
|
39
|
+
const functionToArbitrary = (functionInterface, addresses, projectTraitImplementations) => functionInterface.args.map((arg) => parameterTypeToArbitrary(arg.type, addresses, projectTraitImplementations));
|
|
35
40
|
exports.functionToArbitrary = functionToArbitrary;
|
|
36
41
|
/**
|
|
37
|
-
*
|
|
38
|
-
* @param type The parameter type.
|
|
42
|
+
* Generates a fast-check arbitrary for a given parameter type.
|
|
43
|
+
* @param type The "enriched" parameter type.
|
|
44
|
+
* @param addresses The array of addresses to use for principal types.
|
|
45
|
+
* @param projectTraitImplementations The contract IDs mapped to the traits
|
|
46
|
+
* they implement.
|
|
39
47
|
* @returns Fast-check arbitrary.
|
|
40
48
|
*/
|
|
41
|
-
const parameterTypeToArbitrary = (type, addresses) => {
|
|
49
|
+
const parameterTypeToArbitrary = (type, addresses, projectTraitImplementations) => {
|
|
42
50
|
if (typeof type === "string") {
|
|
43
51
|
// The type is a base type.
|
|
44
52
|
if (type === "principal") {
|
|
@@ -46,9 +54,6 @@ const parameterTypeToArbitrary = (type, addresses) => {
|
|
|
46
54
|
throw new Error("No addresses could be retrieved from the simnet instance!");
|
|
47
55
|
return baseTypesToArbitrary.principal(addresses);
|
|
48
56
|
}
|
|
49
|
-
else if (type === "trait_reference") {
|
|
50
|
-
throw new Error("Unsupported parameter type: trait_reference");
|
|
51
|
-
}
|
|
52
57
|
else
|
|
53
58
|
return baseTypesToArbitrary[type];
|
|
54
59
|
}
|
|
@@ -64,16 +69,19 @@ const parameterTypeToArbitrary = (type, addresses) => {
|
|
|
64
69
|
return complexTypesToArbitrary["string-utf8"](type["string-utf8"].length);
|
|
65
70
|
}
|
|
66
71
|
else if ("list" in type) {
|
|
67
|
-
return complexTypesToArbitrary["list"](type.list.type, type.list.length, addresses);
|
|
72
|
+
return complexTypesToArbitrary["list"](type.list.type, type.list.length, addresses, projectTraitImplementations);
|
|
68
73
|
}
|
|
69
74
|
else if ("tuple" in type) {
|
|
70
|
-
return complexTypesToArbitrary["tuple"](type.tuple, addresses);
|
|
75
|
+
return complexTypesToArbitrary["tuple"](type.tuple, addresses, projectTraitImplementations);
|
|
71
76
|
}
|
|
72
77
|
else if ("optional" in type) {
|
|
73
|
-
return complexTypesToArbitrary["optional"](type.optional, addresses);
|
|
78
|
+
return complexTypesToArbitrary["optional"](type.optional, addresses, projectTraitImplementations);
|
|
74
79
|
}
|
|
75
80
|
else if ("response" in type) {
|
|
76
|
-
return complexTypesToArbitrary.response(type.response.ok, type.response.error, addresses);
|
|
81
|
+
return complexTypesToArbitrary.response(type.response.ok, type.response.error, addresses, projectTraitImplementations);
|
|
82
|
+
}
|
|
83
|
+
else if ("trait_reference" in type) {
|
|
84
|
+
return complexTypesToArbitrary.trait_reference(type.trait_reference, projectTraitImplementations);
|
|
77
85
|
}
|
|
78
86
|
else {
|
|
79
87
|
throw new Error(`Unsupported complex type: ${JSON.stringify(type)}`);
|
|
@@ -88,28 +96,7 @@ const baseTypesToArbitrary = {
|
|
|
88
96
|
uint128: fast_check_1.default.nat(),
|
|
89
97
|
bool: fast_check_1.default.boolean(),
|
|
90
98
|
principal: (addresses) => fast_check_1.default.constantFrom(...addresses),
|
|
91
|
-
trait_reference: undefined,
|
|
92
99
|
};
|
|
93
|
-
/**
|
|
94
|
-
* Custom hexadecimal string generator. The `hexaString` generator from
|
|
95
|
-
* fast-check has been deprecated. This generator is implemented to precisely
|
|
96
|
-
* match the behavior of the deprecated generator.
|
|
97
|
-
*
|
|
98
|
-
* @param constraints Fast-check string constraints.
|
|
99
|
-
* @returns Fast-check arbitrary for hexadecimal strings.
|
|
100
|
-
*
|
|
101
|
-
* Reference for the proposed replacement of the deprecated `hexaString`
|
|
102
|
-
* generator:
|
|
103
|
-
* https://github.com/dubzzz/fast-check/commit/3f4f1203a8863c07d22b45591bf0de1fac02b948
|
|
104
|
-
*/
|
|
105
|
-
const hexaString = (constraints = {}) => {
|
|
106
|
-
const hexa = () => {
|
|
107
|
-
const hexCharSet = "0123456789abcdef";
|
|
108
|
-
return fast_check_1.default.integer({ min: 0, max: 15 }).map((n) => hexCharSet[n]);
|
|
109
|
-
};
|
|
110
|
-
return fast_check_1.default.string(Object.assign(Object.assign({}, constraints), { unit: hexa() }));
|
|
111
|
-
};
|
|
112
|
-
exports.hexaString = hexaString;
|
|
113
100
|
/**
|
|
114
101
|
* Complex types to fast-check arbitraries mapping.
|
|
115
102
|
*/
|
|
@@ -125,51 +112,76 @@ const complexTypesToArbitrary = {
|
|
|
125
112
|
maxLength: length,
|
|
126
113
|
}),
|
|
127
114
|
"string-utf8": (length) => fast_check_1.default.string({ maxLength: length }),
|
|
128
|
-
list: (type, length, addresses) => fast_check_1.default.array(parameterTypeToArbitrary(type, addresses), {
|
|
129
|
-
|
|
115
|
+
list: (type, length, addresses, projectTraitImplementations) => fast_check_1.default.array(parameterTypeToArbitrary(type, addresses, projectTraitImplementations), {
|
|
116
|
+
maxLength: length,
|
|
117
|
+
}),
|
|
118
|
+
tuple: (items, addresses, projectTraitImplementations) => {
|
|
130
119
|
const tupleArbitraries = {};
|
|
131
120
|
items.forEach((item) => {
|
|
132
|
-
tupleArbitraries[item.name] = parameterTypeToArbitrary(item.type, addresses);
|
|
121
|
+
tupleArbitraries[item.name] = parameterTypeToArbitrary(item.type, addresses, projectTraitImplementations);
|
|
133
122
|
});
|
|
134
123
|
return fast_check_1.default.record(tupleArbitraries);
|
|
135
124
|
},
|
|
136
|
-
optional: (type, addresses) => fast_check_1.default.option(parameterTypeToArbitrary(type, addresses)),
|
|
137
|
-
response: (okType, errType, addresses) => fast_check_1.default.oneof(fast_check_1.default.record({
|
|
125
|
+
optional: (type, addresses, projectTraitImplementations) => fast_check_1.default.option(parameterTypeToArbitrary(type, addresses, projectTraitImplementations)),
|
|
126
|
+
response: (okType, errType, addresses, projectTraitImplementations) => fast_check_1.default.oneof(fast_check_1.default.record({
|
|
138
127
|
status: fast_check_1.default.constant("ok"),
|
|
139
|
-
value: parameterTypeToArbitrary(okType, addresses),
|
|
128
|
+
value: parameterTypeToArbitrary(okType, addresses, projectTraitImplementations),
|
|
140
129
|
}), fast_check_1.default.record({
|
|
141
130
|
status: fast_check_1.default.constant("error"),
|
|
142
|
-
value: parameterTypeToArbitrary(errType, addresses),
|
|
131
|
+
value: parameterTypeToArbitrary(errType, addresses, projectTraitImplementations),
|
|
143
132
|
})),
|
|
133
|
+
trait_reference: (traitData, projectTraitImplementations) => {
|
|
134
|
+
return fast_check_1.default.constantFrom(...(0, traits_1.getContractIdsImplementingTrait)(traitData, projectTraitImplementations));
|
|
135
|
+
},
|
|
144
136
|
};
|
|
137
|
+
/**
|
|
138
|
+
* Custom hexadecimal string generator. The `hexaString` generator from
|
|
139
|
+
* fast-check has been deprecated. This generator is implemented to match the
|
|
140
|
+
* behavior of the deprecated generator.
|
|
141
|
+
*
|
|
142
|
+
* @param constraints Fast-check string constraints.
|
|
143
|
+
* @returns Fast-check arbitrary for hexadecimal strings.
|
|
144
|
+
*
|
|
145
|
+
* Reference for the proposed replacement of the deprecated `hexaString`
|
|
146
|
+
* generator:
|
|
147
|
+
* https://github.com/dubzzz/fast-check/commit/3f4f1203a8863c07d22b45591bf0de1fac02b948
|
|
148
|
+
*/
|
|
149
|
+
const hexaString = (constraints = {}) => {
|
|
150
|
+
const hexa = () => {
|
|
151
|
+
const hexCharSet = "0123456789abcdef";
|
|
152
|
+
return fast_check_1.default.integer({ min: 0, max: 15 }).map((n) => hexCharSet[n]);
|
|
153
|
+
};
|
|
154
|
+
return fast_check_1.default.string(Object.assign(Object.assign({}, constraints), { unit: hexa() }));
|
|
155
|
+
};
|
|
156
|
+
exports.hexaString = hexaString;
|
|
145
157
|
/** The character set used for generating ASCII strings.*/
|
|
146
158
|
const charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
|
147
159
|
/**
|
|
148
|
-
*
|
|
149
|
-
* @param
|
|
150
|
-
* @param
|
|
160
|
+
* Converts JavaScript generated function arguments to Clarity values.
|
|
161
|
+
* @param functionInterface The function interface.
|
|
162
|
+
* @param generatedArguments Array of generated arguments.
|
|
151
163
|
* @returns Array of Clarity values.
|
|
152
164
|
*/
|
|
153
|
-
const argsToCV = (
|
|
165
|
+
const argsToCV = (functionInterface, generatedArguments) => functionInterface.args.map((arg, i) => argToCV(generatedArguments[i], arg.type));
|
|
154
166
|
exports.argsToCV = argsToCV;
|
|
155
167
|
/**
|
|
156
|
-
*
|
|
157
|
-
* @param
|
|
168
|
+
* Converts a JavaScript generated function argument to a Clarity value.
|
|
169
|
+
* @param generatedArgument Generated argument.
|
|
158
170
|
* @param type Argument type (base or complex).
|
|
159
171
|
* @returns Clarity value.
|
|
160
172
|
*/
|
|
161
|
-
const argToCV = (
|
|
173
|
+
const argToCV = (generatedArgument, type) => {
|
|
162
174
|
if (isBaseType(type)) {
|
|
163
175
|
// Base type.
|
|
164
176
|
switch (type) {
|
|
165
177
|
case "int128":
|
|
166
|
-
return baseTypesToCV.int128(
|
|
178
|
+
return baseTypesToCV.int128(generatedArgument);
|
|
167
179
|
case "uint128":
|
|
168
|
-
return baseTypesToCV.uint128(
|
|
180
|
+
return baseTypesToCV.uint128(generatedArgument);
|
|
169
181
|
case "bool":
|
|
170
|
-
return baseTypesToCV.bool(
|
|
182
|
+
return baseTypesToCV.bool(generatedArgument);
|
|
171
183
|
case "principal":
|
|
172
|
-
return baseTypesToCV.principal(
|
|
184
|
+
return baseTypesToCV.principal(generatedArgument);
|
|
173
185
|
default:
|
|
174
186
|
throw new Error(`Unsupported base parameter type: ${type}`);
|
|
175
187
|
}
|
|
@@ -177,34 +189,39 @@ const argToCV = (arg, type) => {
|
|
|
177
189
|
else {
|
|
178
190
|
// Complex type.
|
|
179
191
|
if ("buffer" in type) {
|
|
180
|
-
return complexTypesToCV.buffer(
|
|
192
|
+
return complexTypesToCV.buffer(generatedArgument);
|
|
181
193
|
}
|
|
182
194
|
else if ("string-ascii" in type) {
|
|
183
|
-
return complexTypesToCV["string-ascii"](
|
|
195
|
+
return complexTypesToCV["string-ascii"](generatedArgument);
|
|
184
196
|
}
|
|
185
197
|
else if ("string-utf8" in type) {
|
|
186
|
-
return complexTypesToCV["string-utf8"](
|
|
198
|
+
return complexTypesToCV["string-utf8"](generatedArgument);
|
|
187
199
|
}
|
|
188
200
|
else if ("list" in type) {
|
|
189
|
-
const listItems =
|
|
201
|
+
const listItems = generatedArgument.map((item) => argToCV(item, type.list.type));
|
|
190
202
|
return complexTypesToCV.list(listItems);
|
|
191
203
|
}
|
|
192
204
|
else if ("tuple" in type) {
|
|
193
205
|
const tupleData = {};
|
|
194
206
|
type.tuple.forEach((field) => {
|
|
195
|
-
tupleData[field.name] = argToCV(
|
|
207
|
+
tupleData[field.name] = argToCV(generatedArgument[field.name], field.type);
|
|
196
208
|
});
|
|
197
209
|
return complexTypesToCV.tuple(tupleData);
|
|
198
210
|
}
|
|
199
211
|
else if ("optional" in type) {
|
|
200
|
-
return (0, transactions_1.optionalCVOf)(
|
|
212
|
+
return (0, transactions_1.optionalCVOf)(generatedArgument
|
|
213
|
+
? argToCV(generatedArgument, type.optional)
|
|
214
|
+
: undefined);
|
|
201
215
|
}
|
|
202
216
|
else if ("response" in type) {
|
|
203
|
-
const status =
|
|
217
|
+
const status = generatedArgument.status;
|
|
204
218
|
const branchType = type.response[status];
|
|
205
|
-
const responseValue = argToCV(
|
|
219
|
+
const responseValue = argToCV(generatedArgument.value, branchType);
|
|
206
220
|
return complexTypesToCV.response(status, responseValue);
|
|
207
221
|
}
|
|
222
|
+
else if ("trait_reference" in type) {
|
|
223
|
+
return complexTypesToCV.trait_reference(generatedArgument);
|
|
224
|
+
}
|
|
208
225
|
else {
|
|
209
226
|
throw new Error(`Unsupported complex parameter type: ${JSON.stringify(type)}`);
|
|
210
227
|
}
|
|
@@ -241,45 +258,10 @@ const complexTypesToCV = {
|
|
|
241
258
|
else
|
|
242
259
|
throw new Error(`Unsupported response status: ${status}`);
|
|
243
260
|
},
|
|
261
|
+
trait_reference: (traitImplementation) => (0, transactions_1.principalCV)(traitImplementation),
|
|
244
262
|
};
|
|
245
263
|
const isBaseType = (type) => {
|
|
246
264
|
return ["int128", "uint128", "bool", "principal"].includes(type);
|
|
247
265
|
};
|
|
248
|
-
/**
|
|
249
|
-
* Checks if any parameter of the function contains a `trait_reference` type.
|
|
250
|
-
* @param fn The function interface.
|
|
251
|
-
* @returns Boolean - true if the function contains a trait reference, false
|
|
252
|
-
* otherwise.
|
|
253
|
-
*/
|
|
254
|
-
const isTraitReferenceFunction = (fn) => {
|
|
255
|
-
const hasTraitReference = (type) => {
|
|
256
|
-
if (typeof type === "string") {
|
|
257
|
-
// The type is a base type.
|
|
258
|
-
return type === "trait_reference";
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
// The type is a complex type.
|
|
262
|
-
if ("buffer" in type)
|
|
263
|
-
return false;
|
|
264
|
-
if ("string-ascii" in type)
|
|
265
|
-
return false;
|
|
266
|
-
if ("string-utf8" in type)
|
|
267
|
-
return false;
|
|
268
|
-
if ("list" in type)
|
|
269
|
-
return hasTraitReference(type.list.type);
|
|
270
|
-
if ("tuple" in type)
|
|
271
|
-
return type.tuple.some((item) => hasTraitReference(item.type));
|
|
272
|
-
if ("optional" in type)
|
|
273
|
-
return hasTraitReference(type.optional);
|
|
274
|
-
if ("response" in type)
|
|
275
|
-
return (hasTraitReference(type.response.ok) ||
|
|
276
|
-
hasTraitReference(type.response.error));
|
|
277
|
-
// Default to false for unexpected types.
|
|
278
|
-
return false;
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
return fn.args.some((arg) => hasTraitReference(arg.type));
|
|
282
|
-
};
|
|
283
|
-
exports.isTraitReferenceFunction = isTraitReferenceFunction;
|
|
284
266
|
const getContractNameFromContractId = (contractId) => contractId.split(".")[1];
|
|
285
267
|
exports.getContractNameFromContractId = getContractNameFromContractId;
|