Gleam-inspired typed configuration language (POC)
0

Configure Feed

Select the types of activity you want to include in your feed.

glint / test / glint_test.gleam
5.1 kB 256 lines
1import gleam/string 2import gleeunit 3import glint 4import glint/check 5import glint/lsp/diagnostics 6import glint/pipeline 7import glint/position 8import glint/value 9 10pub fn main() -> Nil { 11 gleeunit.main() 12} 13 14pub fn hello_loads_test() { 15 let source = 16 " 17type Mode { 18 Dev 19 Prod 20} 21 22type Config { 23 Config( 24 name: String, 25 mode: Mode, 26 port: Int, 27 ) 28} 29 30pub let config = Config( 31 name: \"hello\", 32 mode: Dev, 33 port: 3000, 34) 35" 36 37 let assert Ok(checked) = pipeline.load(source) 38 let assert "Config" = check.type_to_string(checked.config_type) 39 let assert True = 40 string.contains(value.to_glint(checked.config), "\"hello\"") 41 let assert True = string.contains(value.to_json(checked.config), "hello") 42} 43 44pub fn app_example_test() { 45 let source = 46 " 47type LogLevel { 48 Debug 49 Info 50} 51 52type Database { 53 Database( 54 host: String, 55 port: Int, 56 password: Option(String), 57 ) 58} 59 60type Config { 61 Config( 62 log_level: LogLevel, 63 database: Database, 64 origins: List(String), 65 ) 66} 67 68let database = Database( 69 host: \"localhost\", 70 port: 5432, 71 password: None, 72) 73 74pub let config = Config( 75 log_level: Info, 76 database: database, 77 origins: [\"https://example.com\"], 78) 79" 80 81 let assert Ok(checked) = glint.load(source) 82 let assert value.VVariant("Config", fields) = checked.config 83 let assert True = list_has_tag(fields, "log_level") 84} 85 86pub fn type_mismatch_test() { 87 let source = 88 " 89type Config { 90 Config(port: Int) 91} 92 93pub let config = Config(port: \"nope\") 94" 95 let assert Error(msg) = glint.load(source) 96 let assert True = string.contains(msg, "type mismatch") 97} 98 99pub fn missing_config_test() { 100 let source = 101 " 102type Mode { 103 Dev 104} 105 106let mode = Dev 107" 108 let assert Error(msg) = glint.load(source) 109 let assert True = string.contains(msg, "pub let config") 110} 111 112pub fn unknown_constructor_test() { 113 let source = 114 " 115type Config { 116 Config(name: String) 117} 118 119pub let config = Config(name: \"x\", extra: 1) 120" 121 let assert Error(msg) = glint.load(source) 122 let assert True = string.contains(msg, "unknown field") 123} 124 125pub fn comments_and_trailing_commas_test() { 126 let source = 127 " 128// leading comment 129type Mode { 130 Dev 131 Prod 132} 133 134type Config { 135 Config( 136 mode: Mode, 137 port: Int, 138 ) 139} 140 141pub let config = Config( 142 mode: Dev, 143 port: 1_000, 144) 145" 146 let assert Ok(_) = glint.load(source) 147} 148 149fn list_has_tag(fields: List(#(String, value.Value)), label: String) -> Bool { 150 case fields { 151 [] -> False 152 [#(l, _), ..rest] -> 153 case l == label { 154 True -> True 155 False -> list_has_tag(rest, label) 156 } 157 } 158} 159 160pub fn host_accessors_test() { 161 let source = 162 " 163type Mode { 164 Dev 165 Prod 166} 167 168type Server { 169 Server(host: String, port: Int) 170} 171 172type Config { 173 Config( 174 name: String, 175 mode: Mode, 176 server: Server, 177 ) 178} 179 180pub let config = Config( 181 name: \"hello\", 182 mode: Dev, 183 server: Server(host: \"localhost\", port: 3000), 184) 185" 186 let assert Ok(checked) = glint.load(source) 187 let cfg = checked.config 188 let assert "hello" = glint.string(cfg, "name") 189 let assert "Dev" = glint.unit(cfg, "mode") 190 let assert 3000 = glint.int(cfg, "server.port") 191 let assert "localhost" = glint.string(cfg, "server.host") 192} 193 194// ── Position / diagnostics ────────────────────────────────────────── 195 196pub fn offset_to_position_test() { 197 let source = "abc\ndef" 198 let assert position.Position(0, 0) = position.offset_to_position(source, 0) 199 let assert position.Position(0, 2) = position.offset_to_position(source, 2) 200 let assert position.Position(1, 0) = position.offset_to_position(source, 4) 201 let assert position.Position(1, 2) = position.offset_to_position(source, 6) 202} 203 204pub fn range_from_offsets_test() { 205 let source = "let x = 1" 206 let range = position.range_from_offsets(source, 0, 3) 207 let assert position.Position(0, 0) = range.start 208 let assert position.Position(0, 3) = range.end 209} 210 211pub fn valid_source_no_diagnostics_test() { 212 let source = 213 " 214type Config { 215 Config(port: Int) 216} 217 218pub let config = Config(port: 1) 219" 220 let assert [] = diagnostics.analyse(source) 221} 222 223pub fn lex_error_has_range_test() { 224 // `@` is not a valid token 225 let source = "let x = @" 226 let diags = diagnostics.analyse(source) 227 let assert [d] = diags 228 let assert True = string.contains(d.message, "lex error") 229 // Range should not be the default empty only — character should land near `@` 230 let assert True = d.range.start.line >= 0 231 let assert True = d.range.start.character >= 0 232} 233 234pub fn parse_error_has_range_test() { 235 // Missing `=` after name 236 let source = "let x 1" 237 let diags = diagnostics.analyse(source) 238 let assert [d] = diags 239 let assert True = string.contains(d.message, "parse error") 240 let assert True = 241 d.range.start.character > 0 || d.range.end.character > d.range.start.character 242} 243 244pub fn check_error_diagnostic_test() { 245 let source = 246 " 247type Config { 248 Config(port: Int) 249} 250 251pub let config = Config(port: \"nope\") 252" 253 let diags = diagnostics.analyse(source) 254 let assert [d] = diags 255 let assert True = string.contains(d.message, "type error") 256}