Skip to content

Commit 6ead90f

Browse files
E2E tests for RPC methods
1 parent f851fb2 commit 6ead90f

File tree

4 files changed

+464
-0
lines changed

4 files changed

+464
-0
lines changed

dotnet/test/RpcTests.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
using GitHub.Copilot.SDK.Test.Harness;
6+
using Xunit;
7+
using Xunit.Abstractions;
8+
9+
namespace GitHub.Copilot.SDK.Test;
10+
11+
public class RpcTests(E2ETestFixture fixture, ITestOutputHelper output) : E2ETestBase(fixture, "session", output)
12+
{
13+
[Fact]
14+
public async Task Should_Call_Rpc_Ping_With_Typed_Params_And_Result()
15+
{
16+
var result = await Client.Rpc.PingAsync(message: "typed rpc test");
17+
Assert.Equal("pong: typed rpc test", result.Message);
18+
Assert.True(result.Timestamp >= 0);
19+
}
20+
21+
[Fact]
22+
public async Task Should_Call_Rpc_Models_List_With_Typed_Result()
23+
{
24+
var authStatus = await Client.GetAuthStatusAsync();
25+
if (!authStatus.IsAuthenticated)
26+
{
27+
// Skip if not authenticated - models.list requires auth
28+
return;
29+
}
30+
31+
var result = await Client.Rpc.Models.ListAsync();
32+
Assert.NotNull(result.Models);
33+
}
34+
35+
// account.getQuota is defined in schema but not yet implemented in CLI
36+
[Fact(Skip = "account.getQuota not yet implemented in CLI")]
37+
public async Task Should_Call_Rpc_Account_GetQuota_When_Authenticated()
38+
{
39+
var authStatus = await Client.GetAuthStatusAsync();
40+
if (!authStatus.IsAuthenticated)
41+
{
42+
// Skip if not authenticated - account.getQuota requires auth
43+
return;
44+
}
45+
46+
var result = await Client.Rpc.Account.GetQuotaAsync();
47+
Assert.NotNull(result.QuotaSnapshots);
48+
}
49+
50+
// session.model.getCurrent is defined in schema but not yet implemented in CLI
51+
[Fact(Skip = "session.model.getCurrent not yet implemented in CLI")]
52+
public async Task Should_Call_Session_Rpc_Model_GetCurrent()
53+
{
54+
var session = await Client.CreateSessionAsync(new SessionConfig { Model = "claude-sonnet-4.5" });
55+
56+
var result = await session.Rpc.Model.GetCurrentAsync();
57+
Assert.NotNull(result.ModelId);
58+
Assert.NotEmpty(result.ModelId);
59+
}
60+
61+
// session.model.switchTo is defined in schema but not yet implemented in CLI
62+
[Fact(Skip = "session.model.switchTo not yet implemented in CLI")]
63+
public async Task Should_Call_Session_Rpc_Model_SwitchTo()
64+
{
65+
var session = await Client.CreateSessionAsync(new SessionConfig { Model = "claude-sonnet-4.5" });
66+
67+
// Get initial model
68+
var before = await session.Rpc.Model.GetCurrentAsync();
69+
Assert.NotNull(before.ModelId);
70+
71+
// Switch to a different model
72+
var result = await session.Rpc.Model.SwitchToAsync(modelId: "gpt-4.1");
73+
Assert.Equal("gpt-4.1", result.ModelId);
74+
75+
// Verify the switch persisted
76+
var after = await session.Rpc.Model.GetCurrentAsync();
77+
Assert.Equal("gpt-4.1", after.ModelId);
78+
}
79+
}

