Skip to content

Commit eb25afd

Browse files
committed
Fix non-auth CI test selection and Codex smoke isolation
1 parent 503a61c commit eb25afd

File tree

12 files changed

+29
-5
lines changed

12 files changed

+29
-5
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
run: dotnet build ManagedCode.CodexSharpSDK.slnx -c Release -warnaserror --no-restore
4646

4747
- name: Test (full solution)
48-
run: dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release --no-build
48+
run: dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release --no-build -- --treenode-filter "/*/*/*/*[RequiresCodexAuth!=true]"
4949

5050
- name: Smoke tests (Codex CLI discover + invoke)
5151
run: dotnet test --project CodexSharpSDK.Tests/CodexSharpSDK.Tests.csproj -c Release --no-build -- --treenode-filter "/*/*/*/CodexCli_Smoke_*"

.github/workflows/real-integration.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
run: dotnet build ManagedCode.CodexSharpSDK.slnx -c Release -warnaserror --no-restore
4848

4949
- name: Test (full solution)
50-
run: dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release --no-build
50+
run: dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release --no-build -- --treenode-filter "/*/*/*/*[RequiresCodexAuth!=true]"
5151

5252
- name: Smoke tests (Codex CLI discover + invoke)
5353
run: dotnet test --project CodexSharpSDK.Tests/CodexSharpSDK.Tests.csproj -c Release --no-build -- --treenode-filter "/*/*/*/CodexCli_Smoke_*"

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ jobs:
6262
run: dotnet build ManagedCode.CodexSharpSDK.slnx --configuration Release --warnaserror --no-restore
6363

6464
- name: Test (full solution)
65-
run: dotnet test --solution ManagedCode.CodexSharpSDK.slnx --configuration Release --no-build
65+
run: dotnet test --solution ManagedCode.CodexSharpSDK.slnx --configuration Release --no-build -- --treenode-filter "/*/*/*/*[RequiresCodexAuth!=true]"
6666

6767
- name: Codex CLI smoke tests
6868
run: dotnet test --project CodexSharpSDK.Tests/CodexSharpSDK.Tests.csproj --configuration Release --no-build -- --treenode-filter "/*/*/*/CodexCli_Smoke_*"

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ If no new rule is detected -> do not update the file.
115115
- Testing framework is TUnit (`tests`).
116116
- TUnit/MTP does not support `--filter`; for focused runs use `dotnet test ... -- --treenode-filter "<pattern>"`.
117117
- Always invoke runner options after `--` (for example `dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release -- --treenode-filter "<pattern>"`); if a focused filter runs zero tests, treat it as an invalid filter and correct it before reporting results.
118+
- Before changing TUnit test-selection logic (tree node filters, properties, UIDs), read official TUnit docs first and validate syntax with a local no-auth run before updating CI/release workflows.
118119
- In this repository, method-level `treenode-filter` patterns resolve at depth 3 (`/*/*/*/<TestMethodName>`). For integration subset runs use `dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release -- --treenode-filter "/*/*/*/RunAsync_*_EndToEnd"` (matches current `CodexExecIntegrationTests`).
119120
- For reliable discovery when selecting focused tests, use the built test app directly: `tests/bin/Release/net10.0/ManagedCode.CodexSharpSDK.Tests --list-tests` (the `dotnet test ... -- --list-tests` wrapper can report zero tests in this setup).
120121
- Every behavior change must include or update tests.
@@ -124,6 +125,7 @@ If no new rule is detected -> do not update the file.
124125
- Treat `codex` CLI as a test prerequisite: ensure local/CI test setup installs `codex` before running CLI interaction tests; do not replace this with fakes.
125126
- CI must validate SDK on all Codex-supported desktop/server platforms (macOS, Linux, Windows): run build + tests and include a non-auth smoke check that `codex` is discoverable and invokable.
126127
- CI/release workflow smoke checks are additive gates; they must not replace full `dotnet test --solution ManagedCode.CodexSharpSDK.slnx -c Release` execution.
128+
- CI/release full-solution runs must exclude auth-required tests using `-- --treenode-filter "/*/*/*/*[RequiresCodexAuth!=true]"` so pipelines remain non-auth and deterministic.
127129
- Cross-platform non-auth smoke must run `codex` from local installation in CI and verify unauthenticated behavior explicitly (for example `codex login status` in isolated profile returns "Not logged in"), proving binary discovery + process launch on each platform.
128130
- Real Codex integration tests must rely on existing local Codex CLI login/session only; do not read or require `OPENAI_API_KEY` in test setup.
129131
- Do not use nullable `TryGetSettings()` + early `return` skip patterns in real integration tests; resolve required settings directly and fail fast with actionable errors when missing.

CodexSharpSDK.Tests/Integration/CodexCliSmokeTests.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class CodexCliSmokeTests
1818
private const string OpenAiApiKeyEnvironmentVariable = "OPENAI_API_KEY";
1919
private const string OpenAiBaseUrlEnvironmentVariable = "OPENAI_BASE_URL";
2020
private const string CodexApiKeyEnvironmentVariable = "CODEX_API_KEY";
21+
private const string CodexHomeEnvironmentVariable = "CODEX_HOME";
2122
private const string CodexHomeDirectoryName = ".codex";
2223
private const string AppDataDirectoryName = "AppData";
2324
private const string RoamingDirectoryName = "Roaming";
@@ -31,6 +32,8 @@ public class CodexCliSmokeTests
3132
private const string VersionToken = "codex-cli";
3233
private const string ExecHelpToken = "Run Codex non-interactively";
3334
private const string NotLoggedInToken = "Not logged in";
35+
private const string NotAuthenticatedToken = "Not authenticated";
36+
private const string LoginGuidanceToken = "codex login";
3437

3538
[Test]
3639
public async Task CodexCli_Smoke_FindExecutablePath_ResolvesExistingBinary()
@@ -76,7 +79,7 @@ public async Task CodexCli_Smoke_LoginStatusWithoutAuth_ReportsNotLoggedIn()
7679
await Assert.That(result.ExitCode).IsNotEqualTo(0);
7780

