···
wantTyped: ErrBadRequest,
+
name: "409 maps to ErrConflict",
+
err: &atclient.APIError{StatusCode: 409, Name: "InvalidSwap", Message: "Record CID mismatch"},
+
operation: "putRecord",
+
wantTyped: ErrConflict,
name: "500 wraps without typed error",
err: &atclient.APIError{StatusCode: 500, Name: "InternalError", Message: "Server error"},
operation: "listRecords",
···
+
// TestClient_PutRecord tests the PutRecord method with a mock server.
+
func TestClient_PutRecord(t *testing.T) {
+
serverResponse map[string]any
+
name: "successful put with swapRecord",
+
collection: "social.coves.comment",
+
record: map[string]any{
+
"$type": "social.coves.comment",
+
"content": "Updated comment content",
+
swapRecord: "bafyreigbtj4x7ip5legnfznufuopl4sg4knzc2cof6duas4b3q2fy6swua",
+
serverResponse: map[string]any{
+
"uri": "at://did:plc:test/social.coves.comment/3kjzl5kcb2s2v",
+
"cid": "bafyreihd4q3yqcfvnv5zlp6n4fqzh6z4p4m3mwc7vvr6k2j6y6v2a3b4c5",
+
serverStatus: http.StatusOK,
+
wantURI: "at://did:plc:test/social.coves.comment/3kjzl5kcb2s2v",
+
wantCID: "bafyreihd4q3yqcfvnv5zlp6n4fqzh6z4p4m3mwc7vvr6k2j6y6v2a3b4c5",
+
name: "successful put without swapRecord",
+
collection: "social.coves.comment",
+
record: map[string]any{
+
"$type": "social.coves.comment",
+
"content": "Updated comment",
+
serverResponse: map[string]any{
+
"uri": "at://did:plc:test/social.coves.comment/3kjzl5kcb2s2v",
+
"cid": "bafyreihd4q3yqcfvnv5zlp6n4fqzh6z4p4m3mwc7vvr6k2j6y6v2a3b4c5",
+
serverStatus: http.StatusOK,
+
wantURI: "at://did:plc:test/social.coves.comment/3kjzl5kcb2s2v",
+
wantCID: "bafyreihd4q3yqcfvnv5zlp6n4fqzh6z4p4m3mwc7vvr6k2j6y6v2a3b4c5",
+
name: "conflict error (409)",
+
collection: "social.coves.comment",
+
record: map[string]any{"$type": "social.coves.comment"},
+
swapRecord: "bafyreigbtj4x7ip5legnfznufuopl4sg4knzc2cof6duas4b3q2fy6swua",
+
serverResponse: map[string]any{
+
"error": "InvalidSwap",
+
"message": "Record CID does not match",
+
serverStatus: http.StatusConflict,
+
collection: "social.coves.comment",
+
record: map[string]any{"$type": "social.coves.comment"},
+
serverResponse: map[string]any{
+
"error": "InvalidRequest",
+
"message": "Invalid record",
+
serverStatus: http.StatusBadRequest,
+
for _, tt := range tests {
+
t.Run(tt.name, func(t *testing.T) {
+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
if r.Method != http.MethodPost {
+
t.Errorf("expected POST request, got %s", r.Method)
+
expectedPath := "/xrpc/com.atproto.repo.putRecord"
+
if r.URL.Path != expectedPath {
+
t.Errorf("path = %q, want %q", r.URL.Path, expectedPath)
+
var payload map[string]any
+
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
+
t.Fatalf("failed to decode request body: %v", err)
+
// Check required fields
+
if payload["collection"] != tt.collection {
+
t.Errorf("collection = %v, want %v", payload["collection"], tt.collection)
+
if payload["rkey"] != tt.rkey {
+
t.Errorf("rkey = %v, want %v", payload["rkey"], tt.rkey)
+
// Check swapRecord inclusion
+
if tt.swapRecord != "" {
+
if payload["swapRecord"] != tt.swapRecord {
+
t.Errorf("swapRecord = %v, want %v", payload["swapRecord"], tt.swapRecord)
+
if _, exists := payload["swapRecord"]; exists {
+
t.Error("swapRecord should not be included when empty")
+
w.Header().Set("Content-Type", "application/json")
+
w.WriteHeader(tt.serverStatus)
+
json.NewEncoder(w).Encode(tt.serverResponse)
+
apiClient := atclient.NewAPIClient(server.URL)
+
apiClient.Auth = &bearerAuth{token: "test-token"}
+
ctx := context.Background()
+
uri, cid, err := c.PutRecord(ctx, tt.collection, tt.rkey, tt.record, tt.swapRecord)
+
t.Fatal("expected error, got nil")
+
t.Fatalf("unexpected error: %v", err)
+
t.Errorf("uri = %q, want %q", uri, tt.wantURI)
+
t.Errorf("cid = %q, want %q", cid, tt.wantCID)
+
// TestClient_TypedErrors_PutRecord tests that PutRecord returns typed errors.
+
func TestClient_TypedErrors_PutRecord(t *testing.T) {
+
name: "401 returns ErrUnauthorized",
+
serverStatus: http.StatusUnauthorized,
+
wantErr: ErrUnauthorized,
+
name: "403 returns ErrForbidden",
+
serverStatus: http.StatusForbidden,
+
name: "409 returns ErrConflict",
+
serverStatus: http.StatusConflict,
+
name: "400 returns ErrBadRequest",
+
serverStatus: http.StatusBadRequest,
+
wantErr: ErrBadRequest,
+
for _, tt := range tests {
+
t.Run(tt.name, func(t *testing.T) {
+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
w.Header().Set("Content-Type", "application/json")
+
w.WriteHeader(tt.serverStatus)
+
json.NewEncoder(w).Encode(map[string]any{
+
"message": "Test error message",
+
apiClient := atclient.NewAPIClient(server.URL)
+
apiClient.Auth = &bearerAuth{token: "test-token"}
+
ctx := context.Background()
+
_, _, err := c.PutRecord(ctx, "test.collection", "rkey", map[string]any{}, "")
+
t.Fatal("expected error, got nil")
+
if !errors.Is(err, tt.wantErr) {
+
t.Errorf("expected errors.Is(%v, %v) to be true", err, tt.wantErr)