-- SPDX-FileCopyrightText: © 2026 Vladimir Zorin -- SPDX-License-Identifier: OWL-1.0 or later local testimony = require("testimony") local std = require("std") local mneme = require("mneme") local testify = testimony.new("== mneme: type system ==") local tmp_path = function() return "/tmp/mneme_types_" .. std.nanoid() .. ".mneme" end testify:that("auto-detect: string value stored as Bytes", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:set("k", "hello") local val = ks:get("k") testimony.assert_equal("hello", val) testimony.assert_equal("string", type(val)) db:close() std.fs.remove(path) end) testify:that("auto-detect: integer number stored as Integer", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:set("k", 42) local val = ks:get("k") testimony.assert_equal(42, val) testimony.assert_equal("number", type(val)) db:close() std.fs.remove(path) end) testify:that("auto-detect: float number stored as Float", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:set("k", 3.14) local val = ks:get("k") -- Float precision: should be close to 3.14 testimony.assert_true(math.abs(val - 3.14) < 1e-10) db:close() std.fs.remove(path) end) testify:that("explicit set_integer", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:set_integer("counter", 0) testimony.assert_equal(0, ks:get("counter")) ks:set_integer("big", 2 ^ 50) testimony.assert_equal(2 ^ 50, ks:get("big")) db:close() std.fs.remove(path) end) testify:that("explicit set_float", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:set_float("temp", 36.6) local val = ks:get("temp") testimony.assert_true(math.abs(val - 36.6) < 1e-10) db:close() std.fs.remove(path) end) testify:that("explicit set_bytes", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:set_bytes("bin", "\x00\x01\x02\xff") local val = ks:get("bin") testimony.assert_equal(4, #val) testimony.assert_equal("\x00\x01\x02\xff", val) db:close() std.fs.remove(path) end) testify:that("incr on non-existent key creates at 0 then increments", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") local val = ks:incr("counter") testimony.assert_equal(1, val) val = ks:incr("counter") testimony.assert_equal(2, val) db:close() std.fs.remove(path) end) testify:that("incr with delta", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") local val = ks:incr("c", 10) testimony.assert_equal(10, val) val = ks:incr("c", 5) testimony.assert_equal(15, val) db:close() std.fs.remove(path) end) testify:that("decr decrements", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:incr("c", 20) local val = ks:decr("c", 5) testimony.assert_equal(15, val) val = ks:decr("c") testimony.assert_equal(14, val) db:close() std.fs.remove(path) end) testify:that("incr on Bytes key returns type mismatch", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:set("str", "hello") local val, err = ks:incr("str") testimony.assert_nil(val) testimony.assert_equal("type mismatch", err) db:close() std.fs.remove(path) end) testify:that("negative integers round-trip", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:set("neg", -12345) testimony.assert_equal(-12345, ks:get("neg")) ks:set("zero", 0) testimony.assert_equal(0, ks:get("zero")) db:close() std.fs.remove(path) end) testify:that("large integers round-trip", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") local big = 2 ^ 52 ks:set("big", big) testimony.assert_equal(big, ks:get("big")) local neg_big = -(2 ^ 52) ks:set("neg_big", neg_big) testimony.assert_equal(neg_big, ks:get("neg_big")) db:close() std.fs.remove(path) end) testify:that("batch groups operations in one commit", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:batch(function(b) b:set("a", "1") b:set("b", "2") b:set("c", "3") b:set_integer("count", 42) end) testimony.assert_equal("1", ks:get("a")) testimony.assert_equal("2", ks:get("b")) testimony.assert_equal("3", ks:get("c")) testimony.assert_equal(42, ks:get("count")) db:close() std.fs.remove(path) end) testify:that("batch error rolls back all operations", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:set("existing", "before") local ok, err = ks:batch(function(b) b:set("new_key", "value") error("deliberate error") end) testimony.assert_nil(ok) -- existing key unchanged, new key not created testimony.assert_equal("before", ks:get("existing")) testimony.assert_nil(ks:get("new_key")) db:close() std.fs.remove(path) end) testify:that("batch incr works atomically", function() local path = tmp_path() local db = mneme.open(path, { sync = "none" }) local ks = db:keyspace("t") ks:batch(function(b) b:incr("x", 5) b:incr("x", 3) b:incr("x", 2) end) testimony.assert_equal(10, ks:get("x")) db:close() std.fs.remove(path) end) testify:conclude()