@@ -877,4 +877,166 @@ describe("LitePubRelay", () => {
877877 }
878878 strictEqual ( count , 3 ) ;
879879 } ) ;
880+
881+ test ( "list() returns empty when no followers exist" , async ( ) => {
882+ const kv = new MemoryKvStore ( ) ;
883+
884+ // Verify list is initially empty
885+ let count = 0 ;
886+ for await ( const _ of kv . list ( [ "follower" ] ) ) {
887+ count ++ ;
888+ }
889+ strictEqual ( count , 0 ) ;
890+ } ) ;
891+
892+ test ( "list() returns all followers after additions" , async ( ) => {
893+ const kv = new MemoryKvStore ( ) ;
894+
895+ // Add multiple followers
896+ const followerIds = [
897+ "https://server1.example.com/users/alice" ,
898+ "https://server2.example.com/users/bob" ,
899+ "https://server3.example.com/users/carol" ,
900+ ] ;
901+
902+ for ( const followerId of followerIds ) {
903+ const actor = new Person ( {
904+ id : new URL ( followerId ) ,
905+ preferredUsername : followerId . split ( "/" ) . pop ( ) ,
906+ inbox : new URL ( `${ followerId } /inbox` ) ,
907+ } ) ;
908+ await kv . set (
909+ [ "follower" , followerId ] ,
910+ { actor : await actor . toJsonLd ( ) , state : "accepted" } ,
911+ ) ;
912+ }
913+
914+ // Verify list returns all followers
915+ const retrievedIds : string [ ] = [ ] ;
916+ for await ( const { key, value } of kv . list ( [ "follower" ] ) ) {
917+ strictEqual ( key . length , 2 ) ;
918+ strictEqual ( key [ 0 ] , "follower" ) ;
919+ retrievedIds . push ( key [ 1 ] as string ) ;
920+ ok ( value ) ;
921+ strictEqual ( ( value as any ) . state , "accepted" ) ;
922+ }
923+
924+ strictEqual ( retrievedIds . length , 3 ) ;
925+ for ( const id of followerIds ) {
926+ ok ( retrievedIds . includes ( id ) ) ;
927+ }
928+ } ) ;
929+
930+ test ( "list() excludes followers after deletion" , async ( ) => {
931+ const kv = new MemoryKvStore ( ) ;
932+
933+ // Add three followers
934+ const follower1Id = "https://server1.example.com/users/alice" ;
935+ const follower2Id = "https://server2.example.com/users/bob" ;
936+ const follower3Id = "https://server3.example.com/users/carol" ;
937+
938+ for ( const followerId of [ follower1Id , follower2Id , follower3Id ] ) {
939+ const actor = new Person ( {
940+ id : new URL ( followerId ) ,
941+ preferredUsername : followerId . split ( "/" ) . pop ( ) ,
942+ inbox : new URL ( `${ followerId } /inbox` ) ,
943+ } ) ;
944+ await kv . set (
945+ [ "follower" , followerId ] ,
946+ { actor : await actor . toJsonLd ( ) , state : "accepted" } ,
947+ ) ;
948+ }
949+
950+ // Delete one follower
951+ await kv . delete ( [ "follower" , follower2Id ] ) ;
952+
953+ // Verify list only returns remaining followers
954+ const retrievedIds : string [ ] = [ ] ;
955+ for await ( const { key } of kv . list ( [ "follower" ] ) ) {
956+ retrievedIds . push ( key [ 1 ] as string ) ;
957+ }
958+
959+ strictEqual ( retrievedIds . length , 2 ) ;
960+ ok ( retrievedIds . includes ( follower1Id ) ) ;
961+ ok ( ! retrievedIds . includes ( follower2Id ) ) ; // Deleted follower not in list
962+ ok ( retrievedIds . includes ( follower3Id ) ) ;
963+ } ) ;
964+
965+ test ( "list() distinguishes between pending and accepted followers" , async ( ) => {
966+ const kv = new MemoryKvStore ( ) ;
967+
968+ // Add followers with different states
969+ const pendingFollowerId = "https://server1.example.com/users/alice" ;
970+ const acceptedFollowerId = "https://server2.example.com/users/bob" ;
971+
972+ const pendingFollower = new Person ( {
973+ id : new URL ( pendingFollowerId ) ,
974+ preferredUsername : "alice" ,
975+ inbox : new URL ( "https://server1.example.com/users/alice/inbox" ) ,
976+ } ) ;
977+
978+ const acceptedFollower = new Person ( {
979+ id : new URL ( acceptedFollowerId ) ,
980+ preferredUsername : "bob" ,
981+ inbox : new URL ( "https://server2.example.com/users/bob/inbox" ) ,
982+ } ) ;
983+
984+ await kv . set (
985+ [ "follower" , pendingFollowerId ] ,
986+ { actor : await pendingFollower . toJsonLd ( ) , state : "pending" } ,
987+ ) ;
988+
989+ await kv . set (
990+ [ "follower" , acceptedFollowerId ] ,
991+ { actor : await acceptedFollower . toJsonLd ( ) , state : "accepted" } ,
992+ ) ;
993+
994+ // Verify list returns both with correct states
995+ const followers : { id : string ; state : string } [ ] = [ ] ;
996+ for await ( const { key, value } of kv . list ( [ "follower" ] ) ) {
997+ const followerData = value as any ;
998+ followers . push ( {
999+ id : key [ 1 ] as string ,
1000+ state : followerData . state ,
1001+ } ) ;
1002+ }
1003+
1004+ strictEqual ( followers . length , 2 ) ;
1005+
1006+ const pendingEntry = followers . find ( ( f ) => f . id === pendingFollowerId ) ;
1007+ ok ( pendingEntry ) ;
1008+ strictEqual ( pendingEntry . state , "pending" ) ;
1009+
1010+ const acceptedEntry = followers . find ( ( f ) => f . id === acceptedFollowerId ) ;
1011+ ok ( acceptedEntry ) ;
1012+ strictEqual ( acceptedEntry . state , "accepted" ) ;
1013+ } ) ;
1014+
1015+ test ( "list() returns correct actor data" , async ( ) => {
1016+ const kv = new MemoryKvStore ( ) ;
1017+
1018+ const followerId = "https://remote.example.com/users/alice" ;
1019+ const follower = new Person ( {
1020+ id : new URL ( followerId ) ,
1021+ preferredUsername : "alice" ,
1022+ name : "Alice Wonderland" ,
1023+ inbox : new URL ( "https://remote.example.com/users/alice/inbox" ) ,
1024+ } ) ;
1025+
1026+ await kv . set (
1027+ [ "follower" , followerId ] ,
1028+ { actor : await follower . toJsonLd ( ) , state : "accepted" } ,
1029+ ) ;
1030+
1031+ // Verify list returns complete actor data
1032+ for await ( const { key, value } of kv . list ( [ "follower" ] ) ) {
1033+ strictEqual ( key [ 1 ] , followerId ) ;
1034+ ok ( value ) ;
1035+ const followerData = value as any ;
1036+ strictEqual ( followerData . state , "accepted" ) ;
1037+ ok ( followerData . actor ) ;
1038+ strictEqual ( followerData . actor . preferredUsername , "alice" ) ;
1039+ strictEqual ( followerData . actor . name , "Alice Wonderland" ) ;
1040+ }
1041+ } ) ;
8801042} ) ;
0 commit comments