Skip to content

Migrate to .NET 10, adopt TUnit, add integration tests and MCAF guidance#2

Merged
KSemenenko merged 1 commit intomainfrom
codex/set-up-.net-build-and-tests
Jan 3, 2026
Merged

Migrate to .NET 10, adopt TUnit, add integration tests and MCAF guidance#2
KSemenenko merged 1 commit intomainfrom
codex/set-up-.net-build-and-tests

Conversation

@KSemenenko
Copy link
Copy Markdown
Member

Motivation

  • Move the repository to a single supported SDK version and modernize the test tooling by targeting .NET 10 only.
  • Replace xUnit with the TUnit source-generated testing engine to enable fast, integration-style tests and Native AOT readiness.
  • Provide repository instructions for agent-driven workflows via the MCAF convention and prefer an XML .slnx solution file.
  • Fix runtime issues around MSBuild/Workspace usage on .NET 10 and ensure packaging points to the correct runtime outputs.

Description

  • Updated project targets from multi-targeting/net8.0 to net10.0 across src/ and tests/, converted the solution to ragsharp.slnx, and removed the old .sln.
  • Switched test projects from xUnit to TUnit (added TUnit package and changed test project OutputType to Exe) and added a large integration harness tests/RagSharp.CodeGraph.Tests/CodeGraphIntegrationTests.cs that generates 100 test cases.
  • Modified CodeGraphIndexer to safely register Microsoft.Build.Locator, adapt MSBuildWorkspace call signatures, add null checks, and use explicit JsonSerializerOptions, and added Microsoft.Build.Locator and Microsoft.Extensions.Logging.Abstractions package references.
  • Adjusted RagSharp.Packaging paths to net10.0, fixed Installer.GetStatusAsync to return a compatible files collection, added AGENTS.md and a docs/Wiki/ProjectOverview.md, and updated global.json to opt into the testing platform for .NET 10.

Testing

  • Ran dotnet run --project tests/RagSharp.CodeGraph.Tests/RagSharp.CodeGraph.Tests.csproj, which completed successfully with 103 tests passing.
  • Ran dotnet run --project tests/RagSharp.SkillInstaller.Tests/RagSharp.SkillInstaller.Tests.csproj, which completed successfully with 2 tests passing.
  • Attempted dotnet test ragsharp.slnx earlier in the rollout and observed a failing restore step due to an "Ambiguous project name 'RagSharp'" error during a multi-project restore, so CI should be verified in your environment if you rely on dotnet test over running TUnit test executables directly.

Codex Task

Copilot AI review requested due to automatic review settings January 3, 2026 14:28
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 66416bcf6b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1 to +3
<Solution>
<Folder Name="/src/">
<Project Path="src/RagSharp.CodeGraph.Cli/RagSharp.CodeGraph.Cli.csproj" />
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update indexer to discover .slnx solutions

Switching the repo to a .slnx solution means IndexAsync no longer finds a solution when given the repo root, because CodeGraphIndexer.FindSolution only scans for *.sln. In that case it falls back to the first .csproj it finds and indexes only a single project, which is a functional regression for multi-project indexing. Consider teaching FindSolution to detect .slnx (or prefer it when present) so root indexing still captures the full solution.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes the RagSharp repository by migrating from .NET 8 to .NET 10, replacing xUnit with TUnit for testing, and introducing comprehensive integration tests. The migration includes updating all project files to target net10.0 exclusively, converting the solution to the XML-based .slnx format, and adding repository conventions documentation for agent-driven workflows.

Key changes:

  • Unified target framework from multi-targeting (net8.0/net10.0) to net10.0 only across all projects
  • Replaced xUnit test framework with TUnit, including conversion of test attributes and assertions, plus adding 100 parameterized integration tests
  • Added MSBuild.Locator registration guards and null safety checks to CodeGraphIndexer, plus updated MSBuildWorkspace API calls for .NET 10 compatibility

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/samples/SampleApp/SampleApp.csproj Updated sample app to target net10.0
tests/RagSharp.SkillInstaller.Tests/RagSharp.SkillInstaller.Tests.csproj Migrated test project to net10.0 and TUnit, changed OutputType to Exe
tests/RagSharp.SkillInstaller.Tests/InstallerTests.cs Converted test attributes and assertions from xUnit to TUnit, added new status test
tests/RagSharp.CodeGraph.Tests/RagSharp.CodeGraph.Tests.csproj Migrated test project to net10.0 and TUnit
tests/RagSharp.CodeGraph.Tests/IndexTests.cs Removed old xUnit-based test file
tests/RagSharp.CodeGraph.Tests/CodeGraphIntegrationTests.cs Added comprehensive integration test suite with 100 parameterized test cases
src/RagSharp.SkillInstaller/RagSharp.SkillInstaller.csproj Removed multi-targeting, now targets net10.0 only
src/RagSharp.SkillInstaller/Installer.cs Modified GetStatusAsync return type to maintain consistency
src/RagSharp.Packaging/RagSharp.Packaging.csproj Updated to target net10.0 and fixed packaging paths
src/RagSharp.CodeGraph.Store.LiteGraph/RagSharp.CodeGraph.Store.LiteGraph.csproj Removed multi-targeting, now targets net10.0 only
src/RagSharp.CodeGraph.Core/RagSharp.CodeGraph.Core.csproj Added MSBuild.Locator and Logging.Abstractions packages, removed multi-targeting
src/RagSharp.CodeGraph.Core/CodeGraphIndexer.cs Added MSBuild.Locator registration guards, updated workspace API calls, added null checks for using directives
src/RagSharp.CodeGraph.Cli/RagSharp.CodeGraph.Cli.csproj Removed multi-targeting, now targets net10.0 only
ragsharp.slnx New XML-based solution file replacing traditional .sln
ragsharp.sln Removed legacy solution file
global.json Added testRunner property for .NET 10 testing platform
docs/Wiki/ProjectOverview.md Added project documentation describing components and conventions
README.md Updated requirements to reflect net10.0 as the only target
AGENTS.md Added MCAF agent instructions for repository conventions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


