Add tests [TestListSenderNames] [TestListUserSenderNames]

This commit is contained in:
Mike Schwörer 2024-09-20 16:33:45 +02:00
parent 5dd94eca38
commit 584a9e983f
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
7 changed files with 157 additions and 13 deletions

View File

@ -12,7 +12,7 @@
- exerr.New | exerr.Wrap - exerr.New | exerr.Wrap
- (!!!) Run tests in pipeline !! - Fork go-sqlite with always-on fts5
#### UNSURE #### UNSURE

View File

@ -1,9 +1,12 @@
package handler package handler
import ( import (
"blackforestbytes.com/simplecloudnotifier/api/apierr"
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
"blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/logic"
"blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/models"
"gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/ginext"
"net/http"
) )
// ListUserSenderNames swaggerdoc // ListUserSenderNames swaggerdoc
@ -26,6 +29,7 @@ func (h APIHandler) ListUserSenderNames(pctx ginext.PreContext) ginext.HTTPRespo
UserID models.UserID `uri:"uid" binding:"entityid"` UserID models.UserID `uri:"uid" binding:"entityid"`
} }
type response struct { type response struct {
SenderNames []models.SenderNameStatistics `json:"sender_names"`
} }
var u uri var u uri
@ -41,7 +45,12 @@ func (h APIHandler) ListUserSenderNames(pctx ginext.PreContext) ginext.HTTPRespo
return *permResp 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] // @Router /api/v2/sender-names [GET]
func (h APIHandler) ListSenderNames(pctx ginext.PreContext) ginext.HTTPResponse { func (h APIHandler) ListSenderNames(pctx ginext.PreContext) ginext.HTTPResponse {
type response struct { type response struct {
SenderNames []models.SenderNameStatistics `json:"sender_names"`
} }
ctx, g, errResp := pctx.Start() ctx, g, errResp := pctx.Start()
@ -75,13 +85,18 @@ func (h APIHandler) ListSenderNames(pctx ginext.PreContext) ginext.HTTPResponse
return *permResp 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 *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}))
}) })
} }

View File

@ -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)
}

View File

@ -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"`
}

View File

@ -13,7 +13,7 @@ func TestResponseChannel(t *testing.T) {
data := tt.InitDefaultData(t, ws) 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{ tt.AssertJsonStructureMatch(t, "json[channel]", response, map[string]any{
"channel_id": "id", "channel_id": "id",
@ -63,7 +63,7 @@ func TestResponseKeyToken1(t *testing.T) {
data := tt.InitDefaultData(t, ws) 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{ tt.AssertJsonStructureMatch(t, "json[key]", response, map[string]any{
"keytoken_id": "id", "keytoken_id": "id",
@ -246,7 +246,7 @@ func TestResponseChannelPreview(t *testing.T) {
data := tt.InitDefaultData(t, ws) 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{ tt.AssertJsonStructureMatch(t, "json[channel]", response, map[string]any{
"channel_id": "id", "channel_id": "id",
@ -277,7 +277,7 @@ func TestResponseKeyTokenPreview(t *testing.T) {
data := tt.InitDefaultData(t, ws) 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{ tt.AssertJsonStructureMatch(t, "json[key]", response, map[string]any{
"keytoken_id": "id", "keytoken_id": "id",

View File

@ -1,11 +1,105 @@
package test package test
import "testing" import (
tt "blackforestbytes.com/simplecloudnotifier/test/util"
"fmt"
"github.com/gin-gonic/gin"
"strconv"
"testing"
)
func TestListSenderNames(t *testing.T) { 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) { 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")
}
} }

View File

@ -294,7 +294,8 @@ var messageExamples = []msgex{
{15, "", "", P0, SKEY, "New Feature Available", "ability to schedule appointments", 0}, {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_nosub", "", P0, SKEY, "Account Suspended", "Please contact us", 0},
{15, "chan_other_request", "", P0, SKEY, "Invitation to Beta Test", "", 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, "Chan1", "", P2, SKEY, "Lorem Ipsum 01", Lipsum(30001, 1), 0},
{16, "Chan2", "", P0, SKEY, "Lorem Ipsum 02", Lipsum(30002, 1), 0}, {16, "Chan2", "", P0, SKEY, "Lorem Ipsum 02", Lipsum(30002, 1), 0},