From 584a9e983fb8ddebc8eed49d2f28794d4198d53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Fri, 20 Sep 2024 16:33:45 +0200 Subject: [PATCH] Add tests [TestListSenderNames] [TestListUserSenderNames] --- scnserver/TODO.md | 2 +- scnserver/api/handler/apiSenderNames.go | 23 +++++- scnserver/db/impl/primary/senderNames.go | 26 ++++++ scnserver/models/senderNames.go | 8 ++ scnserver/test/response_test.go | 8 +- scnserver/test/sendername_test.go | 100 ++++++++++++++++++++++- scnserver/test/util/factory.go | 3 +- 7 files changed, 157 insertions(+), 13 deletions(-) create mode 100644 scnserver/db/impl/primary/senderNames.go create mode 100644 scnserver/models/senderNames.go diff --git a/scnserver/TODO.md b/scnserver/TODO.md index e5ecaf7..c281a65 100644 --- a/scnserver/TODO.md +++ b/scnserver/TODO.md @@ -12,7 +12,7 @@ - exerr.New | exerr.Wrap - - (!!!) Run tests in pipeline !! + - Fork go-sqlite with always-on fts5 #### UNSURE diff --git a/scnserver/api/handler/apiSenderNames.go b/scnserver/api/handler/apiSenderNames.go index 1213b6a..5c54c8d 100644 --- a/scnserver/api/handler/apiSenderNames.go +++ b/scnserver/api/handler/apiSenderNames.go @@ -1,9 +1,12 @@ package handler import ( + "blackforestbytes.com/simplecloudnotifier/api/apierr" + "blackforestbytes.com/simplecloudnotifier/api/ginresp" "blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/models" "gogs.mikescher.com/BlackForestBytes/goext/ginext" + "net/http" ) // ListUserSenderNames swaggerdoc @@ -26,6 +29,7 @@ func (h APIHandler) ListUserSenderNames(pctx ginext.PreContext) ginext.HTTPRespo UserID models.UserID `uri:"uid" binding:"entityid"` } type response struct { + SenderNames []models.SenderNameStatistics `json:"sender_names"` } var u uri @@ -41,7 +45,12 @@ func (h APIHandler) ListUserSenderNames(pctx ginext.PreContext) ginext.HTTPRespo return *permResp } - return nil //TODO + names, err := h.database.ListSenderNames(ctx, u.UserID, false) + if err != nil { + return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query messages", err) + } + + return finishSuccess(ginext.JSON(http.StatusOK, response{SenderNames: names})) }) } @@ -61,6 +70,7 @@ func (h APIHandler) ListUserSenderNames(pctx ginext.PreContext) ginext.HTTPRespo // @Router /api/v2/sender-names [GET] func (h APIHandler) ListSenderNames(pctx ginext.PreContext) ginext.HTTPResponse { type response struct { + SenderNames []models.SenderNameStatistics `json:"sender_names"` } ctx, g, errResp := pctx.Start() @@ -75,13 +85,18 @@ func (h APIHandler) ListSenderNames(pctx ginext.PreContext) ginext.HTTPResponse return *permResp } - userid := *ctx.GetPermissionUserID() + userID := *ctx.GetPermissionUserID() - if permResp := ctx.CheckPermissionUserRead(userid); permResp != nil { + if permResp := ctx.CheckPermissionUserRead(userID); permResp != nil { return *permResp } - return nil //TODO + names, err := h.database.ListSenderNames(ctx, userID, true) + if err != nil { + return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query messages", err) + } + + return finishSuccess(ginext.JSON(http.StatusOK, response{SenderNames: names})) }) } diff --git a/scnserver/db/impl/primary/senderNames.go b/scnserver/db/impl/primary/senderNames.go new file mode 100644 index 0000000..a873408 --- /dev/null +++ b/scnserver/db/impl/primary/senderNames.go @@ -0,0 +1,26 @@ +package primary + +import ( + "blackforestbytes.com/simplecloudnotifier/db" + "blackforestbytes.com/simplecloudnotifier/models" + "gogs.mikescher.com/BlackForestBytes/goext/sq" +) + +func (db *Database) ListSenderNames(ctx db.TxContext, userid models.UserID, includeForeignSubscribed bool) ([]models.SenderNameStatistics, error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + var sqlStr string + + prepParams := sq.PP{"uid": userid} + + if includeForeignSubscribed { + sqlStr = "SELECT sender_name AS name, MAX(timestamp_real) AS ts_last, MIN(timestamp_real) AS ts_first, COUNT(*) AS count FROM messages LEFT JOIN subscriptions AS subs on messages.channel_id = subs.channel_id WHERE (subs.subscriber_user_id = :uid AND subs.confirmed = 1) AND sender_NAME NOT NULL GROUP BY sender_name ORDER BY ts_last DESC" + } else { + sqlStr = "SELECT sender_name AS name, MAX(timestamp_real) AS ts_last, MIN(timestamp_real) AS ts_first, COUNT(*) AS count FROM messages WHERE sender_user_id = :uid AND sender_NAME NOT NULL GROUP BY sender_name ORDER BY ts_last DESC" + } + + return sq.QueryAll[models.SenderNameStatistics](ctx, tx, sqlStr, prepParams, sq.SModeExtended, sq.Safe) +} diff --git a/scnserver/models/senderNames.go b/scnserver/models/senderNames.go new file mode 100644 index 0000000..207a23b --- /dev/null +++ b/scnserver/models/senderNames.go @@ -0,0 +1,8 @@ +package models + +type SenderNameStatistics struct { + SenderName string `json:"name" db:"name"` + LastTimestamp SCNTime `json:"last_timestamp" db:"ts_last"` + FirstTimestamp SCNTime `json:"first_timestamp" db:"ts_first"` + Count int `json:"count" db:"count"` +} diff --git a/scnserver/test/response_test.go b/scnserver/test/response_test.go index 135a6ee..a37b45c 100644 --- a/scnserver/test/response_test.go +++ b/scnserver/test/response_test.go @@ -13,7 +13,7 @@ func TestResponseChannel(t *testing.T) { data := tt.InitDefaultData(t, ws) - response := tt.RequestAuthGetRaw(t, data.User[0].AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s", data.User[0].UID, data.User[0].Channels[0])) + response := tt.RequestAuthGetRaw(t, data.User[0].AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s", data.User[0].UID, data.User[0].Channels[0].ChannelID)) tt.AssertJsonStructureMatch(t, "json[channel]", response, map[string]any{ "channel_id": "id", @@ -63,7 +63,7 @@ func TestResponseKeyToken1(t *testing.T) { data := tt.InitDefaultData(t, ws) - response := tt.RequestAuthGetRaw(t, data.User[0].AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/keys/%s", data.User[0].UID, data.User[0].Keys[0])) + response := tt.RequestAuthGetRaw(t, data.User[0].AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/keys/%s", data.User[0].UID, data.User[0].Keys[0].KeyID)) tt.AssertJsonStructureMatch(t, "json[key]", response, map[string]any{ "keytoken_id": "id", @@ -246,7 +246,7 @@ func TestResponseChannelPreview(t *testing.T) { data := tt.InitDefaultData(t, ws) - response := tt.RequestAuthGetRaw(t, data.User[1].AdminKey, baseUrl, fmt.Sprintf("/api/v2/preview/channels/%s", data.User[0].Channels[0])) + response := tt.RequestAuthGetRaw(t, data.User[1].AdminKey, baseUrl, fmt.Sprintf("/api/v2/preview/channels/%s", data.User[0].Channels[0].ChannelID)) tt.AssertJsonStructureMatch(t, "json[channel]", response, map[string]any{ "channel_id": "id", @@ -277,7 +277,7 @@ func TestResponseKeyTokenPreview(t *testing.T) { data := tt.InitDefaultData(t, ws) - response := tt.RequestAuthGetRaw(t, data.User[1].AdminKey, baseUrl, fmt.Sprintf("/api/v2/preview/keys/%s", data.User[0].Keys[0])) + response := tt.RequestAuthGetRaw(t, data.User[1].AdminKey, baseUrl, fmt.Sprintf("/api/v2/preview/keys/%s", data.User[0].Keys[0].KeyID)) tt.AssertJsonStructureMatch(t, "json[key]", response, map[string]any{ "keytoken_id": "id", diff --git a/scnserver/test/sendername_test.go b/scnserver/test/sendername_test.go index 40e2011..45cdf61 100644 --- a/scnserver/test/sendername_test.go +++ b/scnserver/test/sendername_test.go @@ -1,11 +1,105 @@ package test -import "testing" +import ( + tt "blackforestbytes.com/simplecloudnotifier/test/util" + "fmt" + "github.com/gin-gonic/gin" + "strconv" + "testing" +) func TestListSenderNames(t *testing.T) { - t.Fail() + ws, baseUrl, stop := tt.StartSimpleWebserver(t) + defer stop() + + data := tt.InitDefaultData(t, ws) + + type sn struct { + SenderName string `json:"name"` + LastTimestamp string `json:"last_timestamp"` + FirstTimestamp string `json:"first_timestamp"` + Count int `json:"count"` + } + type snlistS struct { + SNList []sn `json:"sender_names"` + } + type snlistH struct { + SNList []gin.H `json:"sender_names"` + } + + responses := []struct { + Idx int + Resp []string + }{ + {0, []string{"Pocket Pal", "Cellular Confidant", "Mobile Mate"}}, + {1, []string{}}, + {2, []string{}}, + {3, []string{}}, + {4, []string{"Server0"}}, + {5, []string{"example.org", "example.com", "localhost"}}, + {6, []string{"server1", "server2"}}, + {7, []string{"localhost"}}, + {8, []string{}}, + {9, []string{"Vincent", "Tim", "Max"}}, + {10, []string{}}, + {11, []string{"192.168.0.1", "#S0", "localhost"}}, + {12, []string{}}, + {13, []string{}}, + {14, []string{"dummy-man"}}, + {15, []string{"dummy-man"}}, + {16, []string{}}, + } + + for _, resp := range responses { + msgList := tt.RequestAuthGet[snlistH](t, data.User[resp.Idx].AdminKey, baseUrl, "/api/v2/sender-names") + tt.AssertMappedArr(t, "sender_names_"+strconv.Itoa(resp.Idx), resp.Resp, msgList.SNList, "name") + } } func TestListUserSenderNames(t *testing.T) { - t.Fail() + ws, baseUrl, stop := tt.StartSimpleWebserver(t) + defer stop() + + data := tt.InitDefaultData(t, ws) + + type sn struct { + SenderName string `json:"name"` + LastTimestamp string `json:"last_timestamp"` + FirstTimestamp string `json:"first_timestamp"` + Count int `json:"count"` + } + type snlistS struct { + SNList []sn `json:"sender_names"` + } + type snlistH struct { + SNList []gin.H `json:"sender_names"` + } + + responses := []struct { + Idx int + Resp []string + }{ + {0, []string{"Pocket Pal", "Cellular Confidant", "Mobile Mate"}}, + {1, []string{}}, + {2, []string{}}, + {3, []string{}}, + {4, []string{"Server0"}}, + {5, []string{"example.org", "example.com", "localhost"}}, + {6, []string{"server1", "server2"}}, + {7, []string{"localhost"}}, + {8, []string{}}, + {9, []string{"Vincent", "Tim", "Max"}}, + {10, []string{}}, + {11, []string{"192.168.0.1", "#S0", "localhost"}}, + {12, []string{}}, + {13, []string{}}, + {14, []string{}}, + {15, []string{"dummy-man"}}, + {16, []string{}}, + } + + for _, resp := range responses { + msgList := tt.RequestAuthGet[snlistH](t, data.User[resp.Idx].AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/sender-names", data.User[resp.Idx].UID)) + tt.AssertMappedArr(t, "sender_names_"+strconv.Itoa(resp.Idx), resp.Resp, msgList.SNList, "name") + } } diff --git a/scnserver/test/util/factory.go b/scnserver/test/util/factory.go index c4287e6..43a272b 100644 --- a/scnserver/test/util/factory.go +++ b/scnserver/test/util/factory.go @@ -294,7 +294,8 @@ var messageExamples = []msgex{ {15, "", "", P0, SKEY, "New Feature Available", "ability to schedule appointments", 0}, {15, "chan_other_nosub", "", P0, SKEY, "Account Suspended", "Please contact us", 0}, {15, "chan_other_request", "", P0, SKEY, "Invitation to Beta Test", "", 0}, - {15, "chan_other_accepted", "", P0, SKEY, "New Blog Post", "Congratulations on your promotion! We are proud", 0}, + {15, "chan_other_request", "", P0, SKEY, "Invitation to Beta Test (with sendername)", "", 0}, + {15, "chan_other_accepted", "dummy-man", P0, SKEY, "New Blog Post", "Congratulations on your promotion! We are proud", 0}, {16, "Chan1", "", P2, SKEY, "Lorem Ipsum 01", Lipsum(30001, 1), 0}, {16, "Chan2", "", P0, SKEY, "Lorem Ipsum 02", Lipsum(30002, 1), 0},