Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm

Add tests for reverse ordering in link queries

Adds test coverage for the reverse parameter in get_links and
get_many_to_many_counts methods. Tests verify that reverse: true
returns results in oldest-first/descending order while reverse:
false returns newest-first/ascending order.

Changed files
+181
constellation
src
storage
+181
constellation/src/storage/mod.rs
··· 844 844 assert_stats(storage.get_stats()?, 5..=5, 1..=1, 5..=5); 845 845 }); 846 846 847 + test_each_storage!(get_links_reverse_order, |storage| { 848 + for i in 1..=5 { 849 + storage.push( 850 + &ActionableEvent::CreateLinks { 851 + record_id: RecordId { 852 + did: format!("did:plc:asdf-{i}").into(), 853 + collection: "app.t.c".into(), 854 + rkey: "asdf".into(), 855 + }, 856 + links: vec![CollectedLink { 857 + target: Link::Uri("a.com".into()), 858 + path: ".abc.uri".into(), 859 + }], 860 + }, 861 + 0, 862 + )?; 863 + } 864 + 865 + // Test reverse: true (oldest first) 866 + let links = storage.get_links( 867 + "a.com", 868 + "app.t.c", 869 + ".abc.uri", 870 + true, 871 + 2, 872 + None, 873 + &HashSet::default(), 874 + )?; 875 + assert_eq!( 876 + links, 877 + PagedAppendingCollection { 878 + version: (5, 0), 879 + items: vec![ 880 + RecordId { 881 + did: "did:plc:asdf-1".into(), 882 + collection: "app.t.c".into(), 883 + rkey: "asdf".into(), 884 + }, 885 + RecordId { 886 + did: "did:plc:asdf-2".into(), 887 + collection: "app.t.c".into(), 888 + rkey: "asdf".into(), 889 + }, 890 + ], 891 + next: Some(3), 892 + total: 5, 893 + } 894 + ); 895 + // Test reverse: false (newest first) 896 + let links = storage.get_links( 897 + "a.com", 898 + "app.t.c", 899 + ".abc.uri", 900 + false, 901 + 2, 902 + None, 903 + &HashSet::default(), 904 + )?; 905 + assert_eq!( 906 + links, 907 + PagedAppendingCollection { 908 + version: (5, 0), 909 + items: vec![ 910 + RecordId { 911 + did: "did:plc:asdf-5".into(), 912 + collection: "app.t.c".into(), 913 + rkey: "asdf".into(), 914 + }, 915 + RecordId { 916 + did: "did:plc:asdf-4".into(), 917 + collection: "app.t.c".into(), 918 + rkey: "asdf".into(), 919 + }, 920 + ], 921 + next: Some(3), 922 + total: 5, 923 + } 924 + ); 925 + assert_stats(storage.get_stats()?, 5..=5, 1..=1, 5..=5); 926 + }); 927 + 847 928 test_each_storage!(get_filtered_links, |storage| { 848 929 let links = storage.get_links( 849 930 "a.com", ··· 1607 1688 next: None, 1608 1689 } 1609 1690 ); 1691 + }); 1692 + 1693 + test_each_storage!(get_m2m_counts_reverse_order, |storage| { 1694 + // Create links from different DIDs to different targets 1695 + storage.push( 1696 + &ActionableEvent::CreateLinks { 1697 + record_id: RecordId { 1698 + did: "did:plc:user1".into(), 1699 + collection: "app.t.c".into(), 1700 + rkey: "post1".into(), 1701 + }, 1702 + links: vec![ 1703 + CollectedLink { 1704 + target: Link::Uri("a.com".into()), 1705 + path: ".abc.uri".into(), 1706 + }, 1707 + CollectedLink { 1708 + target: Link::Uri("b.com".into()), 1709 + path: ".def.uri".into(), 1710 + }, 1711 + ], 1712 + }, 1713 + 0, 1714 + )?; 1715 + storage.push( 1716 + &ActionableEvent::CreateLinks { 1717 + record_id: RecordId { 1718 + did: "did:plc:user2".into(), 1719 + collection: "app.t.c".into(), 1720 + rkey: "post1".into(), 1721 + }, 1722 + links: vec![ 1723 + CollectedLink { 1724 + target: Link::Uri("a.com".into()), 1725 + path: ".abc.uri".into(), 1726 + }, 1727 + CollectedLink { 1728 + target: Link::Uri("c.com".into()), 1729 + path: ".def.uri".into(), 1730 + }, 1731 + ], 1732 + }, 1733 + 1, 1734 + )?; 1735 + storage.push( 1736 + &ActionableEvent::CreateLinks { 1737 + record_id: RecordId { 1738 + did: "did:plc:user3".into(), 1739 + collection: "app.t.c".into(), 1740 + rkey: "post1".into(), 1741 + }, 1742 + links: vec![ 1743 + CollectedLink { 1744 + target: Link::Uri("a.com".into()), 1745 + path: ".abc.uri".into(), 1746 + }, 1747 + CollectedLink { 1748 + target: Link::Uri("d.com".into()), 1749 + path: ".def.uri".into(), 1750 + }, 1751 + ], 1752 + }, 1753 + 2, 1754 + )?; 1755 + 1756 + // Test reverse: false (default order - by target ascending) 1757 + let counts = storage.get_many_to_many_counts( 1758 + "a.com", 1759 + "app.t.c", 1760 + ".abc.uri", 1761 + ".def.uri", 1762 + false, 1763 + 10, 1764 + None, 1765 + &HashSet::new(), 1766 + &HashSet::new(), 1767 + )?; 1768 + assert_eq!(counts.items.len(), 3); 1769 + // Should be sorted by target in ascending order (alphabetical) 1770 + assert_eq!(counts.items[0].0, "b.com"); 1771 + assert_eq!(counts.items[1].0, "c.com"); 1772 + assert_eq!(counts.items[2].0, "d.com"); 1773 + 1774 + // Test reverse: true (descending order - by target descending) 1775 + let counts = storage.get_many_to_many_counts( 1776 + "a.com", 1777 + "app.t.c", 1778 + ".abc.uri", 1779 + ".def.uri", 1780 + true, 1781 + 10, 1782 + None, 1783 + &HashSet::new(), 1784 + &HashSet::new(), 1785 + )?; 1786 + assert_eq!(counts.items.len(), 3); 1787 + // Should be sorted by target in descending order (reverse alphabetical) 1788 + assert_eq!(counts.items[0].0, "d.com"); 1789 + assert_eq!(counts.items[1].0, "c.com"); 1790 + assert_eq!(counts.items[2].0, "b.com"); 1610 1791 }); 1611 1792 }