Files
Elydora-Go-SDK/sdk_compat_integration_test.go
T
ZacharyZhang-NYandClaude Opus 4.6 3683bd28dc Sync types and operations with backend for social auth and agent assignments
Adds SocialAuthProvider, AgentAssignment types, and integration compat test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:42:38 -04:00

175 lines
4.9 KiB
Go

package elydora_test
import (
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"fmt"
"net/http"
"os"
"testing"
"time"
elydora "github.com/Elydora-Infrastructure/Elydora-Go-SDK"
)
func randomHex(n int) string {
buf := make([]byte, n)
_, _ = rand.Read(buf)
return hex.EncodeToString(buf)
}
func randomSeedBase64url(t *testing.T) (seedB64 string, pubB64 string) {
t.Helper()
seed := make([]byte, ed25519.SeedSize)
if _, err := rand.Read(seed); err != nil {
t.Fatalf("read random seed: %v", err)
}
privateKey := ed25519.NewKeyFromSeed(seed)
publicKey := privateKey.Public().(ed25519.PublicKey)
return base64.RawURLEncoding.EncodeToString(seed), base64.RawURLEncoding.EncodeToString(publicKey)
}
func TestSDKCompatibilityAgainstLatestAPI(t *testing.T) {
baseURL := os.Getenv("API_BASE_URL")
if baseURL == "" {
t.Skip("API_BASE_URL is not set; skipping SDK compatibility integration test")
}
suffix := randomHex(6)
email := fmt.Sprintf("go-sdk-%s@elydora.test", suffix)
password := fmt.Sprintf("Go-%s-Password!", suffix)
agentID := fmt.Sprintf("go-agent-%s", randomHex(8))
kid := fmt.Sprintf("go-kid-%s", randomHex(8))
privateKey, publicKey := randomSeedBase64url(t)
if _, err := elydora.Register(baseURL, email, password, elydora.WithDisplayName("go-sdk-user"), elydora.WithOrgName("go-org-"+suffix)); err != nil {
t.Fatalf("register failed: %v", err)
}
login, err := elydora.Login(baseURL, email, password)
if err != nil {
t.Fatalf("login failed: %v", err)
}
client, err := elydora.NewClient(&elydora.Config{
OrgID: login.User.OrgID,
AgentID: agentID,
PrivateKey: privateKey,
BaseURL: baseURL,
TTLMs: 120000,
MaxRetries: 1,
Token: login.Token,
})
if err != nil {
t.Fatalf("new client failed: %v", err)
}
ttl := 1800
tokenResp, err := client.IssueApiToken(&elydora.IssueApiTokenRequest{TTLSeconds: &ttl})
if err != nil {
t.Fatalf("issue api token failed: %v", err)
}
if tokenResp.TokenID == "" {
t.Fatalf("issue api token: token_id missing")
}
oldToken := tokenResp.Token
client.SetToken(oldToken)
meWithAPI, err := client.GetMe()
if err != nil {
t.Fatalf("api token auth failed: %v", err)
}
if meWithAPI.User.OrgID != login.User.OrgID {
t.Fatalf("api token org mismatch: got %s want %s", meWithAPI.User.OrgID, login.User.OrgID)
}
rotateResp, err := client.RotateApiToken()
if err != nil {
t.Fatalf("rotate api token failed: %v", err)
}
if rotateResp.Token == "" || rotateResp.PreviousTokenGraceUntil <= 0 {
t.Fatalf("rotate response incomplete: %+v", rotateResp)
}
client.SetToken(rotateResp.Token)
meWithRotated, err := client.GetMe()
if err != nil {
t.Fatalf("rotated token auth failed: %v", err)
}
if meWithRotated.User.OrgID != login.User.OrgID {
t.Fatalf("rotated token org mismatch: got %s want %s", meWithRotated.User.OrgID, login.User.OrgID)
}
// Old token should still be accepted during grace window.
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v1/auth/me", baseURL), nil)
if err != nil {
t.Fatalf("create old token request: %v", err)
}
req.Header.Set("Authorization", "Bearer "+oldToken)
req.Header.Set("Accept", "application/json")
httpClient := &http.Client{Timeout: 20 * time.Second}
oldTokenResp, err := httpClient.Do(req)
if err != nil {
t.Fatalf("old token grace request failed: %v", err)
}
oldTokenResp.Body.Close()
if oldTokenResp.StatusCode != http.StatusOK {
t.Fatalf("old token grace expected 200, got %d", oldTokenResp.StatusCode)
}
_, err = client.RegisterAgent(&elydora.RegisterAgentRequest{
AgentID: agentID,
DisplayName: "Go SDK Compat Agent",
ResponsibleEntity: "sdk-matrix",
Keys: []elydora.RegisterAgentKeyInput{
{
KID: kid,
PublicKey: publicKey,
Algorithm: "ed25519",
},
},
})
if err != nil {
t.Fatalf("register agent failed: %v", err)
}
op, err := client.CreateOperation(&elydora.CreateOperationParams{
OperationType: "compat.go.submit",
Subject: map[string]interface{}{"entity": "sdk", "id": "go-" + suffix},
Action: map[string]interface{}{"type": "write", "resource": "matrix"},
Payload: map[string]interface{}{"sdk": "go"},
KID: kid,
})
if err != nil {
t.Fatalf("create operation failed: %v", err)
}
submit, err := client.SubmitOperation(op)
if err != nil {
t.Fatalf("submit operation failed: %v", err)
}
if submit.Receipt.OperationID != op.OperationID {
t.Fatalf("receipt operation mismatch: got %s want %s", submit.Receipt.OperationID, op.OperationID)
}
got, err := client.GetOperation(op.OperationID)
if err != nil {
t.Fatalf("get operation failed: %v", err)
}
if got.Operation.OperationID != op.OperationID {
t.Fatalf("get operation mismatch: got %s want %s", got.Operation.OperationID, op.OperationID)
}
verify, err := client.VerifyOperation(op.OperationID)
if err != nil {
t.Fatalf("verify operation failed: %v", err)
}
if !verify.Valid {
t.Fatalf("verify operation invalid: %+v", verify)
}
}