Auto-indexing service and GraphQL API for AT Protocol Records quickslice.slices.network/
atproto gleam graphql
at main 365 lines 10 kB view raw
1/// Tests for DID-based join field generation 2/// 3/// Verifies that DID join fields are added to GraphQL schemas correctly: 4/// - All collections get DID join fields to all other collections 5/// - Cardinality is determined by has_unique_did (literal:self key) 6/// - Field naming follows {TypeName}ByDid pattern 7import gleam/dict 8import gleam/option 9import gleam/string 10import gleeunit/should 11import lexicon_graphql/schema/database as db_schema_builder 12import lexicon_graphql/types 13import swell/introspection 14import swell/schema 15import swell/sdl 16 17// Helper to create a test schema with a mock fetcher 18fn create_test_schema_from_lexicons( 19 lexicons: List(types.Lexicon), 20) -> schema.Schema { 21 // Mock fetcher that returns empty results (we're only testing schema generation) 22 let fetcher = fn(_collection, _params) { 23 Ok(#([], option.None, False, False, option.None)) 24 } 25 26 case 27 db_schema_builder.build_schema_with_fetcher( 28 lexicons, 29 fetcher, 30 option.None, 31 option.None, 32 option.None, 33 option.None, 34 option.None, 35 option.None, 36 ) 37 { 38 Ok(s) -> s 39 Error(_) -> panic as "Failed to build test schema" 40 } 41} 42 43// Test that collections get DID join fields to other collections 44pub fn collections_get_did_join_fields_test() { 45 // Create two collections: a status and a profile (with literal:self) 46 let status_lexicon = 47 types.Lexicon( 48 id: "xyz.statusphere.status", 49 defs: types.Defs( 50 main: option.Some( 51 types.RecordDef(type_: "record", key: option.None, properties: [ 52 #( 53 "text", 54 types.Property( 55 type_: "string", 56 required: True, 57 format: option.None, 58 ref: option.None, 59 refs: option.None, 60 items: option.None, 61 ), 62 ), 63 ]), 64 ), 65 others: dict.new(), 66 ), 67 ) 68 69 let profile_lexicon = 70 types.Lexicon( 71 id: "app.bsky.actor.profile", 72 defs: types.Defs( 73 main: option.Some( 74 types.RecordDef( 75 type_: "record", 76 key: option.Some("literal:self"), 77 properties: [ 78 #( 79 "displayName", 80 types.Property( 81 type_: "string", 82 required: False, 83 format: option.None, 84 ref: option.None, 85 refs: option.None, 86 items: option.None, 87 ), 88 ), 89 ], 90 ), 91 ), 92 others: dict.new(), 93 ), 94 ) 95 96 let test_schema = 97 create_test_schema_from_lexicons([status_lexicon, profile_lexicon]) 98 99 // Get all types and serialize to SDL 100 let all_types = introspection.get_all_schema_types(test_schema) 101 let serialized = sdl.print_types(all_types) 102 103 // Verify that Status has a DID join field to Profile 104 string.contains(serialized, "appBskyActorProfileByDid") 105 |> should.be_true 106 107 // Verify that Profile has a DID join field to Status 108 string.contains(serialized, "xyzStatusphereStatusByDid") 109 |> should.be_true 110} 111 112// Test that literal:self collections return single nullable objects 113pub fn literal_self_returns_single_object_test() { 114 let status_lexicon = 115 types.Lexicon( 116 id: "xyz.statusphere.status", 117 defs: types.Defs( 118 main: option.Some( 119 types.RecordDef(type_: "record", key: option.None, properties: [ 120 #( 121 "text", 122 types.Property( 123 type_: "string", 124 required: True, 125 format: option.None, 126 ref: option.None, 127 refs: option.None, 128 items: option.None, 129 ), 130 ), 131 ]), 132 ), 133 others: dict.new(), 134 ), 135 ) 136 137 let profile_lexicon = 138 types.Lexicon( 139 id: "app.bsky.actor.profile", 140 defs: types.Defs( 141 main: option.Some( 142 types.RecordDef( 143 type_: "record", 144 key: option.Some("literal:self"), 145 properties: [ 146 #( 147 "displayName", 148 types.Property( 149 type_: "string", 150 required: False, 151 format: option.None, 152 ref: option.None, 153 refs: option.None, 154 items: option.None, 155 ), 156 ), 157 ], 158 ), 159 ), 160 others: dict.new(), 161 ), 162 ) 163 164 let test_schema = 165 create_test_schema_from_lexicons([status_lexicon, profile_lexicon]) 166 167 let all_types = introspection.get_all_schema_types(test_schema) 168 let serialized = sdl.print_types(all_types) 169 170 // Profile should be returned as single object (not list) from Status 171 // We check that it's NOT wrapped in a list (no brackets) 172 // The field should be: appBskyActorProfileByDid: AppBskyActorProfile 173 string.contains(serialized, "appBskyActorProfileByDid: AppBskyActorProfile") 174 |> should.be_true 175} 176 177// Test that non-literal:self collections return lists 178pub fn non_literal_self_returns_list_test() { 179 let status_lexicon = 180 types.Lexicon( 181 id: "xyz.statusphere.status", 182 defs: types.Defs( 183 main: option.Some( 184 types.RecordDef(type_: "record", key: option.None, properties: [ 185 #( 186 "text", 187 types.Property( 188 type_: "string", 189 required: True, 190 format: option.None, 191 ref: option.None, 192 refs: option.None, 193 items: option.None, 194 ), 195 ), 196 ]), 197 ), 198 others: dict.new(), 199 ), 200 ) 201 202 let post_lexicon = 203 types.Lexicon( 204 id: "app.bsky.feed.post", 205 defs: types.Defs( 206 main: option.Some( 207 types.RecordDef(type_: "record", key: option.None, properties: [ 208 #( 209 "text", 210 types.Property( 211 type_: "string", 212 required: True, 213 format: option.None, 214 ref: option.None, 215 refs: option.None, 216 items: option.None, 217 ), 218 ), 219 ]), 220 ), 221 others: dict.new(), 222 ), 223 ) 224 225 let test_schema = 226 create_test_schema_from_lexicons([status_lexicon, post_lexicon]) 227 228 let all_types = introspection.get_all_schema_types(test_schema) 229 let serialized = sdl.print_types(all_types) 230 231 // Post should be returned as a connection (paginated list) from Status 232 // The field should be: appBskyFeedPostByDid: AppBskyFeedPostConnection 233 string.contains(serialized, "appBskyFeedPostByDid: AppBskyFeedPostConnection") 234 |> should.be_true 235} 236 237// Test that multiple collections all get DID joins to each other 238pub fn multiple_collections_get_cross_joins_test() { 239 let status_lexicon = 240 types.Lexicon( 241 id: "xyz.statusphere.status", 242 defs: types.Defs( 243 main: option.Some( 244 types.RecordDef(type_: "record", key: option.None, properties: [ 245 #( 246 "text", 247 types.Property( 248 type_: "string", 249 required: True, 250 format: option.None, 251 ref: option.None, 252 refs: option.None, 253 items: option.None, 254 ), 255 ), 256 ]), 257 ), 258 others: dict.new(), 259 ), 260 ) 261 262 let post_lexicon = 263 types.Lexicon( 264 id: "app.bsky.feed.post", 265 defs: types.Defs( 266 main: option.Some( 267 types.RecordDef(type_: "record", key: option.None, properties: [ 268 #( 269 "text", 270 types.Property( 271 type_: "string", 272 required: True, 273 format: option.None, 274 ref: option.None, 275 refs: option.None, 276 items: option.None, 277 ), 278 ), 279 ]), 280 ), 281 others: dict.new(), 282 ), 283 ) 284 285 let like_lexicon = 286 types.Lexicon( 287 id: "app.bsky.feed.like", 288 defs: types.Defs( 289 main: option.Some( 290 types.RecordDef(type_: "record", key: option.None, properties: [ 291 #( 292 "subject", 293 types.Property( 294 type_: "string", 295 required: True, 296 format: option.Some("at-uri"), 297 ref: option.None, 298 refs: option.None, 299 items: option.None, 300 ), 301 ), 302 ]), 303 ), 304 others: dict.new(), 305 ), 306 ) 307 308 let test_schema = 309 create_test_schema_from_lexicons([ 310 status_lexicon, 311 post_lexicon, 312 like_lexicon, 313 ]) 314 315 let all_types = introspection.get_all_schema_types(test_schema) 316 let serialized = sdl.print_types(all_types) 317 318 // Status should have joins to Post and Like 319 string.contains(serialized, "appBskyFeedPostByDid") 320 |> should.be_true 321 322 string.contains(serialized, "appBskyFeedLikeByDid") 323 |> should.be_true 324 325 // Post should have joins to Status and Like 326 string.contains(serialized, "xyzStatusphereStatusByDid") 327 |> should.be_true 328 // Like should have joins to Status and Post 329 // (already checked above) 330} 331 332// Test that collections don't get DID join fields to themselves 333pub fn no_self_join_test() { 334 let status_lexicon = 335 types.Lexicon( 336 id: "xyz.statusphere.status", 337 defs: types.Defs( 338 main: option.Some( 339 types.RecordDef(type_: "record", key: option.None, properties: [ 340 #( 341 "text", 342 types.Property( 343 type_: "string", 344 required: True, 345 format: option.None, 346 ref: option.None, 347 refs: option.None, 348 items: option.None, 349 ), 350 ), 351 ]), 352 ), 353 others: dict.new(), 354 ), 355 ) 356 357 let test_schema = create_test_schema_from_lexicons([status_lexicon]) 358 359 let all_types = introspection.get_all_schema_types(test_schema) 360 let serialized = sdl.print_types(all_types) 361 362 // Status should NOT have a join field to itself 363 string.contains(serialized, "xyzStatusphereStatusByDid") 364 |> should.be_false 365}