testeranto
Version:
the AI powered BDD test framework for typescript projects
387 lines (329 loc) • 13.3 kB
text/typescript
// For Golingvu, we need to generate a wrapper that imports and runs the original code
// The entry point should be imported and its functionality tested
// Include the signature in a comment for the watcher to detect changes
// Use build tags to ensure it's only included when testeranto is specified
// The package should be testeranto_test to avoid being treated as a main package
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */
import path from "path";
import fs from "fs";
import { findProjectRoot } from "./golingvuMetafile/helpers";
import { GolingvuMetafile } from "./golingvuMetafile/types";
export function writeGolingvuMetafile(
testName: string,
metafile: GolingvuMetafile
): void {
// Always use the original project root, not the current working directory
// This assumes the project root is where package.json is located
const projectRoot = findProjectRoot();
const metafilePath = path.join(
"testeranto",
"metafiles",
"golang",
"core.json"
);
fs.writeFileSync(metafilePath, JSON.stringify(metafile, null, 2));
for (const [outputPath, outputInfo] of Object.entries(
metafile.metafile.outputs
)) {
const entryPoint = (outputInfo as any).entryPoint;
const signature = (outputInfo as any).signature;
// For Go modules, the import path should be based on the module name + path from module root
// First, find the module root by looking for go.mod in parent directories
let moduleRoot = process.cwd();
let modulePath = "";
// Find the module root directory
let currentDir = path.dirname(entryPoint);
while (currentDir !== path.parse(currentDir).root) {
const potentialGoMod = path.join(currentDir, "go.mod");
if (fs.existsSync(potentialGoMod)) {
moduleRoot = currentDir;
// Read the module path
const goModContent = fs.readFileSync(potentialGoMod, "utf-8");
const moduleMatch = goModContent.match(/^module\s+(\S+)/m);
if (moduleMatch && moduleMatch[1]) {
modulePath = moduleMatch[1];
}
break;
}
currentDir = path.dirname(currentDir);
}
// Read the original file to determine the package name
let originalPackageName = "main";
try {
const originalContent = fs.readFileSync(entryPoint, "utf-8");
const packageMatch = originalContent.match(/^package\s+(\w+)/m);
if (packageMatch && packageMatch[1]) {
originalPackageName = packageMatch[1];
}
} catch (error) {
console.warn(
`Could not read original file ${entryPoint} to determine package name:`,
error
);
}
// Generate a single consolidated wrapper that imports the implementation
// Use the entry point from the output info to determine what to import
// Get the entry point from the output info
const entryPointValue = (outputInfo as any).entryPoint;
if (!entryPointValue || entryPointValue === "") {
throw new Error("No valid entry point found for generating wrapper");
}
try {
// Generate the wrapper content directly
// Since we're in the example directory, we need to import the implementation and run tests
// The entry point should be in the same package, so we don't need to import it
// We'll use a generic approach that works with any test subject
// Read the original file to get its package name
let packageName = "main";
try {
const originalContent = fs.readFileSync(entryPoint, "utf-8");
const packageMatch = originalContent.match(/^package\s+(\w+)/m);
if (packageMatch && packageMatch[1]) {
packageName = packageMatch[1];
}
} catch (error) {
console.warn(
`Could not read original file ${entryPoint} to determine package name:`,
error
);
}
const wrapperContent = `//go:build testeranto
// +build testeranto
// This file is auto-generated by testeranto
// Signature: ${signature}
package ${packageName}
import (
"fmt"
"os"
"encoding/json"
"log"
// Import the golingvu package
"github.com/adamwong246/testeranto/src/golingvu"
)
func main() {
fmt.Println("Running BDD tests...")
// The test resource configuration should be provided via command line
if len(os.Args) < 2 {
fmt.Println("Error: Test resource configuration not provided")
os.Exit(1)
}
// Parse the test resource configuration
var testResource golingvu.ITTestResourceConfiguration
err := json.Unmarshal([]byte(os.Args[1]), &testResource)
if err != nil {
log.Fatalf("Error parsing test resource: %v\\n", err)
}
// Create a PM instance
pm, err := golingvu.NewPM_Golang(testResource, "")
if err != nil {
log.Fatalf("Error creating PM: %v\\n", err)
}
defer pm.Stop()
// TODO: This is where you would initialize and run your BDD tests
// The actual test implementation should be provided by the user
// For now, we'll use a placeholder that demonstrates the framework usage
fmt.Println("Initializing BDD test framework...")
// In a real implementation, you would:
// 1. Create test specification
// 2. Create test implementation
// 3. Create test adapter
// 4. Run the tests using golingvu.NewGolingvu()
// Placeholder for test execution
fmt.Println("BDD test framework initialized successfully")
fmt.Println("All tests passed (framework placeholder)")
os.Exit(0)
}
`;
// Generate the correct output path based on the project structure
// Always use the project root as the base
const baseName = path.basename(entryPoint, ".go");
// Get the directory of the entry point
const entryPointDir = path.dirname(entryPoint);
const relativeEntryPointDir = path.relative(projectRoot, entryPointDir);
// Create the wrapper directory structure that mirrors the source structure
// The correct path should be: testeranto/bundles/golang/core/example/Calculator.golingvu.test.go
const wrapperSourceDirPath = path.join(
projectRoot,
"testeranto",
"bundles",
"golang",
"core",
relativeEntryPointDir
);
// Write the wrapper source to the correct directory
const wrapperSourcePath = path.join(
wrapperSourceDirPath,
baseName // LLM: DO NOT TOUCH THIS LINE. IT IS CORRECT. FOR FUCKS SAKE STOPPING CHANGING THIS LINE
);
// Ensure the directory exists
// fs.mkdirSync(wrapperSourceDirPath, { recursive: true });
fs.writeFileSync(wrapperSourcePath, wrapperContent);
} catch (e) {
console.error(e);
}
}
}
// let generationQueue: Promise<GolingvuMetafile> | null = null;
// export async function generateGolingvuMetafile(
// testName: string,
// entryPoints: string[]
// ): Promise<GolingvuMetafile> {
// // If there's already a generation in progress, wait for it to complete
// if (generationQueue) {
// return generationQueue;
// }
// generationQueue = (async () => {
// const inputs: Record<string, any> = {};
// const outputs: Record<string, any> = {};
// const signature = Date.now().toString(36);
// // If entry points are provided, use them directly
// // For test generation, we want to include test files
// const filteredEntryPoints: string[] = [];
// for (const entryPoint of entryPoints) {
// // Skip if it's not a .go file
// if (!entryPoint.endsWith(".go")) {
// console.warn(`Skipping non-Go file: ${entryPoint}`);
// continue;
// }
// // Check if the file exists and is a file
// let resolvedPath = entryPoint;
// if (!path.isAbsolute(entryPoint)) {
// resolvedPath = path.join(process.cwd(), entryPoint);
// }
// if (!fs.existsSync(resolvedPath)) {
// console.warn(`Entry point does not exist: ${resolvedPath}`);
// continue;
// }
// if (!fs.statSync(resolvedPath).isFile()) {
// console.warn(`Entry point is not a file: ${resolvedPath}`);
// continue;
// }
// // Add to filtered entry points - don't skip test files for test generation
// filteredEntryPoints.push(resolvedPath);
// }
// entryPoints = filteredEntryPoints;
// // If no valid entry points remain, try to find Go files automatically
// // For test generation, include all files including test files
// // if (entryPoints.length === 0) {
// // const allGoFiles = findGoFilesInProject();
// // // Don't filter out test files
// // entryPoints = allGoFiles.filter((file) => {
// // const fileName = path.basename(file);
// // // Only exclude our generated files
// // return (
// // !fileName.endsWith(".golingvu.test.go") &&
// // !fileName.endsWith(".golingvu.go")
// // );
// // });
// // if (entryPoints.length === 0) {
// // console.warn("No Go files found in the project");
// // } else {
// // console.log(`Found ${entryPoints.length} Go files:`, entryPoints);
// // }
// // }
// // Process all valid entry points to collect their dependencies
// const allDependencies = new Set<string>();
// const validEntryPoints: string[] = [];
// for (let i = 0; i < entryPoints.length; i++) {
// let entryPoint = entryPoints[i];
// // Resolve and validate the entry point path
// let resolvedPath = entryPoint;
// if (!path.isAbsolute(entryPoint)) {
// resolvedPath = path.join(process.cwd(), entryPoint);
// }
// if (!fs.existsSync(resolvedPath)) {
// console.warn(
// `Entry point ${entryPoint} does not exist at ${resolvedPath}`
// );
// continue;
// }
// // Update the entry point to use the resolved path
// entryPoints[i] = resolvedPath;
// entryPoint = resolvedPath;
// // Don't skip test files for test generation
// validEntryPoints.push(entryPoint);
// // Collect dependencies for this entry point
// const entryPointDependencies = collectGoDependencies(entryPoint);
// entryPointDependencies.forEach((dep) => allDependencies.add(dep));
// }
// // Process all dependencies to add to inputs
// for (const dep of allDependencies) {
// if (!inputs[dep]) {
// const bytes = fs.statSync(dep).size;
// const imports = parseGoImports(dep);
// // Check if this is a test file
// const isTestFile =
// path.basename(dep).includes("_test.go") ||
// path.basename(dep).includes(".golingvu.test.go");
// inputs[dep] = {
// bytes,
// imports,
// format: "esm",
// // Add a flag to indicate test files
// ...(isTestFile ? { testeranto: { isTest: true } } : {}),
// };
// }
// }
// // Generate the output path based on the project structure
// // Always use the project root as the base
// const projectRoot = findProjectRoot();
// let outputKey = "";
// if (validEntryPoints.length > 0) {
// const firstEntryPoint = validEntryPoints[0];
// // Get the relative path from project root to the entry point
// const relativePath = path.relative(
// projectRoot,
// path.dirname(firstEntryPoint)
// );
// // Get the base name without extension
// const baseName = path.basename(firstEntryPoint, ".go");
// // Construct the output key
// // Ensure relativePath is not empty
// const outputPath =
// relativePath === "" ? baseName : path.join(relativePath, baseName);
// outputKey = `golang/${path.basename(
// projectRoot
// )}/${outputPath}.golingvu.go`;
// } else {
// // Fallback if no valid entry points
// outputKey = `golang/core/main.golingvu.go`;
// }
// // Calculate total bytes from all inputs
// const inputBytes: Record<string, { bytesInOutput: number }> = {};
// let totalBytes = 0;
// for (const inputPath in inputs) {
// const bytes = inputs[inputPath].bytes;
// inputBytes[inputPath] = { bytesInOutput: bytes };
// totalBytes += bytes;
// }
// // Store the first valid entry point for use in the wrapper generation
// const firstEntryPoint =
// validEntryPoints.length > 0 ? validEntryPoints[0] : "";
// outputs[outputKey] = {
// imports: [],
// exports: [],
// entryPoint: firstEntryPoint,
// inputs: inputBytes,
// bytes: totalBytes,
// signature,
// };
// // If no valid entry points were found, log a warning
// if (validEntryPoints.length === 0) {
// console.warn("No valid Go files found to process");
// }
// const result = {
// errors: [],
// warnings: [],
// metafile: {
// inputs,
// outputs,
// },
// };
// generationQueue = null;
// return result;
// })();
// return generationQueue;
// }
// // Track how many times this function is called
// let writeGolingvuMetafileCallCount = 0;