Fix .NET in-process E2E transport coverage - #1986
Conversation
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
Updates .NET E2E coverage to exercise the FFI transport and supports same-client session resumption.
Changes:
- Selects in-process or TCP transport according to the test matrix.
- Restores host environment state between in-process tests.
- Adds same-client resume replacement behavior and related coverage.
Show a summary per file
| File | Description |
|---|---|
dotnet/src/Client.cs |
Replaces tracked wrappers during resume. |
dotnet/test/Harness/E2ETestFixture.cs |
Selects matrix-appropriate transport. |
dotnet/test/Harness/E2ETestContext.cs |
Restores in-process environment state. |
dotnet/test/Harness/E2ETestBase.cs |
Prepares tests and chooses resume client. |
dotnet/test/E2E/SessionE2ETests.cs |
Covers same-client resume replacement. |
dotnet/test/E2E/RpcWorkspaceCheckpointsE2ETests.cs |
Uses a native-compatible checkpoint value. |
dotnet/test/Unit/E2ETestFixtureTests.cs |
Tests fixture transport selection. |
dotnet/test/Unit/ClientSessionLifetimeTests.cs |
Tests replacement and failure lifetimes. |
Review details
- Files reviewed: 8/8 changed files
- Comments generated: 1
- Review effort level: Medium
This comment has been minimized.
This comment has been minimized.
|
@SteveSandersonMS noticed this while working on other stuff, take a look - we weren't doing actual FFI in the .NET SDK, plus the .NET SDK was unique in not allowing the same session ID to be resumed with a new session object (which obstructed fixing the former). |
ce76fb0 to
e3f21ae
Compare
This comment has been minimized.
This comment has been minimized.
Make the in-process E2E matrix use the FFI runtime, restore the native host environment between tests, and align same-client session resume with the other SDKs while preserving routing after failed resumes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c71188a1-1445-46aa-9faf-3b73cf6a6dd9
e3f21ae to
2cf5855
Compare
Cross-SDK Consistency Review ✅This PR aligns Key behavioral change:
The implementation is careful to restore the previous session on failure, which is a correctness improvement over the implicit behavior in simpler SDKs. No other SDKs need updates. The change is a .NET-only catch-up to the already-established cross-SDK contract.
|
There was a problem hiding this comment.
Review details
Comments suppressed due to low confidence (3)
dotnet/test/Unit/ClientSessionLifetimeTests.cs:1
- These tests manually dispose sessions at the end, but if an assertion fails mid-test the sessions may not be disposed, which can leak tracked sessions and make later tests flaky. Prefer
await using var session = .../await using var resumedSession = ...(or atry/finally) so cleanup happens even on test failure.
/*---------------------------------------------------------------------------------------------
dotnet/src/Client.cs:826
ConcurrentDictionary.AddOrUpdatemay invoke the update delegate multiple times under contention, so capturingdisplacedSessionvia a closure can yield an indeterminatereplacedSession(not necessarily the instance that was ultimately replaced). Consider using a retry loop withTryGetValue+TryUpdate(orTryAdd) to deterministically capture and return the actual replaced session.
if (replaceExisting)
{
CopilotSession? displacedSession = null;
_sessions.AddOrUpdate(
session.SessionId,
session,
(_, current) =>
{
displacedSession = current;
return session;
});
replacedSession = displacedSession;
}
dotnet/test/Unit/ClientSessionLifetimeTests.cs:485
- This test helper relies on reflection and a hard-coded private method name (
GetSession), which is brittle and can break on refactors unrelated to behavior. If feasible, expose a minimal internal test hook (e.g., internal accessor withInternalsVisibleTofor the test assembly) so the tests verify tracking without reflection.
private static CopilotSession? GetTrackedSession(CopilotClient client, string sessionId)
{
var method = typeof(CopilotClient).GetMethod("GetSession", BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new InvalidOperationException("GetSession method was not found.");
return (CopilotSession?)method.Invoke(client, [sessionId]);
}
- Files reviewed: 8/8 changed files
- Comments generated: 1
- Review effort level: Low
Preserve the existing .NET session ownership model while allowing resume E2E coverage to run through the in-process transport. Suspend and locally untrack the original test wrapper before resuming so the runtime session remains available without introducing replacement semantics. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Review details
Comments suppressed due to low confidence (2)
dotnet/test/E2E/SessionE2ETests.cs:231
- This test still requires same-client resume to throw, which contradicts the new contract that replaces the tracked wrapper before
session.resume. With that behavior enabled,Assert.ThrowsAsyncfails and the new path remains unverified. Update this test to expect a successful resume onClientand assert that the returned session has the same ID (and receives routing intended for the replacement).
await using var session1 = await CreateSessionAsync();
dotnet/test/Harness/E2ETestBase.cs:123
- This reflection bypasses the public same-client replacement behavior that this PR is intended to cover and makes the E2E suite depend on the private
CopilotSession.RemoveFromClientimplementation. Keep the suspended wrapper tracked and resume throughCtx.ResumeSessionAsync; then rename this helper and update its callers. That both exercises the real SDK contract and keeps .NET tests limited to public SDK APIs.
var removeFromClient = typeof(CopilotSession).GetMethod(
"RemoveFromClient",
BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new InvalidOperationException("CopilotSession.RemoveFromClient was not found.");
removeFromClient.Invoke(session, null);
- Files reviewed: 9/9 changed files
- Comments generated: 0 new
- Review effort level: Medium
The .NET E2E fixture explicitly selected TCP even in the in-process matrix, so those tests never exercised the FFI transport and hid FFI-specific behavior.
This change:
RuntimeConnection.ForInProcess()for the in-process matrix while preserving TCP coverage in the other matrixu32domainThe complete .NET in-process suite passes through FFI with 672 tests passed and 3 existing skips. Targeted TCP resume coverage and all .NET target-framework builds also pass.