An ATProto Lexicon validator for Gleam.
at main 6.0 kB view raw
1import gleam/json 2import gleeunit 3import gleeunit/should 4import honk/validation/context 5import honk/validation/primitive/bytes 6 7pub fn main() { 8 gleeunit.main() 9} 10 11// ========== SCHEMA VALIDATION TESTS ========== 12 13pub fn valid_bytes_schema_basic_test() { 14 let schema = json.object([#("type", json.string("bytes"))]) 15 16 let assert Ok(ctx) = context.builder() |> context.build 17 18 bytes.validate_schema(schema, ctx) |> should.be_ok 19} 20 21pub fn valid_bytes_schema_with_min_max_test() { 22 let schema = 23 json.object([ 24 #("type", json.string("bytes")), 25 #("minLength", json.int(10)), 26 #("maxLength", json.int(20)), 27 ]) 28 29 let assert Ok(ctx) = context.builder() |> context.build 30 31 bytes.validate_schema(schema, ctx) |> should.be_ok 32} 33 34pub fn valid_bytes_schema_with_description_test() { 35 let schema = 36 json.object([ 37 #("type", json.string("bytes")), 38 #("description", json.string("Binary data")), 39 ]) 40 41 let assert Ok(ctx) = context.builder() |> context.build 42 43 bytes.validate_schema(schema, ctx) |> should.be_ok 44} 45 46pub fn invalid_bytes_schema_extra_fields_test() { 47 let schema = 48 json.object([ 49 #("type", json.string("bytes")), 50 #("extraField", json.string("not allowed")), 51 ]) 52 53 let assert Ok(ctx) = context.builder() |> context.build 54 55 bytes.validate_schema(schema, ctx) |> should.be_error 56} 57 58pub fn invalid_bytes_schema_max_less_than_min_test() { 59 let schema = 60 json.object([ 61 #("type", json.string("bytes")), 62 #("minLength", json.int(20)), 63 #("maxLength", json.int(10)), 64 ]) 65 66 let assert Ok(ctx) = context.builder() |> context.build 67 68 bytes.validate_schema(schema, ctx) |> should.be_error 69} 70 71pub fn invalid_bytes_schema_negative_min_test() { 72 let schema = 73 json.object([ 74 #("type", json.string("bytes")), 75 #("minLength", json.int(-1)), 76 ]) 77 78 let assert Ok(ctx) = context.builder() |> context.build 79 80 bytes.validate_schema(schema, ctx) |> should.be_error 81} 82 83pub fn invalid_bytes_schema_negative_max_test() { 84 let schema = 85 json.object([ 86 #("type", json.string("bytes")), 87 #("maxLength", json.int(-5)), 88 ]) 89 90 let assert Ok(ctx) = context.builder() |> context.build 91 92 bytes.validate_schema(schema, ctx) |> should.be_error 93} 94 95// ========== DATA VALIDATION TESTS ========== 96 97pub fn valid_bytes_data_basic_test() { 98 let schema = json.object([#("type", json.string("bytes"))]) 99 // "123" in base64 is "MTIz" 100 let data = json.object([#("$bytes", json.string("MTIz"))]) 101 102 let assert Ok(ctx) = context.builder() |> context.build 103 104 bytes.validate_data(data, schema, ctx) |> should.be_ok 105} 106 107pub fn valid_bytes_data_with_length_constraints_test() { 108 let schema = 109 json.object([ 110 #("type", json.string("bytes")), 111 #("minLength", json.int(10)), 112 #("maxLength", json.int(20)), 113 ]) 114 // Base64 string that decodes to exactly 16 bytes 115 let data = json.object([#("$bytes", json.string("YXNkZmFzZGZhc2RmYXNkZg"))]) 116 117 let assert Ok(ctx) = context.builder() |> context.build 118 119 bytes.validate_data(data, schema, ctx) |> should.be_ok 120} 121 122pub fn invalid_bytes_data_plain_string_test() { 123 let schema = json.object([#("type", json.string("bytes"))]) 124 // Plain string instead of object with $bytes 125 let data = json.string("green") 126 127 let assert Ok(ctx) = context.builder() |> context.build 128 129 bytes.validate_data(data, schema, ctx) |> should.be_error 130} 131 132pub fn invalid_bytes_data_empty_object_test() { 133 let schema = json.object([#("type", json.string("bytes"))]) 134 // Empty object 135 let data = json.object([]) 136 137 let assert Ok(ctx) = context.builder() |> context.build 138 139 bytes.validate_data(data, schema, ctx) |> should.be_error 140} 141 142pub fn invalid_bytes_data_wrong_field_name_test() { 143 let schema = json.object([#("type", json.string("bytes"))]) 144 // Wrong field name - should be "$bytes" not "bytes" 145 let data = json.object([#("bytes", json.string("YXNkZmFzZGZhc2RmYXNkZg"))]) 146 147 let assert Ok(ctx) = context.builder() |> context.build 148 149 bytes.validate_data(data, schema, ctx) |> should.be_error 150} 151 152pub fn invalid_bytes_data_extra_fields_test() { 153 let schema = json.object([#("type", json.string("bytes"))]) 154 // Object with extra fields - must have exactly one field 155 let data = 156 json.object([ 157 #("$bytes", json.string("MTIz")), 158 #("other", json.string("blah")), 159 ]) 160 161 let assert Ok(ctx) = context.builder() |> context.build 162 163 bytes.validate_data(data, schema, ctx) |> should.be_error 164} 165 166pub fn invalid_bytes_data_non_string_value_test() { 167 let schema = json.object([#("type", json.string("bytes"))]) 168 // $bytes value is not a string 169 let data = 170 json.object([ 171 #("$bytes", json.preprocessed_array([json.int(1), json.int(2)])), 172 ]) 173 174 let assert Ok(ctx) = context.builder() |> context.build 175 176 bytes.validate_data(data, schema, ctx) |> should.be_error 177} 178 179pub fn invalid_bytes_data_invalid_base64_test() { 180 let schema = json.object([#("type", json.string("bytes"))]) 181 // Invalid base64 string (contains invalid characters) 182 let data = json.object([#("$bytes", json.string("not!valid@base64"))]) 183 184 let assert Ok(ctx) = context.builder() |> context.build 185 186 bytes.validate_data(data, schema, ctx) |> should.be_error 187} 188 189pub fn invalid_bytes_data_too_short_test() { 190 let schema = 191 json.object([ 192 #("type", json.string("bytes")), 193 #("minLength", json.int(10)), 194 ]) 195 // "b25l" decodes to "one" which is only 3 bytes 196 let data = json.object([#("$bytes", json.string("b25l"))]) 197 198 let assert Ok(ctx) = context.builder() |> context.build 199 200 bytes.validate_data(data, schema, ctx) |> should.be_error 201} 202 203pub fn invalid_bytes_data_too_long_test() { 204 let schema = 205 json.object([ 206 #("type", json.string("bytes")), 207 #("maxLength", json.int(5)), 208 ]) 209 // "YXNkZmFzZGZhc2RmYXNkZg" decodes to "asdfasdfasdfasdf" which is 16 bytes 210 let data = json.object([#("$bytes", json.string("YXNkZmFzZGZhc2RmYXNkZg"))]) 211 212 let assert Ok(ctx) = context.builder() |> context.build 213 214 bytes.validate_data(data, schema, ctx) |> should.be_error 215}