Auto-indexing service and GraphQL API for AT Protocol Records
quickslice.slices.network/
atproto
gleam
graphql
1/// Tests for forward join field generation
2///
3/// Verifies that forward join fields are added to GraphQL schemas based on lexicon metadata
4import gleam/dict
5import gleam/option.{None, Some}
6import gleam/string
7import gleeunit/should
8import lexicon_graphql/schema/database as db_schema_builder
9import lexicon_graphql/types
10import swell/introspection
11import swell/schema
12import swell/sdl
13
14// Helper to create a test schema with a mock fetcher
15fn create_test_schema_from_lexicons(
16 lexicons: List(types.Lexicon),
17) -> schema.Schema {
18 // Mock fetcher that returns empty results (we're only testing schema generation)
19 let fetcher = fn(_collection, _params) {
20 Ok(#([], option.None, False, False, option.None))
21 }
22
23 case
24 db_schema_builder.build_schema_with_fetcher(
25 lexicons,
26 fetcher,
27 option.None,
28 option.None,
29 option.None,
30 option.None,
31 option.None,
32 option.None,
33 )
34 {
35 Ok(s) -> s
36 Error(_) -> panic as "Failed to build test schema"
37 }
38}
39
40// Test that strongRef fields generate forward join fields
41pub fn strong_ref_generates_forward_join_field_test() {
42 let lexicon =
43 types.Lexicon(
44 id: "app.bsky.actor.profile",
45 defs: types.Defs(
46 main: Some(
47 types.RecordDef(type_: "record", key: None, properties: [
48 #(
49 "displayName",
50 types.Property(
51 type_: "string",
52 required: True,
53 format: None,
54 ref: None,
55 refs: None,
56 items: None,
57 ),
58 ),
59 #(
60 "pinnedPost",
61 types.Property(
62 type_: "ref",
63 required: False,
64 format: None,
65 ref: Some("com.atproto.repo.strongRef"),
66 refs: None,
67 items: None,
68 ),
69 ),
70 ]),
71 ),
72 others: dict.new(),
73 ),
74 )
75
76 let test_schema = create_test_schema_from_lexicons([lexicon])
77
78 // Get all types and serialize to SDL
79 let all_types = introspection.get_all_schema_types(test_schema)
80 let serialized = sdl.print_types(all_types)
81
82 // Verify that pinnedPostResolved field appears in the schema
83 string.contains(serialized, "pinnedPostResolved")
84 |> should.be_true
85}
86
87// Test that at-uri fields generate forward join fields
88pub fn at_uri_generates_forward_join_field_test() {
89 let lexicon =
90 types.Lexicon(
91 id: "app.bsky.feed.like",
92 defs: types.Defs(
93 main: Some(
94 types.RecordDef(type_: "record", key: None, properties: [
95 #(
96 "subject",
97 types.Property(
98 type_: "string",
99 required: True,
100 format: Some("at-uri"),
101 ref: None,
102 refs: None,
103 items: None,
104 ),
105 ),
106 #(
107 "createdAt",
108 types.Property(
109 type_: "string",
110 required: True,
111 format: Some("datetime"),
112 ref: None,
113 refs: None,
114 items: None,
115 ),
116 ),
117 ]),
118 ),
119 others: dict.new(),
120 ),
121 )
122
123 let test_schema = create_test_schema_from_lexicons([lexicon])
124
125 let all_types = introspection.get_all_schema_types(test_schema)
126 let serialized = sdl.print_types(all_types)
127
128 // Verify that subjectResolved field appears in the schema
129 string.contains(serialized, "subjectResolved")
130 |> should.be_true
131}
132
133// Test that multiple forward join fields are all generated
134pub fn multiple_forward_join_fields_test() {
135 let lexicon =
136 types.Lexicon(
137 id: "app.bsky.feed.post",
138 defs: types.Defs(
139 main: Some(
140 types.RecordDef(type_: "record", key: None, properties: [
141 #(
142 "text",
143 types.Property(
144 type_: "string",
145 required: True,
146 format: None,
147 ref: None,
148 refs: None,
149 items: None,
150 ),
151 ),
152 #(
153 "reply",
154 types.Property(
155 type_: "ref",
156 required: False,
157 format: None,
158 ref: Some("com.atproto.repo.strongRef"),
159 refs: None,
160 items: None,
161 ),
162 ),
163 #(
164 "via",
165 types.Property(
166 type_: "string",
167 required: False,
168 format: Some("at-uri"),
169 ref: None,
170 refs: None,
171 items: None,
172 ),
173 ),
174 ]),
175 ),
176 others: dict.new(),
177 ),
178 )
179
180 let test_schema = create_test_schema_from_lexicons([lexicon])
181
182 let all_types = introspection.get_all_schema_types(test_schema)
183 let serialized = sdl.print_types(all_types)
184
185 // Check both forward join fields exist
186 string.contains(serialized, "replyResolved")
187 |> should.be_true
188
189 string.contains(serialized, "viaResolved")
190 |> should.be_true
191}
192
193// Test that collections without join fields don't generate extra fields
194pub fn no_join_fields_test() {
195 let lexicon =
196 types.Lexicon(
197 id: "xyz.statusphere.status",
198 defs: types.Defs(
199 main: Some(
200 types.RecordDef(type_: "record", key: None, properties: [
201 #(
202 "status",
203 types.Property(
204 type_: "string",
205 required: True,
206 format: None,
207 ref: None,
208 refs: None,
209 items: None,
210 ),
211 ),
212 #(
213 "createdAt",
214 types.Property(
215 type_: "string",
216 required: True,
217 format: Some("datetime"),
218 ref: None,
219 refs: None,
220 items: None,
221 ),
222 ),
223 ]),
224 ),
225 others: dict.new(),
226 ),
227 )
228
229 let test_schema = create_test_schema_from_lexicons([lexicon])
230
231 let all_types = introspection.get_all_schema_types(test_schema)
232 let serialized = sdl.print_types(all_types)
233
234 // Should not have any "Resolved" fields for non-join fields
235 let has_status_resolved = string.contains(serialized, "statusResolved")
236 let has_created_at_resolved = string.contains(serialized, "createdAtResolved")
237
238 has_status_resolved
239 |> should.be_false
240
241 has_created_at_resolved
242 |> should.be_false
243}
244
245// Test that Record union is generated for forward joins
246pub fn record_union_type_exists_test() {
247 let lexicon =
248 types.Lexicon(
249 id: "app.bsky.feed.post",
250 defs: types.Defs(
251 main: Some(
252 types.RecordDef(type_: "record", key: None, properties: [
253 #(
254 "text",
255 types.Property(
256 type_: "string",
257 required: True,
258 format: None,
259 ref: None,
260 refs: None,
261 items: None,
262 ),
263 ),
264 #(
265 "reply",
266 types.Property(
267 type_: "ref",
268 required: False,
269 format: None,
270 ref: Some("com.atproto.repo.strongRef"),
271 refs: None,
272 items: None,
273 ),
274 ),
275 ]),
276 ),
277 others: dict.new(),
278 ),
279 )
280
281 let test_schema = create_test_schema_from_lexicons([lexicon])
282
283 let all_types = introspection.get_all_schema_types(test_schema)
284 let serialized = sdl.print_types(all_types)
285
286 // Verify that Record union exists
287 string.contains(serialized, "union Record")
288 |> should.be_true
289}
290
291// Test that forward join fields have Record union type
292pub fn forward_join_field_has_union_type_test() {
293 let lexicon =
294 types.Lexicon(
295 id: "app.bsky.feed.post",
296 defs: types.Defs(
297 main: Some(
298 types.RecordDef(type_: "record", key: None, properties: [
299 #(
300 "text",
301 types.Property(
302 type_: "string",
303 required: True,
304 format: None,
305 ref: None,
306 refs: None,
307 items: None,
308 ),
309 ),
310 #(
311 "reply",
312 types.Property(
313 type_: "ref",
314 required: False,
315 format: None,
316 ref: Some("com.atproto.repo.strongRef"),
317 refs: None,
318 items: None,
319 ),
320 ),
321 ]),
322 ),
323 others: dict.new(),
324 ),
325 )
326
327 let test_schema = create_test_schema_from_lexicons([lexicon])
328
329 let all_types = introspection.get_all_schema_types(test_schema)
330 let serialized = sdl.print_types(all_types)
331
332 // Verify that replyResolved field has Record type
333 string.contains(serialized, "replyResolved: Record")
334 |> should.be_true
335}