go/internal/e2e/rpc_test.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package e2e
2+
3+
import (
4+
"testing"
5+
6+
copilot "github.com/github/copilot-sdk/go"
7+
"github.com/github/copilot-sdk/go/internal/e2e/testharness"
8+
"github.com/github/copilot-sdk/go/rpc"
9+
)
10+
11+
func TestRpc(t *testing.T) {
12+
cliPath := testharness.CLIPath()
13+
if cliPath == "" {
14+
t.Fatal("CLI not found. Run 'npm install' in the nodejs directory first.")
15+
}
16+
17+
t.Run("should call RPC.Ping with typed params and result", func(t *testing.T) {
18+
client := copilot.NewClient(&copilot.ClientOptions{
19+
CLIPath: cliPath,
20+
UseStdio: copilot.Bool(true),
21+
})
22+
t.Cleanup(func() { client.ForceStop() })
23+
24+
if err := client.Start(t.Context()); err != nil {
25+
t.Fatalf("Failed to start client: %v", err)
26+
}
27+
28+
result, err := client.RPC.Ping(t.Context(), &rpc.PingParams{Message: copilot.String("typed rpc test")})
29+
if err != nil {
30+
t.Fatalf("Failed to call RPC.Ping: %v", err)
31+
}
32+
33+
if result.Message != "pong: typed rpc test" {
34+
t.Errorf("Expected message 'pong: typed rpc test', got %q", result.Message)
35+
}
36+
37+
if result.Timestamp < 0 {
38+
t.Errorf("Expected timestamp >= 0, got %d", result.Timestamp)
39+
}
40+
41+
if err := client.Stop(); err != nil {
42+
t.Errorf("Expected no errors on stop, got %v", err)
43+
}
44+
})
45+
46+
t.Run("should call RPC.Models.List with typed result", func(t *testing.T) {
47+
client := copilot.NewClient(&copilot.ClientOptions{
48+
CLIPath: cliPath,
49+
UseStdio: copilot.Bool(true),
50+
})
51+
t.Cleanup(func() { client.ForceStop() })
52+
53+
if err := client.Start(t.Context()); err != nil {
54+
t.Fatalf("Failed to start client: %v", err)
55+
}
56+
57+
authStatus, err := client.GetAuthStatus(t.Context())
58+
if err != nil {
59+
t.Fatalf("Failed to get auth status: %v", err)
60+
}
61+
62+
if !authStatus.IsAuthenticated {
63+
t.Skip("Not authenticated - skipping models.list test")
64+
}
65+
66+
result, err := client.RPC.Models.List(t.Context())
67+
if err != nil {
68+
t.Fatalf("Failed to call RPC.Models.List: %v", err)
69+
}
70+
71+
if result.Models == nil {
72+
t.Error("Expected models to be defined")
73+
}
74+
75+
if err := client.Stop(); err != nil {
76+
t.Errorf("Expected no errors on stop, got %v", err)
77+
}
78+
})
79+
80+
// account.getQuota is defined in schema but not yet implemented in CLI
81+
t.Run("should call RPC.Account.GetQuota when authenticated", func(t *testing.T) {
82+
t.Skip("account.getQuota not yet implemented in CLI")
83+
84+
client := copilot.NewClient(&copilot.ClientOptions{
85+
CLIPath: cliPath,
86+
UseStdio: copilot.Bool(true),
87+
})
88+
t.Cleanup(func() { client.ForceStop() })
89+
90+
if err := client.Start(t.Context()); err != nil {
91+
t.Fatalf("Failed to start client: %v", err)
92+
}
93+
94+
authStatus, err := client.GetAuthStatus(t.Context())
95+
if err != nil {
96+
t.Fatalf("Failed to get auth status: %v", err)
97+
}
98+
99+
if !authStatus.IsAuthenticated {
100+
t.Skip("Not authenticated - skipping account.getQuota test")
101+
}
102+
103+
result, err := client.RPC.Account.GetQuota(t.Context())
104+
if err != nil {
105+
t.Fatalf("Failed to call RPC.Account.GetQuota: %v", err)
106+
}
107+
108+
if result.QuotaSnapshots == nil {
109+
t.Error("Expected quotaSnapshots to be defined")
110+
}
111+
112+
if err := client.Stop(); err != nil {
113+
t.Errorf("Expected no errors on stop, got %v", err)
114+
}
115+
})
116+
}
117+
118+
func TestSessionRpc(t *testing.T) {
119+
ctx := testharness.NewE2ETestContext(t)
120+
121+
// session.model.getCurrent is defined in schema but not yet implemented in CLI
122+
t.Run("should call session.RPC.Model.GetCurrent", func(t *testing.T) {
123+
t.Skip("session.model.getCurrent not yet implemented in CLI")
124+
125+
session, err := ctx.Client.CreateSession(t.Context(), &copilot.SessionConfig{
126+
Model: "claude-sonnet-4.5",
127+
})
128+
if err != nil {
129+
t.Fatalf("Failed to create session: %v", err)
130+
}
131+
132+
result, err := session.RPC.Model.GetCurrent(t.Context())
133+
if err != nil {
134+
t.Fatalf("Failed to call session.RPC.Model.GetCurrent: %v", err)
135+
}
136+
137+
if result.ModelID == "" {
138+
t.Error("Expected modelId to be defined")
139+
}
140+
})
141+
142+
// session.model.switchTo is defined in schema but not yet implemented in CLI
143+
t.Run("should call session.RPC.Model.SwitchTo", func(t *testing.T) {
144+
t.Skip("session.model.switchTo not yet implemented in CLI")
145+
146+
session, err := ctx.Client.CreateSession(t.Context(), &copilot.SessionConfig{
147+
Model: "claude-sonnet-4.5",
148+
})
149+
if err != nil {
150+
t.Fatalf("Failed to create session: %v", err)
151+
}
152+
153+
// Get initial model
154+
before, err := session.RPC.Model.GetCurrent(t.Context())
155+
if err != nil {
156+
t.Fatalf("Failed to get current model: %v", err)
157+
}
158+
if before.ModelID == "" {
159+
t.Error("Expected initial modelId to be defined")
160+
}
161+
162+
// Switch to a different model
163+
result, err := session.RPC.Model.SwitchTo(t.Context(), &rpc.SessionModelSwitchToParams{
164+
ModelID: "gpt-4.1",
165+
})
166+
if err != nil {
167+
t.Fatalf("Failed to switch model: %v", err)
168+
}
169+
if result.ModelID != "gpt-4.1" {
170+
t.Errorf("Expected modelId 'gpt-4.1', got %q", result.ModelID)
171+
}
172+
173+
// Verify the switch persisted
174+
after, err := session.RPC.Model.GetCurrent(t.Context())
175+
if err != nil {
176+
t.Fatalf("Failed to get current model after switch: %v", err)
177+
}
178+
if after.ModelID != "gpt-4.1" {
179+
t.Errorf("Expected modelId 'gpt-4.1' after switch, got %q", after.ModelID)
180+
}
181+
})
182+
}

