From 402fe2b7dfa465318d0bd5b451ec994ca6799240 Mon Sep 17 00:00:00 2001 From: kolaente Date: Sun, 7 Feb 2021 20:59:18 +0100 Subject: [PATCH] Add notification to db routing --- pkg/mail/send_mail.go | 5 ++ pkg/mail/testing.go | 48 ++++++++++++++ pkg/migration/20210207192805.go | 3 +- pkg/models/notifications.go | 3 +- pkg/models/task_reminder.go | 3 +- pkg/notifications/main_test.go | 55 ++++++++++++++++ pkg/notifications/notification.go | 44 +++++++++---- pkg/notifications/notification_test.go | 86 ++++++++++++++++++++++++++ 8 files changed, 233 insertions(+), 14 deletions(-) create mode 100644 pkg/mail/testing.go create mode 100644 pkg/notifications/main_test.go create mode 100644 pkg/notifications/notification_test.go diff --git a/pkg/mail/send_mail.go b/pkg/mail/send_mail.go index 8e607282e..b962c3a0f 100644 --- a/pkg/mail/send_mail.go +++ b/pkg/mail/send_mail.go @@ -95,6 +95,11 @@ func sendMail(opts *Opts) *gomail.Message { // SendMail puts a mail in the queue func SendMail(opts *Opts) { + if isUnderTest { + sentMails = append(sentMails, opts) + return + } + m := sendMail(opts) Queue <- m } diff --git a/pkg/mail/testing.go b/pkg/mail/testing.go new file mode 100644 index 000000000..737150b2e --- /dev/null +++ b/pkg/mail/testing.go @@ -0,0 +1,48 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-2021 Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public Licensee as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public Licensee for more details. +// +// You should have received a copy of the GNU Affero General Public Licensee +// along with this program. If not, see . + +package mail + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +var ( + isUnderTest bool + sentMails []*Opts +) + +// Fake stops any mails from being sent and instead allows for recording and querying them. +func Fake() { + isUnderTest = true + sentMails = nil +} + +// AssertSent asserts if a mail has been sent +func AssertSent(t *testing.T, opts *Opts) { + var found bool + for _, testMail := range sentMails { + if reflect.DeepEqual(testMail, opts) { + found = true + break + } + } + + assert.True(t, found, "Failed to assert mail '%v' has been sent.", opts) +} diff --git a/pkg/migration/20210207192805.go b/pkg/migration/20210207192805.go index 30ba87b8e..8acd3ff82 100644 --- a/pkg/migration/20210207192805.go +++ b/pkg/migration/20210207192805.go @@ -17,8 +17,9 @@ package migration import ( - "src.techknowlogick.com/xormigrate" "time" + + "src.techknowlogick.com/xormigrate" "xorm.io/xorm" ) diff --git a/pkg/models/notifications.go b/pkg/models/notifications.go index 0a0ba41fe..af655aaae 100644 --- a/pkg/models/notifications.go +++ b/pkg/models/notifications.go @@ -17,10 +17,11 @@ package models import ( + "strconv" + "code.vikunja.io/api/pkg/config" "code.vikunja.io/api/pkg/notifications" "code.vikunja.io/api/pkg/user" - "strconv" ) // ReminderDueNotification represents a ReminderDueNotification notification diff --git a/pkg/models/task_reminder.go b/pkg/models/task_reminder.go index a6c63b746..4746fc797 100644 --- a/pkg/models/task_reminder.go +++ b/pkg/models/task_reminder.go @@ -17,9 +17,10 @@ package models import ( - "code.vikunja.io/api/pkg/notifications" "time" + "code.vikunja.io/api/pkg/notifications" + "code.vikunja.io/api/pkg/db" "xorm.io/xorm" diff --git a/pkg/notifications/main_test.go b/pkg/notifications/main_test.go new file mode 100644 index 000000000..36050157d --- /dev/null +++ b/pkg/notifications/main_test.go @@ -0,0 +1,55 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-2021 Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public Licensee as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public Licensee for more details. +// +// You should have received a copy of the GNU Affero General Public Licensee +// along with this program. If not, see . + +package notifications + +import ( + "os" + "testing" + + "code.vikunja.io/api/pkg/db" + "code.vikunja.io/api/pkg/log" + "code.vikunja.io/api/pkg/mail" + + "code.vikunja.io/api/pkg/config" +) + +// SetupTests initializes all db tests +func SetupTests() { + var err error + x, err := db.CreateTestEngine() + if err != nil { + log.Fatal(err) + } + + err = x.Sync2(&DatabaseNotification{}) + if err != nil { + log.Fatal(err) + } +} + +// TestMain is the main test function used to bootstrap the test env +func TestMain(m *testing.M) { + // Set default config + config.InitDefaultConfig() + // We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly + config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH")) + + SetupTests() + + mail.Fake() + os.Exit(m.Run()) +} diff --git a/pkg/notifications/notification.go b/pkg/notifications/notification.go index 615b9ab71..da3898c09 100644 --- a/pkg/notifications/notification.go +++ b/pkg/notifications/notification.go @@ -17,8 +17,10 @@ package notifications import ( - "code.vikunja.io/api/pkg/db" + "encoding/json" "time" + + "code.vikunja.io/api/pkg/db" ) // Notification is a notification which can be sent via mail or db. @@ -29,7 +31,7 @@ type Notification interface { // Notifiable is an entity which can be notified. Usually a user. type Notifiable interface { - // Should return the email adress this notifiable has. + // Should return the email address this notifiable has. RouteForMail() string // Should return the id of the notifiable entity RouteForDB() int64 @@ -55,27 +57,47 @@ func (d *DatabaseNotification) TableName() string { } // Notify notifies a notifiable of a notification -func Notify(notifiable Notifiable, notification Notification) error { - mail := notification.ToMail() - if mail != nil { - err := SendMail(mail) - if err != nil { - return err - } +func Notify(notifiable Notifiable, notification Notification) (err error) { + + err = notifyMail(notifiable, notification) + if err != nil { + return } + return notifyDB(notifiable, notification) +} + +func notifyMail(notifiable Notifiable, notification Notification) error { + mail := notification.ToMail() + if mail == nil { + return nil + } + + mail.To(notifiable.RouteForMail()) + + return SendMail(mail) +} + +func notifyDB(notifiable Notifiable, notification Notification) (err error) { + dbContent := notification.ToDB() if dbContent == nil { return nil } + content, err := json.Marshal(dbContent) + if err != nil { + return err + } + s := db.NewSession() dbNotification := &DatabaseNotification{ NotifiableID: notifiable.RouteForDB(), - Notification: dbContent, + Notification: content, } - if _, err := s.Insert(dbNotification); err != nil { + _, err = s.Insert(dbNotification) + if err != nil { _ = s.Rollback() return err } diff --git a/pkg/notifications/notification_test.go b/pkg/notifications/notification_test.go new file mode 100644 index 000000000..f0baa0e8a --- /dev/null +++ b/pkg/notifications/notification_test.go @@ -0,0 +1,86 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-2021 Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public Licensee as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public Licensee for more details. +// +// You should have received a copy of the GNU Affero General Public Licensee +// along with this program. If not, see . + +package notifications + +import ( + "testing" + + "code.vikunja.io/api/pkg/db" + "github.com/stretchr/testify/assert" + "xorm.io/xorm/schemas" +) + +type testNotification struct { + Test string + OtherValue int64 +} + +// ToMail returns the mail notification for testNotification +func (n *testNotification) ToMail() *Mail { + return NewMail(). + Subject("Test Notification"). + Line(n.Test) +} + +// ToDB returns the testNotification notification in a format which can be saved in the db +func (n *testNotification) ToDB() interface{} { + data := make(map[string]interface{}, 2) + data["test"] = n.Test + data["other_value"] = n.OtherValue + return data +} + +type testNotifiable struct { +} + +// RouteForMail routes a test notification for mail +func (t *testNotifiable) RouteForMail() string { + return "some@email.com" +} + +// RouteForDB routes a test notification for db +func (t *testNotifiable) RouteForDB() int64 { + return 42 +} + +func TestNotify(t *testing.T) { + tn := &testNotification{ + Test: "somethingsomething", + OtherValue: 42, + } + tnf := &testNotifiable{} + + err := Notify(tnf, tn) + + assert.NoError(t, err) + vals := map[string]interface{}{ + "notifiable_id": 42, + "notification": "'{\"other_value\":42,\"test\":\"somethingsomething\"}'", + } + + if db.Type() == schemas.POSTGRES { + vals["notification::jsonb"] = vals["notification"].(string) + "::jsonb" + delete(vals, "notification") + } + + if db.Type() == schemas.SQLITE { + vals["CAST(notification AS BLOB)"] = "CAST(" + vals["notification"].(string) + " AS BLOB)" + delete(vals, "notification") + } + + db.AssertExists(t, "notifications", vals, true) +}