feat: Phase 2 Workstream 3 — Go SDK (github.com/sentryagent/idp-sdk-go)
Single-package agentidp SDK in sdk-go/: - AgentIdPClient composing AgentRegistryClient, CredentialClient, TokenServiceClient, AuditClient — all 14 endpoints covered - Goroutine-safe TokenManager (sync.Mutex) with 60s refresh buffer - AgentIdPError implementing error interface with Code/HTTPStatus/Details - Context-aware: all service methods take context.Context as first arg - doRequest shared helper; token endpoints use form-encoded POST directly - go vet: 0 warnings | staticcheck: 0 warnings - go test ./...: 37/37 passed | coverage: 81.0% (>80% gate) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
124
sdk-go/client_test.go
Normal file
124
sdk-go/client_test.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package agentidp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// integrationServer returns a minimal mock server that handles the token endpoint
|
||||
// plus a provided handler for all other routes.
|
||||
func integrationServer(t *testing.T, handler http.HandlerFunc) *httptest.Server {
|
||||
t.Helper()
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/api/v1/token", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"access_token": "integration-token",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 3600,
|
||||
"scope": "agents:read agents:write tokens:read audit:read",
|
||||
})
|
||||
})
|
||||
mux.HandleFunc("/", handler)
|
||||
return httptest.NewServer(mux)
|
||||
}
|
||||
|
||||
func TestNewAgentIdPClient_GetAgent(t *testing.T) {
|
||||
srv := integrationServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if !strings.HasPrefix(r.URL.Path, "/api/v1/agents/") {
|
||||
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
if r.Header.Get("Authorization") != "Bearer integration-token" {
|
||||
t.Errorf("unexpected Authorization: %q", r.Header.Get("Authorization"))
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(mockAgent)
|
||||
})
|
||||
defer srv.Close()
|
||||
|
||||
client := NewAgentIdPClient(AgentIdPClientConfig{
|
||||
BaseURL: srv.URL,
|
||||
ClientID: "cid",
|
||||
ClientSecret: "secret",
|
||||
})
|
||||
|
||||
agent, err := client.Agents.GetAgent(context.Background(), "uuid-1")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if agent.AgentID != "uuid-1" {
|
||||
t.Errorf("expected uuid-1, got %q", agent.AgentID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAgentIdPClient_ClearTokenCache(t *testing.T) {
|
||||
callCount := 0
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/api/v1/token" {
|
||||
callCount++
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"access_token": "tok",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 3600,
|
||||
"scope": "agents:read",
|
||||
})
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(mockAgent)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
client := NewAgentIdPClient(AgentIdPClientConfig{
|
||||
BaseURL: srv.URL,
|
||||
ClientID: "cid",
|
||||
ClientSecret: "secret",
|
||||
})
|
||||
|
||||
_, _ = client.Agents.GetAgent(context.Background(), "uuid-1")
|
||||
client.ClearTokenCache()
|
||||
_, _ = client.Agents.GetAgent(context.Background(), "uuid-1")
|
||||
|
||||
if callCount != 2 {
|
||||
t.Errorf("expected 2 token fetches after ClearTokenCache, got %d", callCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAgentIdPClient_DefaultScope(t *testing.T) {
|
||||
var capturedScope string
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/api/v1/token" {
|
||||
_ = r.ParseForm()
|
||||
capturedScope = r.FormValue("scope")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"access_token": "tok",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 3600,
|
||||
"scope": capturedScope,
|
||||
})
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(mockAgent)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
client := NewAgentIdPClient(AgentIdPClientConfig{
|
||||
BaseURL: srv.URL,
|
||||
ClientID: "cid",
|
||||
ClientSecret: "secret",
|
||||
// Scope intentionally omitted → defaults applied
|
||||
})
|
||||
_, _ = client.Agents.GetAgent(context.Background(), "uuid-1")
|
||||
|
||||
expected := "agents:read agents:write tokens:read audit:read"
|
||||
if capturedScope != expected {
|
||||
t.Errorf("expected scope %q, got %q", expected, capturedScope)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user