Files
at-container-registry/pkg/hold/pds/delete_test.go
2026-02-10 22:51:51 -06:00

334 lines
8.4 KiB
Go

package pds
import (
"testing"
"atcr.io/pkg/atproto"
bsky "github.com/bluesky-social/indigo/api/bsky"
)
func TestPostMentionsUser(t *testing.T) {
tests := []struct {
name string
post *bsky.FeedPost
userDID string
expected bool
}{
{
name: "post mentions user",
post: &bsky.FeedPost{
Text: "@alice.bsky.social pushed myapp:latest",
Facets: []*bsky.RichtextFacet{{
Index: &bsky.RichtextFacet_ByteSlice{
ByteStart: 0,
ByteEnd: 20,
},
Features: []*bsky.RichtextFacet_Features_Elem{{
RichtextFacet_Mention: &bsky.RichtextFacet_Mention{
Did: "did:plc:alice123",
},
}},
}},
},
userDID: "did:plc:alice123",
expected: true,
},
{
name: "post mentions different user",
post: &bsky.FeedPost{
Text: "@bob.bsky.social pushed myapp:latest",
Facets: []*bsky.RichtextFacet{{
Index: &bsky.RichtextFacet_ByteSlice{
ByteStart: 0,
ByteEnd: 18,
},
Features: []*bsky.RichtextFacet_Features_Elem{{
RichtextFacet_Mention: &bsky.RichtextFacet_Mention{
Did: "did:plc:bob456",
},
}},
}},
},
userDID: "did:plc:alice123",
expected: false,
},
{
name: "post with no facets",
post: &bsky.FeedPost{
Text: "Just a regular post",
Facets: nil,
},
userDID: "did:plc:alice123",
expected: false,
},
{
name: "post with empty facets",
post: &bsky.FeedPost{
Text: "Just a regular post",
Facets: []*bsky.RichtextFacet{},
},
userDID: "did:plc:alice123",
expected: false,
},
{
name: "post with link facet only (no mention)",
post: &bsky.FeedPost{
Text: "Check out https://example.com",
Facets: []*bsky.RichtextFacet{{
Index: &bsky.RichtextFacet_ByteSlice{
ByteStart: 10,
ByteEnd: 30,
},
Features: []*bsky.RichtextFacet_Features_Elem{{
RichtextFacet_Link: &bsky.RichtextFacet_Link{
Uri: "https://example.com",
},
}},
}},
},
userDID: "did:plc:alice123",
expected: false,
},
{
name: "post with multiple mentions - match second",
post: &bsky.FeedPost{
Text: "@bob.bsky.social and @alice.bsky.social pushed",
Facets: []*bsky.RichtextFacet{
{
Index: &bsky.RichtextFacet_ByteSlice{
ByteStart: 0,
ByteEnd: 18,
},
Features: []*bsky.RichtextFacet_Features_Elem{{
RichtextFacet_Mention: &bsky.RichtextFacet_Mention{
Did: "did:plc:bob456",
},
}},
},
{
Index: &bsky.RichtextFacet_ByteSlice{
ByteStart: 23,
ByteEnd: 43,
},
Features: []*bsky.RichtextFacet_Features_Elem{{
RichtextFacet_Mention: &bsky.RichtextFacet_Mention{
Did: "did:plc:alice123",
},
}},
},
},
},
userDID: "did:plc:alice123",
expected: true,
},
{
name: "facet with nil features",
post: &bsky.FeedPost{
Text: "Some post",
Facets: []*bsky.RichtextFacet{{
Index: &bsky.RichtextFacet_ByteSlice{
ByteStart: 0,
ByteEnd: 4,
},
Features: nil,
}},
},
userDID: "did:plc:alice123",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := postMentionsUser(tt.post, tt.userDID)
if result != tt.expected {
t.Errorf("postMentionsUser() = %v, want %v", result, tt.expected)
}
})
}
}
func TestDeleteBlueskyPosts_NoPosts(t *testing.T) {
// Create a test PDS with records index
pds := setupTestPDSWithIndex(t, "did:plc:testowner")
ctx := sharedCtx
// Delete posts for a user that has no posts
deleted, err := pds.deleteBlueskyPosts(ctx, "did:plc:nonexistent")
if err != nil {
t.Fatalf("deleteBlueskyPosts() error = %v", err)
}
if deleted != 0 {
t.Errorf("deleteBlueskyPosts() deleted = %d, want 0", deleted)
}
}
func TestListBlueskyPostsForUser_NoPosts(t *testing.T) {
// Create a test PDS with records index
pds := setupTestPDSWithIndex(t, "did:plc:testowner")
ctx := sharedCtx
// List posts for a user that has no posts
posts, err := pds.ListBlueskyPostsForUser(ctx, "did:plc:nonexistent")
if err != nil {
t.Fatalf("ListBlueskyPostsForUser() error = %v", err)
}
if len(posts) != 0 {
t.Errorf("ListBlueskyPostsForUser() returned %d posts, want 0", len(posts))
}
}
func TestDeleteAndListBlueskyPosts_WithPosts(t *testing.T) {
// Create a test PDS with records index
pds := setupTestPDSWithIndex(t, "did:plc:testowner")
ctx := sharedCtx
// Create a test post that mentions alice
aliceDID := "did:plc:alice123"
post := &bsky.FeedPost{
LexiconTypeID: atproto.BskyPostCollection,
Text: "@alice.bsky.social pushed myapp:latest",
Facets: []*bsky.RichtextFacet{{
Index: &bsky.RichtextFacet_ByteSlice{
ByteStart: 0,
ByteEnd: 20,
},
Features: []*bsky.RichtextFacet_Features_Elem{{
RichtextFacet_Mention: &bsky.RichtextFacet_Mention{
Did: aliceDID,
},
}},
}},
CreatedAt: "2025-01-01T00:00:00Z",
}
// Create the post in the PDS
rkey, _, err := pds.repomgr.CreateRecord(ctx, pds.uid, atproto.BskyPostCollection, post)
if err != nil {
t.Fatalf("Failed to create test post: %v", err)
}
// Index the record
if pds.recordsIndex != nil {
err = pds.recordsIndex.IndexRecord(atproto.BskyPostCollection, rkey, "testcid", aliceDID, "", 0)
if err != nil {
t.Fatalf("Failed to index test post: %v", err)
}
}
// List posts for alice - should find 1
posts, err := pds.ListBlueskyPostsForUser(ctx, aliceDID)
if err != nil {
t.Fatalf("ListBlueskyPostsForUser() error = %v", err)
}
if len(posts) != 1 {
t.Errorf("ListBlueskyPostsForUser() returned %d posts, want 1", len(posts))
}
if len(posts) > 0 {
if posts[0].Text != "@alice.bsky.social pushed myapp:latest" {
t.Errorf("Post text = %q, want %q", posts[0].Text, "@alice.bsky.social pushed myapp:latest")
}
if posts[0].CreatedAt != "2025-01-01T00:00:00Z" {
t.Errorf("Post createdAt = %q, want %q", posts[0].CreatedAt, "2025-01-01T00:00:00Z")
}
}
// List posts for bob - should find 0
bobPosts, err := pds.ListBlueskyPostsForUser(ctx, "did:plc:bob456")
if err != nil {
t.Fatalf("ListBlueskyPostsForUser(bob) error = %v", err)
}
if len(bobPosts) != 0 {
t.Errorf("ListBlueskyPostsForUser(bob) returned %d posts, want 0", len(bobPosts))
}
// Delete posts for alice
deleted, err := pds.deleteBlueskyPosts(ctx, aliceDID)
if err != nil {
t.Fatalf("deleteBlueskyPosts() error = %v", err)
}
if deleted != 1 {
t.Errorf("deleteBlueskyPosts() deleted = %d, want 1", deleted)
}
// List posts for alice again - should find 0 now
postsAfterDelete, err := pds.ListBlueskyPostsForUser(ctx, aliceDID)
if err != nil {
t.Fatalf("ListBlueskyPostsForUser() after delete error = %v", err)
}
if len(postsAfterDelete) != 0 {
t.Errorf("ListBlueskyPostsForUser() after delete returned %d posts, want 0", len(postsAfterDelete))
}
}
func TestDeleteUserData_IncludesPosts(t *testing.T) {
// Create a test PDS with records index
pds := setupTestPDSWithIndex(t, "did:plc:testowner")
ctx := sharedCtx
// Create a test post that mentions alice
aliceDID := "did:plc:alice123"
post := &bsky.FeedPost{
LexiconTypeID: atproto.BskyPostCollection,
Text: "@alice.bsky.social pushed myapp:latest",
Facets: []*bsky.RichtextFacet{{
Index: &bsky.RichtextFacet_ByteSlice{
ByteStart: 0,
ByteEnd: 20,
},
Features: []*bsky.RichtextFacet_Features_Elem{{
RichtextFacet_Mention: &bsky.RichtextFacet_Mention{
Did: aliceDID,
},
}},
}},
CreatedAt: "2025-01-01T00:00:00Z",
}
// Create the post in the PDS
rkey, _, err := pds.repomgr.CreateRecord(ctx, pds.uid, atproto.BskyPostCollection, post)
if err != nil {
t.Fatalf("Failed to create test post: %v", err)
}
// Index the record
if pds.recordsIndex != nil {
err = pds.recordsIndex.IndexRecord(atproto.BskyPostCollection, rkey, "testcid", aliceDID, "", 0)
if err != nil {
t.Fatalf("Failed to index test post: %v", err)
}
}
// Call DeleteUserData
result, err := pds.DeleteUserData(ctx, aliceDID)
if err != nil {
t.Fatalf("DeleteUserData() error = %v", err)
}
// Verify posts were deleted
if result.PostsDeleted != 1 {
t.Errorf("DeleteUserData() PostsDeleted = %d, want 1", result.PostsDeleted)
}
// Verify post is actually gone
posts, err := pds.ListBlueskyPostsForUser(ctx, aliceDID)
if err != nil {
t.Fatalf("ListBlueskyPostsForUser() error = %v", err)
}
if len(posts) != 0 {
t.Errorf("Posts still exist after DeleteUserData, count = %d", len(posts))
}
}