Runtime assertions for Ruby literal.fun
ruby
5
fork

Configure Feed

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

at main 433 lines 9.6 kB view raw
1# frozen_string_literal: true 2 3Example = Literal::Object 4 5test "positional params are required by default" do 6 example = Class.new(Example) do 7 prop :example, String, :positional 8 end 9 10 assert_raises(ArgumentError) { example.new } 11 refute_raises { example.new("Hello") } 12end 13 14test "keyword params are required by default" do 15 example = Class.new(Example) do 16 prop :example, String 17 end 18 19 assert_raises(ArgumentError) { example.new } 20 refute_raises { example.new(example: "Hello") } 21end 22 23test "nilable positional params are optional" do 24 example = Class.new(Example) do 25 prop :example, _Nilable(String), :positional 26 end 27 28 refute_raises { example.new } 29 refute_raises { example.new("Hello") } 30end 31 32test "nilable keyword params are optional" do 33 example = Class.new(Example) do 34 prop :example, _Nilable(String) 35 end 36 37 refute_raises { example.new } 38 refute_raises { example.new(example: "Hello") } 39end 40 41test "prop? accepts a description" do 42 example = Class.new(Example) do 43 prop? :example, String, description: "An optional example" 44 end 45 46 assert_equal example.literal_properties[:example].description, "An optional example" 47end 48 49test "positional splats are optional" do 50 example = Class.new(Example) do 51 prop :example, _Array(String), :* 52 end 53 54 refute_raises { example.new } 55 refute_raises { example.new("Hello") } 56 refute_raises { example.new("Hello", "World") } 57 refute example.literal_properties[:example].required? { "Expected example to not be required" } 58end 59 60test "keyword splats are optional" do 61 example = Class.new(Example) do 62 prop :example, _Hash(Symbol, String), :** 63 end 64 65 refute_raises { example.new } 66 refute_raises { example.new(example: "Hello") } 67 refute_raises { example.new(example: "Hello", world: "World") } 68 refute example.literal_properties[:example].required? { "Expected example to not be required" } 69end 70 71test "block params are required by default" do 72 example = Class.new(Example) do 73 prop :example, Proc, :& 74 end 75 76 assert_raises(Literal::TypeError) { example.new } 77 refute_raises { example.new { "Hello" } } 78end 79 80test "nilable block params are optional" do 81 example = Class.new(Example) do 82 prop :example, _Nilable(Proc), :& 83 end 84 85 refute_raises { example.new } 86 refute_raises { example.new { "Hello" } } 87end 88 89class Person 90 extend Literal::Properties 91 92 prop :name, String, :positional, reader: :public 93 prop :age, Integer, reader: :public 94end 95 96class Random 97 extend Literal::Properties 98 prop :begin, Integer, :positional, reader: :public 99end 100 101class WithDefaultBlock 102 extend Literal::Properties 103 prop :block, Proc, :&, reader: :public, default: -> { proc { "Hello" } } 104end 105 106class WithContextualDefault 107 extend Literal::Properties 108 prop :hello, String, reader: :private, default: "Hello" 109 prop :world, String, reader: :private, default: "World" 110 prop :combined, String, reader: :public, default: -> { "#{hello} #{world}" } 111end 112 113class WithNilableType 114 extend Literal::Properties 115 prop :name, Literal::Types::NilableType.new(String), :positional 116end 117 118class Empty 119 extend Literal::Properties 120end 121 122test "empty initializer" do 123 refute_raises { Empty.new } 124end 125 126test do 127 person = Person.new("John", age: 30) 128 129 assert_equal person.name, "John" 130 assert_equal person.age, 30 131end 132 133test "initializer type check" do 134 error = assert_raises(Literal::TypeError) { Person.new(1, age: "Joel") } 135 136 assert_equal error.message, <<~ERROR 137 Type mismatch 138 139 #{Person}#initialize (from #{error.backtrace[1]}) 140 name 141 Expected: String 142 Actual (Integer): 1 143ERROR 144end 145 146test "initializer keyword check" do 147 random = Random.new(1) 148 149 assert_equal random.begin, 1 150end 151 152test "default block" do 153 object = WithDefaultBlock.new 154 assert_equal object.block.call, "Hello" 155 156 object = WithDefaultBlock.new { "World" } 157 assert_equal object.block.call, "World" 158end 159 160test "default value (as a proc) executes in the context of the receiver" do 161 object = WithContextualDefault.new 162 assert_equal object.combined, "Hello World" 163end 164 165test "properties are enumerable" do 166 props = Person.literal_properties 167 assert_equal props.size, 2 168 assert_equal props.map(&:name), [:name, :age] 169 170 props = Empty.literal_properties 171 assert_equal props.size, 0 172end 173 174test "introspection" do 175 prop1, prop2 = *Person.literal_properties 176 177 assert_equal prop1.name, :name 178 assert_equal prop1.type, String 179 180 assert(prop1.positional?) { "Expected name to be kind :positional" } 181 refute(prop1.keyword?) { "Expected name to not be kind :keyword" } 182 refute(prop1.block?) { "Expected name to not be kind :&" } 183 refute(prop1.splat?) { "Expected name to not be bind :*" } 184 refute(prop1.double_splat?) { "Expected name to not be kind :**" } 185 assert(prop1.required?) { "Expected name to be required" } 186 refute(prop1.optional?) { "Expected name to not be optional" } 187 188 assert_equal prop2.name, :age 189 assert_equal prop2.type, Integer 190 191 assert(prop2.keyword?) { "Expected age to be kind :keyword" } 192 assert(prop2.required?) { "Expected age to be required" } 193 194 props = WithDefaultBlock.literal_properties 195 prop_block = props.first 196 assert(prop_block.block?) { "Expected block to be kind :&" } 197 assert(prop_block.optional?) { "Expected block to be optional" } 198 199 props = WithNilableType.literal_properties 200 prop_name = props.first 201 assert(prop_name.optional?) { "Expected name to be optional" } 202end 203 204test "after initialize callback" do 205 callback_called = false 206 207 public_callback = Class.new do 208 extend Literal::Properties 209 210 prop :name, String 211 212 define_method :after_initialize do 213 callback_called = true 214 end 215 end 216 217 public_callback.new(name: "John") 218 219 assert callback_called 220 221 callback_called = false 222 223 protected_callback = Class.new do 224 extend Literal::Properties 225 226 prop :name, String 227 228 define_method :after_initialize do 229 callback_called = true 230 end 231 232 protected :after_initialize 233 end 234 235 protected_callback.new(name: "John") 236 237 assert callback_called 238 239 callback_called = false 240 241 private_callback = Class.new do 242 extend Literal::Properties 243 244 prop :name, String 245 246 define_method :after_initialize do 247 callback_called = true 248 end 249 250 private :after_initialize 251 end 252 253 private_callback.new(name: "John") 254 255 assert callback_called 256 257 callback_called = false 258 259 empty = Class.new do 260 extend Literal::Properties 261 262 define_method :after_initialize do 263 callback_called = true 264 end 265 end 266 267 empty.new 268 269 assert callback_called 270end 271 272class Friend < Person 273 prop :age, Float, reader: :public 274end 275 276test "inheritance" do 277 friend = Friend.new("John", age: 30.5) 278 279 assert_equal friend.name, "John" 280 assert_equal friend.age, 30.5 281end 282 283class WithPredicate 284 extend Literal::Properties 285 286 prop :enabled, _Boolean, predicate: :public 287end 288 289test "predicates" do 290 enabled = WithPredicate.new(enabled: true) 291 disabled = WithPredicate.new(enabled: false) 292 293 assert_equal enabled.enabled?, true 294 assert_equal disabled.enabled?, false 295end 296 297class WithWriters < Example 298 extend Literal::Properties 299 300 prop :example, _Nilable(String), writer: :public 301 prop :a, _Nilable(_Array(String)), writer: :public 302end 303 304test "writer type error" do 305 instance = WithWriters.new 306 307 error = assert_raises(Literal::TypeError) do 308 instance.example = 0 309 end 310 311 assert_equal error.message, <<~ERROR 312 Type mismatch 313 314 #{WithWriters}#example=(value) (from #{error.backtrace[1]}) 315 Expected: _Nilable(String) 316 Actual (Integer): 0 317ERROR 318 319 error = assert_raises(Literal::TypeError) do 320 instance.a = [1] 321 end 322 323 assert_equal error.message, <<~ERROR 324 Type mismatch 325 326 #{WithWriters}#a=(value) (from #{error.backtrace[1]}) 327 [0] 328 Expected: String 329 Actual (Integer): 1 330ERROR 331end 332 333class Family 334 extend Literal::Properties 335 336 prop :members, _Array(_Map(person: Person, role: Symbol)), :positional, reader: :public 337 prop :last_reunion_year, _Nilable(Integer) 338end 339 340test "nested properties raise in initializer" do 341 error = assert_raises(Literal::TypeError) do 342 Family.new( 343 [ 344 { 345 person: Person.new("Json", age: 1), 346 role: 1, 347 }, 348 { 349 person: Person.new("John", age: 30), 350 role: "Father", 351 }, 352 { 353 1 => 2, 354 }, 355 ], 356 ) 357 end 358 359 assert_equal error.message, <<~ERROR 360 Type mismatch 361 362 #{Family}#initialize (from #{error.backtrace[1]}) 363 members 364 [0] 365 [:role] 366 Expected: Symbol 367 Actual (Integer): 1 368 [1] 369 [:role] 370 Expected: Symbol 371 Actual (String): "Father" 372 [2] 373 [:person] 374 Expected: #{Person.inspect} 375 Actual (NilClass): nil 376 [:role] 377 Expected: Symbol 378 Actual (NilClass): nil 379 ERROR 380 381 error = assert_raises(Literal::TypeError) { Family.new([1]) } 382 383 assert_equal error.message, <<~ERROR 384 Type mismatch 385 386 #{Family}#initialize (from #{error.backtrace[1]}) 387 members 388 [0] 389 Expected: _Map(#{{ person: Person, role: Symbol }}) 390 Actual (Integer): 1 391ERROR 392 393 error = assert_raises(Literal::TypeError) do 394 Family.new([], last_reunion_year: :two_thousand) 395 end 396 397 assert_equal error.message, <<~ERROR 398 Type mismatch 399 400 #{Family}#initialize (from #{error.backtrace[1]}) 401 last_reunion_year: 402 Expected: _Nilable(Integer) 403 Actual (Symbol): :two_thousand 404 ERROR 405end 406 407test "nested properties succeed in initializer" do 408 refute_raises do 409 Family.new( 410 [ 411 { 412 person: Person.new("Json", age: 1), 413 role: :son, 414 }, 415 { 416 person: Person.new("John", age: 30), 417 role: :brother, 418 }, 419 ], 420 ) 421 end 422 423 refute_raises { Family.new([]) } 424 refute_raises { Family.new([], last_reunion_year: 0) } 425end 426 427test "#to_h" do 428 person = Person.new("John", age: 30) 429 assert_equal person.to_h, { name: "John", age: 30 } 430 431 empty = Empty.new 432 assert_equal empty.to_h, {} 433end