Auto-indexing service and GraphQL API for AT Protocol Records
quickslice.slices.network/
atproto
gleam
graphql
1import gleam/option.{None, Some}
2import gleam/string
3import gleeunit/should
4import handlers/oauth/jwks
5import wisp
6
7/// Test JWKS endpoint with no signing key returns empty JWKS
8pub fn handle_no_signing_key_test() {
9 let response = jwks.handle(None)
10
11 // Should return 200 with empty JWKS
12 response.status |> should.equal(200)
13
14 // Should have correct content type
15 let content_type = get_header(response, "content-type")
16 content_type |> should.equal(Some("application/json"))
17
18 // Body should contain empty keys array
19 let body = get_body_text(response)
20 should.be_true(string_contains(body, "\"keys\":[]"))
21}
22
23/// Test JWKS endpoint with valid signing key returns proper JWKS
24pub fn handle_with_signing_key_test() {
25 // Use the test key from environment (same format as OAUTH_SIGNING_KEY)
26 let test_key = "z42tn4DpCKfYBwSYEbaoR5z5Amir9HsY5WvzB3wSvFQcXvSH"
27 let response = jwks.handle(Some(test_key))
28
29 // Should return 200
30 response.status |> should.equal(200)
31
32 // Should have correct content type
33 let content_type = get_header(response, "content-type")
34 content_type |> should.equal(Some("application/json"))
35
36 // Body should contain required JWK fields
37 let body = get_body_text(response)
38
39 // Should contain "keys" array
40 should.be_true(string_contains(body, "\"keys\""))
41
42 // Should contain EC key type
43 should.be_true(string_contains(body, "\"kty\":\"EC\""))
44
45 // Should contain P-256 curve
46 should.be_true(string_contains(body, "\"crv\":\"P-256\""))
47
48 // Should contain x and y coordinates
49 should.be_true(string_contains(body, "\"x\":"))
50 should.be_true(string_contains(body, "\"y\":"))
51
52 // Should contain ES256 algorithm
53 should.be_true(string_contains(body, "\"alg\":\"ES256\""))
54
55 // Should contain sig use
56 should.be_true(string_contains(body, "\"use\":\"sig\""))
57
58 // Should contain kid (key ID) as did:key
59 should.be_true(string_contains(body, "\"kid\":\"did:key:"))
60}
61
62/// Test JWKS endpoint with invalid signing key returns empty JWKS
63pub fn handle_with_invalid_signing_key_test() {
64 // Invalid key format
65 let invalid_key = "invalid-key-format"
66 let response = jwks.handle(Some(invalid_key))
67
68 // Should return 200 (graceful degradation)
69 response.status |> should.equal(200)
70
71 // Body should contain empty keys array
72 let body = get_body_text(response)
73 should.be_true(string_contains(body, "\"keys\":[]"))
74}
75
76// Helper functions
77
78fn get_header(response: wisp.Response, name: String) -> option.Option(String) {
79 case
80 response.headers
81 |> find_header(name)
82 {
83 Ok(value) -> Some(value)
84 Error(_) -> None
85 }
86}
87
88fn find_header(
89 headers: List(#(String, String)),
90 name: String,
91) -> Result(String, Nil) {
92 case headers {
93 [] -> Error(Nil)
94 [#(key, value), ..rest] ->
95 case string.lowercase(key) == string.lowercase(name) {
96 True -> Ok(value)
97 False -> find_header(rest, name)
98 }
99 }
100}
101
102fn get_body_text(response: wisp.Response) -> String {
103 case response.body {
104 wisp.Text(text) -> text
105 _ -> ""
106 }
107}
108
109fn string_contains(haystack: String, needle: String) -> Bool {
110 erlang_string_find(haystack, needle) != "nomatch"
111}
112
113@external(erlang, "string", "find")
114fn erlang_string_find(haystack: String, needle: String) -> String