Refactor tests

This commit is contained in:
Andras Schmelczer 2026-01-18 13:46:59 +00:00
commit f53ac121e8
19 changed files with 353 additions and 571 deletions

View file

@ -5,229 +5,101 @@ import { ServerControl } from "./server-control";
import type { TestDefinition } from "./test-definition";
import { writeWriteConflictTest } from "./tests/write-write-conflict.test";
import { renameCreateConflictTest } from "./tests/rename-create-conflict.test";
import { TOKEN, REMOTE_URI, SERVER_BINARY_PATH, CONFIG_PATH } from "./consts";
import * as path from "node:path";
import * as fs from "node:fs";
import { debugging, Logger } from "sync-client";
// Global error handlers to catch unhandled errors
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection at:", promise);
console.error("Reason:", reason);
const logger = new Logger();
debugging.logToConsole(logger);
process.on("unhandledRejection", (reason) => {
logger.error(`Unhandled Rejection: ${reason}`);
process.exit(1);
});
process.on("uncaughtException", (error) => {
console.error("Uncaught Exception:", error);
logger.error(`Uncaught Exception: ${error}`);
process.exit(1);
});
// Available tests - using Partial to allow undefined lookup
const TESTS: Partial<Record<string, TestDefinition>> = {
"write-write-conflict": writeWriteConflictTest,
"rename-create-conflict": renameCreateConflictTest
};
function printHelp(): void {
console.log(`
Deterministic Test Runner for VaultLink
Usage:
npm run test [options]
Options:
--test <name> Run specific test (or "all")
--list List available tests
--server <path> Path to sync_server binary (default: auto-detect)
--config <path> Path to config file (default: config-e2e.yml)
--no-manage-server Don't start/stop server (assume it's running)
--help, -h Show this help
Examples:
npm run test
npm run test -- --test write-write-conflict
npm run test -- --test all
npm run test -- --list
npm run test -- --no-manage-server --test rename-create-conflict
`);
}
async function main(): Promise<void> {
const args = process.argv.slice(2);
const cwd = process.cwd();
let projectRoot = cwd;
// Parse arguments
let testName: string | undefined = undefined;
let serverPath: string | undefined = undefined;
let configPath: string | undefined = undefined;
let manageServer = true;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === "--test" && i + 1 < args.length) {
testName = args[++i];
} else if (arg === "--server" && i + 1 < args.length) {
serverPath = args[++i];
} else if (arg === "--config" && i + 1 < args.length) {
configPath = args[++i];
} else if (arg === "--no-manage-server") {
manageServer = false;
} else if (arg === "--list") {
console.log("\nAvailable tests:");
for (const [name, test] of Object.entries(TESTS)) {
if (test !== undefined) {
console.log(` ${name}: ${test.description ?? test.name}`);
}
}
process.exit(0);
} else if (arg === "--help" || arg === "-h") {
printHelp();
process.exit(0);
}
if (cwd.endsWith("frontend/deterministic-tests")) {
projectRoot = path.resolve(cwd, "../..");
} else if (cwd.endsWith("frontend")) {
projectRoot = path.resolve(cwd, "..");
}
// Default values
if (serverPath === undefined) {
// Try to find project root from current working directory
const cwd = process.cwd();
let projectRoot = cwd;
// If we're in frontend/deterministic-tests, go up two levels
if (
cwd.endsWith("frontend/deterministic-tests") ||
cwd.endsWith("frontend\\deterministic-tests")
) {
projectRoot = path.resolve(cwd, "../..");
}
// If we're in frontend, go up one level
else if (cwd.endsWith("frontend") || cwd.endsWith("frontend\\")) {
projectRoot = path.resolve(cwd, "..");
}
serverPath = path.join(
projectRoot,
"sync-server/target/debug/sync_server"
);
// Check if server binary exists
if (!fs.existsSync(serverPath)) {
console.error(`Server binary not found at: ${serverPath}`);
console.error(
"Please build the server first: cd sync-server && cargo build"
);
console.error(`Current working directory: ${cwd}`);
console.error(`Project root detected as: ${projectRoot}`);
process.exit(1);
}
const serverPath = path.join(projectRoot, SERVER_BINARY_PATH);
if (!fs.existsSync(serverPath)) {
logger.error(`Server binary not found at: ${serverPath}`);
process.exit(1);
}
if (configPath === undefined) {
const cwd = process.cwd();
let projectRoot = cwd;
if (
cwd.endsWith("frontend/deterministic-tests") ||
cwd.endsWith("frontend\\deterministic-tests")
) {
projectRoot = path.resolve(cwd, "../..");
} else if (cwd.endsWith("frontend") || cwd.endsWith("frontend\\")) {
projectRoot = path.resolve(cwd, "..");
}
configPath = path.join(projectRoot, "sync-server/config-e2e.yml");
if (!fs.existsSync(configPath)) {
console.error(`Config file not found at: ${configPath}`);
process.exit(1);
}
const configPath = path.join(projectRoot, CONFIG_PATH);
if (!fs.existsSync(configPath)) {
logger.error(`Config file not found at: ${configPath}`);
process.exit(1);
}
// Determine which tests to run
const testsToRun: TestDefinition[] = [];
// Collect all defined tests
const allTests: TestDefinition[] = [];
for (const test of Object.values(TESTS)) {
if (test !== undefined) {
allTests.push(test);
}
}
if (testName !== undefined) {
if (testName === "all") {
testsToRun.push(...allTests);
} else {
const test = TESTS[testName];
if (test === undefined) {
console.error(`Unknown test: ${testName}`);
console.error(
`Available tests: ${Object.keys(TESTS).join(", ")}, all`
);
process.exit(1);
}
if (test) {
testsToRun.push(test);
}
} else {
// Default: run all tests
testsToRun.push(...allTests);
}
console.log(`\nDeterministic Test Suite`);
console.log("=".repeat(80));
console.log(`Server: ${serverPath}`);
console.log(`Config: ${configPath}`);
console.log(`Manage server: ${manageServer}`);
console.log(`Tests to run: ${testsToRun.length}`);
console.log(`${"=".repeat(80)}\n`);
logger.info(`Server: ${serverPath}`);
logger.info(`Config: ${configPath}`);
logger.info(`Tests to run: ${testsToRun.length}`);
// Initialize server control
const serverControl = new ServerControl(serverPath, configPath);
const serverControl = new ServerControl(serverPath, configPath, logger);
let allPassed = true;
try {
// Start server if we're managing it
if (manageServer) {
await serverControl.start();
} else {
console.log("Assuming server is already running...");
await serverControl.waitForReady();
}
await serverControl.start();
await serverControl.waitForReady();
// Run tests
for (const test of testsToRun) {
const runner = new TestRunner(serverControl);
const runner = new TestRunner(
serverControl,
logger,
TOKEN,
REMOTE_URI
);
const result = await runner.runTest(test);
if (!result.success) {
allPassed = false;
console.error(`\n✗ FAILED: ${test.name}`);
console.error(`Error: ${result.error}`);
logger.error(`\n✗ FAILED: ${test.name}`);
logger.error(`Error: ${result.error}`);
} else {
console.log(`\n✓ PASSED: ${test.name} (${result.duration}ms)`);
}
// Add delay between tests
if (testsToRun.indexOf(test) < testsToRun.length - 1) {
console.log("\nWaiting 2s before next test...\n");
await new Promise((resolve) => setTimeout(resolve, 2000));
logger.info(`\n✓ PASSED: ${test.name} (${result.duration}ms)`);
}
}
} finally {
// Stop server if we're managing it
if (manageServer) {
await serverControl.stop();
}
await serverControl.stop();
}
console.log(`\n${"=".repeat(80)}`);
if (allPassed) {
console.log("✓ All tests passed!");
logger.info("✓ All tests passed!");
process.exit(0);
} else {
console.log("✗ Some tests failed");
logger.info("✗ Some tests failed");
process.exit(1);
}
}
main().catch((err: unknown) => {
console.error("Unexpected error:", err);
logger.error(`Unexpected error: ${err}`);
process.exit(1);
});