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:
181
sdk-go/agents_test.go
Normal file
181
sdk-go/agents_test.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package agentidp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// mockAgent is the canonical test agent fixture.
|
||||
var mockAgent = Agent{
|
||||
AgentID: "uuid-1",
|
||||
Email: "a@b.ai",
|
||||
AgentType: "screener",
|
||||
Version: "1.0.0",
|
||||
Capabilities: []string{"read"},
|
||||
Owner: "team",
|
||||
DeploymentEnv: "production",
|
||||
Status: "active",
|
||||
CreatedAt: "2026-01-01T00:00:00Z",
|
||||
UpdatedAt: "2026-01-01T00:00:00Z",
|
||||
}
|
||||
|
||||
var mockPaginatedAgents = PaginatedAgents{
|
||||
Data: []Agent{mockAgent},
|
||||
Total: 1,
|
||||
Page: 1,
|
||||
Limit: 20,
|
||||
}
|
||||
|
||||
// staticToken returns a fixed token for all test service clients.
|
||||
func staticToken(_ context.Context) (string, error) {
|
||||
return "test-bearer-token", nil
|
||||
}
|
||||
|
||||
func newAgentServer(t *testing.T, method, path string, status int, body interface{}) *httptest.Server {
|
||||
t.Helper()
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != method {
|
||||
t.Errorf("expected method %s, got %s", method, r.Method)
|
||||
}
|
||||
if r.URL.Path != path {
|
||||
t.Errorf("expected path %s, got %s", path, r.URL.Path)
|
||||
}
|
||||
if r.Header.Get("Authorization") == "" {
|
||||
t.Error("missing Authorization header")
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
if body != nil {
|
||||
_ = json.NewEncoder(w).Encode(body)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
func TestAgentRegistryClient_RegisterAgent(t *testing.T) {
|
||||
srv := newAgentServer(t, http.MethodPost, "/api/v1/agents", 201, mockAgent)
|
||||
defer srv.Close()
|
||||
|
||||
client := newAgentRegistryClient(srv.URL, staticToken, &http.Client{})
|
||||
agent, err := client.RegisterAgent(context.Background(), RegisterAgentRequest{
|
||||
Email: "a@b.ai", AgentType: "screener", Version: "1.0.0",
|
||||
Capabilities: []string{"read"}, Owner: "team", DeploymentEnv: "production",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if agent.AgentID != "uuid-1" {
|
||||
t.Errorf("expected uuid-1, got %q", agent.AgentID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentRegistryClient_ListAgents(t *testing.T) {
|
||||
srv := newAgentServer(t, http.MethodGet, "/api/v1/agents", 200, mockPaginatedAgents)
|
||||
defer srv.Close()
|
||||
|
||||
client := newAgentRegistryClient(srv.URL, staticToken, &http.Client{})
|
||||
result, err := client.ListAgents(context.Background(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if result.Total != 1 {
|
||||
t.Errorf("expected total 1, got %d", result.Total)
|
||||
}
|
||||
if len(result.Data) != 1 || result.Data[0].AgentID != "uuid-1" {
|
||||
t.Error("unexpected data in paginated result")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentRegistryClient_ListAgents_WithParams(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("status") != "active" {
|
||||
t.Errorf("expected status=active, got %q", r.URL.Query().Get("status"))
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(mockPaginatedAgents)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
client := newAgentRegistryClient(srv.URL, staticToken, &http.Client{})
|
||||
_, err := client.ListAgents(context.Background(), &ListAgentsParams{Status: "active"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentRegistryClient_GetAgent(t *testing.T) {
|
||||
srv := newAgentServer(t, http.MethodGet, "/api/v1/agents/uuid-1", 200, mockAgent)
|
||||
defer srv.Close()
|
||||
|
||||
client := newAgentRegistryClient(srv.URL, staticToken, &http.Client{})
|
||||
agent, err := client.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 TestAgentRegistryClient_GetAgent_NotFound(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(404)
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{
|
||||
"code": "AgentNotFoundError",
|
||||
"message": "Agent not found.",
|
||||
})
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
client := newAgentRegistryClient(srv.URL, staticToken, &http.Client{})
|
||||
_, err := client.GetAgent(context.Background(), "bad-id")
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
apiErr, ok := err.(*AgentIdPError)
|
||||
if !ok {
|
||||
t.Fatalf("expected *AgentIdPError, got %T", err)
|
||||
}
|
||||
if apiErr.Code != "AgentNotFoundError" {
|
||||
t.Errorf("expected AgentNotFoundError, got %q", apiErr.Code)
|
||||
}
|
||||
if apiErr.HTTPStatus != 404 {
|
||||
t.Errorf("expected 404, got %d", apiErr.HTTPStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentRegistryClient_UpdateAgent(t *testing.T) {
|
||||
updated := mockAgent
|
||||
updated.Version = "2.0.0"
|
||||
srv := newAgentServer(t, http.MethodPatch, "/api/v1/agents/uuid-1", 200, updated)
|
||||
defer srv.Close()
|
||||
|
||||
v := "2.0.0"
|
||||
client := newAgentRegistryClient(srv.URL, staticToken, &http.Client{})
|
||||
agent, err := client.UpdateAgent(context.Background(), "uuid-1", UpdateAgentRequest{Version: &v})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if agent.Version != "2.0.0" {
|
||||
t.Errorf("expected version 2.0.0, got %q", agent.Version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentRegistryClient_DecommissionAgent(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
t.Errorf("expected DELETE, got %s", r.Method)
|
||||
}
|
||||
w.WriteHeader(204)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
client := newAgentRegistryClient(srv.URL, staticToken, &http.Client{})
|
||||
err := client.DecommissionAgent(context.Background(), "uuid-1")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user