scrypt-testgen 1.0.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/.prettierrc +7 -0
- package/README.md +17 -0
- package/bin/scrypt-testgen.js +2 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +48 -0
- package/dist/cli.js.map +1 -0
- package/dist/generator/index.d.ts +6 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +33 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/test-generator.d.ts +17 -0
- package/dist/generator/test-generator.d.ts.map +1 -0
- package/dist/generator/test-generator.js +177 -0
- package/dist/generator/test-generator.js.map +1 -0
- package/dist/generator/value-generator.d.ts +19 -0
- package/dist/generator/value-generator.d.ts.map +1 -0
- package/dist/generator/value-generator.js +130 -0
- package/dist/generator/value-generator.js.map +1 -0
- package/dist/model/contract-model.d.ts +42 -0
- package/dist/model/contract-model.d.ts.map +1 -0
- package/dist/model/contract-model.js +10 -0
- package/dist/model/contract-model.js.map +1 -0
- package/dist/parser/ast-utils.d.ts +7 -0
- package/dist/parser/ast-utils.d.ts.map +1 -0
- package/dist/parser/ast-utils.js +114 -0
- package/dist/parser/ast-utils.js.map +1 -0
- package/dist/parser/contract-parser.d.ts +18 -0
- package/dist/parser/contract-parser.d.ts.map +1 -0
- package/dist/parser/contract-parser.js +224 -0
- package/dist/parser/contract-parser.js.map +1 -0
- package/dist/parser/index.d.ts +3 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +6 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/utils/file-utils.d.ts +4 -0
- package/dist/utils/file-utils.d.ts.map +1 -0
- package/dist/utils/file-utils.js +26 -0
- package/dist/utils/file-utils.js.map +1 -0
- package/package.json +30 -0
- package/src/cli.ts +51 -0
- package/src/generator/index.ts +45 -0
- package/src/generator/test-generator.ts +216 -0
- package/src/generator/value-generator.ts +138 -0
- package/src/model/contract-model.ts +46 -0
- package/src/parser/ast-utils.ts +75 -0
- package/src/parser/contract-parser.ts +246 -0
- package/src/parser/index.ts +2 -0
- package/src/utils/file-utils.ts +22 -0
- package/templates/test-template.ts +0 -0
- package/test/integration/demo.ts +16 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getDecoratorText = getDecoratorText;
|
|
37
|
+
exports.getTypeText = getTypeText;
|
|
38
|
+
exports.getDefaultValueForType = getDefaultValueForType;
|
|
39
|
+
exports.isSmartContractClass = isSmartContractClass;
|
|
40
|
+
exports.findDecorator = findDecorator;
|
|
41
|
+
const ts = __importStar(require("typescript"));
|
|
42
|
+
function getDecoratorText(decorator) {
|
|
43
|
+
const decoratorText = decorator.getText();
|
|
44
|
+
if (decoratorText.includes('@prop')) {
|
|
45
|
+
return '@prop';
|
|
46
|
+
}
|
|
47
|
+
else if (decoratorText.includes('@method')) {
|
|
48
|
+
return '@method';
|
|
49
|
+
}
|
|
50
|
+
return decoratorText;
|
|
51
|
+
}
|
|
52
|
+
function getTypeText(type, checker) {
|
|
53
|
+
if (!type)
|
|
54
|
+
return 'any';
|
|
55
|
+
const typeText = type.getText();
|
|
56
|
+
// Handle sCrypt specific types
|
|
57
|
+
if (typeText.includes('bigint'))
|
|
58
|
+
return 'bigint';
|
|
59
|
+
if (typeText.includes('ByteString'))
|
|
60
|
+
return 'ByteString';
|
|
61
|
+
if (typeText.includes('PubKey'))
|
|
62
|
+
return 'PubKey';
|
|
63
|
+
if (typeText.includes('Sig'))
|
|
64
|
+
return 'Sig';
|
|
65
|
+
if (typeText.includes('boolean'))
|
|
66
|
+
return 'boolean';
|
|
67
|
+
if (typeText.includes('number'))
|
|
68
|
+
return 'number';
|
|
69
|
+
return typeText;
|
|
70
|
+
}
|
|
71
|
+
function getDefaultValueForType(type) {
|
|
72
|
+
switch (type) {
|
|
73
|
+
case 'bigint':
|
|
74
|
+
return '1n';
|
|
75
|
+
case 'boolean':
|
|
76
|
+
return 'true';
|
|
77
|
+
case 'ByteString':
|
|
78
|
+
return `toByteString('00', false)`;
|
|
79
|
+
case 'PubKey':
|
|
80
|
+
return `myPublicKey`;
|
|
81
|
+
case 'Sig':
|
|
82
|
+
return `mySignature`;
|
|
83
|
+
case 'number':
|
|
84
|
+
return '0';
|
|
85
|
+
case 'string':
|
|
86
|
+
return "''";
|
|
87
|
+
default:
|
|
88
|
+
if (type.includes('[]'))
|
|
89
|
+
return '[]';
|
|
90
|
+
return 'undefined';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function isSmartContractClass(node) {
|
|
94
|
+
if (!ts.isClassDeclaration(node))
|
|
95
|
+
return false;
|
|
96
|
+
// Check if extends SmartContract
|
|
97
|
+
if (node.heritageClauses) {
|
|
98
|
+
for (const heritage of node.heritageClauses) {
|
|
99
|
+
for (const type of heritage.types) {
|
|
100
|
+
if (type.getText().includes('SmartContract')) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
function findDecorator(node, decoratorName) {
|
|
109
|
+
if (!ts.canHaveDecorators(node))
|
|
110
|
+
return undefined;
|
|
111
|
+
const decorators = ts.getDecorators(node);
|
|
112
|
+
return decorators?.find((decorator) => decorator.getText().includes(decoratorName));
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=ast-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast-utils.js","sourceRoot":"","sources":["../../src/parser/ast-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ContractModel } from '../model/contract-model';
|
|
2
|
+
export declare class ContractParser {
|
|
3
|
+
private program;
|
|
4
|
+
private checker;
|
|
5
|
+
private sourceFile;
|
|
6
|
+
constructor(filePath: string);
|
|
7
|
+
parse(): ContractModel;
|
|
8
|
+
private findContractClass;
|
|
9
|
+
private parseConstructor;
|
|
10
|
+
private parseProperties;
|
|
11
|
+
private parseMethods;
|
|
12
|
+
private parseMethodParameters;
|
|
13
|
+
private classifyMethod;
|
|
14
|
+
private doesMethodMutateState;
|
|
15
|
+
private parseImports;
|
|
16
|
+
private getExtendedClass;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=contract-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-parser.d.ts","sourceRoot":"","sources":["../../src/parser/contract-parser.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,aAAa,EACd,MAAM,yBAAyB,CAAC;AASjC,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,UAAU,CAAgB;gBAEtB,QAAQ,EAAE,MAAM;IAgB5B,KAAK,IAAI,aAAa;IAmBtB,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,eAAe;IA4BvB,OAAO,CAAC,YAAY;IAgDpB,OAAO,CAAC,qBAAqB;IAkB7B,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,gBAAgB;CAczB"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ContractParser = void 0;
|
|
37
|
+
const ts = __importStar(require("typescript"));
|
|
38
|
+
const contract_model_1 = require("../model/contract-model");
|
|
39
|
+
const ast_utils_1 = require("./ast-utils");
|
|
40
|
+
class ContractParser {
|
|
41
|
+
constructor(filePath) {
|
|
42
|
+
this.program = ts.createProgram([filePath], {
|
|
43
|
+
target: ts.ScriptTarget.ES2020,
|
|
44
|
+
module: ts.ModuleKind.CommonJS,
|
|
45
|
+
experimentalDecorators: true,
|
|
46
|
+
emitDecoratorMetadata: true
|
|
47
|
+
});
|
|
48
|
+
this.checker = this.program.getTypeChecker();
|
|
49
|
+
this.sourceFile = this.program.getSourceFile(filePath);
|
|
50
|
+
if (!this.sourceFile) {
|
|
51
|
+
throw new Error(`Cannot read source file: ${filePath}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
parse() {
|
|
55
|
+
const contractClass = this.findContractClass();
|
|
56
|
+
if (!contractClass) {
|
|
57
|
+
throw new Error('No SmartContract class found in file');
|
|
58
|
+
}
|
|
59
|
+
const className = contractClass.name?.text || 'UnknownContract';
|
|
60
|
+
return {
|
|
61
|
+
name: className,
|
|
62
|
+
filePath: this.sourceFile.fileName,
|
|
63
|
+
constructorArgs: this.parseConstructor(contractClass),
|
|
64
|
+
props: this.parseProperties(contractClass),
|
|
65
|
+
methods: this.parseMethods(contractClass),
|
|
66
|
+
imports: this.parseImports(),
|
|
67
|
+
extendsClass: this.getExtendedClass(contractClass)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
findContractClass() {
|
|
71
|
+
let contractClass;
|
|
72
|
+
const visit = (node) => {
|
|
73
|
+
if ((0, ast_utils_1.isSmartContractClass)(node)) {
|
|
74
|
+
if (contractClass) {
|
|
75
|
+
throw new Error('Multiple SmartContract classes found. Only one per file is supported.');
|
|
76
|
+
}
|
|
77
|
+
contractClass = node;
|
|
78
|
+
}
|
|
79
|
+
ts.forEachChild(node, visit);
|
|
80
|
+
};
|
|
81
|
+
visit(this.sourceFile);
|
|
82
|
+
return contractClass;
|
|
83
|
+
}
|
|
84
|
+
parseConstructor(classNode) {
|
|
85
|
+
const constructor = classNode.members.find(member => ts.isConstructorDeclaration(member));
|
|
86
|
+
if (!constructor || !constructor.parameters) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
return constructor.parameters.map(param => {
|
|
90
|
+
const paramName = param.name.getText();
|
|
91
|
+
const type = (0, ast_utils_1.getTypeText)(param.type, this.checker);
|
|
92
|
+
return {
|
|
93
|
+
name: paramName,
|
|
94
|
+
type: type,
|
|
95
|
+
defaultValue: param.initializer ? param.initializer.getText() : undefined
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
parseProperties(classNode) {
|
|
100
|
+
const properties = [];
|
|
101
|
+
for (const member of classNode.members) {
|
|
102
|
+
if (ts.isPropertyDeclaration(member)) {
|
|
103
|
+
const decorators = ts.getDecorators(member);
|
|
104
|
+
if (decorators && decorators.length > 0) {
|
|
105
|
+
const propDecorator = (0, ast_utils_1.findDecorator)(member, '@prop');
|
|
106
|
+
if (propDecorator) {
|
|
107
|
+
const propName = member.name.getText();
|
|
108
|
+
const type = (0, ast_utils_1.getTypeText)(member.type, this.checker);
|
|
109
|
+
const decoratorText = decorators[0].getText();
|
|
110
|
+
const isMutable = decoratorText.includes('true');
|
|
111
|
+
properties.push({
|
|
112
|
+
name: propName,
|
|
113
|
+
type: type,
|
|
114
|
+
isMutable: isMutable,
|
|
115
|
+
decoratorText: decoratorText
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return properties;
|
|
122
|
+
}
|
|
123
|
+
parseMethods(classNode) {
|
|
124
|
+
const methods = [];
|
|
125
|
+
for (const member of classNode.members) {
|
|
126
|
+
if (ts.isMethodDeclaration(member)) {
|
|
127
|
+
const decorators = ts.getDecorators(member);
|
|
128
|
+
if (decorators && decorators.length > 0) {
|
|
129
|
+
const methodDecorator = (0, ast_utils_1.findDecorator)(member, '@method');
|
|
130
|
+
if (methodDecorator) {
|
|
131
|
+
const methodName = member.name.getText();
|
|
132
|
+
// Skip private methods
|
|
133
|
+
if (!member.modifiers?.some(m => m.kind === ts.SyntaxKind.PublicKeyword)) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const parameters = this.parseMethodParameters(member);
|
|
137
|
+
const returnType = (0, ast_utils_1.getTypeText)(member.type, this.checker);
|
|
138
|
+
const methodText = member.getText();
|
|
139
|
+
const usesHashOutputs = methodText.includes('this.ctx.hashOutputs');
|
|
140
|
+
const hasAssert = methodText.includes('assert(');
|
|
141
|
+
// Classify method
|
|
142
|
+
const category = this.classifyMethod(member, methodText);
|
|
143
|
+
// Determine if method mutates state
|
|
144
|
+
const mutatesState = this.doesMethodMutateState(member, methodText);
|
|
145
|
+
methods.push({
|
|
146
|
+
name: methodName,
|
|
147
|
+
parameters: parameters,
|
|
148
|
+
returnType: returnType,
|
|
149
|
+
category: category,
|
|
150
|
+
isPublic: true,
|
|
151
|
+
mutatesState: mutatesState,
|
|
152
|
+
usesHashOutputs: usesHashOutputs,
|
|
153
|
+
hasAssert: hasAssert,
|
|
154
|
+
decoratorText: (0, ast_utils_1.getDecoratorText)(methodDecorator)
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return methods;
|
|
161
|
+
}
|
|
162
|
+
parseMethodParameters(method) {
|
|
163
|
+
if (!method.parameters) {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
return method.parameters.map(param => {
|
|
167
|
+
const paramName = param.name.getText();
|
|
168
|
+
const type = (0, ast_utils_1.getTypeText)(param.type, this.checker);
|
|
169
|
+
const isOptional = !!param.questionToken || !!param.initializer;
|
|
170
|
+
return {
|
|
171
|
+
name: paramName,
|
|
172
|
+
type: type,
|
|
173
|
+
isOptional: isOptional
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
classifyMethod(method, methodText) {
|
|
178
|
+
const returnType = method.type?.getText() || 'void';
|
|
179
|
+
// Methods that return boolean are often validation methods
|
|
180
|
+
if (returnType === 'boolean') {
|
|
181
|
+
return contract_model_1.MethodCategory.PURE_VALIDATION;
|
|
182
|
+
}
|
|
183
|
+
// Methods that modify @prop(true) properties
|
|
184
|
+
if (this.doesMethodMutateState(method, methodText)) {
|
|
185
|
+
return contract_model_1.MethodCategory.STATE_TRANSITION;
|
|
186
|
+
}
|
|
187
|
+
// Methods that check spending conditions
|
|
188
|
+
if (methodText.includes('this.ctx') || methodText.includes('hashOutputs')) {
|
|
189
|
+
return contract_model_1.MethodCategory.SPENDING_CONSTRAINT;
|
|
190
|
+
}
|
|
191
|
+
return contract_model_1.MethodCategory.PURE_VALIDATION;
|
|
192
|
+
}
|
|
193
|
+
doesMethodMutateState(method, methodText) {
|
|
194
|
+
// Check for assignment to this.props (especially mutable ones)
|
|
195
|
+
const assignmentRegex = /this\.\w+\s*=/;
|
|
196
|
+
return assignmentRegex.test(methodText);
|
|
197
|
+
}
|
|
198
|
+
parseImports() {
|
|
199
|
+
const imports = [];
|
|
200
|
+
const visit = (node) => {
|
|
201
|
+
if (ts.isImportDeclaration(node)) {
|
|
202
|
+
imports.push(node.getText());
|
|
203
|
+
}
|
|
204
|
+
ts.forEachChild(node, visit);
|
|
205
|
+
};
|
|
206
|
+
visit(this.sourceFile);
|
|
207
|
+
return imports;
|
|
208
|
+
}
|
|
209
|
+
getExtendedClass(classNode) {
|
|
210
|
+
if (!classNode.heritageClauses)
|
|
211
|
+
return '';
|
|
212
|
+
for (const heritage of classNode.heritageClauses) {
|
|
213
|
+
for (const type of heritage.types) {
|
|
214
|
+
const typeText = type.getText();
|
|
215
|
+
if (typeText.includes('SmartContract')) {
|
|
216
|
+
return typeText;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return '';
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
exports.ContractParser = ContractParser;
|
|
224
|
+
//# sourceMappingURL=contract-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-parser.js","sourceRoot":"","sources":["../../src/parser/contract-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContractParser = void 0;
|
|
4
|
+
var contract_parser_1 = require("./contract-parser");
|
|
5
|
+
Object.defineProperty(exports, "ContractParser", { enumerable: true, get: function () { return contract_parser_1.ContractParser; } });
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":";;;AAAA,qDAAmD;AAA1C,iHAAA,cAAc,OAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-utils.d.ts","sourceRoot":"","sources":["../../src/utils/file-utils.ts"],"names":[],"mappings":"AAGA,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAK5D;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAGrE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureDirectoryExists = ensureDirectoryExists;
|
|
4
|
+
exports.readContractFile = readContractFile;
|
|
5
|
+
exports.writeTestFile = writeTestFile;
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
function ensureDirectoryExists(filePath) {
|
|
9
|
+
const dir = (0, path_1.dirname)(filePath);
|
|
10
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
11
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function readContractFile(filePath) {
|
|
15
|
+
try {
|
|
16
|
+
return (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
throw new Error(`Cannot read contract file: ${filePath}. ${error}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function writeTestFile(filePath, content) {
|
|
23
|
+
ensureDirectoryExists(filePath);
|
|
24
|
+
(0, fs_1.writeFileSync)(filePath, content, 'utf-8');
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=file-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-utils.js","sourceRoot":"","sources":["../../src/utils/file-utils.ts"],"names":[],"mappings":";;AAGA,sDAKC;AAED,4CAMC;AAED,sCAGC;AArBD,2BAAwE;AACxE,+BAA+B;AAE/B,SAAgB,qBAAqB,CAAC,QAAgB;IACpD,MAAM,GAAG,GAAG,IAAA,cAAO,EAAC,QAAQ,CAAC,CAAC;IAC9B,IAAI,CAAC,IAAA,eAAU,EAAC,GAAG,CAAC,EAAE,CAAC;QACrB,IAAA,cAAS,EAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED,SAAgB,gBAAgB,CAAC,QAAgB;IAC/C,IAAI,CAAC;QACH,OAAO,IAAA,iBAAY,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;IACtE,CAAC;AACH,CAAC;AAED,SAAgB,aAAa,CAAC,QAAgB,EAAE,OAAe;IAC7D,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAChC,IAAA,kBAAa,EAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "scrypt-testgen",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Automatic test generator for sCrypt-ts smart contracts",
|
|
5
|
+
"main": "dist/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"scrypt-testgen": "./bin/scrypt-testgen.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "ts-node src/cli.ts",
|
|
12
|
+
"dev": "ts-node src/cli.ts",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": ["scrypt", "bitcoin", "smart-contract", "test", "mocha"],
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@types/node": "^20.0.0",
|
|
20
|
+
"chalk": "^4.1.2",
|
|
21
|
+
"commander": "^11.0.0",
|
|
22
|
+
"typescript": "^5.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/chai": "^4.3.0",
|
|
26
|
+
"@types/mocha": "^10.0.0",
|
|
27
|
+
"ts-node": "^10.9.0",
|
|
28
|
+
"prettier": "^3.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { generateTests } from './generator';
|
|
5
|
+
import { resolve, dirname, basename, join } from 'path';
|
|
6
|
+
import { mkdirSync, existsSync } from 'fs';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
const program = new Command();
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.name('scrypt-testgen')
|
|
13
|
+
.description('Generate Mocha tests for sCrypt-ts smart contracts')
|
|
14
|
+
.version('1.0.0');
|
|
15
|
+
|
|
16
|
+
program
|
|
17
|
+
.argument('<contract-path>', 'Path to the sCrypt contract TypeScript file')
|
|
18
|
+
.option('-m, --minimal', 'Generate only happy-path tests', false)
|
|
19
|
+
.option('-c, --coverage', 'Include revert and edge-case tests', false)
|
|
20
|
+
.option('-o, --overwrite', 'Replace existing test file', false)
|
|
21
|
+
.option('-o, --output <path>', 'Custom output path for test file')
|
|
22
|
+
.action(async (contractPath, options) => {
|
|
23
|
+
try {
|
|
24
|
+
const resolvedPath = resolve(process.cwd(), contractPath);
|
|
25
|
+
|
|
26
|
+
if (!existsSync(resolvedPath)) {
|
|
27
|
+
console.error(chalk.red(`Error: Contract file not found: ${resolvedPath}`));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const outputPath = options.output
|
|
32
|
+
? resolve(process.cwd(), options.output)
|
|
33
|
+
: join(dirname(resolvedPath), '..', 'test', basename(resolvedPath).replace('.ts', '.test.ts'));
|
|
34
|
+
|
|
35
|
+
// Ensure test directory exists
|
|
36
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
37
|
+
|
|
38
|
+
await generateTests(resolvedPath, outputPath, {
|
|
39
|
+
minimal: options.minimal,
|
|
40
|
+
coverage: options.coverage,
|
|
41
|
+
overwrite: options.overwrite
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
console.log(chalk.green(`✓ Tests generated: ${outputPath}`));
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error(chalk.red(`Error: ${error}`));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
program.parse();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ContractParser } from '../parser';
|
|
2
|
+
import { TestGenerator } from './test-generator';
|
|
3
|
+
import { existsSync, writeFileSync, readFileSync } from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
|
|
6
|
+
export async function generateTests(
|
|
7
|
+
contractPath: string,
|
|
8
|
+
outputPath: string,
|
|
9
|
+
options: {
|
|
10
|
+
minimal: boolean;
|
|
11
|
+
coverage: boolean;
|
|
12
|
+
overwrite: boolean;
|
|
13
|
+
}
|
|
14
|
+
): Promise<void> {
|
|
15
|
+
|
|
16
|
+
// Check if output file exists
|
|
17
|
+
if (existsSync(outputPath) && !options.overwrite) {
|
|
18
|
+
throw new Error(`Test file already exists: ${outputPath}. Use --overwrite to replace.`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Parse the contract
|
|
22
|
+
const parser = new ContractParser(contractPath);
|
|
23
|
+
const model = parser.parse();
|
|
24
|
+
|
|
25
|
+
// Generate tests
|
|
26
|
+
const generator = new TestGenerator();
|
|
27
|
+
const testCode = generator.generateTestFile(model, {
|
|
28
|
+
minimal: options.minimal,
|
|
29
|
+
coverage: options.coverage
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Format the output (simplified - in production, use prettier API)
|
|
33
|
+
const formattedCode = formatCode(testCode);
|
|
34
|
+
|
|
35
|
+
// Write to file
|
|
36
|
+
writeFileSync(outputPath, formattedCode, 'utf-8');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function formatCode(code: string): string {
|
|
40
|
+
// Simple formatting - in production, integrate with Prettier
|
|
41
|
+
return code
|
|
42
|
+
.replace(/\n\s*\n\s*\n/g, '\n\n')
|
|
43
|
+
.replace(/\s+$/gm, '')
|
|
44
|
+
.trim() + '\n';
|
|
45
|
+
}
|