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:
85
sdk-go/errors_test.go
Normal file
85
sdk-go/errors_test.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package agentidp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAgentIdPError_Error(t *testing.T) {
|
||||
err := &AgentIdPError{Code: "AgentNotFoundError", Message: "Agent not found.", HTTPStatus: 404}
|
||||
if err.Error() != "Agent not found." {
|
||||
t.Errorf("expected 'Agent not found.', got %q", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAPIError_ValidBody(t *testing.T) {
|
||||
body := []byte(`{"code":"AgentNotFoundError","message":"Not found.","details":{"id":"x"}}`)
|
||||
err := parseAPIError(body, 404)
|
||||
if err.Code != "AgentNotFoundError" {
|
||||
t.Errorf("expected code AgentNotFoundError, got %q", err.Code)
|
||||
}
|
||||
if err.HTTPStatus != 404 {
|
||||
t.Errorf("expected status 404, got %d", err.HTTPStatus)
|
||||
}
|
||||
if err.Details == nil {
|
||||
t.Error("expected non-nil Details")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAPIError_UnparseableBody(t *testing.T) {
|
||||
err := parseAPIError([]byte("not json"), 500)
|
||||
if err.Code != "UNKNOWN_ERROR" {
|
||||
t.Errorf("expected UNKNOWN_ERROR, got %q", err.Code)
|
||||
}
|
||||
if err.HTTPStatus != 500 {
|
||||
t.Errorf("expected 500, got %d", err.HTTPStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAPIError_EmptyCode(t *testing.T) {
|
||||
// Valid JSON but no "code" field → falls back to UNKNOWN_ERROR
|
||||
err := parseAPIError([]byte(`{"message":"oops"}`), 503)
|
||||
if err.Code != "UNKNOWN_ERROR" {
|
||||
t.Errorf("expected UNKNOWN_ERROR, got %q", err.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseOAuth2Error_ValidBody(t *testing.T) {
|
||||
body := []byte(`{"error":"invalid_client","error_description":"Bad credentials."}`)
|
||||
err := parseOAuth2Error(body, 401)
|
||||
if err.Code != "invalid_client" {
|
||||
t.Errorf("expected invalid_client, got %q", err.Code)
|
||||
}
|
||||
if err.Message != "Bad credentials." {
|
||||
t.Errorf("expected 'Bad credentials.', got %q", err.Message)
|
||||
}
|
||||
if err.HTTPStatus != 401 {
|
||||
t.Errorf("expected 401, got %d", err.HTTPStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseOAuth2Error_UnparseableBody(t *testing.T) {
|
||||
err := parseOAuth2Error([]byte("garbage"), 400)
|
||||
if err.Code != "UNKNOWN_ERROR" {
|
||||
t.Errorf("expected UNKNOWN_ERROR, got %q", err.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewNetworkError(t *testing.T) {
|
||||
cause := &testError{msg: "connection refused"}
|
||||
err := newNetworkError(cause)
|
||||
if err.Code != "NETWORK_ERROR" {
|
||||
t.Errorf("expected NETWORK_ERROR, got %q", err.Code)
|
||||
}
|
||||
if err.HTTPStatus != 0 {
|
||||
t.Errorf("expected HTTPStatus 0, got %d", err.HTTPStatus)
|
||||
}
|
||||
if !strings.Contains(err.Message, "connection refused") {
|
||||
t.Errorf("expected message to contain 'connection refused', got %q", err.Message)
|
||||
}
|
||||
}
|
||||
|
||||
// testError is a simple error implementation for testing.
|
||||
type testError struct{ msg string }
|
||||
|
||||
func (e *testError) Error() string { return e.msg }
|
||||
Reference in New Issue
Block a user