Skip to content

Commit 920e32f

Browse files
committed
test: confirm MEAI provider failure with nonzero root exit
1 parent 2f2eee3 commit 920e32f

1 file changed

Lines changed: 30 additions & 3 deletions

File tree

‎ClaudeCodeSharpSDK.Tests/MEAI/StreamingEventMapperTests.cs‎

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Diagnostics;
2+
using System.Globalization;
13
using ManagedCode.ClaudeCodeSharpSDK.Configuration;
24
using ManagedCode.ClaudeCodeSharpSDK.Extensions.AI.Internal;
35
using ManagedCode.ClaudeCodeSharpSDK.Models;
@@ -30,10 +32,14 @@ public class StreamingEventMapperTests
3032
private const string TestsDirectoryName = "tests";
3133
private const string UserPrompt = "request";
3234
private const string ProviderFailureJson = "{\"type\":\"result\",\"is_error\":true,\"result\":\"authentication failed\"}";
35+
private const string ProcessIdFileName = "root.pid";
36+
private const string ProcessIdEnvironmentName = "CLAUDE_TEST_ROOT_PID";
37+
private const string ExitCodeEnvironmentName = "CLAUDE_TEST_EXIT_CODE";
3338
private const string NodeScript = "const args = process.argv.slice(2);\n"
3439
+ "if (args.includes('" + CliVersionFlag + "')) { console.log('" + MetadataScriptVersion + "'); process.exit(0); }\n"
40+
+ "require('node:fs').writeFileSync(process.env." + ProcessIdEnvironmentName + ", String(process.pid));\n"
3541
+ "process.stdin.resume();\n"
36-
+ "process.stdin.on('end', () => process.stdout.write('" + ProviderFailureJson + "' + '\\n'));\n";
42+
+ "process.stdin.on('end', () => process.stdout.write('" + ProviderFailureJson + "' + '\\n', () => { process.exitCode = Number(process.env." + ExitCodeEnvironmentName + "); }));\n";
3743

3844
[Test]
3945
public async Task ToUpdates_MapsThreadStartAssistantMessageAndUsage()
@@ -125,12 +131,15 @@ await CollectUpdates(ProviderFailureEvents(() => tailConsumed = true, () => sour
125131
}
126132

127133
[Test]
128-
public async Task ClaudeChatClient_ProviderFailureConfirmsOnlyAfterRealCliDisposal()
134+
[Arguments(0)]
135+
[Arguments(1)]
136+
public async Task ClaudeChatClient_ProviderFailureConfirmsOnlyAfterRealCliDisposal(int exitCode)
129137
{
130138
var sandboxDirectory = Path.Combine(Environment.CurrentDirectory, TestsDirectoryName, SandboxDirectoryName,
131139
$"{Guid.NewGuid():N}");
132140
Directory.CreateDirectory(sandboxDirectory);
133141
var scriptPath = Path.Combine(sandboxDirectory, ScriptFileName);
142+
var processIdPath = Path.Combine(sandboxDirectory, ProcessIdFileName);
134143
await File.WriteAllTextAsync(scriptPath, NodeScript);
135144

136145
try
@@ -141,6 +150,11 @@ public async Task ClaudeChatClient_ProviderFailureConfirmsOnlyAfterRealCliDispos
141150
{
142151
ClaudeExecutablePath = scriptPath,
143152
ProcessTerminationTimeout = TimeSpan.FromSeconds(3),
153+
EnvironmentVariables = new Dictionary<string, string>
154+
{
155+
[ProcessIdEnvironmentName] = processIdPath,
156+
[ExitCodeEnvironmentName] = exitCode.ToString(CultureInfo.InvariantCulture),
157+
},
144158
},
145159
});
146160
var action = async () =>
@@ -153,8 +167,21 @@ [new ChatMessage(ChatRole.User, UserPrompt)]))
153167

154168
var exception = await Assert.That(action).ThrowsException();
155169
await Assert.That(exception).IsTypeOf<CliExecutionFailureException>();
156-
await Assert.That(((CliExecutionFailureException)exception!).ExitCode).IsNull();
170+
await Assert.That(((CliExecutionFailureException)exception!).ExitCode).IsEqualTo(exitCode == 0 ? (int?)null : exitCode);
157171
await Assert.That(((CliExecutionFailureException)exception).RootProcessExitConfirmed).IsTrue();
172+
var processId = int.Parse(await File.ReadAllTextAsync(processIdPath), CultureInfo.InvariantCulture);
173+
var processes = Process.GetProcesses();
174+
try
175+
{
176+
await Assert.That(processes.Any(process => process.Id == processId)).IsFalse();
177+
}
178+
finally
179+
{
180+
foreach (var process in processes)
181+
{
182+
process.Dispose();
183+
}
184+
}
158185
}
159186
finally
160187
{

0 commit comments

Comments
 (0)