Skip to content

Commit f1a4f9b

Browse files
committed
Add new assertion functions: assert_lt, assert_gt, assert_le, assert_ge, assert_starts_with, assert_ends_with, assert_contains
1 parent 7e172c2 commit f1a4f9b

15 files changed

+283
-33
lines changed

lua/assertions.lua

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,54 @@ function assertions.assert_ne(left, right, message)
2525
end
2626
end
2727

28+
function assertions.assert_lt(left, right, message)
29+
if not (left < right) then
30+
if message ~= nil then
31+
message = string.format("assertion `left < right` failed: %s", tostring(message))
32+
else
33+
message = "assertion `left < right` failed!"
34+
end
35+
local frame_level = opts.level or 2
36+
error(string.format("%s\n left: %s\n right: %s", message, tostring(left), tostring(right)), frame_level)
37+
end
38+
end
39+
40+
function assertions.assert_gt(left, right, message)
41+
if not (left > right) then
42+
if message ~= nil then
43+
message = string.format("assertion `left > right` failed: %s", tostring(message))
44+
else
45+
message = "assertion `left > right` failed!"
46+
end
47+
local frame_level = opts.level or 2
48+
error(string.format("%s\n left: %s\n right: %s", message, tostring(left), tostring(right)), frame_level)
49+
end
50+
end
51+
52+
function assertions.assert_le(left, right, message)
53+
if not (left <= right) then
54+
if message ~= nil then
55+
message = string.format("assertion `left <= right` failed: %s", tostring(message))
56+
else
57+
message = "assertion `left <= right` failed!"
58+
end
59+
local frame_level = opts.level or 2
60+
error(string.format("%s\n left: %s\n right: %s", message, tostring(left), tostring(right)), frame_level)
61+
end
62+
end
63+
64+
function assertions.assert_ge(left, right, message)
65+
if not (left >= right) then
66+
if message ~= nil then
67+
message = string.format("assertion `left >= right` failed: %s", tostring(message))
68+
else
69+
message = "assertion `left >= right` failed!"
70+
end
71+
local frame_level = opts.level or 2
72+
error(string.format("%s\n left: %s\n right: %s", message, tostring(left), tostring(right)), frame_level)
73+
end
74+
end
75+
2876
function assertions.assert_match(obj, pattern, message)
2977
if not tostring(obj):match(pattern) then
3078
if message ~= nil then
@@ -102,4 +150,46 @@ function assertions.assert_same(left, right, message)
102150
end
103151
end
104152

