An ATProto Lexicon validator for Gleam.
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}