nodejs/test/e2e/rpc.test.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { describe, expect, it, onTestFinished } from "vitest";
2+
import { CopilotClient } from "../../src/index.js";
3+
import { createSdkTestContext } from "./harness/sdkTestContext.js";
4+
5+
function onTestFinishedForceStop(client: CopilotClient) {
6+
onTestFinished(async () => {
7+
try {
8+
await client.forceStop();
9+
} catch {
10+
// Ignore cleanup errors - process may already be stopped
11+
}
12+
});
13+
}
14+
15+
describe("RPC", () => {
16+
it("should call rpc.ping with typed params and result", async () => {
17+
const client = new CopilotClient({ useStdio: true });
18+
onTestFinishedForceStop(client);
19+
20+
await client.start();
21+
22+
const result = await client.rpc.ping({ message: "typed rpc test" });
23+
expect(result.message).toBe("pong: typed rpc test");
24+
expect(typeof result.timestamp).toBe("number");
25+
26+
await client.stop();
27+
});
28+
29+
it("should call rpc.models.list with typed result", async () => {
30+
const client = new CopilotClient({ useStdio: true });
31+
onTestFinishedForceStop(client);
32+
33+
await client.start();
34+
35+
const authStatus = await client.getAuthStatus();
36+
if (!authStatus.isAuthenticated) {
37+
await client.stop();
38+
return;
39+
}
40+
41+
const result = await client.rpc.models.list();
42+
expect(result.models).toBeDefined();
43+
expect(Array.isArray(result.models)).toBe(true);
44+
45+
await client.stop();
46+
});
47+
48+
// account.getQuota is defined in schema but not yet implemented in CLI
49+
it.skip("should call rpc.account.getQuota when authenticated", async () => {
50+
const client = new CopilotClient({ useStdio: true });
51+
onTestFinishedForceStop(client);
52+
53+
await client.start();
54+
55+
const authStatus = await client.getAuthStatus();
56+
if (!authStatus.isAuthenticated) {
57+
await client.stop();
58+
return;
59+
}
60+
61+
const result = await client.rpc.account.getQuota();
62+
expect(result.quotaSnapshots).toBeDefined();
63+
expect(typeof result.quotaSnapshots).toBe("object");
64+
65+
await client.stop();
66+
});
67+
});
68+
69+
describe("Session RPC", async () => {
70+
const { copilotClient: client } = await createSdkTestContext();
71+
72+
// session.model.getCurrent is defined in schema but not yet implemented in CLI
73+
it.skip("should call session.rpc.model.getCurrent", async () => {
74+
const session = await client.createSession({ model: "claude-sonnet-4.5" });
75+
76+
const result = await session.rpc.model.getCurrent();
77+
expect(result.modelId).toBeDefined();
78+
expect(typeof result.modelId).toBe("string");
79+
});
80+
81+
// session.model.switchTo is defined in schema but not yet implemented in CLI
82+
it.skip("should call session.rpc.model.switchTo", async () => {
83+
const session = await client.createSession({ model: "claude-sonnet-4.5" });
84+
85+
// Get initial model
86+
const before = await session.rpc.model.getCurrent();
87+
expect(before.modelId).toBeDefined();
88+
89+
// Switch to a different model
90+
const result = await session.rpc.model.switchTo({ modelId: "gpt-4.1" });
91+
expect(result.modelId).toBe("gpt-4.1");
92+
93+
// Verify the switch persisted
94+
const after = await session.rpc.model.getCurrent();
95+
expect(after.modelId).toBe("gpt-4.1");
96+
});
97+
});

0 commit comments

Comments
 (0)