153+
function assertions.assert_starts_with(str, prefix, message)
154+
str = tostring(str)
155+
prefix = tostring(prefix)
156+
if str:sub(1, #prefix) ~= prefix then
157+
if message ~= nil then
158+
message = string.format("assertion `str:starts_with(prefix)` failed: %s", tostring(message))
159+
else
160+
message = "assertion `str:starts_with(prefix)` failed!"
161+
end
162+
local frame_level = opts.level or 2
163+
error(string.format("%s\n prefix: %s\n str: %s", message, prefix, str), frame_level)
164+
end
165+
end
166+
167+
function assertions.assert_ends_with(str, suffix, message)
168+
str = tostring(str)
169+
suffix = tostring(suffix)
170+
if suffix ~= "" and str:sub(-#suffix) ~= suffix then
171+
if message ~= nil then
172+
message = string.format("assertion `str:ends_with(suffix)` failed: %s", tostring(message))
173+
else
174+
message = "assertion `str:ends_with(suffix)` failed!"
175+
end
176+
local frame_level = opts.level or 2
177+
error(string.format("%s\n suffix: %s\n str: %s", message, suffix, str), frame_level)
178+
end
179+
end
180+
181+
function assertions.assert_contains(str, substr, message)
182+
str = tostring(str)
183+
substr = tostring(substr)
184+
if not str:find(substr, 1, true) then
185+
if message ~= nil then
186+
message = string.format("assertion `str:contains(substr)` failed: %s", tostring(message))
187+
else
188+
message = "assertion `str:contains(substr)` failed!"
189+
end
190+
local frame_level = opts.level or 2
191+
error(string.format("%s\n substr: %s\n str: %s", message, substr, str), frame_level)
192+
end
193+
end
194+
105195
return assertions

lua/testing.lua

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,22 @@ function TestContext.assert_ne(a, b, msg)
4040
assertions.assert_ne(a, b, msg)
4141
end
4242

43+
function TestContext.assert_lt(a, b, msg)
44+
assertions.assert_lt(a, b, msg)
45+
end
46+
47+
function TestContext.assert_gt(a, b, msg)
48+
assertions.assert_gt(a, b, msg)
49+
end
50+
51+
function TestContext.assert_le(a, b, msg)
52+
assertions.assert_le(a, b, msg)
53+
end
54+
55+
function TestContext.assert_ge(a, b, msg)
56+
assertions.assert_ge(a, b, msg)
57+
end
58+
4359
function TestContext.assert_match(a, b, msg)
4460
assertions.assert_match(a, b, msg)
4561
end
@@ -48,6 +64,18 @@ function TestContext.assert_same(a, b, msg)
4864
assertions.assert_same(a, b, msg)
4965
end
5066

67+
function TestContext.assert_starts_with(a, b, msg)
68+
assertions.assert_starts_with(a, b, msg)
69+
end
70+
71+
function TestContext.assert_ends_with(a, b, msg)
72+
assertions.assert_ends_with(a, b, msg)
73+
end
74+
75+
function TestContext.assert_contains(a, b, msg)
76+
assertions.assert_contains(a, b, msg)
77+
end
78+
5179
function TestContext.assert(cond, msg)
5280
if not cond then
5381
if msg ~= nil then

src/assertions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use mlua::{Lua, Result, Table};
33
/// A loader for the `assertions` module.
44
fn loader(lua: &Lua) -> Result<Table> {
55
lua.load(include_str!("../lua/assertions.lua"))
6-
.set_name("@mlua-stdlib/assertions.lua")
6+
.set_name("@mlua-stdlib/lua/assertions.lua")
77
.call(())
88
}
99

tests/lua/assertions_tests.lua

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@ local assertions = require("@assertions")
22

33
local assert_eq = assertions.assert_eq
44
local assert_ne = assertions.assert_ne
5+
local assert_lt = assertions.assert_lt
6+
local assert_gt = assertions.assert_gt
7+
local assert_le = assertions.assert_le
8+
local assert_ge = assertions.assert_ge
59
local assert_match = assertions.assert_match
610
local assert_same = assertions.assert_same
11+
local assert_starts_with = assertions.assert_starts_with
12+
local assert_ends_with = assertions.assert_ends_with
13+
local assert_contains = assertions.assert_contains
714

815
testing:test("assert_eq", function()
916
assert_eq(1, 1)
@@ -53,6 +60,74 @@ testing:test("assert_ne", function()
5360
assert(err:match(" right: nil"))
5461
end)
5562

63+
testing:test("assert_lt", function()
64+
assert_lt(1, 2)
65+
assert_lt(-1, 0)
66+
67+
local ok, err = pcall(assert_lt, 1, 1)
68+
assert(not ok)
69+
assert(err:match("assertion `left < right` failed!"))
70+
assert(err:match(" left: 1"))
71+
assert(err:match(" right: 1"))
72+
73+
ok, err = pcall(assert_lt, 2, 1, "custom message")
74+
assert(not ok)
75+
assert(err:match("assertion `left < right` failed: custom message"))
76+
assert(err:match(" left: 2"))
77+
assert(err:match(" right: 1"))
78+
end)
79+
80+
testing:test("assert_gt", function()
81+
assert_gt(2, 1)
82+
assert_gt(0, -1)
83+
84+
local ok, err = pcall(assert_gt, 1, 1)
85+
assert(not ok)
86+
assert(err:match("assertion `left > right` failed!"))
87+
assert(err:match(" left: 1"))
88+
assert(err:match(" right: 1"))
89+
90+
ok, err = pcall(assert_gt, 1, 2, "custom message")
91+
assert(not ok)
92+
assert(err:match("assertion `left > right` failed: custom message"))
93+
assert(err:match(" left: 1"))
94+
assert(err:match(" right: 2"))
95+
end)
96+
97+
testing:test("assert_le", function()
98+
assert_le(1, 2)
99+
assert_le(1, 1)
100+
101+
local ok, err = pcall(assert_le, 2, 1)
102+
assert(not ok)
103+
assert(err:match("assertion `left <= right` failed!"))
104+
assert(err:match(" left: 2"))
105+
assert(err:match(" right: 1"))
106+
107+
ok, err = pcall(assert_le, 2, 1, "custom message")
108+
assert(not ok)
109+
assert(err:match("assertion `left <= right` failed: custom message"))
110+
assert(err:match(" left: 2"))
111+
assert(err:match(" right: 1"))
112+
end)
113+
114+
testing:test("assert_ge", function()
115+
assert_ge(2, 1)
116+
assert_ge(1, 1)
117+
118+
local ok, err = pcall(assert_ge, 1, 2)
119+
assert(not ok)
120+
assert(err:match("assertion `left >= right` failed!"))
121+
assert(err:match(" left: 1"))
122+
assert(err:match(" right: 2"))
123+
124+
ok, err = pcall(assert_ge, 1, 2, "custom message")
125+
assert(not ok)
126+
assert(err:match("assertion `left >= right` failed: custom message"))
127+
assert(err:match(" left: 1"))
128+
assert(err:match(" right: 2"))
129+
end)
130+
56131
testing:test("assert_match", function()
57132
assert_match("hello world", "hello")
58133
assert_match("12345", "%d+")
@@ -119,3 +194,55 @@ testing:test("assert_same", function()
119194
assert(err:match(" left%[1%]: table:"))
120195
assert(err:match(" right%[1%]: table:"))
121196
end)
197+
198+
testing:test("assert_starts_with", function()
199+
assert_starts_with("hello world", "hello")
200+
assert_starts_with("hello world", "")
201+
202+
local ok, err = pcall(assert_starts_with, "hello world", "bye")
203+
assert(not ok)
204+
assert(err:match("assertion `str:starts_with%(prefix%)` failed!"))
205+
assert(err:match(" prefix: bye"))
206+
assert(err:match(" str: hello world"))
207+
208+
ok, err = pcall(assert_starts_with, "foo", "bar", "custom message")
209+
assert(not ok)
210+
assert(err:match("assertion `str:starts_with%(prefix%)` failed: custom message"))
211+
assert(err:match(" prefix: bar"))
212+
assert(err:match(" str: foo"))
213+
end)
214+
215+
testing:test("assert_ends_with", function()
216+
assert_ends_with("hello world", "world")
217+
assert_ends_with("hello world", "")
218+
219+
local ok, err = pcall(assert_ends_with, "hello world", "bye")
220+
assert(not ok)
221+
assert(err:match("assertion `str:ends_with%(suffix%)` failed!"))
222+
assert(err:match(" suffix: bye"))
223+
assert(err:match(" str: hello world"))
224+
225+
ok, err = pcall(assert_ends_with, "foo", "bar", "custom message")
226+
assert(not ok)
227+
assert(err:match("assertion `str:ends_with%(suffix%)` failed: custom message"))
228+
assert(err:match(" suffix: bar"))
229+
assert(err:match(" str: foo"))
230+
end)
231+
232+
testing:test("assert_contains", function()
233+
assert_contains("hello world", "world")
234+
assert_contains("hello world", "hello")
235+
assert_contains("hello world", "o w")
236+
237+
local ok, err = pcall(assert_contains, "hello world", "bye")
238+
assert(not ok)
239+
assert(err:match("assertion `str:contains%(substr%)` failed!"))
240+
assert(err:match(" substr: bye"))
241+
assert(err:match(" str: hello world"))
242+
243+
ok, err = pcall(assert_contains, "foo", "bar", "custom message")
244+
assert(not ok)
245+
assert(err:match("assertion `str:contains%(substr%)` failed: custom message"))
246+
assert(err:match(" substr: bar"))
247+
assert(err:match(" str: foo"))
248+
end)

tests/lua/env_tests.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ testing:test("set_current_dir", function(t)
2121
-- Verify the directory changed
2222
local new_dir, err2 = env.current_dir()
2323
t.assert_eq(err2, nil)
24-
t.assert(new_dir ~= original_dir, "directory should have changed")
24+
t.assert_ne(new_dir, original_dir, "directory should have changed")
2525

2626
-- Change back to original directory
2727
local _, err3 = env.set_current_dir(original_dir)
@@ -40,7 +40,7 @@ testing:test("current_exe", function(t)
4040
t.assert(#exe > 0, "current_exe should not be empty")
4141
-- The executable path should be a valid path (contains forward slash on Unix systems)
4242
if env.FAMILY == "unix" then
43-
t.assert(exe:match("/"), "executable should be a full path")
43+
t.assert_contains(exe, "/", "executable should be a full path")
4444
end
4545
end)
4646

tests/lua/http/headers_tests.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,14 @@ testing:test("Headers errors", function(t)
8888
return http.Headers.new(123)
8989
end)
9090
t.assert_eq(ok, false)
91-
t.assert_match(err, "error converting Lua integer to table")
91+
t.assert_contains(err, "error converting Lua integer to table")
9292

9393
-- Invalid header name
9494
ok, err = pcall(function()
9595
headers:set("Invalid Header", "value")
9696
end)
9797
t.assert_eq(ok, false)
98-
t.assert_match(err, "invalid HTTP header name")
98+
t.assert_contains(err, "invalid HTTP header name")
9999

100100
-- Invalid header value
101101
ok, err = pcall(function()

tests/lua/json_tests.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ testing:test("encode", function(t)
88

99
data, err = json.encode({ f = function() end })
1010
t.assert_eq(data, nil)
11-
t.assert(err:find("cannot serialize <function>"), "unexpected error message: " .. err)
11+
t.assert_contains(err, "cannot serialize <function>", "unexpected error message: " .. err)
1212

1313
-- Relaxed mode
1414
data = json.encode({ f = function() end, a = 1 }, { relaxed = true })
@@ -30,7 +30,7 @@ testing:test("decode", function(t)
3030
-- Invalid JSON
3131
value, err = json.decode("{a:1}")
3232
t.assert_eq(value, nil)
33-
t.assert(err:find("key must be a string"), "unexpected error message: " .. err)
33+
t.assert_contains(err, "key must be a string", "unexpected error message: " .. err)
3434

3535
-- No array metatable by default
3636
value, err = json.decode("[1,2,3]", { set_array_metatable = false })

tests/lua/net/tcp_tests.lua

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ testing:test("TCP connect timeout", function(t)
4040
-- The IP address is reserved for documentation and should be non-routable
4141
local stream, err = tcp.connect("203.0.113.95", 1234, { timeout = "100ms" })
4242
t.assert_eq(stream, nil)
43-
t.assert_match(err, "deadline has elapsed")
43+
t.assert_contains(err, "deadline has elapsed")
4444
end)
4545

4646
testing:test("TCP read/write timeout", function(t)
@@ -59,15 +59,16 @@ testing:test("TCP read/write timeout", function(t)
5959
local data, read_err = stream:read(10)
6060
local elapsed = start:elapsed():as_secs()
6161
t.assert_eq(data, nil)
62-
t.assert_match(read_err, "deadline has elapsed")
63-
t.assert(elapsed >= 0.1, "elapsed time should be at least 100ms, got " .. tostring(elapsed))
62+
t.assert_contains(read_err, "deadline has elapsed")
63+
t.assert_ge(elapsed, 0.1, "elapsed time should be at least 100ms")
6464

6565
start = time.Instant.now()
6666
local ok, write_err = stream:write_all(string.rep("abcdef", 100000))
6767
elapsed = start:elapsed():as_secs()
6868
t.assert_eq(ok, nil)
69-
t.assert_match(write_err, "deadline has elapsed")
70-
t.assert(elapsed >= 0.02 and elapsed < 0.03, "elapsed time should be at least 20ms, got " .. tostring(elapsed))
69+
t.assert_contains(write_err, "deadline has elapsed")
70+
t.assert_ge(elapsed, 0.02, "elapsed time should be at least 20ms")
71+
t.assert_lt(elapsed, 0.03, "elapsed time should be less than 30ms")
7172

7273
stream:shutdown()
7374
end)

tests/lua/net/tls_tests.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ testing:test("TLS basic connection to external server", function(t)
2929
t.assert_ne(write_ok, nil, write_err)
3030
local data, read_err = tls_stream:read(1024)
3131
t.assert_ne(data, nil, read_err)
32-
t.assert_match(data, "HTTP/1.1", "Should receive HTTP response")
32+
t.assert_contains(data, "HTTP/1.1", "Should receive HTTP response")
3333

3434
tls_stream:shutdown()
3535
end)

0 commit comments

Comments
 (0)