From 5284171dc5d6564f8d073ab8a87c9606c1352fe5 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sun, 29 Sep 2024 11:46:01 -0700 Subject: [PATCH] chore: create Lua test helper (#532) * Create lua test helper Signed-off-by: James Hillyerd * Assert labels Signed-off-by: James Hillyerd --------- Signed-off-by: James Hillyerd --- pkg/extension/luahost/bind_address_test.go | 10 ++- .../luahost/bind_inboundmessage_test.go | 42 +++------ pkg/extension/luahost/bind_inbucket_test.go | 6 +- pkg/extension/luahost/bind_message_test.go | 22 ++--- pkg/extension/luahost/lua_test.go | 82 ++++------------- pkg/test/lua.go | 90 +++++++++++++++++++ 6 files changed, 135 insertions(+), 117 deletions(-) create mode 100644 pkg/test/lua.go diff --git a/pkg/extension/luahost/bind_address_test.go b/pkg/extension/luahost/bind_address_test.go index a7ca5e6..69326ac 100644 --- a/pkg/extension/luahost/bind_address_test.go +++ b/pkg/extension/luahost/bind_address_test.go @@ -4,9 +4,9 @@ import ( "net/mail" "testing" + "github.com/inbucket/inbucket/v3/pkg/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - lua "github.com/yuin/gopher-lua" ) func TestMailAddressGetters(t *testing.T) { @@ -26,8 +26,9 @@ func TestMailAddressGetters(t *testing.T) { assert(got == want, string.format("got address %q, want %q", got, want)) ` - ls := lua.NewState() + ls, _ := test.NewLuaState() registerMailAddressType(ls) + ls.SetGlobal("addr", wrapMailAddress(ls, want)) require.NoError(t, ls.DoString(script)) } @@ -44,9 +45,10 @@ func TestMailAddressSetters(t *testing.T) { addr.address = "ri@example.com" ` - got := &mail.Address{} - ls := lua.NewState() + ls, _ := test.NewLuaState() registerMailAddressType(ls) + + got := &mail.Address{} ls.SetGlobal("addr", wrapMailAddress(ls, got)) require.NoError(t, ls.DoString(script)) diff --git a/pkg/extension/luahost/bind_inboundmessage_test.go b/pkg/extension/luahost/bind_inboundmessage_test.go index 657577c..e63f8df 100644 --- a/pkg/extension/luahost/bind_inboundmessage_test.go +++ b/pkg/extension/luahost/bind_inboundmessage_test.go @@ -5,29 +5,11 @@ import ( "testing" "github.com/inbucket/inbucket/v3/pkg/extension/event" + "github.com/inbucket/inbucket/v3/pkg/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - lua "github.com/yuin/gopher-lua" ) -// LuaInit holds useful test globals. -const LuaInit = ` - function assert_eq(got, want) - if type(got) == "table" and type(want) == "table" then - assert(#got == #want, string.format("got %d element(s), wanted %d", #got, #want)) - - for i, gotv in ipairs(got) do - local wantv = want[i] - assert_eq(gotv, wantv, "got[%d] = %q, wanted %q", gotv, wantv) - end - - return - end - - assert(got == want, string.format("got %q, wanted %q", got, want)) - end -` - func TestInboundMessageGetters(t *testing.T) { want := &event.InboundMessage{ Mailboxes: []string{"mb1", "mb2"}, @@ -44,23 +26,23 @@ func TestInboundMessageGetters(t *testing.T) { assert_eq(msg.mailboxes, {"mb1", "mb2"}) assert_eq(msg.subject, "subj1") - assert_eq(msg.size, 42) + assert_eq(msg.size, 42, "msg.size") - assert_eq(msg.from.name, "name1") - assert_eq(msg.from.address, "addr1") + assert_eq(msg.from.name, "name1", "from.name") + assert_eq(msg.from.address, "addr1", "from.address") - assert_eq(#msg.to, 2) - assert_eq(msg.to[1].name, "name2") - assert_eq(msg.to[1].address, "addr2") - assert_eq(msg.to[2].name, "name3") - assert_eq(msg.to[2].address, "addr3") + assert_eq(#msg.to, 2, "#msg.to") + assert_eq(msg.to[1].name, "name2", "to[1].name") + assert_eq(msg.to[1].address, "addr2", "to[1].address") + assert_eq(msg.to[2].name, "name3", "to[2].name") + assert_eq(msg.to[2].address, "addr3", "to[2].address") ` - ls := lua.NewState() + ls, _ := test.NewLuaState() registerInboundMessageType(ls) registerMailAddressType(ls) ls.SetGlobal("msg", wrapInboundMessage(ls, want)) - require.NoError(t, ls.DoString(LuaInit+script)) + require.NoError(t, ls.DoString(script)) } func TestInboundMessageSetters(t *testing.T) { @@ -83,7 +65,7 @@ func TestInboundMessageSetters(t *testing.T) { ` got := &event.InboundMessage{} - ls := lua.NewState() + ls, _ := test.NewLuaState() registerInboundMessageType(ls) registerMailAddressType(ls) ls.SetGlobal("msg", wrapInboundMessage(ls, got)) diff --git a/pkg/extension/luahost/bind_inbucket_test.go b/pkg/extension/luahost/bind_inbucket_test.go index ac391a5..5214034 100644 --- a/pkg/extension/luahost/bind_inbucket_test.go +++ b/pkg/extension/luahost/bind_inbucket_test.go @@ -3,8 +3,8 @@ package luahost import ( "testing" + "github.com/inbucket/inbucket/v3/pkg/test" "github.com/stretchr/testify/require" - lua "github.com/yuin/gopher-lua" ) func TestInbucketAfterFuncs(t *testing.T) { @@ -49,7 +49,7 @@ func TestInbucketAfterFuncs(t *testing.T) { end ` - ls := lua.NewState() + ls, _ := test.NewLuaState() registerInbucketTypes(ls) require.NoError(t, ls.DoString(script)) } @@ -96,7 +96,7 @@ func TestInbucketBeforeFuncs(t *testing.T) { end ` - ls := lua.NewState() + ls, _ := test.NewLuaState() registerInbucketTypes(ls) require.NoError(t, ls.DoString(script)) } diff --git a/pkg/extension/luahost/bind_message_test.go b/pkg/extension/luahost/bind_message_test.go index dd6d584..011d8fb 100644 --- a/pkg/extension/luahost/bind_message_test.go +++ b/pkg/extension/luahost/bind_message_test.go @@ -6,9 +6,9 @@ import ( "time" "github.com/inbucket/inbucket/v3/pkg/extension/event" + "github.com/inbucket/inbucket/v3/pkg/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - lua "github.com/yuin/gopher-lua" ) func TestMessageMetadataGetters(t *testing.T) { @@ -24,26 +24,22 @@ func TestMessageMetadataGetters(t *testing.T) { script := ` assert(msg, "msg should not be nil") - function assert_eq(got, want) - assert(got == want, string.format("got name %q, wanted %q", got, want)) - end - assert_eq(msg.mailbox, "mb1") assert_eq(msg.id, "id1") assert_eq(msg.subject, "subj1") - assert_eq(msg.size, 42) + assert_eq(msg.size, 42, "msg.size") - assert_eq(msg.from.name, "name1") - assert_eq(msg.from.address, "addr1") + assert_eq(msg.from.name, "name1", "from.name") + assert_eq(msg.from.address, "addr1", "from.address") assert_eq(table.getn(msg.to), 1) - assert_eq(msg.to[1].name, "name2") - assert_eq(msg.to[1].address, "addr2") + assert_eq(msg.to[1].name, "name2", "to.name") + assert_eq(msg.to[1].address, "addr2", "to.address") - assert_eq(msg.date, 981173106) + assert_eq(msg.date, 981173106, "msg.date") ` - ls := lua.NewState() + ls, _ := test.NewLuaState() registerMessageMetadataType(ls) registerMailAddressType(ls) ls.SetGlobal("msg", wrapMessageMetadata(ls, want)) @@ -75,7 +71,7 @@ func TestMessageMetadataSetters(t *testing.T) { ` got := &event.MessageMetadata{} - ls := lua.NewState() + ls, _ := test.NewLuaState() registerMessageMetadataType(ls) registerMailAddressType(ls) ls.SetGlobal("msg", wrapMessageMetadata(ls, got)) diff --git a/pkg/extension/luahost/lua_test.go b/pkg/extension/luahost/lua_test.go index dc16937..dd626b3 100644 --- a/pkg/extension/luahost/lua_test.go +++ b/pkg/extension/luahost/lua_test.go @@ -9,54 +9,12 @@ import ( "github.com/inbucket/inbucket/v3/pkg/extension" "github.com/inbucket/inbucket/v3/pkg/extension/event" "github.com/inbucket/inbucket/v3/pkg/extension/luahost" + "github.com/inbucket/inbucket/v3/pkg/test" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - lua "github.com/yuin/gopher-lua" ) -// LuaInit holds useful test globals. -const LuaInit = ` - local logger = require("logger") - - async = false - test_ok = true - - -- Sends marks tests as failed instead of erroring when enabled. - function assert_async(value, message) - if not value then - if async then - logger.error(message, {from = "assert_async"}) - test_ok = false - else - error(message) - end - end - end - - -- Verifies plain values and list-style tables. - function assert_eq(got, want) - if type(got) == "table" and type(want) == "table" then - assert_async(#got == #want, string.format("got %d elements, wanted %d", #got, #want)) - - for i, gotv in ipairs(got) do - local wantv = want[i] - assert_eq(gotv, wantv, "got[%d] = %q, wanted %q", gotv, wantv) - end - - return - end - - assert_async(got == want, string.format("got %q, wanted %q", got, want)) - end - - -- Verifies string want contains string got. - function assert_contains(got, want) - assert_async(string.find(got, want), - string.format("got %q, wanted it to contain %q", got, want)) - end -` - var consoleLogger = zerolog.New(zerolog.NewConsoleWriter()) func TestEmptyScript(t *testing.T) { @@ -92,11 +50,12 @@ func TestAfterMessageDeleted(t *testing.T) { -- Full message bindings tested elsewhere. assert_eq(msg.mailbox, "mb1") assert_eq(msg.id, "id1") - notify:send(test_ok) + notify:send(asserts_ok) end ` extHost := extension.NewHost() - luaHost, err := luahost.NewFromReader(consoleLogger, extHost, strings.NewReader(LuaInit+script), "test.lua") + luaHost, err := luahost.NewFromReader(consoleLogger, extHost, + strings.NewReader(test.LuaInit+script), "test.lua") require.NoError(t, err) notify := luaHost.CreateChannel("notify") @@ -111,7 +70,7 @@ func TestAfterMessageDeleted(t *testing.T) { Size: 42, } extHost.Events.AfterMessageDeleted.Emit(msg) - assertNotified(t, notify) + test.AssertNotified(t, notify) } func TestAfterMessageStored(t *testing.T) { @@ -123,11 +82,12 @@ func TestAfterMessageStored(t *testing.T) { -- Full message bindings tested elsewhere. assert_eq(msg.mailbox, "mb1") assert_eq(msg.id, "id1") - notify:send(test_ok) + notify:send(asserts_ok) end ` extHost := extension.NewHost() - luaHost, err := luahost.NewFromReader(consoleLogger, extHost, strings.NewReader(LuaInit+script), "test.lua") + luaHost, err := luahost.NewFromReader(consoleLogger, extHost, + strings.NewReader(test.LuaInit+script), "test.lua") require.NoError(t, err) notify := luaHost.CreateChannel("notify") @@ -142,7 +102,7 @@ func TestAfterMessageStored(t *testing.T) { Size: 42, } extHost.Events.AfterMessageStored.Emit(msg) - assertNotified(t, notify) + test.AssertNotified(t, notify) } func TestBeforeMailAccepted(t *testing.T) { @@ -197,14 +157,14 @@ func TestBeforeMessageStored(t *testing.T) { assert_eq(msg.mailboxes, {"one", "two"}) assert_eq(msg.from.name, "From Name") assert_eq(msg.from.address, "from@example.com") - assert_eq(2, #msg.to) + assert_eq(2, #msg.to, "#msg.to") assert_eq(msg.to[1].name, "To1 Name") assert_eq(msg.to[1].address, "to1@example.com") assert_eq(msg.to[2].name, "To2 Name") assert_eq(msg.to[2].address, "to2@example.com") assert_eq(msg.subject, "inbound subj") - assert_eq(msg.size, 42) - notify:send(test_ok) + assert_eq(msg.size, 42, "msg.size") + notify:send(asserts_ok) -- Generate response. res = inbound_message.new() @@ -219,7 +179,8 @@ func TestBeforeMessageStored(t *testing.T) { end ` extHost := extension.NewHost() - luaHost, err := luahost.NewFromReader(consoleLogger, extHost, strings.NewReader(LuaInit+script), "test.lua") + luaHost, err := luahost.NewFromReader(consoleLogger, extHost, + strings.NewReader(test.LuaInit+script), "test.lua") require.NoError(t, err) notify := luaHost.CreateChannel("notify") @@ -228,7 +189,7 @@ func TestBeforeMessageStored(t *testing.T) { require.NotNil(t, got, "Expected result from Emit()") // Verify Lua assertions passed. - assertNotified(t, notify) + test.AssertNotified(t, notify) // Verify response values. want := &event.InboundMessage{ @@ -243,16 +204,3 @@ func TestBeforeMessageStored(t *testing.T) { } assert.Equal(t, want, got, "Response InboundMessage did not match") } - -func assertNotified(t *testing.T, notify chan lua.LValue) { - t.Helper() - select { - case reslv := <-notify: - // Lua function received event. - if lua.LVIsFalse(reslv) { - t.Error("Lua responsed with false, wanted true") - } - case <-time.After(2 * time.Second): - t.Fatal("Lua did not respond to event within timeout") - } -} diff --git a/pkg/test/lua.go b/pkg/test/lua.go new file mode 100644 index 0000000..45ae62b --- /dev/null +++ b/pkg/test/lua.go @@ -0,0 +1,90 @@ +package test + +import ( + "strings" + "testing" + "time" + + "github.com/cosmotek/loguago" + "github.com/rs/zerolog" + lua "github.com/yuin/gopher-lua" +) + +// LuaInit holds useful test globals. +const LuaInit = ` + local logger = require("logger") + + async = false + asserts_ok = true + + -- With async: marks tests as failed via asserts_ok, logs error. + -- Without async: erroring when tests fail. + function assert_async(value, message, label) + if not value then + if label then + message = string.format("%s for %s", message, label) + end + + if async then + logger.error(message, {from = "assert_async"}) + asserts_ok = false + else + error(message) + end + end + end + + -- Verifies plain values and list-style tables. + function assert_eq(got, want, label) + if type(got) == "table" and type(want) == "table" then + assert_async(#got == #want, + string.format("got %d elements, wanted %d", #got, #want), label) + + for i, gotv in ipairs(got) do + local wantv = want[i] + assert_eq(gotv, wantv, + string.format("got[%d] = %q, wanted %q", gotv, wantv), label) + end + + return + end + + assert_async(got == want, string.format("got %q, wanted %q", got, want), label) + end + + -- Verifies string want contains string got. + function assert_contains(got, want, label) + assert_async(string.find(got, want), + string.format("got %q, wanted it to contain %q", got, want), label) + end +` + +// NewLuaState creates a new Lua LState initialized with logging and the test helpers in `LuaInit`. +// +// Returns a pointer to the created LState and a string builder to hold the log output. +func NewLuaState() (*lua.LState, *strings.Builder) { + output := &strings.Builder{} + logger := loguago.NewLogger(zerolog.New(output)) + + ls := lua.NewState() + ls.PreloadModule("logger", logger.Loader) + if err := ls.DoString(LuaInit); err != nil { + panic(err) + } + + return ls, output +} + +// AssertNotified requires a truthy LValue on the notify channel. +func AssertNotified(t *testing.T, notify chan lua.LValue) { + t.Helper() + select { + case reslv := <-notify: + // Lua function received event. + if lua.LVIsFalse(reslv) { + t.Error("Lua responsed with false, wanted true") + } + case <-time.After(2 * time.Second): + t.Fatal("Lua did not respond to event within timeout") + } +}