From 760811a0da3a93fa3b7a3d04a94e2a3d6dd15814 Mon Sep 17 00:00:00 2001 From: nully0x <40327060+nully0x@users.noreply.github.com> Date: Sun, 8 Dec 2024 03:46:39 +0100 Subject: [PATCH] chore(utils): refactor runner test logic to special test cases (#16) * chore(utils): refactor runner test logic to special test cases * remove dummy text --- baseImages/python/Dockerfile | 2 +- src/utils/resultReporter.ts | 2 +- src/utils/runScriptGenerator.ts | 51 +++++++++++++++++---------------- src/utils/runner.ts | 36 ++++++++++++++++++++++- src/utils/sseManager.ts | 40 ++++++++++++++++++++++---- 5 files changed, 97 insertions(+), 34 deletions(-) diff --git a/baseImages/python/Dockerfile b/baseImages/python/Dockerfile index be1dc4a..91aafb3 100644 --- a/baseImages/python/Dockerfile +++ b/baseImages/python/Dockerfile @@ -1 +1 @@ -FROM python:3.9-slim +FROM python:3.11-slim diff --git a/src/utils/resultReporter.ts b/src/utils/resultReporter.ts index 447f081..a3f6e37 100644 --- a/src/utils/resultReporter.ts +++ b/src/utils/resultReporter.ts @@ -7,7 +7,7 @@ export async function reportResults( result: TestResult, ): Promise { try { - logger.info("Reporting test results:", { result }); + logger.info("Reporting test results:", { commitSha }); const channel = getChannel(); const message = JSON.stringify({ result }); diff --git a/src/utils/runScriptGenerator.ts b/src/utils/runScriptGenerator.ts index b916889..635aad4 100644 --- a/src/utils/runScriptGenerator.ts +++ b/src/utils/runScriptGenerator.ts @@ -1,4 +1,3 @@ -import { TestRepoManager } from "./testRepoManager"; import path from "path"; import fs from "fs/promises"; @@ -9,32 +8,34 @@ export async function generateRunScript( testContent: string | null, ): Promise { const runScript = `#!/bin/bash - set -e # Exit on any error +set -e # Exit on any error - if [ -f "requirements.txt" ]; then - # For Python projects - ${ - testContent - ? `pytest ./app/stage${currentStep}${TestRepoManager.getTestExtension(language)} -v` - : "python ./app/main.py" - } - elif [ -f "Cargo.toml" ]; then - # For Rust projects - cargo build ${testContent ? "--quiet" : ""} - ${ - testContent ? `cargo test --test stage${currentStep}_test` : "cargo run" - } - else - # For TypeScript projects - ${ - testContent - ? `bun test ./app/stage${currentStep}${TestRepoManager.getTestExtension(language)}` - : "bun run start" - } - fi - `; +${generateCommandForLanguage(language, currentStep, testContent)} +`; const runScriptPath = path.join(repoDir, ".hxckr", "run.sh"); await fs.writeFile(runScriptPath, runScript); - await fs.chmod(runScriptPath, 0o755); // Making it executable + await fs.chmod(runScriptPath, 0o755); +} + +function generateCommandForLanguage( + language: string, + currentStep: number, + testContent: string | null, +): string { + switch (language) { + case "python": + return `pytest ./app/stage${currentStep}_test.py -v`; + case "rust": + return `cargo build + cargo test ${ + testContent + ? `--test stage${currentStep}_test` + : `--test stage${currentStep}` + }`; + case "typescript": + return `bun test ./app/stage${currentStep}.test.ts`; + default: + throw new Error(`Unsupported language: ${language}`); + } } diff --git a/src/utils/runner.ts b/src/utils/runner.ts index a37a1b3..6b0d188 100644 --- a/src/utils/runner.ts +++ b/src/utils/runner.ts @@ -34,16 +34,50 @@ export async function runTestProcess(request: TestRunRequest): Promise { if (!challengeId) { throw new Error("Challenge ID not found in progress data."); } + + // Check if the challenge is completed + if (progress.status === "completed") { + logger.info("Challenge already completed", { commitSha }); + + // Ensure connection is ready + const isConnected = + await SSEManager.getInstance().ensureConnection(commitSha); + if (!isConnected) { + logger.warn("SSE connection not established in time", { commitSha }); + } + SSELogger.log(commitSha, "Challenge completed."); + + const testResult: TestResult = { + event_type: EVENT_TYPE, + repoUrl, + commitSha, + success: true, + output: "Challenge completed. No further testing needed.", + }; + await reportResults(commitSha, testResult); + + await SSEManager.getInstance().closeConnection(commitSha); + return; + } + // Check if the status is "not_started" if (progress.status === "not_started") { + logger.info("First run detected, confirming repository setup", { + commitSha, + }); + // Just verify we can clone the repository + repoDir = await cloneRepository(repoUrl, branch, commitSha); + // Report success without running any tests const testResult: TestResult = { event_type: EVENT_TYPE, repoUrl, commitSha, success: true, - output: "Challenge setup completed successfully.", + output: "Challenge setup completed successfully", }; await reportResults(commitSha, testResult); + SSELogger.log(commitSha, "Challenge setup completed successfully."); + SSEManager.getInstance().closeConnection(commitSha); return; } diff --git a/src/utils/sseManager.ts b/src/utils/sseManager.ts index 1051caa..e3751f0 100644 --- a/src/utils/sseManager.ts +++ b/src/utils/sseManager.ts @@ -4,9 +4,11 @@ import logger from "./logger"; export class SSEManager { private static instance: SSEManager; private connections: Map; + private connectionReadyCallbacks: Map void>; private constructor() { this.connections = new Map(); + this.connectionReadyCallbacks = new Map(); } public static getInstance(): SSEManager { @@ -17,27 +19,52 @@ export class SSEManager { } public addConnection(commitSha: string, res: Response): void { - // Check if connection already exists if (this.connections.has(commitSha)) { logger.warn(`Connection already exists for commit: ${commitSha}`); return; } - // Set headers only once res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); - // Store the connection this.connections.set(commitSha, res); - // Send initial message - this.sendMessage(commitSha, "Connected to test runner..."); + // Resolve the ready promise if any waiting + const readyCallback = this.connectionReadyCallbacks.get(commitSha); + if (readyCallback) { + readyCallback(); + this.connectionReadyCallbacks.delete(commitSha); + } res.on("close", () => { this.removeConnection(commitSha); logger.info(`SSE connection closed for commit: ${commitSha}`); }); + + // Send initial message + this.sendMessage(commitSha, "Connected to test runner..."); + } + + public async ensureConnection( + commitSha: string, + timeout = 5000, + ): Promise { + if (this.connections.has(commitSha)) { + return true; + } + + return new Promise((resolve) => { + const timeoutId = setTimeout(() => { + this.connectionReadyCallbacks.delete(commitSha); + resolve(false); + }, timeout); + + this.connectionReadyCallbacks.set(commitSha, () => { + clearTimeout(timeoutId); + resolve(true); + }); + }); } public sendMessage(commitSha: string, message: string): void { @@ -57,10 +84,11 @@ export class SSEManager { public removeConnection(commitSha: string): void { this.connections.delete(commitSha); + this.connectionReadyCallbacks.delete(commitSha); logger.debug(`Removed connection for ${commitSha}`); } - public closeConnection(commitSha: string): void { + public async closeConnection(commitSha: string): Promise { const connection = this.connections.get(commitSha); if (connection) { try {