Implement the previously stubbed stdio connect mode for the GitHub
Copilot provider, allowing picoclaw to automatically spawn and
communicate with the Copilot CLI subprocess via stdin/stdout.
Changes:
- github_copilot_provider.go: implement stdio mode using
copilot.ClientOptions{CLIPath}, sharing Start/CreateSession/Chat
flow with grpc mode
- factory_provider.go: skip default apiBase for stdio mode (SDK
uses 'copilot' from PATH)
- factory.go: skip default apiBase for stdio mode in legacy path
- github_copilot_provider_test.go: add 9 test cases for stdio mode
- factory_test.go: add 3 test cases for copilot stdio factory logic
120 lines
4.3 KiB
Go
120 lines
4.3 KiB
Go
package providers
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/sipeed/picoclaw/pkg/config"
|
|
)
|
|
|
|
func TestNewGitHubCopilotProvider_UnknownConnectMode(t *testing.T) {
|
|
_, err := NewGitHubCopilotProvider("localhost:4321", "unknown", "gpt-4.1")
|
|
if err == nil {
|
|
t.Fatal("expected error for unknown connect mode, got nil")
|
|
}
|
|
if got := err.Error(); got != "unknown connect mode: unknown" {
|
|
t.Fatalf("error = %q, want %q", got, "unknown connect mode: unknown")
|
|
}
|
|
}
|
|
|
|
func TestNewGitHubCopilotProvider_DefaultConnectMode(t *testing.T) {
|
|
// When connectMode is empty, it should default to "grpc" and attempt to connect.
|
|
// Since there's no server, we expect a connection error (not a "not implemented" error).
|
|
_, err := NewGitHubCopilotProvider("localhost:19999", "", "gpt-4.1")
|
|
if err == nil {
|
|
t.Fatal("expected connection error, got nil")
|
|
}
|
|
// Should NOT get "not implemented" error — that was the old behavior before stdio was implemented
|
|
if got := err.Error(); got == "stdio mode not implemented for GitHub Copilot provider; please use 'grpc' mode instead" {
|
|
t.Fatal("got old 'not implemented' error; stdio mode should be implemented now")
|
|
}
|
|
}
|
|
|
|
func TestNewGitHubCopilotProvider_StdioModeAccepted(t *testing.T) {
|
|
// Stdio mode should no longer return "not implemented".
|
|
// It will fail to start because the copilot CLI binary is likely not installed,
|
|
// but the error should be about starting the CLI process, not about the mode
|
|
// being unimplemented.
|
|
_, err := NewGitHubCopilotProvider("", "stdio", "gpt-4.1")
|
|
if err == nil {
|
|
// If it succeeds, that's fine too (copilot CLI is installed).
|
|
return
|
|
}
|
|
got := err.Error()
|
|
if got == "stdio mode not implemented for GitHub Copilot provider; please use 'grpc' mode instead" {
|
|
t.Fatal("got old 'not implemented' error; stdio mode should be implemented now")
|
|
}
|
|
// Expect a startup/connection error, not an implementation error
|
|
t.Logf("expected startup error (copilot CLI likely not installed): %v", err)
|
|
}
|
|
|
|
func TestNewGitHubCopilotProvider_StdioWithCustomCLIPath(t *testing.T) {
|
|
// When a custom CLI path is provided in stdio mode, the error should reference
|
|
// the startup failure, not "not implemented".
|
|
_, err := NewGitHubCopilotProvider("/nonexistent/copilot", "stdio", "gpt-4.1")
|
|
if err == nil {
|
|
t.Fatal("expected error for nonexistent CLI path, got nil")
|
|
}
|
|
got := err.Error()
|
|
if got == "stdio mode not implemented for GitHub Copilot provider; please use 'grpc' mode instead" {
|
|
t.Fatal("got old 'not implemented' error; stdio mode should be implemented now")
|
|
}
|
|
t.Logf("got expected startup error: %v", err)
|
|
}
|
|
|
|
func TestGitHubCopilotProvider_GetDefaultModel(t *testing.T) {
|
|
p := &GitHubCopilotProvider{}
|
|
if got := p.GetDefaultModel(); got != "gpt-4.1" {
|
|
t.Fatalf("GetDefaultModel() = %q, want %q", got, "gpt-4.1")
|
|
}
|
|
}
|
|
|
|
func TestGitHubCopilotProvider_CloseNilClient(t *testing.T) {
|
|
// Close on a provider with nil client should not panic
|
|
p := &GitHubCopilotProvider{}
|
|
p.Close() // should not panic
|
|
}
|
|
|
|
func TestGitHubCopilotProvider_ChatNilSession(t *testing.T) {
|
|
p := &GitHubCopilotProvider{}
|
|
_, err := p.Chat(nil, nil, nil, "gpt-4.1", nil)
|
|
if err == nil {
|
|
t.Fatal("expected error for nil session, got nil")
|
|
}
|
|
if got := err.Error(); got != "provider closed" {
|
|
t.Fatalf("error = %q, want %q", got, "provider closed")
|
|
}
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_CopilotStdioMode(t *testing.T) {
|
|
cfg := &config.ModelConfig{
|
|
Model: "copilot/gpt-4.1",
|
|
ConnectMode: "stdio",
|
|
}
|
|
|
|
// This will try to start the copilot CLI which likely isn't installed,
|
|
// but the error should be a startup error, not "unknown protocol" or "not implemented".
|
|
_, _, err := CreateProviderFromConfig(cfg)
|
|
if err == nil {
|
|
return // copilot CLI is installed, all good
|
|
}
|
|
got := err.Error()
|
|
if got == "stdio mode not implemented for GitHub Copilot provider; please use 'grpc' mode instead" {
|
|
t.Fatal("got old 'not implemented' error; stdio should be supported now")
|
|
}
|
|
t.Logf("expected startup error: %v", err)
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_CopilotGrpcModeDefaultAPIBase(t *testing.T) {
|
|
cfg := &config.ModelConfig{
|
|
Model: "github-copilot/gpt-4.1",
|
|
ConnectMode: "grpc",
|
|
}
|
|
|
|
// Will fail to connect, but should attempt localhost:4321
|
|
_, _, err := CreateProviderFromConfig(cfg)
|
|
if err == nil {
|
|
return
|
|
}
|
|
// The error should mention the connection failure, not "not implemented"
|
|
t.Logf("expected connection error: %v", err)
|
|
}
|