229 lines
6.1 KiB
Go
229 lines
6.1 KiB
Go
package bluesky
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/bluesky-social/indigo/api/atproto"
|
|
"github.com/bluesky-social/indigo/api/bsky"
|
|
"github.com/bluesky-social/indigo/lex/util"
|
|
"github.com/bluesky-social/indigo/xrpc"
|
|
)
|
|
|
|
// setupMockServer creates and returns a new httptest.Server that mocks the Bluesky API.
|
|
func setupMockServer() *httptest.Server {
|
|
mux := http.NewServeMux()
|
|
|
|
// Mock for atproto.ServerCreateSession
|
|
mux.HandleFunc("/xrpc/com.atproto.server.createSession", func(w http.ResponseWriter, r *http.Request) {
|
|
// Simulate an error if the handle is "fail.test"
|
|
var input atproto.ServerCreateSession_Input
|
|
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
|
http.Error(w, "Bad request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if input.Identifier == "fail.test" {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
fmt.Fprintln(w, `{"error": "InvalidCredentials", "message": "Invalid handle or password"}`)
|
|
return
|
|
}
|
|
|
|
// Successful response
|
|
resp := atproto.ServerCreateSession_Output{
|
|
AccessJwt: "test_access_jwt",
|
|
RefreshJwt: "test_refresh_jwt",
|
|
Handle: "test.handle",
|
|
Did: "did:plc:test",
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(resp)
|
|
})
|
|
|
|
// Mock for bsky.FeedGetAuthorFeed
|
|
mux.HandleFunc("/xrpc/app.bsky.feed.getAuthorFeed", func(w http.ResponseWriter, r *http.Request) {
|
|
actor := r.URL.Query().Get("actor")
|
|
limitStr := r.URL.Query().Get("limit")
|
|
limit, _ := strconv.ParseInt(limitStr, 10, 64)
|
|
|
|
if actor == "" {
|
|
http.Error(w, `{"error": "MissingParameter", "message": "actor parameter is required"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Create a mock feed
|
|
feed := []*bsky.FeedDefs_FeedViewPost{}
|
|
for i := 0; i < int(limit); i++ {
|
|
post := &bsky.FeedDefs_PostView{
|
|
Uri: fmt.Sprintf("at://%s/app.bsky.feed.post/%d", actor, i),
|
|
Cid: fmt.Sprintf("bafy...%d", i),
|
|
Author: &bsky.ActorDefs_ProfileViewBasic{
|
|
Did: "did:plc:test",
|
|
Handle: actor,
|
|
},
|
|
Record: &util.LexiconTypeDecoder{
|
|
Val: &bsky.FeedPost{
|
|
Text: fmt.Sprintf("This is post number %d", i),
|
|
CreatedAt: "2023-10-27T12:00:00Z",
|
|
},
|
|
},
|
|
}
|
|
feed = append(feed, &bsky.FeedDefs_FeedViewPost{Post: post})
|
|
}
|
|
|
|
resp := bsky.FeedGetAuthorFeed_Output{
|
|
Feed: feed,
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(resp)
|
|
})
|
|
|
|
// Mock for bsky.FeedGetFeed
|
|
mux.HandleFunc("/xrpc/app.bsky.feed.getFeed", func(w http.ResponseWriter, r *http.Request) {
|
|
feedURI := r.URL.Query().Get("feed")
|
|
|
|
if feedURI == "" {
|
|
http.Error(w, `{"error": "MissingParameter", "message": "feed parameter is required"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
resp := bsky.FeedGetFeed_Output{
|
|
Feed: []*bsky.FeedDefs_FeedViewPost{
|
|
{
|
|
Post: &bsky.FeedDefs_PostView{
|
|
Uri: "at://did:plc:test/app.bsky.feed.post/1",
|
|
Author: &bsky.ActorDefs_ProfileViewBasic{
|
|
Did: "did:plc:test",
|
|
Handle: "someuser.test",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(resp)
|
|
})
|
|
|
|
return httptest.NewServer(mux)
|
|
}
|
|
|
|
func TestNewBlueskyClient(t *testing.T) {
|
|
server := setupMockServer()
|
|
defer server.Close()
|
|
|
|
ctx := context.Background()
|
|
|
|
t.Run("SuccessfulClientCreation", func(t *testing.T) {
|
|
client, err := NewBlueskyClient(ctx, "test.user", "password", server.URL)
|
|
if err != nil {
|
|
t.Fatalf("Expected no error, got %v", err)
|
|
}
|
|
|
|
if client == nil {
|
|
t.Fatal("Expected client not to be nil")
|
|
}
|
|
|
|
if client.client.Auth == nil {
|
|
t.Fatal("Expected client.Auth not to be nil")
|
|
}
|
|
|
|
if client.client.Auth.AccessJwt != "test_access_jwt" {
|
|
t.Errorf("Expected AccessJwt to be 'test_access_jwt', got %s", client.client.Auth.AccessJwt)
|
|
}
|
|
if client.client.Auth.Did != "did:plc:test" {
|
|
t.Errorf("Expected Did to be 'did:plc:test', got %s", client.client.Auth.Did)
|
|
}
|
|
})
|
|
|
|
t.Run("FailedClientCreation", func(t *testing.T) {
|
|
client, err := NewBlueskyClient(ctx, "fail.test", "wrongpassword", server.URL)
|
|
if err == nil {
|
|
t.Fatal("Expected an error, got nil")
|
|
}
|
|
|
|
if client != nil {
|
|
t.Fatal("Expected client to be nil on failure")
|
|
}
|
|
|
|
expectedErrorMsg := "failed to create bluesky session"
|
|
if !contains(err.Error(), expectedErrorMsg) {
|
|
t.Errorf("Expected error message to contain '%s', got '%s'", expectedErrorMsg, err.Error())
|
|
}
|
|
})
|
|
}
|
|
|
|
// A helper function for checking substrings.
|
|
func contains(s, substr string) bool {
|
|
return len(s) >= len(substr) && s[:len(substr)] == substr
|
|
}
|
|
|
|
// newTestClient is a helper to quickly create a client for other tests.
|
|
func newTestClient(t *testing.T, serverURL string) *BlueskyClient {
|
|
return &BlueskyClient{
|
|
client: &xrpc.Client{
|
|
Host: serverURL,
|
|
Auth: &xrpc.AuthInfo{
|
|
AccessJwt: "test_access_jwt",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestGetLatestNPosts(t *testing.T) {
|
|
server := setupMockServer()
|
|
defer server.Close()
|
|
|
|
client := newTestClient(t, server.URL)
|
|
ctx := context.Background()
|
|
actor := "did:plc:testactor"
|
|
var limit int = 5
|
|
|
|
output, err := client.GetLatestNPosts(ctx, actor, limit)
|
|
if err != nil {
|
|
t.Fatalf("Expected no error, got %v", err)
|
|
}
|
|
|
|
if output == nil {
|
|
t.Fatal("Expected output not to be nil")
|
|
}
|
|
|
|
if len(output) != int(limit) {
|
|
t.Errorf("Expected feed length to be %d, got %d", limit, len(output))
|
|
}
|
|
|
|
if output[0].Post.Author.Handle != actor {
|
|
t.Errorf("Expected post author handle to be '%s', got '%s'", actor, output[0].Post.Author.Handle)
|
|
}
|
|
}
|
|
|
|
func TestGetFeed(t *testing.T) {
|
|
server := setupMockServer()
|
|
defer server.Close()
|
|
|
|
client := newTestClient(t, server.URL)
|
|
ctx := context.Background()
|
|
feedURI := "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/with-friends"
|
|
var limit int = 10
|
|
|
|
output, err := client.GetFeed(ctx, feedURI, limit)
|
|
if err != nil {
|
|
t.Fatalf("Expected no error, got %v", err)
|
|
}
|
|
|
|
if output == nil {
|
|
t.Fatal("Expected output not to be nil")
|
|
}
|
|
|
|
if len(output.Feed) == 0 {
|
|
t.Fatal("Expected at least one post in the feed")
|
|
}
|
|
|
|
if output.Feed[0].Post.Uri != "at://did:plc:test/app.bsky.feed.post/1" {
|
|
t.Errorf("Unexpected post URI: %s", output.Feed[0].Post.Uri)
|
|
}
|
|
}
|