test_lazy.jl (7375B)
1 @testset "Lazy Evaluation" begin 2 @testset "nickel_open returns NickelValue" begin 3 is_nickel_value = nickel_open("{ x = 1 }") do cfg 4 cfg isa NickelValue 5 end 6 @test is_nickel_value 7 end 8 9 @testset "nickel_kind" begin 10 nickel_open("{ x = 1 }") do cfg 11 @test nickel_kind(cfg) == :record 12 end 13 nickel_open("[1, 2, 3]") do cfg 14 @test nickel_kind(cfg) == :array 15 end 16 nickel_open("42") do cfg 17 @test nickel_kind(cfg) == :number 18 end 19 nickel_open("\"hello\"") do cfg 20 @test nickel_kind(cfg) == :string 21 end 22 nickel_open("true") do cfg 23 @test nickel_kind(cfg) == :bool 24 end 25 nickel_open("null") do cfg 26 @test nickel_kind(cfg) == :null 27 end 28 end 29 30 @testset "Record field access" begin 31 # getproperty (dot syntax) 32 nickel_open("{ x = 42 }") do cfg 33 @test cfg.x === Int64(42) 34 end 35 36 # getindex (bracket syntax) 37 nickel_open("{ x = 42 }") do cfg 38 @test cfg["x"] === Int64(42) 39 end 40 41 # Nested navigation 42 nickel_open("{ a = { b = { c = 99 } } }") do cfg 43 @test cfg.a.b.c === Int64(99) 44 end 45 46 # Mixed types in record 47 nickel_open("{ name = \"test\", count = 42, flag = true }") do cfg 48 @test cfg.name == "test" 49 @test cfg.count === Int64(42) 50 @test cfg.flag === true 51 end 52 53 # Null field 54 nickel_open("{ x = null }") do cfg 55 @test cfg.x === nothing 56 end 57 end 58 59 @testset "Array access" begin 60 nickel_open("[10, 20, 30]") do cfg 61 @test cfg[1] === Int64(10) 62 @test cfg[2] === Int64(20) 63 @test cfg[3] === Int64(30) 64 end 65 66 # Array of records (lazy) 67 nickel_open("[{ x = 1 }, { x = 2 }]") do cfg 68 @test cfg[1].x === Int64(1) 69 @test cfg[2].x === Int64(2) 70 end 71 72 # Nested: record containing array 73 nickel_open("{ items = [10, 20, 30] }") do cfg 74 @test cfg.items[2] === Int64(20) 75 end 76 end 77 78 @testset "collect" begin 79 # Record 80 nickel_open("{ a = 1, b = \"two\", c = true }") do cfg 81 result = collect(cfg) 82 @test result isa Dict{String, Any} 83 @test result == nickel_eval("{ a = 1, b = \"two\", c = true }") 84 end 85 86 # Nested record 87 nickel_open("{ x = { y = 42 } }") do cfg 88 result = collect(cfg) 89 @test result["x"]["y"] === Int64(42) 90 end 91 92 # Array 93 nickel_open("[1, 2, 3]") do cfg 94 result = collect(cfg) 95 @test result == Any[1, 2, 3] 96 end 97 98 # Collect a sub-tree 99 nickel_open("{ a = 1, b = { c = 2, d = 3 } }") do cfg 100 sub = collect(cfg.b) 101 @test sub == Dict{String, Any}("c" => 2, "d" => 3) 102 end 103 104 # Primitive passthrough 105 nickel_open("42") do cfg 106 @test collect(cfg) === Int64(42) 107 end 108 end 109 110 @testset "keys and length" begin 111 nickel_open("{ a = 1, b = 2, c = 3 }") do cfg 112 k = keys(cfg) 113 @test k isa Vector{String} 114 @test sort(k) == ["a", "b", "c"] 115 @test length(cfg) == 3 116 end 117 118 nickel_open("[10, 20, 30, 40]") do cfg 119 @test length(cfg) == 4 120 end 121 122 # keys on non-record throws 123 nickel_open("[1, 2]") do cfg 124 @test_throws ArgumentError keys(cfg) 125 end 126 end 127 128 @testset "File evaluation" begin 129 mktempdir() do dir 130 # Simple file 131 f = joinpath(dir, "config.ncl") 132 write(f, "{ host = \"localhost\", port = 8080 }") 133 nickel_open(f) do cfg 134 @test cfg.host == "localhost" 135 @test cfg.port === Int64(8080) 136 end 137 138 # File with imports 139 shared = joinpath(dir, "shared.ncl") 140 write(shared, "{ version = \"1.0\" }") 141 main = joinpath(dir, "main.ncl") 142 write(main, """ 143 let s = import "shared.ncl" in 144 { app_version = s.version, name = "myapp" } 145 """) 146 nickel_open(main) do cfg 147 @test cfg.app_version == "1.0" 148 @test cfg.name == "myapp" 149 end 150 end 151 end 152 153 @testset "Iteration" begin 154 # Record iteration yields pairs 155 nickel_open("{ a = 1, b = 2 }") do cfg 156 pairs = Dict(k => v for (k, v) in cfg) 157 @test pairs["a"] === Int64(1) 158 @test pairs["b"] === Int64(2) 159 end 160 161 # Array iteration 162 nickel_open("[10, 20, 30]") do cfg 163 values = [x for x in cfg] 164 @test values == Any[10, 20, 30] 165 end 166 end 167 168 @testset "Error handling" begin 169 # Closed session 170 local stale_ref 171 nickel_open("{ x = 1 }") do cfg 172 stale_ref = cfg 173 end 174 @test_throws ArgumentError stale_ref.x 175 176 # Wrong access type: dot on array 177 nickel_open("[1, 2, 3]") do cfg 178 @test_throws ArgumentError cfg.x 179 end 180 181 # Wrong access type: integer index on record 182 nickel_open("{ x = 1 }") do cfg 183 @test_throws ArgumentError cfg[1] 184 end 185 186 # Out of bounds 187 nickel_open("[1, 2]") do cfg 188 @test_throws BoundsError cfg[3] 189 end 190 191 # Syntax error in code 192 @test_throws NickelError nickel_open("{ x = }") 193 end 194 195 @testset "Enum handling" begin 196 # Bare enum tag at top level: nickel_open returns NickelValue, collect to get NickelEnum 197 nickel_open("let x = 'Foo in x") do cfg 198 @test cfg isa NickelValue 199 @test nickel_kind(cfg) == :enum 200 result = collect(cfg) 201 @test result isa NickelEnum 202 @test result.tag == :Foo 203 end 204 205 # Enum variant at top level 206 nickel_open("let x = 'Some 42 in x") do cfg 207 result = collect(cfg) 208 @test result isa NickelEnum 209 @test result.tag == :Some 210 @test result.arg === Int64(42) 211 end 212 213 # Enum as record field: resolved immediately by field access 214 nickel_open("{ x = 'None, y = 'Some 42 }") do cfg 215 @test cfg.x isa NickelEnum 216 @test cfg.x.tag == :None 217 end 218 219 # Enum in collect 220 nickel_open("{ x = 'None, y = 'Some 42 }") do cfg 221 result = collect(cfg) 222 @test result["x"] isa NickelEnum 223 @test result["x"].tag == :None 224 @test result["y"] isa NickelEnum 225 @test result["y"].tag == :Some 226 end 227 end 228 229 @testset "Manual session" begin 230 cfg = nickel_open("{ x = 42, y = \"hello\" }") 231 @test cfg.x === Int64(42) 232 @test cfg.y == "hello" 233 close(cfg) 234 235 # Double close is safe 236 close(cfg) 237 238 # Access after close throws 239 @test_throws ArgumentError cfg.x 240 end 241 242 @testset "show" begin 243 nickel_open("{ x = 1, y = 2, z = 3 }") do cfg 244 s = repr(cfg) 245 @test occursin("record", s) 246 @test occursin("3", s) 247 end 248 nickel_open("[1, 2]") do cfg 249 s = repr(cfg) 250 @test occursin("array", s) 251 @test occursin("2", s) 252 end 253 end 254 end