var manifest = JsonSerializer.Deserialize<InstallManifest>(await File.ReadAllTextAsync(manifestPath, cancellationToken).ConfigureAwait(false));
return new InstallStatus(true, manifest?.Version, manifest?.InstalledAtUtc, targetDir, manifest?.Files ?? Array.Empty<string>());
var files = manifest?.Files ?? new List<string>();
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type of the files variable has changed from Array.Empty<string>() to new List<string>(). While both implement IReadOnlyList<string>, this inconsistency means line 75 returns an array while line 80 returns a List. For consistency and to match the expected return type, both should use the same implementation. Consider using Array.Empty<string>() on line 79 instead of new List<string>() to maintain consistency with line 75.

Suggested change
var files = manifest?.Files ?? new List<string>();
IReadOnlyList<string> files = (manifest?.Files as IReadOnlyList<string>) ?? Array.Empty<string>();

Copilot uses AI. Check for mistakes.
Comment on lines +206 to +211
public static async Task Cleanup()
{
if (IndexTestContextHolder.Context is { } context)
{
await context.DisposeAsync();
IndexTestContextHolder.Context = null;
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IndexTestContextHolder.Context static field can be accessed concurrently from multiple tests. While GetContextAsync() uses a semaphore for initialization, the cleanup in CodeGraphTestHooks.Cleanup() at line 211 sets Context = null without synchronization. This could create a race condition where one test is reading the context while cleanup is nulling it out, potentially leading to null reference exceptions or cleanup occurring while tests are still running.

Copilot uses AI. Check for mistakes.
Comment on lines +5 to +6
"allowPrerelease": true,
"testRunner": "Microsoft.Testing.Platform"
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The testRunner property is not a standard or documented property of the sdk section in global.json. The global.json schema supports properties like version, rollForward, and allowPrerelease, but testRunner is not recognized. This property will likely be ignored by the .NET SDK and may cause confusion. If this is intended for a specific tool or extension, consider documenting its purpose or removing it if it serves no function.

Suggested change
"allowPrerelease": true,
"testRunner": "Microsoft.Testing.Platform"
"allowPrerelease": true

Copilot uses AI. Check for mistakes.
Comment on lines +191 to +193
if (Directory.Exists(RootPath))
{
Directory.Delete(RootPath, true);
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Directory.Delete(RootPath, true) call at line 193 may fail on Windows if the SQLite database file is still locked after Store.DisposeAsync(). SQLite can hold locks briefly even after disposal. Consider adding retry logic or a small delay to ensure the database connection is fully released before attempting to delete the directory, or wrap the delete operation in a try-catch to handle potential IOException gracefully.

Suggested change
if (Directory.Exists(RootPath))
{
Directory.Delete(RootPath, true);
// SQLite can briefly hold file locks after disposal, especially on Windows.
// Retry deletion a few times to avoid transient failures during cleanup.
const int maxAttempts = 3;
const int delayMilliseconds = 100;
for (var attempt = 0; attempt < maxAttempts; attempt++)
{
if (!Directory.Exists(RootPath))
{
break;
}
try
{
Directory.Delete(RootPath, true);
break;
}
catch (IOException) when (attempt < maxAttempts - 1)
{
await Task.Delay(delayMilliseconds);
}
catch (UnauthorizedAccessException) when (attempt < maxAttempts - 1)
{
await Task.Delay(delayMilliseconds);
}

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +88
if (IndexTestContextHolder.Context is not null)
{
return IndexTestContextHolder.Context;
}

await InitLock.WaitAsync();
try
{
IndexTestContextHolder.Context ??= await IndexTestContext.CreateAsync();
}
finally
{
InitLock.Release();
}

Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition is always null because of ... is ....

Suggested change
if (IndexTestContextHolder.Context is not null)
{
return IndexTestContextHolder.Context;
}
await InitLock.WaitAsync();
try
{
IndexTestContextHolder.Context ??= await IndexTestContext.CreateAsync();
}
finally
{
InitLock.Release();
}
if (IndexTestContextHolder.Context is null)
{
await InitLock.WaitAsync();
try
{
if (IndexTestContextHolder.Context is null)
{
IndexTestContextHolder.Context = await IndexTestContext.CreateAsync();
}
}
finally
{
InitLock.Release();
}
}

Copilot uses AI. Check for mistakes.
@KSemenenko KSemenenko merged commit e10206a into main Jan 3, 2026
6 checks passed
@KSemenenko KSemenenko deleted the codex/set-up-.net-build-and-tests branch January 3, 2026 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants