Skip to content

Commit dea981f

Browse files
tolgakiCopilotclaude
authored
Add unit tests for all sample apps (104 tests) (#11)
* Add unit tests for all sample apps (104 tests) Add comprehensive test coverage across all four sample apps: - dotnet/a2a-raw.tests: 24 tests (ExtractText JSON parsing, arg parsing) - dotnet/rest.tests: 25 tests (BuildChatBody, Trunc, arg parsing) - dotnet/a2a.tests: 21 tests (Extract/Join with A2A SDK types, arg parsing) - rust/a2a: 34 inline tests (auth code extraction, JWT decode, message building, text joining, delta printing) - swift/a2a: 8 tests (ChatMessage model) + commented test suggestions for private members ~60% of tests cover non-happy-path scenarios: malformed JSON, empty/null inputs, arg parsing crashes (IndexOutOfRangeException, FormatException), JSON injection attempts, unicode/emoji, large payloads, and auth error priority handling. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Extract shared helpers, fix arg-parsing bugs, add test runner - Move testable pure functions (ParseArgs, ExtractText, BuildChatBody, Trunc, Extract, Join) from Program.cs into Helpers.cs files so tests exercise real production code instead of duplicated copies - Fix args[++i] crash when a flag is the last argument (returns error instead of IndexOutOfRangeException) in all 3 .NET samples - Fix int.Parse on --verbosity (use TryParse, return error instead of FormatException) in rest and a2a samples - Add WorkIQSamples.sln and scripts/test.sh for running all tests - Make Swift BearerTokenAuth internal and extract renderMarkdown as a free function so both can be unit tested (9 new Swift tests enabled) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix cross-platform documentation gaps - Add platform column to root sample table, include a2a-raw and Swift - Fix .NET SDK version: 8.0 -> 10.0, add Xcode to prerequisites - Add pre-obtained JWT examples to all .NET READMEs for macOS/Linux - Add explicit "WAM is Windows-only" notes to all .NET quick starts - Fix rest README: add missing --graph flag, --verbosity, --header params - Add Azure CLI install commands for all platforms (brew/winget/apt link) - Document Windows token cache path alongside Unix path in Rust README - Note NTFS vs Unix file permissions for cached tokens Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f98abe3 commit dea981f

30 files changed

Lines changed: 2042 additions & 163 deletions

README.md

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,13 @@
22

33
Sample clients for the [Work IQ](https://learn.microsoft.com/en-us/microsoft-365/copilot/extensibility/workiq-overview) API — Microsoft's AI-native interface to Microsoft 365 work intelligence.
44

5-
| Sample | Language | Protocol | Description |
6-
|--------|----------|----------|-------------|
7-
| [**dotnet/a2a/**](dotnet/a2a/) | C# | [A2A (Agent-to-Agent)](https://a2a-protocol.org) | Interactive agent session using the open A2A protocol over JSON-RPC |
8-
| [**dotnet/rest/**](dotnet/rest/) | C# | REST | Interactive chat using the [Copilot Chat API](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/api/ai-services/chat/overview) with sync and streaming modes |
9-
| [**rust/a2a/**](rust/a2a/) | Rust | [A2A (Agent-to-Agent)](https://a2a-protocol.org) | Interactive agent session with device code auth, token caching, and SSE streaming |
10-
11-
## Swift Samples (`swift/`)
12-
13-
| Sample | Protocol | Description |
14-
|--------|----------|-------------|
15-
| [**swift/a2a/**](swift/a2a/) | [A2A (Agent-to-Agent)](https://a2a-protocol.org) | SwiftUI iOS/iPadOS chat app using A2A v0.3 with streaming responses |
16-
17-
## Rust Samples (`rust/`)
18-
19-
| Sample | Protocol | Description |
20-
|--------|----------|-------------|
21-
| [**rust/a2a/**](rust/a2a/) | [A2A (Agent-to-Agent)](https://a2a-protocol.org) | Interactive agent session with device code auth, token caching, and SSE streaming |
5+
| Sample | Language | Platform | Protocol | Description |
6+
|--------|----------|----------|----------|-------------|
7+
| [**dotnet/a2a/**](dotnet/a2a/) | C# | Windows, macOS, Linux | [A2A](https://a2a-protocol.org) | Interactive agent session using the A2A protocol over JSON-RPC |
8+
| [**dotnet/a2a-raw/**](dotnet/a2a-raw/) | C# | Windows, macOS, Linux | [A2A](https://a2a-protocol.org) | Same, but with raw `HttpClient` + JSON (no A2A SDK) |
9+
| [**dotnet/rest/**](dotnet/rest/) | C# | Windows, macOS, Linux | REST | Interactive chat using the [Copilot Chat API](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/api/ai-services/chat/overview) |
10+
| [**rust/a2a/**](rust/a2a/) | Rust | Windows, macOS, Linux | [A2A](https://a2a-protocol.org) | Interactive agent session with device code auth and token caching |
11+
| [**swift/a2a/**](swift/a2a/) | Swift | iOS/iPadOS (macOS to build) | [A2A](https://a2a-protocol.org) | SwiftUI chat app with streaming responses |
2212

2313
> **Current state**: Work IQ is accessed through the Microsoft Graph API at `graph.microsoft.com`. All samples use Graph endpoints and Graph authentication today.
2414
>
@@ -54,12 +44,15 @@ After adding permissions, click **Grant admin consent for [your tenant]**.
5444

5545
### 3. Language-specific SDKs
5646

57-
- **dotnet/** samples: [.NET 8.0 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) or later
47+
- **dotnet/** samples: [.NET 10.0 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) or later
5848
- **rust/** samples: [Rust toolchain](https://rustup.rs/) (stable)
49+
- **swift/** samples: [Xcode 26+](https://developer.apple.com/xcode/) (macOS only)
5950

6051
## Authentication
6152

62-
### WAM (Windows Account Manager) — recommended on Windows (.NET only)
53+
All samples support multiple authentication methods. Choose the one that fits your platform:
54+
55+
### WAM (Windows Account Manager) — Windows only, .NET samples
6356

6457
Uses the Windows broker for silent SSO. No browser popup for returning users.
6558

@@ -68,20 +61,24 @@ cd dotnet/a2a
6861
dotnet run -- --graph --token WAM --appid <your-app-client-id>
6962
```
7063

71-
### Device code flow — any platform (Rust)
64+
> **Note:** WAM is only available on Windows. On macOS and Linux, use a pre-obtained JWT token instead (see below).
65+
66+
### Device code flow — all platforms (Rust, Swift)
67+
68+
The Rust CLI and Swift app use device code flow, which works on any platform with a web browser.
7269

7370
```bash
7471
cd rust/a2a
7572
cargo run -- --appid <your-app-client-id>
7673
# Follow the on-screen instructions to authenticate in a browser
7774
```
7875

79-
### Pre-obtained JWT token — any platform
76+
### Pre-obtained JWT token — all platforms, all samples
8077

81-
Acquire a token externally (e.g., via [Graph Explorer](https://developer.microsoft.com/en-us/graph/graph-explorer), `az account get-access-token`, or your own MSAL code) and pass it directly:
78+
Acquire a token externally (e.g., via [Graph Explorer](https://developer.microsoft.com/en-us/graph/graph-explorer), `az account get-access-token`, or your own MSAL code) and pass it directly. This works on Windows, macOS, and Linux.
8279

8380
```bash
84-
# .NET
81+
# .NET samples
8582
cd dotnet/a2a
8683
dotnet run -- --graph --token eyJ0eXAiOiJKV1Qi...
8784

dotnet/WorkIQSamples.sln

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "a2a-raw", "a2a-raw", "{CCFF03C2-3201-45FB-2E8F-00A04B2D057D}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "workiq-a2a-raw", "a2a-raw\workiq-a2a-raw.csproj", "{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "a2a-raw.tests", "a2a-raw.tests", "{ECE5971F-AE9E-BA12-55AA-4929917FAF06}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "workiq-a2a-raw-tests", "a2a-raw.tests\workiq-a2a-raw-tests.csproj", "{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}"
13+
EndProject
14+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "rest", "rest", "{CAF2E94B-1F3E-339F-03CF-980AEBF14E62}"
15+
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "workiq-rest-sample", "rest\workiq-rest-sample.csproj", "{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}"
17+
EndProject
18+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "rest.tests", "rest.tests", "{99F3A7AA-53A2-3584-6717-8D7F80AF0238}"
19+
EndProject
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "workiq-rest-tests", "rest.tests\workiq-rest-tests.csproj", "{44DDF8F5-469B-4650-A170-721FFACB4B08}"
21+
EndProject
22+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "a2a", "a2a", "{DF537D00-43B0-169F-440F-F716863E989D}"
23+
EndProject
24+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "workiq-a2a-sample", "a2a\workiq-a2a-sample.csproj", "{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}"
25+
EndProject
26+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "a2a.tests", "a2a.tests", "{80F48381-A032-BB77-AC00-2E31FC5BD3EE}"
27+
EndProject
28+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "workiq-a2a-tests", "a2a.tests\workiq-a2a-tests.csproj", "{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}"
29+
EndProject
30+
Global
31+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
32+
Debug|Any CPU = Debug|Any CPU
33+
Debug|x64 = Debug|x64
34+
Debug|x86 = Debug|x86
35+
Release|Any CPU = Release|Any CPU
36+
Release|x64 = Release|x64
37+
Release|x86 = Release|x86
38+
EndGlobalSection
39+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
40+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Debug|Any CPU.Build.0 = Debug|Any CPU
42+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Debug|x64.ActiveCfg = Debug|Any CPU
43+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Debug|x64.Build.0 = Debug|Any CPU
44+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Debug|x86.ActiveCfg = Debug|Any CPU
45+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Debug|x86.Build.0 = Debug|Any CPU
46+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Release|Any CPU.ActiveCfg = Release|Any CPU
47+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Release|Any CPU.Build.0 = Release|Any CPU
48+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Release|x64.ActiveCfg = Release|Any CPU
49+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Release|x64.Build.0 = Release|Any CPU
50+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Release|x86.ActiveCfg = Release|Any CPU
51+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791}.Release|x86.Build.0 = Release|Any CPU
52+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Debug|Any CPU.Build.0 = Debug|Any CPU
54+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Debug|x64.ActiveCfg = Debug|Any CPU
55+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Debug|x64.Build.0 = Debug|Any CPU
56+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Debug|x86.ActiveCfg = Debug|Any CPU
57+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Debug|x86.Build.0 = Debug|Any CPU
58+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Release|Any CPU.ActiveCfg = Release|Any CPU
59+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Release|Any CPU.Build.0 = Release|Any CPU
60+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Release|x64.ActiveCfg = Release|Any CPU
61+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Release|x64.Build.0 = Release|Any CPU
62+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Release|x86.ActiveCfg = Release|Any CPU
63+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A}.Release|x86.Build.0 = Release|Any CPU
64+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
65+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Debug|Any CPU.Build.0 = Debug|Any CPU
66+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Debug|x64.ActiveCfg = Debug|Any CPU
67+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Debug|x64.Build.0 = Debug|Any CPU
68+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Debug|x86.ActiveCfg = Debug|Any CPU
69+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Debug|x86.Build.0 = Debug|Any CPU
70+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Release|Any CPU.ActiveCfg = Release|Any CPU
71+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Release|Any CPU.Build.0 = Release|Any CPU
72+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Release|x64.ActiveCfg = Release|Any CPU
73+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Release|x64.Build.0 = Release|Any CPU
74+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Release|x86.ActiveCfg = Release|Any CPU
75+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790}.Release|x86.Build.0 = Release|Any CPU
76+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
77+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Debug|Any CPU.Build.0 = Debug|Any CPU
78+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Debug|x64.ActiveCfg = Debug|Any CPU
79+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Debug|x64.Build.0 = Debug|Any CPU
80+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Debug|x86.ActiveCfg = Debug|Any CPU
81+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Debug|x86.Build.0 = Debug|Any CPU
82+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Release|Any CPU.ActiveCfg = Release|Any CPU
83+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Release|Any CPU.Build.0 = Release|Any CPU
84+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Release|x64.ActiveCfg = Release|Any CPU
85+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Release|x64.Build.0 = Release|Any CPU
86+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Release|x86.ActiveCfg = Release|Any CPU
87+
{44DDF8F5-469B-4650-A170-721FFACB4B08}.Release|x86.Build.0 = Release|Any CPU
88+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
89+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Debug|Any CPU.Build.0 = Debug|Any CPU
90+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Debug|x64.ActiveCfg = Debug|Any CPU
91+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Debug|x64.Build.0 = Debug|Any CPU
92+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Debug|x86.ActiveCfg = Debug|Any CPU
93+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Debug|x86.Build.0 = Debug|Any CPU
94+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Release|Any CPU.ActiveCfg = Release|Any CPU
95+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Release|Any CPU.Build.0 = Release|Any CPU
96+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Release|x64.ActiveCfg = Release|Any CPU
97+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Release|x64.Build.0 = Release|Any CPU
98+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Release|x86.ActiveCfg = Release|Any CPU
99+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783}.Release|x86.Build.0 = Release|Any CPU
100+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
101+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
102+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Debug|x64.ActiveCfg = Debug|Any CPU
103+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Debug|x64.Build.0 = Debug|Any CPU
104+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Debug|x86.ActiveCfg = Debug|Any CPU
105+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Debug|x86.Build.0 = Debug|Any CPU
106+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
107+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Release|Any CPU.Build.0 = Release|Any CPU
108+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Release|x64.ActiveCfg = Release|Any CPU
109+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Release|x64.Build.0 = Release|Any CPU
110+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Release|x86.ActiveCfg = Release|Any CPU
111+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F}.Release|x86.Build.0 = Release|Any CPU
112+
EndGlobalSection
113+
GlobalSection(SolutionProperties) = preSolution
114+
HideSolutionNode = FALSE
115+
EndGlobalSection
116+
GlobalSection(NestedProjects) = preSolution
117+
{1FDE52B4-ADE3-45C1-92B1-7FBDED0AF791} = {CCFF03C2-3201-45FB-2E8F-00A04B2D057D}
118+
{64FD8CEC-4C54-4873-B4E9-CA3F6BDA108A} = {ECE5971F-AE9E-BA12-55AA-4929917FAF06}
119+
{521D7579-B37C-47B5-AE2C-6ACFCC1AD790} = {CAF2E94B-1F3E-339F-03CF-980AEBF14E62}
120+
{44DDF8F5-469B-4650-A170-721FFACB4B08} = {99F3A7AA-53A2-3584-6717-8D7F80AF0238}
121+
{709CB2BA-85F0-41E1-8D28-F46CAC8BF783} = {DF537D00-43B0-169F-440F-F716863E989D}
122+
{F1B663D7-DBB9-4FB0-B8D5-60A8A077CD4F} = {80F48381-A032-BB77-AC00-2E31FC5BD3EE}
123+
EndGlobalSection
124+
EndGlobal
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using WorkIQ.A2ARaw;
5+
using Xunit;
6+
7+
namespace WorkIQ.A2ARaw.Tests;
8+
9+
/// <summary>
10+
/// Tests for <see cref="Helpers.ParseArgs"/> — the arg-parsing logic
11+
/// used by the a2a-raw sample app.
12+
/// </summary>
13+
public class ArgParsingTests
14+
{
15+
[Fact]
16+
public void ValidArgs_AllParsedCorrectly()
17+
{
18+
var r = Helpers.ParseArgs(["--endpoint", "https://example.com", "--token", "abc", "--appid", "id1", "--stream"]);
19+
Assert.Null(r.Error);
20+
Assert.Equal("https://example.com", r.Endpoint);
21+
Assert.Equal("abc", r.Token);
22+
Assert.Equal("id1", r.AppId);
23+
Assert.True(r.Stream);
24+
}
25+
26+
[Fact]
27+
public void ShortFlags_Work()
28+
{
29+
var r = Helpers.ParseArgs(["-e", "https://example.com", "-t", "tok", "-a", "app"]);
30+
Assert.Null(r.Error);
31+
Assert.Equal("https://example.com", r.Endpoint);
32+
Assert.Equal("tok", r.Token);
33+
Assert.Equal("app", r.AppId);
34+
}
35+
36+
[Fact]
37+
public void UnknownFlag_ReturnsError()
38+
{
39+
var r = Helpers.ParseArgs(["--unknown"]);
40+
Assert.NotNull(r.Error);
41+
Assert.Contains("Unknown flag", r.Error);
42+
}
43+
44+
[Fact]
45+
public void AllHeadersFlag_Parsed()
46+
{
47+
var r = Helpers.ParseArgs(["--all-headers", "--endpoint", "url", "--token", "t"]);
48+
Assert.True(r.AllHeaders);
49+
}
50+
51+
[Fact]
52+
public void AccountFlag_Parsed()
53+
{
54+
var r = Helpers.ParseArgs(["--endpoint", "url", "--token", "t", "--account", "user@example.com"]);
55+
Assert.Equal("user@example.com", r.Account);
56+
}
57+
58+
[Fact]
59+
public void MissingValueAfterToken_ReturnsError()
60+
{
61+
var r = Helpers.ParseArgs(["--token"]);
62+
Assert.NotNull(r.Error);
63+
Assert.Contains("Missing value", r.Error);
64+
Assert.Contains("--token", r.Error);
65+
}
66+
67+
[Fact]
68+
public void MissingValueAfterEndpoint_ReturnsError()
69+
{
70+
var r = Helpers.ParseArgs(["--endpoint"]);
71+
Assert.NotNull(r.Error);
72+
Assert.Contains("Missing value", r.Error);
73+
Assert.Contains("--endpoint", r.Error);
74+
}
75+
76+
[Fact]
77+
public void MissingValueAfterShortFlag_ReturnsError()
78+
{
79+
var r = Helpers.ParseArgs(["-t"]);
80+
Assert.NotNull(r.Error);
81+
Assert.Contains("Missing value", r.Error);
82+
}
83+
84+
[Fact]
85+
public void EmptyArgs_NoError()
86+
{
87+
var r = Helpers.ParseArgs([]);
88+
Assert.Null(r.Error);
89+
Assert.Null(r.Endpoint);
90+
Assert.Null(r.Token);
91+
}
92+
}

0 commit comments

Comments
 (0)