Auto-indexing service and GraphQL API for AT Protocol Records quickslice.slices.network/
atproto gleam graphql
at main 114 lines 3.2 kB view raw
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