An ATProto Lexicon validator for Gleam.
at v1.2.0 5.4 kB view raw
1import gleam/json 2import gleeunit 3import gleeunit/should 4import honk/errors 5import honk/validation/context 6import honk/validation/field 7 8pub fn main() { 9 gleeunit.main() 10} 11 12// Test valid object schema 13pub fn valid_object_schema_test() { 14 let schema = 15 json.object([ 16 #("type", json.string("object")), 17 #( 18 "properties", 19 json.object([ 20 #("title", json.object([#("type", json.string("string"))])), 21 #("count", json.object([#("type", json.string("integer"))])), 22 ]), 23 ), 24 #("required", json.array([json.string("title")], fn(x) { x })), 25 ]) 26 27 let assert Ok(ctx) = context.builder() |> context.build 28 let result = field.validate_object_schema(schema, ctx) 29 result |> should.be_ok 30} 31 32// Test valid object data 33pub fn valid_object_data_test() { 34 let schema = 35 json.object([ 36 #("type", json.string("object")), 37 #( 38 "properties", 39 json.object([ 40 #("title", json.object([#("type", json.string("string"))])), 41 #("count", json.object([#("type", json.string("integer"))])), 42 ]), 43 ), 44 #("required", json.array([json.string("title")], fn(x) { x })), 45 ]) 46 47 let data = 48 json.object([ 49 #("title", json.string("Hello")), 50 #("count", json.int(42)), 51 ]) 52 53 let assert Ok(ctx) = context.builder() |> context.build 54 let result = field.validate_object_data(data, schema, ctx) 55 result |> should.be_ok 56} 57 58// Test missing required field 59pub fn missing_required_field_test() { 60 let schema = 61 json.object([ 62 #("type", json.string("object")), 63 #( 64 "properties", 65 json.object([ 66 #("title", json.object([#("type", json.string("string"))])), 67 ]), 68 ), 69 #("required", json.array([json.string("title")], fn(x) { x })), 70 ]) 71 72 let data = json.object([#("other", json.string("value"))]) 73 74 let assert Ok(ctx) = context.builder() |> context.build 75 let result = field.validate_object_data(data, schema, ctx) 76 result |> should.be_error 77} 78 79// Test missing required field error message at root level (no path) 80pub fn missing_required_field_message_root_test() { 81 let schema = 82 json.object([ 83 #("type", json.string("object")), 84 #( 85 "properties", 86 json.object([ 87 #("title", json.object([#("type", json.string("string"))])), 88 ]), 89 ), 90 #("required", json.array([json.string("title")], fn(x) { x })), 91 ]) 92 93 let data = json.object([#("other", json.string("value"))]) 94 95 let assert Ok(ctx) = context.builder() |> context.build 96 let assert Error(error) = field.validate_object_data(data, schema, ctx) 97 98 let error_message = errors.to_string(error) 99 error_message 100 |> should.equal("Data validation failed: required field 'title' is missing") 101} 102 103// Test nullable field accepts null value 104pub fn nullable_field_accepts_null_test() { 105 let schema = 106 json.object([ 107 #("type", json.string("object")), 108 #( 109 "properties", 110 json.object([ 111 #("name", json.object([#("type", json.string("string"))])), 112 #("duration", json.object([#("type", json.string("integer"))])), 113 ]), 114 ), 115 #("nullable", json.array([json.string("duration")], fn(x) { x })), 116 ]) 117 118 let data = 119 json.object([ 120 #("name", json.string("test")), 121 #("duration", json.null()), 122 ]) 123 124 let assert Ok(ctx) = context.builder() |> context.build 125 let result = field.validate_object_data(data, schema, ctx) 126 result |> should.be_ok 127} 128 129// Test non-nullable field rejects null value 130pub fn non_nullable_field_rejects_null_test() { 131 let schema = 132 json.object([ 133 #("type", json.string("object")), 134 #( 135 "properties", 136 json.object([ 137 #("name", json.object([#("type", json.string("string"))])), 138 #("count", json.object([#("type", json.string("integer"))])), 139 ]), 140 ), 141 // No nullable array - count cannot be null 142 ]) 143 144 let data = 145 json.object([ 146 #("name", json.string("test")), 147 #("count", json.null()), 148 ]) 149 150 let assert Ok(ctx) = context.builder() |> context.build 151 let result = field.validate_object_data(data, schema, ctx) 152 result |> should.be_error 153} 154 155// Test nullable field must exist in properties (schema validation) 156pub fn nullable_field_not_in_properties_fails_test() { 157 let schema = 158 json.object([ 159 #("type", json.string("object")), 160 #( 161 "properties", 162 json.object([ 163 #("name", json.object([#("type", json.string("string"))])), 164 ]), 165 ), 166 // "nonexistent" is not in properties 167 #("nullable", json.array([json.string("nonexistent")], fn(x) { x })), 168 ]) 169 170 let assert Ok(ctx) = context.builder() |> context.build 171 let result = field.validate_object_schema(schema, ctx) 172 result |> should.be_error 173} 174 175// Test valid nullable schema passes validation 176pub fn valid_nullable_schema_test() { 177 let schema = 178 json.object([ 179 #("type", json.string("object")), 180 #( 181 "properties", 182 json.object([ 183 #("name", json.object([#("type", json.string("string"))])), 184 #("duration", json.object([#("type", json.string("integer"))])), 185 ]), 186 ), 187 #("nullable", json.array([json.string("duration")], fn(x) { x })), 188 ]) 189 190 let assert Ok(ctx) = context.builder() |> context.build 191 let result = field.validate_object_schema(schema, ctx) 192 result |> should.be_ok 193}