@patchworkdev/common
Version:
Patchwork Development Kit
213 lines (212 loc) • 9.85 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const child_process_1 = require("child_process");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const types_1 = require("../../types");
const contractSchema_1 = require("../contractSchema");
const mainContractGen_1 = require("../mainContractGen");
const userContractGen_1 = require("../userContractGen");
function generateField(features) {
const fields = [
{
id: 1,
key: "single_char_field",
type: "char16",
description: "A single char field",
},
];
// Special case for DYNAMICREFLIBRARY feature
if (features.includes(types_1.Feature.DYNAMICREFLIBRARY)) {
fields.push({
id: fields.length + 1,
key: 'dynamic_literef',
type: 'literef',
arrayLength: 0,
description: 'Dynamic literef array for DYNAMICREFLIBRARY feature',
});
}
return fields;
}
function generateFeaturePermutations() {
const baseFeatures = [types_1.Feature.MINTABLE, types_1.Feature.LITEREF];
const patchTypes = [types_1.Feature.PATCH, types_1.Feature["1155PATCH"], types_1.Feature.ACCOUNTPATCH];
const fragmentTypes = [types_1.Feature.FRAGMENTMULTI, types_1.Feature.FRAGMENTSINGLE];
let permutations = [[]];
// Add base features
baseFeatures.forEach((feature) => {
permutations = permutations.flatMap((perm) => [perm, [...perm, feature]]);
});
// Add patch types (mutually exclusive)
permutations = permutations.flatMap((perm) => [
perm,
...patchTypes.map((patchType) => [...perm, patchType]),
]);
// Add fragment types (mutually exclusive)
permutations = permutations.flatMap((perm) => [
perm,
...fragmentTypes.map((fragmentType) => [...perm, fragmentType]),
]);
// Add REVERSIBLE only to permutations with a patch type
permutations = permutations.flatMap((perm) => patchTypes.some((patch) => perm.includes(patch))
? [perm, [...perm, types_1.Feature.REVERSIBLE]]
: [perm]);
// Add WEAKREF only to permutations with FRAGMENTSINGLE
permutations = permutations.flatMap((perm) => perm.includes(types_1.Feature.FRAGMENTSINGLE)
? [perm, [...perm, types_1.Feature.WEAKREF]]
: [perm]);
// Add DYNAMICREFLIBRARY only to permutations with LITEREF
permutations = permutations.flatMap((perm) => perm.includes(types_1.Feature.LITEREF)
? [perm, [...perm, types_1.Feature.DYNAMICREFLIBRARY]]
: [perm]);
return permutations;
}
function generateContractSchemaPermutations() {
const baseConfig = {
scopeName: "TestScope",
name: "TestContract",
symbol: "TST",
baseURI: "https://example.com/",
schemaURI: "https://example.com/schema",
imageURI: "https://example.com/image",
fields: [],
features: [],
fragments: []
};
const featurePermutations = generateFeaturePermutations();
return featurePermutations.map((features) => ({
...baseConfig,
fields: generateField(features),
features,
}));
}
function removeDirectoryRecursive(dirPath) {
if (fs_1.default.existsSync(dirPath)) {
fs_1.default.readdirSync(dirPath).forEach((file) => {
const curPath = path_1.default.join(dirPath, file);
if (fs_1.default.lstatSync(curPath).isDirectory()) {
// Recursive call for directories
removeDirectoryRecursive(curPath);
}
else {
// Delete file
fs_1.default.unlinkSync(curPath);
}
});
fs_1.default.rmdirSync(dirPath);
}
}
describe("Generate and Build Contract Schema Permutations", () => {
const outputDir = path_1.default.join(__dirname, "generated_contracts");
const schemaNoVerifyDir = path_1.default.join(__dirname, "schema_noverify");
const schemaNoBuildDir = path_1.default.join(__dirname, "schema_nobuild");
beforeAll(() => {
// Create directories if they don't exist
[outputDir, schemaNoVerifyDir, schemaNoBuildDir].forEach(dir => {
if (!fs_1.default.existsSync(dir)) {
fs_1.default.mkdirSync(dir, { recursive: true });
}
});
// Clear contents of schema_noverify and schema_nobuild
[schemaNoVerifyDir, schemaNoBuildDir].forEach(dir => {
removeDirectoryRecursive(dir);
fs_1.default.mkdirSync(dir);
});
// Clear contents of generated_contracts, preserving remappings.txt and foundry.toml
fs_1.default.readdirSync(outputDir).forEach(file => {
const filePath = path_1.default.join(outputDir, file);
if (file !== 'remappings.txt' && file !== 'foundry.toml') {
if (fs_1.default.lstatSync(filePath).isDirectory()) {
removeDirectoryRecursive(filePath);
}
else {
fs_1.default.unlinkSync(filePath);
}
}
});
});
it("should generate, save, and build all contract schema permutations", async () => {
console.log("Generating contract schema permutations...");
const permutations = generateContractSchemaPermutations();
const mainContractGen = new mainContractGen_1.MainContractGen();
const userContractGen = new userContractGen_1.UserContractGen();
let errors = [];
for (let index = 0; index < permutations.length; index++) {
// if (index > 1) {
// break;
// }
const config = permutations[index];
console.log(`Processing permutation ${index}:`, config);
const schema = new contractSchema_1.ContractSchemaImpl(config);
try {
schema.validate();
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`Schema validation failed for contract ${index}:`, errorMessage);
fs_1.default.writeFileSync(path_1.default.join(schemaNoVerifyDir, `schema_${index}.json`), JSON.stringify(config, null, 2));
errors.push(`Schema validation failed for contract ${index}: ${errorMessage}`);
continue; // Skip to the next permutation
}
try {
const mainCode = mainContractGen.gen(schema);
const userCode = userContractGen.gen(schema);
const mainFileName = path_1.default.join(outputDir, `TestContractGenerated.sol`);
const userFileName = path_1.default.join(outputDir, `TestContract.sol`);
fs_1.default.writeFileSync(mainFileName, mainCode);
fs_1.default.writeFileSync(userFileName, userCode);
console.log(`Generated contract ${index}`);
// Call forge build
await new Promise((resolve, reject) => {
(0, child_process_1.exec)('forge build', { cwd: outputDir }, (error, stdout, stderr) => {
if (error) {
console.error(`forge build failed for contract ${index}:`);
console.error(error);
fs_1.default.writeFileSync(path_1.default.join(schemaNoBuildDir, `schema_${index}.json`), JSON.stringify(config, null, 2));
errors.push(`forge build failed for contract ${index}: ${stderr}`);
resolve(); // Resolve even on error to continue processing
}
else {
console.log(`Successfully built contract ${index}`);
resolve();
}
});
});
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`Error in contract ${index}:`, errorMessage);
errors.push(`Error in contract ${index}: ${errorMessage}`);
}
finally {
// Clean up .sol files after each attempt
const mainFileName = path_1.default.join(outputDir, `TestContractGenerated.sol`);
const userFileName = path_1.default.join(outputDir, `TestContract.sol`);
if (fs_1.default.existsSync(mainFileName))
fs_1.default.unlinkSync(mainFileName);
if (fs_1.default.existsSync(userFileName))
fs_1.default.unlinkSync(userFileName);
}
// Add a small delay between iterations to allow for any pending I/O operations
await new Promise(resolve => setTimeout(resolve, 100));
}
console.log(`Total permutations processed: ${permutations.length}`);
console.log(`Total errors encountered: ${errors.length}`);
if (errors.length > 0) {
console.error("Errors encountered during processing:");
errors.forEach((error, index) => {
console.error(`${index + 1}: ${error}`);
});
throw new Error(`${errors.length} errors encountered during processing`);
}
expect(permutations.length).toBeGreaterThan(0);
});
afterAll(() => {
// Clean up after all tests
removeDirectoryRecursive(path_1.default.join(outputDir, 'out'));
removeDirectoryRecursive(path_1.default.join(outputDir, 'cache'));
});
});