7881
var output = string.Concat(result.StandardOutput, result.StandardError);
79-
await Assert.That(output.Contains(NotLoggedInToken, StringComparison.OrdinalIgnoreCase)).IsTrue();
82+
await Assert.That(ContainsUnauthenticatedSignal(output)).IsTrue();
8083
}
8184
finally
8285
{
@@ -150,6 +153,7 @@ private static Dictionary<string, string> CreateUnauthenticatedEnvironmentOverri
150153

151154
return new Dictionary<string, string>(StringComparer.Ordinal)
152155
{
156+
[CodexHomeEnvironmentVariable] = codexHome,
153157
[HomeEnvironmentVariable] = sandboxDirectory,
154158
[UserProfileEnvironmentVariable] = sandboxDirectory,
155159
[XdgConfigHomeEnvironmentVariable] = configHome,
@@ -215,6 +219,13 @@ private static async Task<CodexProcessResult> RunCodexAsync(
215219
return new CodexProcessResult(process.ExitCode, standardOutput, standardError);
216220
}
217221

222+
private static bool ContainsUnauthenticatedSignal(string output)
223+
{
224+
return output.Contains(NotLoggedInToken, StringComparison.OrdinalIgnoreCase)
225+
|| output.Contains(NotAuthenticatedToken, StringComparison.OrdinalIgnoreCase)
226+
|| output.Contains(LoginGuidanceToken, StringComparison.OrdinalIgnoreCase);
227+
}
228+
218229
private sealed record CodexProcessResult(
219230
int ExitCode,
220231
string StandardOutput,

CodexSharpSDK.Tests/Integration/CodexExecIntegrationTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace ManagedCode.CodexSharpSDK.Tests.Integration;
66

7+
[Property("RequiresCodexAuth", "true")]
78
public class CodexExecIntegrationTests
89
{
910
private const string FirstPrompt = "Reply with short plain text: first.";

CodexSharpSDK.Tests/Integration/RealCodexIntegrationTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace ManagedCode.CodexSharpSDK.Tests.Integration;
66

7+
[Property("RequiresCodexAuth", "true")]
78
public class RealCodexIntegrationTests
89
{
910
[Test]

CodexSharpSDK.Tests/Unit/CodexClientTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ public async Task CodexCli_Smoke_GetCliUpdateStatus_ReturnsInstalledVersion()
251251
}
252252

253253
[Test]
254+
[Property("RequiresCodexAuth", "true")]
254255
public async Task ResumeThread_WithThreadOptions_RunsWithRealCodexCli()
255256
{
256257
var settings = RealCodexTestSupport.GetRequiredSettings();

CodexSharpSDK.Tests/Unit/CodexExecTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ public async Task RunAsync_WithNullLogger_StillPropagatesFailure()
253253
}
254254

255255
[Test]
256+
[Property("RequiresCodexAuth", "true")]
256257
public async Task RunAsync_WithNullLogger_CompletesSuccessfully_WithRealCodexCli()
257258
{
258259
var settings = RealCodexTestSupport.GetRequiredSettings();

CodexSharpSDK.Tests/Unit/CodexThreadTests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace ManagedCode.CodexSharpSDK.Tests.Unit;
1212
public class CodexThreadTests
1313
{
1414
[Test]
15+
[Property("RequiresCodexAuth", "true")]
1516
public async Task RunAsync_WithRealCodexCli_ReturnsCompletedTurnAndUpdatesThreadId()
1617
{
1718
var settings = RealCodexTestSupport.GetRequiredSettings();
@@ -30,6 +31,7 @@ public async Task RunAsync_WithRealCodexCli_ReturnsCompletedTurnAndUpdatesThread
3031
}
3132

3233
[Test]
34+
[Property("RequiresCodexAuth", "true")]
3335
public async Task RunAsync_WithStructuredInput_ReturnsTypedJson()
3436
{
3537
var settings = RealCodexTestSupport.GetRequiredSettings();
@@ -53,6 +55,7 @@ public async Task RunAsync_WithStructuredInput_ReturnsTypedJson()
5355
}
5456

5557
[Test]
58+
[Property("RequiresCodexAuth", "true")]
5659
public async Task RunAsync_GenericStructuredOutput_ReturnsTypedResponse()
5760
{
5861
var settings = RealCodexTestSupport.GetRequiredSettings();
@@ -78,6 +81,7 @@ public async Task RunAsync_GenericStructuredOutput_ReturnsTypedResponse()
7881
}
7982

8083
[Test]
84+
[Property("RequiresCodexAuth", "true")]
8185
public async Task RunAsync_GenericStructuredOutput_WithSchemaShortcutAndStringInput_ReturnsTypedResponse()
8286
{
8387
var settings = RealCodexTestSupport.GetRequiredSettings();
@@ -99,6 +103,7 @@ public async Task RunAsync_GenericStructuredOutput_WithSchemaShortcutAndStringIn
99103
}
100104

101105
[Test]
106+
[Property("RequiresCodexAuth", "true")]
102107
public async Task RunAsync_SecondTurnKeepsThreadId_WithRealCodexCli()
103108
{
104109
var settings = RealCodexTestSupport.GetRequiredSettings();
@@ -124,6 +129,7 @@ public async Task RunAsync_SecondTurnKeepsThreadId_WithRealCodexCli()
124129
}
125130

126131
[Test]
132+
[Property("RequiresCodexAuth", "true")]
127133
public async Task RunStreamedAsync_YieldsCompletedTurnEvent_WithRealCodexCli()
128134
{
129135
var settings = RealCodexTestSupport.GetRequiredSettings();

0 commit comments

Comments
 (0)