diff --git a/pkg/extension/luahost/lua.go b/pkg/extension/luahost/lua.go index eb74e7b..121a7c3 100644 --- a/pkg/extension/luahost/lua.go +++ b/pkg/extension/luahost/lua.go @@ -88,6 +88,7 @@ func (h *Host) CreateChannel(name string) chan lua.LValue { return h.pool.createChannel(name) } +const afterMessageDeletedFnName string = "after_message_deleted" const afterMessageStoredFnName string = "after_message_stored" const beforeMailAcceptedFnName string = "before_mail_accepted" @@ -113,6 +114,9 @@ func (h *Host) wireFunctions(logger zerolog.Logger, ls *lua.LState) { events := h.extHost.Events const listenerName string = "lua" + if detectFn(afterMessageDeletedFnName) { + events.AfterMessageDeleted.AddListener(listenerName, h.handleAfterMessageDeleted) + } if detectFn(afterMessageStoredFnName) { events.AfterMessageStored.AddListener(listenerName, h.handleAfterMessageStored) } @@ -121,6 +125,25 @@ func (h *Host) wireFunctions(logger zerolog.Logger, ls *lua.LState) { } } +func (h *Host) handleAfterMessageDeleted(msg event.MessageMetadata) *extension.Void { + logger, ls, lfunc, ok := h.prepareFuncCall(afterMessageDeletedFnName) + if !ok { + return nil + } + defer h.pool.putState(ls) + + // Call lua function. + logger.Debug().Msgf("Calling Lua function with %+v", msg) + if err := ls.CallByParam( + lua.P{Fn: lfunc, NRet: 0, Protect: true}, + wrapMessageMetadata(ls, &msg), + ); err != nil { + logger.Error().Err(err).Msg("Failed to call Lua function") + } + + return nil +} + func (h *Host) handleAfterMessageStored(msg event.MessageMetadata) *extension.Void { logger, ls, lfunc, ok := h.prepareFuncCall(afterMessageStoredFnName) if !ok { diff --git a/pkg/extension/luahost/lua_test.go b/pkg/extension/luahost/lua_test.go index b0de09d..953c17f 100644 --- a/pkg/extension/luahost/lua_test.go +++ b/pkg/extension/luahost/lua_test.go @@ -13,6 +13,45 @@ import ( lua "github.com/yuin/gopher-lua" ) +// LuaInit holds useful test globals. +const LuaInit = ` + async = false + test_ok = true + + -- Sends marks tests failed instead of erroring when enabled. + function assert_async(value, message) + if not value then + if async then + print(message) + test_ok = false + else + error(message) + end + end + end + + -- Tests 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 + + function assert_contains(got, want) + assert_async(string.find(got, want), + string.format("got %q, wanted it to contain %q", got, want)) + end +` + func TestEmptyScript(t *testing.T) { script := "" extHost := extension.NewHost() @@ -21,39 +60,51 @@ func TestEmptyScript(t *testing.T) { require.NoError(t, err) } -func TestAfterMessageStored(t *testing.T) { +func TestAfterMessageDeleted(t *testing.T) { // Register lua event listener, setup notify channel. script := ` - local test_ok = true + async = true - function assert_eq(got, want) - if got ~= want then - -- Incorrect value, schedule test to fail. - print("got '" .. got .. "', wanted '" .. want .. "'") - test_ok = false - end - end - - function after_message_stored(msg) + function after_message_deleted(msg) + -- Full message bindings tested elsewhere. assert_eq(msg.mailbox, "mb1") assert_eq(msg.id, "id1") - assert_eq(msg.subject, "subj1") - assert_eq(msg.size, 42) - - assert_eq(msg.from.name, "name1") - assert_eq(msg.from.address, "addr1") - - assert_eq(table.getn(msg.to), 1) - assert_eq(msg.to[1].name, "name2") - assert_eq(msg.to[1].address, "addr2") - - assert_eq(msg.date, 981173106) - notify:send(test_ok) end ` extHost := extension.NewHost() - luaHost, err := luahost.NewFromReader(extHost, strings.NewReader(script), "test.lua") + luaHost, err := luahost.NewFromReader(extHost, strings.NewReader(LuaInit+script), "test.lua") + require.NoError(t, err) + notify := luaHost.CreateChannel("notify") + + // Send event, check channel response is true. + msg := &event.MessageMetadata{ + Mailbox: "mb1", + ID: "id1", + From: &mail.Address{Name: "name1", Address: "addr1"}, + To: []*mail.Address{{Name: "name2", Address: "addr2"}}, + Date: time.Date(2001, time.February, 3, 4, 5, 6, 0, time.UTC), + Subject: "subj1", + Size: 42, + } + go extHost.Events.AfterMessageDeleted.Emit(msg) + assertNotified(t, notify) +} + +func TestAfterMessageStored(t *testing.T) { + // Register lua event listener, setup notify channel. + script := ` + async = true + + function after_message_stored(msg) + -- Full message bindings tested elsewhere. + assert_eq(msg.mailbox, "mb1") + assert_eq(msg.id, "id1") + notify:send(test_ok) + end + ` + extHost := extension.NewHost() + luaHost, err := luahost.NewFromReader(extHost, strings.NewReader(LuaInit+script), "test.lua") require.NoError(t, err) notify := luaHost.CreateChannel("notify")