unit tests

This commit is contained in:
Evan Jarrett
2025-10-28 17:40:11 -05:00
parent 93b1d0d4ba
commit b0799cd94d
56 changed files with 9857 additions and 58 deletions
+397
View File
@@ -691,3 +691,400 @@ func TestContextCancellation(t *testing.T) {
t.Error("Expected error due to context cancellation, got nil")
}
}
// TestListReposByCollection tests listing repositories by collection
func TestListReposByCollection(t *testing.T) {
tests := []struct {
name string
collection string
limit int
cursor string
serverResponse string
serverStatus int
wantErr bool
checkFunc func(*testing.T, *ListReposByCollectionResult)
}{
{
name: "successful list with results",
collection: ManifestCollection,
limit: 100,
cursor: "",
serverResponse: `{
"repos": [
{"did": "did:plc:alice123"},
{"did": "did:plc:bob456"}
],
"cursor": "nextcursor789"
}`,
serverStatus: http.StatusOK,
wantErr: false,
checkFunc: func(t *testing.T, result *ListReposByCollectionResult) {
if len(result.Repos) != 2 {
t.Errorf("len(Repos) = %v, want 2", len(result.Repos))
}
if result.Repos[0].DID != "did:plc:alice123" {
t.Errorf("Repos[0].DID = %v, want did:plc:alice123", result.Repos[0].DID)
}
if result.Cursor != "nextcursor789" {
t.Errorf("Cursor = %v, want nextcursor789", result.Cursor)
}
},
},
{
name: "empty results",
collection: ManifestCollection,
limit: 50,
cursor: "cursor123",
serverResponse: `{"repos": []}`,
serverStatus: http.StatusOK,
wantErr: false,
checkFunc: func(t *testing.T, result *ListReposByCollectionResult) {
if len(result.Repos) != 0 {
t.Errorf("len(Repos) = %v, want 0", len(result.Repos))
}
},
},
{
name: "server error",
collection: ManifestCollection,
limit: 100,
cursor: "",
serverResponse: `{"error":"InternalError"}`,
serverStatus: http.StatusInternalServerError,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Verify query parameters
query := r.URL.Query()
if query.Get("collection") != tt.collection {
t.Errorf("collection = %v, want %v", query.Get("collection"), tt.collection)
}
if tt.limit > 0 && query.Get("limit") != strings.TrimSpace(string(rune(tt.limit))) {
// Check if limit param exists when specified
if !strings.Contains(r.URL.RawQuery, "limit=") {
t.Error("limit parameter missing")
}
}
if tt.cursor != "" && query.Get("cursor") != tt.cursor {
t.Errorf("cursor = %v, want %v", query.Get("cursor"), tt.cursor)
}
// Send response
w.WriteHeader(tt.serverStatus)
w.Write([]byte(tt.serverResponse))
}))
defer server.Close()
client := NewClient(server.URL, "did:plc:test123", "test-token")
result, err := client.ListReposByCollection(context.Background(), tt.collection, tt.limit, tt.cursor)
if (err != nil) != tt.wantErr {
t.Errorf("ListReposByCollection() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && tt.checkFunc != nil {
tt.checkFunc(t, result)
}
})
}
}
// TestGetActorProfile tests fetching actor profiles
func TestGetActorProfile(t *testing.T) {
tests := []struct {
name string
actor string
serverResponse string
serverStatus int
wantErr bool
checkFunc func(*testing.T, *ActorProfile)
}{
{
name: "successful profile fetch by handle",
actor: "alice.bsky.social",
serverResponse: `{
"did": "did:plc:alice123",
"handle": "alice.bsky.social",
"displayName": "Alice Smith",
"description": "Test user",
"avatar": "https://cdn.example.com/avatar.jpg"
}`,
serverStatus: http.StatusOK,
wantErr: false,
checkFunc: func(t *testing.T, profile *ActorProfile) {
if profile.DID != "did:plc:alice123" {
t.Errorf("DID = %v, want did:plc:alice123", profile.DID)
}
if profile.Handle != "alice.bsky.social" {
t.Errorf("Handle = %v, want alice.bsky.social", profile.Handle)
}
if profile.DisplayName != "Alice Smith" {
t.Errorf("DisplayName = %v, want Alice Smith", profile.DisplayName)
}
},
},
{
name: "successful profile fetch by DID",
actor: "did:plc:bob456",
serverResponse: `{
"did": "did:plc:bob456",
"handle": "bob.example.com"
}`,
serverStatus: http.StatusOK,
wantErr: false,
checkFunc: func(t *testing.T, profile *ActorProfile) {
if profile.DID != "did:plc:bob456" {
t.Errorf("DID = %v, want did:plc:bob456", profile.DID)
}
},
},
{
name: "profile not found",
actor: "nonexistent.example.com",
serverResponse: "",
serverStatus: http.StatusNotFound,
wantErr: true,
},
{
name: "server error",
actor: "error.example.com",
serverResponse: `{"error":"InternalError"}`,
serverStatus: http.StatusInternalServerError,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Verify query parameter
query := r.URL.Query()
if query.Get("actor") != tt.actor {
t.Errorf("actor = %v, want %v", query.Get("actor"), tt.actor)
}
// Verify path
if !strings.Contains(r.URL.Path, "app.bsky.actor.getProfile") {
t.Errorf("Path = %v, should contain app.bsky.actor.getProfile", r.URL.Path)
}
// Send response
w.WriteHeader(tt.serverStatus)
w.Write([]byte(tt.serverResponse))
}))
defer server.Close()
client := NewClient(server.URL, "did:plc:test123", "test-token")
profile, err := client.GetActorProfile(context.Background(), tt.actor)
if (err != nil) != tt.wantErr {
t.Errorf("GetActorProfile() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && tt.checkFunc != nil {
tt.checkFunc(t, profile)
}
})
}
}
// TestGetProfileRecord tests fetching profile records from PDS
func TestGetProfileRecord(t *testing.T) {
tests := []struct {
name string
did string
serverResponse string
serverStatus int
wantErr bool
checkFunc func(*testing.T, *ProfileRecord)
}{
{
name: "successful profile record fetch",
did: "did:plc:alice123",
serverResponse: `{
"uri": "at://did:plc:alice123/app.bsky.actor.profile/self",
"cid": "bafytest",
"value": {
"displayName": "Alice Smith",
"description": "Test description",
"avatar": {
"$type": "blob",
"ref": {"$link": "bafyavatar"},
"mimeType": "image/jpeg",
"size": 12345
}
}
}`,
serverStatus: http.StatusOK,
wantErr: false,
checkFunc: func(t *testing.T, profile *ProfileRecord) {
if profile.DisplayName != "Alice Smith" {
t.Errorf("DisplayName = %v, want Alice Smith", profile.DisplayName)
}
if profile.Description != "Test description" {
t.Errorf("Description = %v, want Test description", profile.Description)
}
if profile.Avatar == nil {
t.Fatal("Avatar should not be nil")
}
if profile.Avatar.Ref.Link != "bafyavatar" {
t.Errorf("Avatar.Ref.Link = %v, want bafyavatar", profile.Avatar.Ref.Link)
}
},
},
{
name: "profile record not found",
did: "did:plc:nonexistent",
serverResponse: "",
serverStatus: http.StatusNotFound,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Verify query parameters
query := r.URL.Query()
if query.Get("repo") != tt.did {
t.Errorf("repo = %v, want %v", query.Get("repo"), tt.did)
}
if query.Get("collection") != "app.bsky.actor.profile" {
t.Errorf("collection = %v, want app.bsky.actor.profile", query.Get("collection"))
}
if query.Get("rkey") != "self" {
t.Errorf("rkey = %v, want self", query.Get("rkey"))
}
// Send response
w.WriteHeader(tt.serverStatus)
w.Write([]byte(tt.serverResponse))
}))
defer server.Close()
client := NewClient(server.URL, "did:plc:test123", "test-token")
profile, err := client.GetProfileRecord(context.Background(), tt.did)
if (err != nil) != tt.wantErr {
t.Errorf("GetProfileRecord() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && tt.checkFunc != nil {
tt.checkFunc(t, profile)
}
})
}
}
// TestClientDID tests the DID() getter method
func TestClientDID(t *testing.T) {
expectedDID := "did:plc:test123"
client := NewClient("https://pds.example.com", expectedDID, "token")
if client.DID() != expectedDID {
t.Errorf("DID() = %v, want %v", client.DID(), expectedDID)
}
}
// TestClientPDSEndpoint tests the PDSEndpoint() getter method
func TestClientPDSEndpoint(t *testing.T) {
expectedEndpoint := "https://pds.example.com"
client := NewClient(expectedEndpoint, "did:plc:test123", "token")
if client.PDSEndpoint() != expectedEndpoint {
t.Errorf("PDSEndpoint() = %v, want %v", client.PDSEndpoint(), expectedEndpoint)
}
}
// TestNewClientWithIndigoClient tests client initialization with Indigo client
func TestNewClientWithIndigoClient(t *testing.T) {
// Note: We can't easily create a real indigo client in tests without complex setup
// We pass nil for the indigo client, which is acceptable for testing the constructor
// The actual client.go code will handle nil indigo client by checking before use
// Skip this test for now as it requires a real indigo client
// The function is tested indirectly through integration tests
t.Skip("Skipping TestNewClientWithIndigoClient - requires real indigo client setup")
// When properly set up with a real indigo client, the test would look like:
// client := NewClientWithIndigoClient("https://pds.example.com", "did:plc:test123", indigoClient)
// if !client.useIndigoClient { t.Error("useIndigoClient should be true") }
}
// TestListRecordsError tests error handling in ListRecords
func TestListRecordsError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(`{"error":"InternalError"}`))
}))
defer server.Close()
client := NewClient(server.URL, "did:plc:test123", "test-token")
_, err := client.ListRecords(context.Background(), ManifestCollection, 10)
if err == nil {
t.Error("Expected error from ListRecords, got nil")
}
}
// TestUploadBlobError tests error handling in UploadBlob
func TestUploadBlobError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error":"InvalidBlob"}`))
}))
defer server.Close()
client := NewClient(server.URL, "did:plc:test123", "test-token")
_, err := client.UploadBlob(context.Background(), []byte("test"), "application/octet-stream")
if err == nil {
t.Error("Expected error from UploadBlob, got nil")
}
}
// TestGetBlobServerError tests error handling in GetBlob for non-404 errors
func TestGetBlobServerError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(`{"error":"InternalError"}`))
}))
defer server.Close()
client := NewClient(server.URL, "did:plc:test123", "test-token")
_, err := client.GetBlob(context.Background(), "bafytest")
if err == nil {
t.Error("Expected error from GetBlob, got nil")
}
if !strings.Contains(err.Error(), "failed with status 500") {
t.Errorf("Error should mention status 500, got: %v", err)
}
}
// TestGetBlobInvalidBase64 tests error handling for invalid base64 in JSON-wrapped blob
func TestGetBlobInvalidBase64(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Return JSON string with invalid base64
w.WriteHeader(http.StatusOK)
w.Write([]byte(`"not-valid-base64!!!"`))
}))
defer server.Close()
client := NewClient(server.URL, "did:plc:test123", "test-token")
_, err := client.GetBlob(context.Background(), "bafytest")
if err == nil {
t.Error("Expected error from GetBlob with invalid base64, got nil")
}
if !strings.Contains(err.Error(), "base64") {
t.Errorf("Error should mention base64, got: %v", err)
}
}