Add tests [TestListSenderNames] [TestListUserSenderNames]
This commit is contained in:
parent
5dd94eca38
commit
584a9e983f
@ -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
|
||||||
|
|
||||||
|
@ -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}))
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
26
scnserver/db/impl/primary/senderNames.go
Normal file
26
scnserver/db/impl/primary/senderNames.go
Normal 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)
|
||||||
|
}
|
8
scnserver/models/senderNames.go
Normal file
8
scnserver/models/senderNames.go
Normal 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"`
|
||||||
|
}
|
@ -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",
|
||||||
|
@ -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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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},
|
||||||
|
Loading…
Reference in New Issue
Block a user