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.
Files changed (52) hide show
  1. package/.prettierrc +7 -0
  2. package/README.md +17 -0
  3. package/bin/scrypt-testgen.js +2 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +48 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/generator/index.d.ts +6 -0
  9. package/dist/generator/index.d.ts.map +1 -0
  10. package/dist/generator/index.js +33 -0
  11. package/dist/generator/index.js.map +1 -0
  12. package/dist/generator/test-generator.d.ts +17 -0
  13. package/dist/generator/test-generator.d.ts.map +1 -0
  14. package/dist/generator/test-generator.js +177 -0
  15. package/dist/generator/test-generator.js.map +1 -0
  16. package/dist/generator/value-generator.d.ts +19 -0
  17. package/dist/generator/value-generator.d.ts.map +1 -0
  18. package/dist/generator/value-generator.js +130 -0
  19. package/dist/generator/value-generator.js.map +1 -0
  20. package/dist/model/contract-model.d.ts +42 -0
  21. package/dist/model/contract-model.d.ts.map +1 -0
  22. package/dist/model/contract-model.js +10 -0
  23. package/dist/model/contract-model.js.map +1 -0
  24. package/dist/parser/ast-utils.d.ts +7 -0
  25. package/dist/parser/ast-utils.d.ts.map +1 -0
  26. package/dist/parser/ast-utils.js +114 -0
  27. package/dist/parser/ast-utils.js.map +1 -0
  28. package/dist/parser/contract-parser.d.ts +18 -0
  29. package/dist/parser/contract-parser.d.ts.map +1 -0
  30. package/dist/parser/contract-parser.js +224 -0
  31. package/dist/parser/contract-parser.js.map +1 -0
  32. package/dist/parser/index.d.ts +3 -0
  33. package/dist/parser/index.d.ts.map +1 -0
  34. package/dist/parser/index.js +6 -0
  35. package/dist/parser/index.js.map +1 -0
  36. package/dist/utils/file-utils.d.ts +4 -0
  37. package/dist/utils/file-utils.d.ts.map +1 -0
  38. package/dist/utils/file-utils.js +26 -0
  39. package/dist/utils/file-utils.js.map +1 -0
  40. package/package.json +30 -0
  41. package/src/cli.ts +51 -0
  42. package/src/generator/index.ts +45 -0
  43. package/src/generator/test-generator.ts +216 -0
  44. package/src/generator/value-generator.ts +138 -0
  45. package/src/model/contract-model.ts +46 -0
  46. package/src/parser/ast-utils.ts +75 -0
  47. package/src/parser/contract-parser.ts +246 -0
  48. package/src/parser/index.ts +2 -0
  49. package/src/utils/file-utils.ts +22 -0
  50. package/templates/test-template.ts +0 -0
  51. package/test/integration/demo.ts +16 -0
  52. 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,3 @@
1
+ export { ContractParser } from './contract-parser';
2
+ export type { ContractModel } from '../model/contract-model';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,4 @@
1
+ export declare function ensureDirectoryExists(filePath: string): void;
2
+ export declare function readContractFile(filePath: string): string;
3
+ export declare function writeTestFile(filePath: string, content: string): void;
4
+ //# sourceMappingURL=file-utils.d.ts.map
@@ -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
+ }