diff --git a/codewit/api/src/controllers/attempt.ts b/codewit/api/src/controllers/attempt.ts index 20c6fd5..21d511c 100644 --- a/codewit/api/src/controllers/attempt.ts +++ b/codewit/api/src/controllers/attempt.ts @@ -6,6 +6,17 @@ import { AttemptWithEval } from '../typings/response.types'; import { EvaluationPayload, EvaluationResponse, executeCodeEvaluation } from '../utils/codeEvalService'; import { Language as LanguageEnum } from '@codewit/language'; +function getEvaluationError(response: EvaluationResponse): string { + if (response.compilation_error) return response.compilation_error; + if (response.runtime_error) return response.runtime_error; + if (response.execution_time_exceeded) return 'Execution time exceeded'; + if (response.memory_exceeded) return 'Memory limit exceeded'; + + const failureMessage = response.failure_details.find((detail) => detail.error_message)?.error_message; + if (failureMessage) return failureMessage; + + return response.state === 'passed' ? '' : `Evaluation failed (${response.state})`; +} async function createAttempt( exerciseId: number, @@ -75,12 +86,15 @@ async function createAttempt( const response = await executeCodeEvaluation(evaluationPayload, cookies); evalResponse = response; console.log('Code evaluation response:', response); - const { tests_run, passed, error: eval_error } = response; + const { tests_run, passed } = response; + const evalError = getEvaluationError(response); + + attempt.completionPercentage = 0; + attempt.error = evalError; if (tests_run > 0) { const completionPercentage = Math.round((passed / tests_run) * 100); attempt.completionPercentage = completionPercentage; - attempt.error = eval_error; console.log(`Completion Percentage: ${completionPercentage}%`); // Update UserExerciseCompletion @@ -166,10 +180,8 @@ async function createAttempt( updatedModules.push({ moduleUid, completion: maxCompletion }); } } - - } else { - attempt.error = eval_error; - console.warn('Invalid response data for completion percentage calculation:', tests_run, passed, eval_error); + } else if (response.state === 'passed') { + console.warn('Code evaluation returned a passed state without runnable tests:', response); } } catch (err) { console.error('Code evaluation failed:', err.message); diff --git a/codewit/api/src/utils/codeEvalService.ts b/codewit/api/src/utils/codeEvalService.ts index 80791d5..e27e7cf 100644 --- a/codewit/api/src/utils/codeEvalService.ts +++ b/codewit/api/src/utils/codeEvalService.ts @@ -1,4 +1,5 @@ import { createHttpClient, post } from '../utils/httpClient'; +import type { FailureDetail, TestResult } from '@codewit/interfaces'; const codeEvalUrl = process.env.CODEEVAL_URL || 'http://codeeval:3002'; const httpClient = createHttpClient(codeEvalUrl); @@ -10,17 +11,12 @@ export interface EvaluationPayload { testCode: string; } -export interface EvaluationResponse { - tests_run: number; - passed: number; - failed: number; - error: string; - state: string; -} +export type EvaluationFailureDetail = FailureDetail; +export type EvaluationResponse = TestResult; export const executeCodeEvaluation = async ( payload: EvaluationPayload, cookies?: string ): Promise => { return await post(httpClient, '/execute', payload, cookies); -}; \ No newline at end of file +}; diff --git a/codewit/client/src/components/codeblock/CodeSubmission.tsx b/codewit/client/src/components/codeblock/CodeSubmission.tsx index 47af79f..0fbfdfe 100644 --- a/codewit/client/src/components/codeblock/CodeSubmission.tsx +++ b/codewit/client/src/components/codeblock/CodeSubmission.tsx @@ -1,28 +1,9 @@ import { BiSolidRightArrow, BiSolidLeftArrow } from 'react-icons/bi'; -import { useState, useMemo } from 'react'; - -type FailureDetail = { - test_case: string; - expected?: string; - received?: string; - error_message: string; - rawout?: string; -}; +import { useState } from 'react'; +import type { EvaluationResponse } from '../../interfaces/evaluation'; type EvalProps = { - evaluation: { - state: string; - tests_run: number; - passed: number; - failed: number; - failure_details?: FailureDetail[]; - compilation_error?: string; - runtime_error?: string; - execution_time_exceeded?: boolean; - memory_exceeded?: boolean; - rawout?: string; - error?: string; - } | null; + evaluation: EvaluationResponse | null; }; const CodeSubmission = ({ evaluation }: EvalProps): JSX.Element => { @@ -47,9 +28,9 @@ const CodeSubmission = ({ evaluation }: EvalProps): JSX.Element => { runtime_error = '', execution_time_exceeded = false, memory_exceeded = false, - rawout = '', - error = '', } = evaluation; + const error = 'error' in evaluation ? evaluation.error : ''; + const rawout = failure_details[issueIdx]?.rawout || ''; let errorMessage = null; if (compilation_error) errorMessage = compilation_error; diff --git a/codewit/client/src/interfaces/evaluation.ts b/codewit/client/src/interfaces/evaluation.ts index 41a907e..e6c422c 100644 --- a/codewit/client/src/interfaces/evaluation.ts +++ b/codewit/client/src/interfaces/evaluation.ts @@ -1,23 +1,24 @@ -// Evaluation-related types for client-side consumption +import type { AttemptDTO, FailureDetail, TestResult } from '@codewit/interfaces'; -export interface EvaluationTestCase { - message: string; - passed: boolean; - expected?: string; - actual?: string; +export interface EvaluationErrorResponse { + state: 'error'; + tests_run: 0; + passed: 0; + failed: 0; + errors: 0; + no_tests_collected: false; + exit_code: null; + failure_details: FailureDetail[]; + compilation_error: ''; + runtime_error: ''; + execution_time_exceeded: false; + memory_exceeded: false; + error: string; } -export interface EvaluationResponse { - tests_run: number; - passed: number; - failed: number; - error?: string; - state: string; - test_cases?: EvaluationTestCase[]; - rawout?: string; -} +export type EvaluationResponse = TestResult | EvaluationErrorResponse; export interface AttemptWithEval { - attempt: any; // Can refine if Attempt type is imported + attempt: AttemptDTO | null; evaluation: EvaluationResponse; } diff --git a/codewit/client/src/pages/Read.tsx b/codewit/client/src/pages/Read.tsx index fc8e2e6..58091aa 100644 --- a/codewit/client/src/pages/Read.tsx +++ b/codewit/client/src/pages/Read.tsx @@ -378,10 +378,21 @@ function RightPanel({info, course_id}: RightPanelProps) { set_last_attempt({ attempt : null, evaluation: { - state : 'error', - error : 'Unexpected error: ' + err.message, + state: 'error', + tests_run: 0, + passed: 0, + failed: 0, + errors: 0, + no_tests_collected: false, + exit_code: null, + failure_details: [], + compilation_error: '', + runtime_error: '', + execution_time_exceeded: false, + memory_exceeded: false, + error: 'Unexpected error: ' + err.message, }, - } as AttemptWithEval); + }); } }); diff --git a/codewit/client/src/pages/create/exercise/exercise_id.tsx b/codewit/client/src/pages/create/exercise/exercise_id.tsx index 26b4b73..824f012 100644 --- a/codewit/client/src/pages/create/exercise/exercise_id.tsx +++ b/codewit/client/src/pages/create/exercise/exercise_id.tsx @@ -607,7 +607,7 @@ function ExerciseTest({ } interface FailureDetailProps { - test_case: string, + test_case: string | number, expected: string, received: string, error_message: string, diff --git a/codewit/lib/shared/interfaces/src/lib/output.ts b/codewit/lib/shared/interfaces/src/lib/output.ts index f4a6ec7..2476c09 100644 --- a/codewit/lib/shared/interfaces/src/lib/output.ts +++ b/codewit/lib/shared/interfaces/src/lib/output.ts @@ -1,18 +1,29 @@ +type EvaluationState = + | 'passed' + | 'failed' + | 'compile_error' + | 'runtime_error' + | 'execution_error' + | 'execution_blocked'; + interface FailureDetail { - test_case: string; + test_case: string | number; expected: string; received: string; error_message: string; + rawout: string; + stderr?: string; } interface TestResult { - state: "passed" | "failed"; + state: EvaluationState; tests_run: number; passed: number; failed: number; + errors: number; + no_tests_collected: boolean; + exit_code: number | null; failure_details: FailureDetail[]; - stdout: string; - stderr: string; compilation_error: string; runtime_error: string; execution_time_exceeded: boolean; @@ -20,5 +31,7 @@ interface TestResult { } export type { - TestResult -} \ No newline at end of file + EvaluationState, + FailureDetail, + TestResult, +};