Skip to content

Merge dev into main. Integrate latest bug fixes and improvements. #1617

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace OJS.Workers.Common

Check warning on line 1 in Services/Common/OJS.Workers/OJS.Workers.Common/ExecutionStrategiesConstants.cs

View workflow job for this annotation

GitHub Actions / integration-tests

Convert to file-scoped namespace (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0161)

Check warning on line 1 in Services/Common/OJS.Workers/OJS.Workers.Common/ExecutionStrategiesConstants.cs

View workflow job for this annotation

GitHub Actions / integration-tests

Convert to file-scoped namespace (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0161)

Check warning on line 1 in Services/Common/OJS.Workers/OJS.Workers.Common/ExecutionStrategiesConstants.cs

View workflow job for this annotation

GitHub Actions / integration-tests

Convert to file-scoped namespace (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0161)
{
using System.Collections.Generic;

Expand Down Expand Up @@ -51,6 +51,7 @@

// TypeScript
public const string TypeScriptCodeV20 = "typescript-codeV20";
public const string TypeScriptUnitTestsWithMochaV20 = "typescript-unit-tests-with-mochaV20";

// Python
public const string PythonCode = "python-code";
Expand Down Expand Up @@ -149,7 +150,8 @@
{ ExecutionStrategyNames.JavaScriptCodeAgainstUnitTestsWithMochaV20, ExecutionStrategyType.NodeJsV20PreprocessExecuteAndRunCodeAgainstUnitTestsWithMochaExecutionStrategy },

// TypeScript
{ ExecutionStrategyNames.TypeScriptCodeV20, ExecutionStrategyType.TypeScriptV20PreprocessExecuteAndCheck },
{ ExecutionStrategyNames.TypeScriptCodeV20, ExecutionStrategyType.TypeScriptV20PreprocessExecuteAndCheck },
{ ExecutionStrategyNames.TypeScriptUnitTestsWithMochaV20, ExecutionStrategyType.TypeScriptV20PreprocessExecuteAndRunUnitTestsWithMocha },

// Java
{ ExecutionStrategyNames.JavaCode, ExecutionStrategyType.JavaPreprocessCompileExecuteAndCheck },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,6 @@ public enum ExecutionStrategyType
NodeJsV20ZipExecuteHtmlAndCssStrategy = 72,
NodeJsV20RunSpaAndExecuteMochaTestsExecutionStrategySeparateTests = 73,
TypeScriptV20PreprocessExecuteAndCheck = 74,
TypeScriptV20PreprocessExecuteAndRunUnitTestsWithMocha = 75,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ ExecutionStrategyType.NodeJsV20PreprocessExecuteAndCheck or
=> GetForNodeJsPreprocessExecuteAndCheck(),

ExecutionStrategyType.NodeJsPreprocessExecuteAndRunUnitTestsWithMocha or
ExecutionStrategyType.NodeJsV20PreprocessExecuteAndRunUnitTestsWithMocha
ExecutionStrategyType.NodeJsV20PreprocessExecuteAndRunUnitTestsWithMocha or
ExecutionStrategyType.TypeScriptV20PreprocessExecuteAndRunUnitTestsWithMocha
=> GetForNodeJsPreprocessExecuteAndRunUnitTestsWithMocha(),

ExecutionStrategyType.NodeJsPreprocessExecuteAndRunJsDomUnitTests
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
namespace OJS.Workers.ExecutionStrategies.NodeJs.Typescript;

using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using OJS.Workers.Common;
using OJS.Workers.Common.Extensions;
using OJS.Workers.Common.Helpers;
using OJS.Workers.Common.Models;
using OJS.Workers.Compilers;
using OJS.Workers.ExecutionStrategies.Models;
using OJS.Workers.Executors;

public class TypeScriptV20PreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategy<TSettings>(
IOjsSubmission submission,
IProcessExecutorFactory processExecutorFactory,
IExecutionStrategySettingsProvider settingsProvider,
ILogger<BaseExecutionStrategy<TSettings>> logger,
ICompilerFactory compilerFactory)
: NodeJsPreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategy<TSettings>(submission, processExecutorFactory,
settingsProvider, logger)
where TSettings : NodeJsPreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategySettings
{
protected const string TestsPlaceholder = "/*#testsCode*/";
protected const string UserCodePlaceholder = "/*#userCode#*/";
protected const string SolutionSkeletonPlaceholder = "/*#solutionSkeleton#*/";

private string TypeScriptTemplateContent => @$"
// Imports
// @ts-ignore
const chai = require('{this.Settings.ChaiModulePath}');
// @ts-ignore
const sinon = require('{this.Settings.SinonModulePath}');
// @ts-ignore
const sinonChai = require('{this.Settings.SinonChaiModulePath}');

chai.use(sinonChai);

const expect = chai.expect;
const assert = chai.assert;
const should = chai.should();

// Skeleton
{SolutionSkeletonPlaceholder}

// User code
let result = {UserCodePlaceholder}

// Tests
// @ts-ignore
describe('Tests', function () {{
{TestsPlaceholder}
}});
";

protected override async Task<IExecutionResult<TestResult>> ExecuteAgainstTestsInput(
IExecutionContext<TestsInputModel> executionContext,
IExecutionResult<TestResult> result)
{
// Prepare TypeScript file with combined user code and tests
var typeScriptTemplate = this.TypeScriptTemplateContent;
var userCode = executionContext.Code.Trim();
typeScriptTemplate = typeScriptTemplate
.Replace(SolutionSkeletonPlaceholder, executionContext.Input.TaskSkeletonAsString)
.Replace(UserCodePlaceholder, userCode);

// Process each test and wrap it in an it() block with @ts-ignore
if (executionContext.Input.Tests.Any())
{
var formattedTests = FormatTests(executionContext.Input.Tests);
typeScriptTemplate = typeScriptTemplate.Replace(TestsPlaceholder, formattedTests);
}

// Save the combined TypeScript file
var tsCodeSavePath = FileHelpers.SaveStringToTempFile(this.WorkingDirectory, typeScriptTemplate);

// Run TypeScript compiler to generate JavaScript
var compiler = compilerFactory.CreateCompiler(executionContext.CompilerType, this.Type);
var compilerPath = compilerFactory.GetCompilerPath(executionContext.CompilerType, this.Type);
var compilerResult = compiler.Compile(compilerPath, tsCodeSavePath, executionContext.AdditionalCompilerArguments);

if (!compilerResult.IsCompiledSuccessfully)
{
result.IsCompiledSuccessfully = false;
result.CompilerComment = compilerResult.CompilerComment;
return result;
}

// Execute tests using Node.js on the generated JavaScript file
var executor = this.CreateRestrictedExecutor();
var checker = executionContext.Input.GetChecker();

var testResults = await this.ProcessTests(
executionContext,
executor,
checker,
compilerResult.OutputFile);
result.Results.AddRange(testResults);

return result;
}

private static string FormatTests(IEnumerable<TestContext> tests)
{
var formattedTests = new List<string>();
var testCounter = 1;

foreach (var test in tests)
{
// Use simple sequential test names
var testName = $"Test{testCounter}";
var testContent = test.Input.Trim();

// Format the test with proper it() wrapper
var formattedTest = $@"
// @ts-ignore
it('{testName}', function () {{
{testContent}
}})";

formattedTests.Add(formattedTest);
testCounter++;
}

// Join all formatted tests
return string.Join("\n", formattedTests);
}

protected override async Task<List<TestResult>> ProcessTests(
IExecutionContext<TestsInputModel> executionContext,
IExecutor executor,
IChecker checker,
string codeSavePath)
{
var testResults = new List<TestResult>();

// Configure arguments for Mocha with Node.js
var arguments = new List<string>
{
this.Settings.MochaModulePath,
codeSavePath,
"--reporter", "json"
};
arguments.AddRange(this.AdditionalExecutionArguments);

// Execute Mocha with Node.js
var processExecutionResult = await executor.Execute(
this.Settings.NodeJsExecutablePath,
executionContext.TimeLimit,
executionContext.MemoryLimit,
string.Empty,
arguments);

// Parse the results
var mochaResult = JsonExecutionResult.Parse(processExecutionResult.ReceivedOutput);

// Map results to each test
var currentTest = 0;
foreach (var test in executionContext.Input.Tests)
{
var message = "yes";
if (!string.IsNullOrEmpty(mochaResult.Error))
{
message = mochaResult.Error;
}
else if (currentTest < mochaResult.TestErrors.Count && mochaResult.TestErrors[currentTest] != null)
{
message = $"Test failed: {mochaResult.TestErrors[currentTest]}";
}

var testResult = CheckAndGetTestResult(
test,
processExecutionResult,
checker,
message);

currentTest++;
testResults.Add(testResult);
}

return testResults;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ public IExecutionStrategy CreateExecutionStrategy(IOjsSubmission submission)
loggerFactory.CreateStrategyLogger<TypeScriptPreprocessExecuteAndCheckExecutionStrategy<TypeScriptPreprocessExecuteAndCheckExecutionStrategySettings>>(submissionId, verbosely, logFileMaxBytes),
compilerFactory);
break;
case ExecutionStrategyType.TypeScriptV20PreprocessExecuteAndRunUnitTestsWithMocha:
executionStrategy = new TypeScriptV20PreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategy<NodeJsPreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategySettings>(
submission,
processExecutorFactory,
executionStrategySettingsProvider,
loggerFactory.CreateStrategyLogger<TypeScriptV20PreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategy<NodeJsPreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategySettings>>(submissionId, verbosely, logFileMaxBytes),
compilerFactory);
break;
case ExecutionStrategyType.NodeJsPreprocessExecuteAndRunUnitTestsWithMocha:
case ExecutionStrategyType.NodeJsV20PreprocessExecuteAndRunUnitTestsWithMocha:
executionStrategy = new NodeJsPreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategy<NodeJsPreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategySettings>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ ExecutionStrategyType.NodeJsPreprocessExecuteAndCheck or
this.GetNodeJsExecutablePath(executionStrategyType),
this.GetNodeResourcePath(executionStrategyType, this.settings.UnderscoreModulePath))

as TSettings,
ExecutionStrategyType.TypeScriptV20PreprocessExecuteAndRunUnitTestsWithMocha => new
NodeJsPreprocessExecuteAndRunUnitTestsWithMochaExecutionStrategySettings(
GetBaseTimeUsed(submission, this.settings.NodeJsBaseTimeUsedInMilliseconds * 2),
GetBaseMemoryUsed(submission, this.settings.NodeJsBaseMemoryUsedInBytes),
this.GetNodeJsExecutablePath(executionStrategyType),
this.GetNodeResourcePath(executionStrategyType, this.settings.UnderscoreModulePath),
this.GetNodeResourcePath(executionStrategyType, this.settings.MochaModulePath),
this.GetNodeResourcePath(executionStrategyType, this.settings.ChaiModulePath),
this.GetNodeResourcePath(executionStrategyType, this.settings.SinonModulePath),
this.GetNodeResourcePath(executionStrategyType, this.settings.SinonChaiModulePath))

as TSettings,
ExecutionStrategyType.JavaPreprocessCompileExecuteAndCheck or
ExecutionStrategyType.Java21PreprocessCompileExecuteAndCheck => new
Expand Down
Loading