diff --git a/scnserver/README.md b/scnserver/README.md index fb327c6..2f8cccf 100644 --- a/scnserver/README.md +++ b/scnserver/README.md @@ -23,6 +23,8 @@ ------------------------------------------------------------------------------------------------------------------------------- + - (?) default-priority for channels + - (?) ack/read deliveries && return ack-count (? or not, how to query?) - (?) "login" on website and list/search/filter messages diff --git a/scnserver/api/apierr/enums.go b/scnserver/api/apierr/enums.go index 1ff7709..34f3a37 100644 --- a/scnserver/api/apierr/enums.go +++ b/scnserver/api/apierr/enums.go @@ -20,13 +20,14 @@ const ( BINDFAIL_URI_PARAM APIError = 1153 INVALID_ENUM_VALUE APIError = 1171 - NO_TITLE APIError = 1201 - TITLE_TOO_LONG APIError = 1202 - CONTENT_TOO_LONG APIError = 1203 - USR_MSG_ID_TOO_LONG APIError = 1204 - TIMESTAMP_OUT_OF_RANGE APIError = 1205 - SENDERNAME_TOO_LONG APIError = 1206 - CHANNEL_TOO_LONG APIError = 1207 + NO_TITLE APIError = 1201 + TITLE_TOO_LONG APIError = 1202 + CONTENT_TOO_LONG APIError = 1203 + USR_MSG_ID_TOO_LONG APIError = 1204 + TIMESTAMP_OUT_OF_RANGE APIError = 1205 + SENDERNAME_TOO_LONG APIError = 1206 + CHANNEL_TOO_LONG APIError = 1207 + CHANNEL_NAME_WOULD_CHANGE APIError = 1207 USER_NOT_FOUND APIError = 1301 CLIENT_NOT_FOUND APIError = 1302 diff --git a/scnserver/api/handler/api.go b/scnserver/api/handler/api.go index 2622e48..8e4af79 100644 --- a/scnserver/api/handler/api.go +++ b/scnserver/api/handler/api.go @@ -667,9 +667,10 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse { return *permResp } - channelName := h.app.NormalizeChannelName(b.Name) + channelDisplayName := h.app.NormalizeChannelDisplayName(b.Name) + channelInternalName := h.app.NormalizeChannelInternalName(b.Name) - channelExisting, err := h.database.GetChannelByName(ctx, u.UserID, channelName) + channelExisting, err := h.database.GetChannelByName(ctx, u.UserID, channelInternalName) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err) } @@ -682,7 +683,10 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query user", err) } - if len(channelName) > user.MaxChannelNameLength() { + if len(channelDisplayName) > user.MaxChannelNameLength() { + return ginresp.SendAPIError(g, 400, apierr.CHANNEL_TOO_LONG, hl.CHANNEL, fmt.Sprintf("Channel too long (max %d characters)", user.MaxChannelNameLength()), nil) + } + if len(channelInternalName) > user.MaxChannelNameLength() { return ginresp.SendAPIError(g, 400, apierr.CHANNEL_TOO_LONG, hl.CHANNEL, fmt.Sprintf("Channel too long (max %d characters)", user.MaxChannelNameLength()), nil) } @@ -693,7 +697,7 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse { subscribeKey := h.app.GenerateRandomAuthKey() sendKey := h.app.GenerateRandomAuthKey() - channel, err := h.database.CreateChannel(ctx, u.UserID, channelName, subscribeKey, sendKey) + channel, err := h.database.CreateChannel(ctx, u.UserID, channelDisplayName, channelInternalName, subscribeKey, sendKey) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create channel", err) } @@ -726,6 +730,7 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse { // // @Param subscribe_key body string false "Send `true` to create a new subscribe_key" // @Param send_key body string false "Send `true` to create a new send_key" +// @Param display_name body string false "Change the cahnnel display-name (only chnages to lowercase/uppercase are allowed - internal_name must stay the same)" // // @Success 200 {object} models.ChannelWithSubscriptionJSON // @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid" @@ -740,8 +745,9 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse { ChannelID models.ChannelID `uri:"cid"` } type body struct { - RefreshSubscribeKey *bool `json:"subscribe_key"` - RefreshSendKey *bool `json:"send_key"` + RefreshSubscribeKey *bool `json:"subscribe_key"` + RefreshSendKey *bool `json:"send_key"` + DisplayName *string `json:"display_name"` } var u uri @@ -756,7 +762,7 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse { return *permResp } - _, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID) + oldChannel, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID) if err == sql.ErrNoRows { return ginresp.APIError(g, 404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err) } @@ -769,7 +775,7 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse { err := h.database.UpdateChannelSendKey(ctx, u.ChannelID, newkey) if err != nil { - return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err) + return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update channel", err) } } @@ -778,13 +784,29 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse { err := h.database.UpdateChannelSubscribeKey(ctx, u.ChannelID, newkey) if err != nil { - return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err) + return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update channel", err) } } + if b.DisplayName != nil { + + newDisplayName := h.app.NormalizeChannelDisplayName(*b.DisplayName) + newInternalName := h.app.NormalizeChannelInternalName(*b.DisplayName) + + if newInternalName != oldChannel.InternalName { + return ginresp.APIError(g, 400, apierr.CHANNEL_NAME_WOULD_CHANGE, "Cannot substantially change the channel name", err) + } + + err := h.database.UpdateChannelDisplayName(ctx, u.ChannelID, newDisplayName) + if err != nil { + return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update channel", err) + } + + } + channel, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID) if err != nil { - return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) user", err) + return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) channel", err) } return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.JSON(true))) @@ -1191,7 +1213,9 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse { return *permResp } - channel, err := h.database.GetChannelByName(ctx, b.ChannelOwnerUserID, h.app.NormalizeChannelName(b.Channel)) + channelInternalName := h.app.NormalizeChannelInternalName(b.Channel) + + channel, err := h.database.GetChannelByName(ctx, b.ChannelOwnerUserID, channelInternalName) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err) } diff --git a/scnserver/api/handler/message.go b/scnserver/api/handler/message.go index 7a92ede..37c8bd5 100644 --- a/scnserver/api/handler/message.go +++ b/scnserver/api/handler/message.go @@ -166,9 +166,11 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query user", err) } - channelName := user.DefaultChannel() + channelDisplayName := user.DefaultChannel() + channelInternalName := user.DefaultChannel() if Channel != nil { - channelName = h.app.NormalizeChannelName(*Channel) + channelDisplayName = h.app.NormalizeChannelDisplayName(*Channel) + channelInternalName = h.app.NormalizeChannelInternalName(*Channel) } if len(*Title) > user.MaxTitleLength() { @@ -177,7 +179,10 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex if Content != nil && len(*Content) > user.MaxContentLength() { return ginresp.SendAPIError(g, 400, apierr.CONTENT_TOO_LONG, hl.CONTENT, fmt.Sprintf("Content too long (%d characters; max := %d characters)", len(*Content), user.MaxContentLength()), nil) } - if len(channelName) > user.MaxChannelNameLength() { + if len(channelDisplayName) > user.MaxChannelNameLength() { + return ginresp.SendAPIError(g, 400, apierr.CHANNEL_TOO_LONG, hl.CHANNEL, fmt.Sprintf("Channel too long (max %d characters)", user.MaxChannelNameLength()), nil) + } + if len(channelInternalName) > user.MaxChannelNameLength() { return ginresp.SendAPIError(g, 400, apierr.CHANNEL_TOO_LONG, hl.CHANNEL, fmt.Sprintf("Channel too long (max %d characters)", user.MaxChannelNameLength()), nil) } if SenderName != nil && len(*SenderName) > user.MaxSenderName() { @@ -220,7 +225,7 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex if ChanKey != nil { // foreign channel (+ channel send-key) - foreignChan, err := h.database.GetChannelByNameAndSendKey(ctx, channelName, *ChanKey) + foreignChan, err := h.database.GetChannelByNameAndSendKey(ctx, channelInternalName, *ChanKey) if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query (foreign) channel", err) } @@ -231,7 +236,7 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex } else { // own channel - channel, err = h.app.GetOrCreateChannel(ctx, *UserID, channelName) + channel, err = h.app.GetOrCreateChannel(ctx, *UserID, channelDisplayName, channelInternalName) if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query/create (owned) channel", err) } diff --git a/scnserver/db/channels.go b/scnserver/db/channels.go index 7f6270d..f2b47f8 100644 --- a/scnserver/db/channels.go +++ b/scnserver/db/channels.go @@ -13,7 +13,7 @@ func (db *Database) GetChannelByName(ctx TxContext, userid models.UserID, chanNa return nil, err } - rows, err := tx.Query(ctx, "SELECT * FROM channels WHERE owner_user_id = :uid AND name = :nam LIMIT 1", sq.PP{ + rows, err := tx.Query(ctx, "SELECT * FROM channels WHERE owner_user_id = :uid AND internal_name = :nam LIMIT 1", sq.PP{ "uid": userid, "nam": chanName, }) @@ -38,7 +38,7 @@ func (db *Database) GetChannelByNameAndSendKey(ctx TxContext, chanName string, s return nil, err } - rows, err := tx.Query(ctx, "SELECT * FROM channels WHERE name = :chan_name OR send_key = :send_key LIMIT 1", sq.PP{ + rows, err := tx.Query(ctx, "SELECT * FROM channels WHERE internal_name = :chan_name OR send_key = :send_key LIMIT 1", sq.PP{ "chan_name": chanName, "send_key": sendKey, }) @@ -57,7 +57,7 @@ func (db *Database) GetChannelByNameAndSendKey(ctx TxContext, chanName string, s return &channel, nil } -func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, name string, subscribeKey string, sendKey string) (models.Channel, error) { +func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, dispName string, intName string, subscribeKey string, sendKey string) (models.Channel, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.Channel{}, err @@ -65,9 +65,10 @@ func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, name stri now := time.Now().UTC() - res, err := tx.Exec(ctx, "INSERT INTO channels (owner_user_id, name, subscribe_key, send_key, timestamp_created) VALUES (:ouid, :nam, :subkey, :sendkey, :ts)", sq.PP{ + res, err := tx.Exec(ctx, "INSERT INTO channels (owner_user_id, display_name, internal_name, subscribe_key, send_key, timestamp_created) VALUES (:ouid, :dnam, :inam, :subkey, :sendkey, :ts)", sq.PP{ "ouid": userid, - "nam": name, + "dnam": dispName, + "inam": intName, "subkey": subscribeKey, "sendkey": sendKey, "ts": time2DB(now), @@ -84,7 +85,8 @@ func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, name stri return models.Channel{ ChannelID: models.ChannelID(liid), OwnerUserID: userid, - Name: name, + DisplayName: dispName, + InternalName: intName, SubscribeKey: subscribeKey, SendKey: sendKey, TimestampCreated: now, @@ -246,3 +248,20 @@ func (db *Database) UpdateChannelSubscribeKey(ctx TxContext, channelid models.Ch return nil } + +func (db *Database) UpdateChannelDisplayName(ctx TxContext, channelid models.ChannelID, dispname string) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "UPDATE channels SET display_name = :nam WHERE channel_id = :cid", sq.PP{ + "nam": dispname, + "cid": channelid, + }) + if err != nil { + return err + } + + return nil +} diff --git a/scnserver/db/messages.go b/scnserver/db/messages.go index f639642..8c5d345 100644 --- a/scnserver/db/messages.go +++ b/scnserver/db/messages.go @@ -64,10 +64,10 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID models.UserID, cha now := time.Now().UTC() - res, err := tx.Exec(ctx, "INSERT INTO messages (sender_user_id, owner_user_id, channel_name, channel_id, timestamp_real, timestamp_client, title, content, priority, usr_message_id, sender_ip, sender_name) VALUES (:suid, :ouid, :cnam, :cid, :tsr, :tsc, :tit, :cnt, :prio, :umid, :ip, :snam)", sq.PP{ + res, err := tx.Exec(ctx, "INSERT INTO messages (sender_user_id, owner_user_id, channel_internal_name, channel_id, timestamp_real, timestamp_client, title, content, priority, usr_message_id, sender_ip, sender_name) VALUES (:suid, :ouid, :cnam, :cid, :tsr, :tsc, :tit, :cnt, :prio, :umid, :ip, :snam)", sq.PP{ "suid": senderUserID, "ouid": channel.OwnerUserID, - "cnam": channel.Name, + "cnam": channel.InternalName, "cid": channel.ChannelID, "tsr": time2DB(now), "tsc": time2DBOpt(timestampSend), @@ -88,19 +88,19 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID models.UserID, cha } return models.Message{ - SCNMessageID: models.SCNMessageID(liid), - SenderUserID: senderUserID, - OwnerUserID: channel.OwnerUserID, - ChannelName: channel.Name, - ChannelID: channel.ChannelID, - SenderIP: senderIP, - SenderName: senderName, - TimestampReal: now, - TimestampClient: timestampSend, - Title: title, - Content: content, - Priority: priority, - UserMessageID: userMsgId, + SCNMessageID: models.SCNMessageID(liid), + SenderUserID: senderUserID, + OwnerUserID: channel.OwnerUserID, + ChannelInternalName: channel.InternalName, + ChannelID: channel.ChannelID, + SenderIP: senderIP, + SenderName: senderName, + TimestampReal: now, + TimestampClient: timestampSend, + Title: title, + Content: content, + Priority: priority, + UserMessageID: userMsgId, }, nil } diff --git a/scnserver/db/schema/schema_3.ddl b/scnserver/db/schema/schema_3.ddl index 273f83a..1d67bb3 100644 --- a/scnserver/db/schema/schema_3.ddl +++ b/scnserver/db/schema/schema_3.ddl @@ -46,7 +46,8 @@ CREATE TABLE channels owner_user_id INTEGER NOT NULL, - name TEXT NOT NULL, + internal_name TEXT NOT NULL, + display_name TEXT NOT NULL, subscribe_key TEXT NOT NULL, send_key TEXT NOT NULL, @@ -56,7 +57,7 @@ CREATE TABLE channels messages_sent INTEGER NOT NULL DEFAULT '0' ) STRICT; -CREATE UNIQUE INDEX "idx_channels_identity" ON channels (owner_user_id, name); +CREATE UNIQUE INDEX "idx_channels_identity" ON channels (owner_user_id, internal_name); CREATE TABLE subscriptions ( @@ -64,40 +65,45 @@ CREATE TABLE subscriptions subscriber_user_id INTEGER NOT NULL, channel_owner_user_id INTEGER NOT NULL, - channel_name TEXT NOT NULL, + channel_internal_name TEXT NOT NULL, channel_id INTEGER NOT NULL, timestamp_created INTEGER NOT NULL, confirmed INTEGER CHECK(confirmed IN (0, 1)) NOT NULL ) STRICT; -CREATE UNIQUE INDEX "idx_subscriptions_ref" ON subscriptions (subscriber_user_id, channel_owner_user_id, channel_name); +CREATE UNIQUE INDEX "idx_subscriptions_ref" ON subscriptions (subscriber_user_id, channel_owner_user_id, channel_internal_name); +CREATE INDEX "idx_subscriptions_chan" ON subscriptions (channel_id); +CREATE INDEX "idx_subscriptions_subuser" ON subscriptions (subscriber_user_id); +CREATE INDEX "idx_subscriptions_ownuser" ON subscriptions (channel_owner_user_id); +CREATE INDEX "idx_subscriptions_tsc" ON subscriptions (timestamp_created); +CREATE INDEX "idx_subscriptions_conf" ON subscriptions (confirmed); CREATE TABLE messages ( - scn_message_id INTEGER PRIMARY KEY AUTOINCREMENT, - sender_user_id INTEGER NOT NULL, - owner_user_id INTEGER NOT NULL, - channel_name TEXT NOT NULL, - channel_id INTEGER NOT NULL, - sender_ip TEXT NOT NULL, - sender_name TEXT NULL, + scn_message_id INTEGER PRIMARY KEY AUTOINCREMENT, + sender_user_id INTEGER NOT NULL, + owner_user_id INTEGER NOT NULL, + channel_internal_name TEXT NOT NULL, + channel_id INTEGER NOT NULL, + sender_ip TEXT NOT NULL, + sender_name TEXT NULL, - timestamp_real INTEGER NOT NULL, - timestamp_client INTEGER NULL, + timestamp_real INTEGER NOT NULL, + timestamp_client INTEGER NULL, - title TEXT NOT NULL, - content TEXT NULL, - priority INTEGER CHECK(priority IN (0, 1, 2)) NOT NULL, - usr_message_id TEXT NULL, + title TEXT NOT NULL, + content TEXT NULL, + priority INTEGER CHECK(priority IN (0, 1, 2)) NOT NULL, + usr_message_id TEXT NULL, - deleted INTEGER CHECK(deleted IN (0, 1)) NOT NULL DEFAULT '0' + deleted INTEGER CHECK(deleted IN (0, 1)) NOT NULL DEFAULT '0' ) STRICT; -CREATE INDEX "idx_messages_owner_channel" ON messages (owner_user_id, channel_name COLLATE BINARY); -CREATE INDEX "idx_messages_owner_channel_nc" ON messages (owner_user_id, channel_name COLLATE NOCASE); -CREATE INDEX "idx_messages_channel" ON messages (channel_name COLLATE BINARY); -CREATE INDEX "idx_messages_channel_nc" ON messages (channel_name COLLATE NOCASE); +CREATE INDEX "idx_messages_owner_channel" ON messages (owner_user_id, channel_internal_name COLLATE BINARY); +CREATE INDEX "idx_messages_owner_channel_nc" ON messages (owner_user_id, channel_internal_name COLLATE NOCASE); +CREATE INDEX "idx_messages_channel" ON messages (channel_internal_name COLLATE BINARY); +CREATE INDEX "idx_messages_channel_nc" ON messages (channel_internal_name COLLATE NOCASE); CREATE UNIQUE INDEX "idx_messages_idempotency" ON messages (owner_user_id, usr_message_id COLLATE BINARY); CREATE INDEX "idx_messages_senderip" ON messages (sender_ip COLLATE BINARY); CREATE INDEX "idx_messages_sendername" ON messages (sender_name COLLATE BINARY); @@ -109,7 +115,7 @@ CREATE INDEX "idx_messages_deleted" ON messages (deleted); CREATE VIRTUAL TABLE messages_fts USING fts5 ( - channel_name, + channel_internal_name, sender_name, title, content, @@ -120,16 +126,16 @@ CREATE VIRTUAL TABLE messages_fts USING fts5 ); CREATE TRIGGER fts_insert AFTER INSERT ON messages BEGIN - INSERT INTO messages_fts (rowid, channel_name, sender_name, title, content) VALUES (new.scn_message_id, new.channel_name, new.sender_name, new.title, new.content); + INSERT INTO messages_fts (rowid, channel_internal_name, sender_name, title, content) VALUES (new.scn_message_id, new.channel_internal_name, new.sender_name, new.title, new.content); END; CREATE TRIGGER fts_update AFTER UPDATE ON messages BEGIN - INSERT INTO messages_fts (messages_fts, rowid, channel_name, sender_name, title, content) VALUES ('delete', old.scn_message_id, old.channel_name, old.sender_name, old.title, old.content); - INSERT INTO messages_fts ( rowid, channel_name, sender_name, title, content) VALUES ( new.scn_message_id, new.channel_name, new.sender_name, new.title, new.content); + INSERT INTO messages_fts (messages_fts, rowid, channel_internal_name, sender_name, title, content) VALUES ('delete', old.scn_message_id, old.channel_internal_name, old.sender_name, old.title, old.content); + INSERT INTO messages_fts ( rowid, channel_internal_name, sender_name, title, content) VALUES ( new.scn_message_id, new.channel_internal_name, new.sender_name, new.title, new.content); END; CREATE TRIGGER fts_delete AFTER DELETE ON messages BEGIN - INSERT INTO messages_fts (messages_fts, rowid, channel_name, sender_name, title, content) VALUES ('delete', old.scn_message_id, old.channel_name, old.sender_name, old.title, old.content); + INSERT INTO messages_fts (messages_fts, rowid, channel_internal_name, sender_name, title, content) VALUES ('delete', old.scn_message_id, old.channel_internal_name, old.sender_name, old.title, old.content); END; diff --git a/scnserver/db/subscriptions.go b/scnserver/db/subscriptions.go index ce9c64f..0873f1b 100644 --- a/scnserver/db/subscriptions.go +++ b/scnserver/db/subscriptions.go @@ -15,10 +15,10 @@ func (db *Database) CreateSubscription(ctx TxContext, subscriberUID models.UserI now := time.Now().UTC() - res, err := tx.Exec(ctx, "INSERT INTO subscriptions (subscriber_user_id, channel_owner_user_id, channel_name, channel_id, timestamp_created, confirmed) VALUES (:suid, :ouid, :cnam, :cid, :ts, :conf)", sq.PP{ + res, err := tx.Exec(ctx, "INSERT INTO subscriptions (subscriber_user_id, channel_owner_user_id, channel_internal_name, channel_id, timestamp_created, confirmed) VALUES (:suid, :ouid, :cnam, :cid, :ts, :conf)", sq.PP{ "suid": subscriberUID, "ouid": channel.OwnerUserID, - "cnam": channel.Name, + "cnam": channel.InternalName, "cid": channel.ChannelID, "ts": time2DB(now), "conf": confirmed, @@ -33,13 +33,13 @@ func (db *Database) CreateSubscription(ctx TxContext, subscriberUID models.UserI } return models.Subscription{ - SubscriptionID: models.SubscriptionID(liid), - SubscriberUserID: subscriberUID, - ChannelOwnerUserID: channel.OwnerUserID, - ChannelID: channel.ChannelID, - ChannelName: channel.Name, - TimestampCreated: now, - Confirmed: confirmed, + SubscriptionID: models.SubscriptionID(liid), + SubscriberUserID: subscriberUID, + ChannelOwnerUserID: channel.OwnerUserID, + ChannelID: channel.ChannelID, + ChannelInternalName: channel.InternalName, + TimestampCreated: now, + Confirmed: confirmed, }, nil } diff --git a/scnserver/logic/application.go b/scnserver/logic/application.go index da43612..7ad1cb8 100644 --- a/scnserver/logic/application.go +++ b/scnserver/logic/application.go @@ -282,10 +282,8 @@ func (app *Application) getPermissions(ctx *AppContext, hdr string) (PermissionS return NewEmptyPermissions(), nil } -func (app *Application) GetOrCreateChannel(ctx *AppContext, userid models.UserID, chanName string) (models.Channel, error) { - chanName = app.NormalizeChannelName(chanName) - - existingChan, err := app.Database.GetChannelByName(ctx, userid, chanName) +func (app *Application) GetOrCreateChannel(ctx *AppContext, userid models.UserID, displayChanName string, intChanName string) (models.Channel, error) { + existingChan, err := app.Database.GetChannelByName(ctx, userid, intChanName) if err != nil { return models.Channel{}, err } @@ -297,7 +295,7 @@ func (app *Application) GetOrCreateChannel(ctx *AppContext, userid models.UserID subscribeKey := app.GenerateRandomAuthKey() sendKey := app.GenerateRandomAuthKey() - newChan, err := app.Database.CreateChannel(ctx, userid, chanName, subscribeKey, sendKey) + newChan, err := app.Database.CreateChannel(ctx, userid, displayChanName, intChanName, subscribeKey, sendKey) if err != nil { return models.Channel{}, err } @@ -310,12 +308,22 @@ func (app *Application) GetOrCreateChannel(ctx *AppContext, userid models.UserID return newChan, nil } -func (app *Application) NormalizeChannelName(v string) string { - rex := regexp.MustCompile("[^[:alnum:]\\-_]") +var rexWhitespaceStart = regexp.MustCompile("^\\s+") +var rexWhitespaceEnd = regexp.MustCompile("\\s+$") +func (app *Application) NormalizeChannelDisplayName(v string) string { + v = strings.TrimSpace(v) + v = rexWhitespaceStart.ReplaceAllString(v, "") + v = rexWhitespaceEnd.ReplaceAllString(v, "") + + return v +} + +func (app *Application) NormalizeChannelInternalName(v string) string { v = strings.TrimSpace(v) v = strings.ToLower(v) - v = rex.ReplaceAllString(v, "") + v = rexWhitespaceStart.ReplaceAllString(v, "") + v = rexWhitespaceEnd.ReplaceAllString(v, "") return v } diff --git a/scnserver/models/channel.go b/scnserver/models/channel.go index 712de04..6ac00b4 100644 --- a/scnserver/models/channel.go +++ b/scnserver/models/channel.go @@ -10,7 +10,8 @@ import ( type Channel struct { ChannelID ChannelID OwnerUserID UserID - Name string + InternalName string + DisplayName string SubscribeKey string SendKey string TimestampCreated time.Time @@ -22,7 +23,8 @@ func (c Channel) JSON(includeKey bool) ChannelJSON { return ChannelJSON{ ChannelID: c.ChannelID, OwnerUserID: c.OwnerUserID, - Name: c.Name, + InternalName: c.InternalName, + DisplayName: c.DisplayName, SubscribeKey: langext.Conditional(includeKey, langext.Ptr(c.SubscribeKey), nil), SendKey: langext.Conditional(includeKey, langext.Ptr(c.SendKey), nil), TimestampCreated: c.TimestampCreated.Format(time.RFC3339Nano), @@ -57,7 +59,8 @@ func (c ChannelWithSubscription) JSON(includeChannelKey bool) ChannelWithSubscri type ChannelJSON struct { ChannelID ChannelID `json:"channel_id"` OwnerUserID UserID `json:"owner_user_id"` - Name string `json:"name"` + InternalName string `json:"internal_name"` + DisplayName string `json:"display_name"` SubscribeKey *string `json:"subscribe_key"` // can be nil, depending on endpoint SendKey *string `json:"send_key"` // can be nil, depending on endpoint TimestampCreated string `json:"timestamp_created"` @@ -73,7 +76,8 @@ type ChannelWithSubscriptionJSON struct { type ChannelDB struct { ChannelID ChannelID `db:"channel_id"` OwnerUserID UserID `db:"owner_user_id"` - Name string `db:"name"` + InternalName string `db:"internal_name"` + DisplayName string `db:"display_name"` SubscribeKey string `db:"subscribe_key"` SendKey string `db:"send_key"` TimestampCreated int64 `db:"timestamp_created"` @@ -86,7 +90,8 @@ func (c ChannelDB) Model() Channel { return Channel{ ChannelID: c.ChannelID, OwnerUserID: c.OwnerUserID, - Name: c.Name, + InternalName: c.InternalName, + DisplayName: c.DisplayName, SubscribeKey: c.SubscribeKey, SendKey: c.SendKey, TimestampCreated: time.UnixMilli(c.TimestampCreated), diff --git a/scnserver/models/message.go b/scnserver/models/message.go index 71cd238..0ae3ca2 100644 --- a/scnserver/models/message.go +++ b/scnserver/models/message.go @@ -13,55 +13,55 @@ const ( ) type Message struct { - SCNMessageID SCNMessageID - SenderUserID UserID - OwnerUserID UserID - ChannelName string - ChannelID ChannelID - SenderName *string - SenderIP string - TimestampReal time.Time - TimestampClient *time.Time - Title string - Content *string - Priority int - UserMessageID *string - Deleted bool + SCNMessageID SCNMessageID + SenderUserID UserID + OwnerUserID UserID + ChannelInternalName string + ChannelID ChannelID + SenderName *string + SenderIP string + TimestampReal time.Time + TimestampClient *time.Time + Title string + Content *string + Priority int + UserMessageID *string + Deleted bool } func (m Message) FullJSON() MessageJSON { return MessageJSON{ - SCNMessageID: m.SCNMessageID, - SenderUserID: m.SenderUserID, - OwnerUserID: m.OwnerUserID, - ChannelName: m.ChannelName, - ChannelID: m.ChannelID, - SenderName: m.SenderName, - SenderIP: m.SenderIP, - Timestamp: m.Timestamp().Format(time.RFC3339Nano), - Title: m.Title, - Content: m.Content, - Priority: m.Priority, - UserMessageID: m.UserMessageID, - Trimmed: false, + SCNMessageID: m.SCNMessageID, + SenderUserID: m.SenderUserID, + OwnerUserID: m.OwnerUserID, + ChannelInternalName: m.ChannelInternalName, + ChannelID: m.ChannelID, + SenderName: m.SenderName, + SenderIP: m.SenderIP, + Timestamp: m.Timestamp().Format(time.RFC3339Nano), + Title: m.Title, + Content: m.Content, + Priority: m.Priority, + UserMessageID: m.UserMessageID, + Trimmed: false, } } func (m Message) TrimmedJSON() MessageJSON { return MessageJSON{ - SCNMessageID: m.SCNMessageID, - SenderUserID: m.SenderUserID, - OwnerUserID: m.OwnerUserID, - ChannelName: m.ChannelName, - ChannelID: m.ChannelID, - SenderName: m.SenderName, - SenderIP: m.SenderIP, - Timestamp: m.Timestamp().Format(time.RFC3339Nano), - Title: m.Title, - Content: m.TrimmedContent(), - Priority: m.Priority, - UserMessageID: m.UserMessageID, - Trimmed: m.NeedsTrim(), + SCNMessageID: m.SCNMessageID, + SenderUserID: m.SenderUserID, + OwnerUserID: m.OwnerUserID, + ChannelInternalName: m.ChannelInternalName, + ChannelID: m.ChannelID, + SenderName: m.SenderName, + SenderIP: m.SenderIP, + Timestamp: m.Timestamp().Format(time.RFC3339Nano), + Title: m.Title, + Content: m.TrimmedContent(), + Priority: m.Priority, + UserMessageID: m.UserMessageID, + Trimmed: m.NeedsTrim(), } } @@ -94,54 +94,54 @@ func (m Message) ShortContent() string { } type MessageJSON struct { - SCNMessageID SCNMessageID `json:"scn_message_id"` - SenderUserID UserID `json:"sender_user_id"` - OwnerUserID UserID `json:"owner_user_id"` - ChannelName string `json:"channel_name"` - ChannelID ChannelID `json:"channel_id"` - SenderName *string `json:"sender_name"` - SenderIP string `json:"sender_ip"` - Timestamp string `json:"timestamp"` - Title string `json:"title"` - Content *string `json:"content"` - Priority int `json:"priority"` - UserMessageID *string `json:"usr_message_id"` - Trimmed bool `json:"trimmed"` + SCNMessageID SCNMessageID `json:"scn_message_id"` + SenderUserID UserID `json:"sender_user_id"` + OwnerUserID UserID `json:"owner_user_id"` + ChannelInternalName string `json:"channel_internal_name"` + ChannelID ChannelID `json:"channel_id"` + SenderName *string `json:"sender_name"` + SenderIP string `json:"sender_ip"` + Timestamp string `json:"timestamp"` + Title string `json:"title"` + Content *string `json:"content"` + Priority int `json:"priority"` + UserMessageID *string `json:"usr_message_id"` + Trimmed bool `json:"trimmed"` } type MessageDB struct { - SCNMessageID SCNMessageID `db:"scn_message_id"` - SenderUserID UserID `db:"sender_user_id"` - OwnerUserID UserID `db:"owner_user_id"` - ChannelName string `db:"channel_name"` - ChannelID ChannelID `db:"channel_id"` - SenderName *string `db:"sender_name"` - SenderIP string `db:"sender_ip"` - TimestampReal int64 `db:"timestamp_real"` - TimestampClient *int64 `db:"timestamp_client"` - Title string `db:"title"` - Content *string `db:"content"` - Priority int `db:"priority"` - UserMessageID *string `db:"usr_message_id"` - Deleted int `db:"deleted"` + SCNMessageID SCNMessageID `db:"scn_message_id"` + SenderUserID UserID `db:"sender_user_id"` + OwnerUserID UserID `db:"owner_user_id"` + ChannelInternalName string `db:"channel_internal_name"` + ChannelID ChannelID `db:"channel_id"` + SenderName *string `db:"sender_name"` + SenderIP string `db:"sender_ip"` + TimestampReal int64 `db:"timestamp_real"` + TimestampClient *int64 `db:"timestamp_client"` + Title string `db:"title"` + Content *string `db:"content"` + Priority int `db:"priority"` + UserMessageID *string `db:"usr_message_id"` + Deleted int `db:"deleted"` } func (m MessageDB) Model() Message { return Message{ - SCNMessageID: m.SCNMessageID, - SenderUserID: m.SenderUserID, - OwnerUserID: m.OwnerUserID, - ChannelName: m.ChannelName, - ChannelID: m.ChannelID, - SenderName: m.SenderName, - SenderIP: m.SenderIP, - TimestampReal: time.UnixMilli(m.TimestampReal), - TimestampClient: timeOptFromMilli(m.TimestampClient), - Title: m.Title, - Content: m.Content, - Priority: m.Priority, - UserMessageID: m.UserMessageID, - Deleted: m.Deleted != 0, + SCNMessageID: m.SCNMessageID, + SenderUserID: m.SenderUserID, + OwnerUserID: m.OwnerUserID, + ChannelInternalName: m.ChannelInternalName, + ChannelID: m.ChannelID, + SenderName: m.SenderName, + SenderIP: m.SenderIP, + TimestampReal: time.UnixMilli(m.TimestampReal), + TimestampClient: timeOptFromMilli(m.TimestampClient), + Title: m.Title, + Content: m.Content, + Priority: m.Priority, + UserMessageID: m.UserMessageID, + Deleted: m.Deleted != 0, } } diff --git a/scnserver/models/subscription.go b/scnserver/models/subscription.go index c889779..af8b56f 100644 --- a/scnserver/models/subscription.go +++ b/scnserver/models/subscription.go @@ -8,56 +8,56 @@ import ( ) type Subscription struct { - SubscriptionID SubscriptionID - SubscriberUserID UserID - ChannelOwnerUserID UserID - ChannelID ChannelID - ChannelName string - TimestampCreated time.Time - Confirmed bool + SubscriptionID SubscriptionID + SubscriberUserID UserID + ChannelOwnerUserID UserID + ChannelID ChannelID + ChannelInternalName string + TimestampCreated time.Time + Confirmed bool } func (s Subscription) JSON() SubscriptionJSON { return SubscriptionJSON{ - SubscriptionID: s.SubscriptionID, - SubscriberUserID: s.SubscriberUserID, - ChannelOwnerUserID: s.ChannelOwnerUserID, - ChannelID: s.ChannelID, - ChannelName: s.ChannelName, - TimestampCreated: s.TimestampCreated.Format(time.RFC3339Nano), - Confirmed: s.Confirmed, + SubscriptionID: s.SubscriptionID, + SubscriberUserID: s.SubscriberUserID, + ChannelOwnerUserID: s.ChannelOwnerUserID, + ChannelID: s.ChannelID, + ChannelInternalName: s.ChannelInternalName, + TimestampCreated: s.TimestampCreated.Format(time.RFC3339Nano), + Confirmed: s.Confirmed, } } type SubscriptionJSON struct { - SubscriptionID SubscriptionID `json:"subscription_id"` - SubscriberUserID UserID `json:"subscriber_user_id"` - ChannelOwnerUserID UserID `json:"channel_owner_user_id"` - ChannelID ChannelID `json:"channel_id"` - ChannelName string `json:"channel_name"` - TimestampCreated string `json:"timestamp_created"` - Confirmed bool `json:"confirmed"` + SubscriptionID SubscriptionID `json:"subscription_id"` + SubscriberUserID UserID `json:"subscriber_user_id"` + ChannelOwnerUserID UserID `json:"channel_owner_user_id"` + ChannelID ChannelID `json:"channel_id"` + ChannelInternalName string `json:"channel_internal_name"` + TimestampCreated string `json:"timestamp_created"` + Confirmed bool `json:"confirmed"` } type SubscriptionDB struct { - SubscriptionID SubscriptionID `db:"subscription_id"` - SubscriberUserID UserID `db:"subscriber_user_id"` - ChannelOwnerUserID UserID `db:"channel_owner_user_id"` - ChannelID ChannelID `db:"channel_id"` - ChannelName string `db:"channel_name"` - TimestampCreated int64 `db:"timestamp_created"` - Confirmed int `db:"confirmed"` + SubscriptionID SubscriptionID `db:"subscription_id"` + SubscriberUserID UserID `db:"subscriber_user_id"` + ChannelOwnerUserID UserID `db:"channel_owner_user_id"` + ChannelID ChannelID `db:"channel_id"` + ChannelInternalName string `db:"channel_internal_name"` + TimestampCreated int64 `db:"timestamp_created"` + Confirmed int `db:"confirmed"` } func (s SubscriptionDB) Model() Subscription { return Subscription{ - SubscriptionID: s.SubscriptionID, - SubscriberUserID: s.SubscriberUserID, - ChannelOwnerUserID: s.ChannelOwnerUserID, - ChannelID: s.ChannelID, - ChannelName: s.ChannelName, - TimestampCreated: time.UnixMilli(s.TimestampCreated), - Confirmed: s.Confirmed != 0, + SubscriptionID: s.SubscriptionID, + SubscriberUserID: s.SubscriberUserID, + ChannelOwnerUserID: s.ChannelOwnerUserID, + ChannelID: s.ChannelID, + ChannelInternalName: s.ChannelInternalName, + TimestampCreated: time.UnixMilli(s.TimestampCreated), + Confirmed: s.Confirmed != 0, } } diff --git a/scnserver/swagger/swagger.json b/scnserver/swagger/swagger.json index b15f02b..9ef4466 100644 --- a/scnserver/swagger/swagger.json +++ b/scnserver/swagger/swagger.json @@ -1356,6 +1356,14 @@ "schema": { "type": "string" } + }, + { + "description": "Change the cahnnel display-name (only chnages to lowercase/uppercase are allowed - internal_name must stay the same)", + "name": "display_name", + "in": "body", + "schema": { + "type": "string" + } } ], "responses": { @@ -2826,12 +2834,15 @@ "channel_id": { "type": "integer" }, + "display_name": { + "type": "string" + }, + "internal_name": { + "type": "string" + }, "messages_sent": { "type": "integer" }, - "name": { - "type": "string" - }, "owner_user_id": { "type": "integer" }, @@ -2912,7 +2923,7 @@ "channel_id": { "type": "integer" }, - "channel_name": { + "channel_internal_name": { "type": "string" }, "content": { @@ -2956,7 +2967,7 @@ "channel_id": { "type": "integer" }, - "channel_name": { + "channel_internal_name": { "type": "string" }, "channel_owner_user_id": { diff --git a/scnserver/swagger/swagger.yaml b/scnserver/swagger/swagger.yaml index d7664ec..fb43d35 100644 --- a/scnserver/swagger/swagger.yaml +++ b/scnserver/swagger/swagger.yaml @@ -340,10 +340,12 @@ definitions: properties: channel_id: type: integer + display_name: + type: string + internal_name: + type: string messages_sent: type: integer - name: - type: string owner_user_id: type: integer send_key: @@ -397,7 +399,7 @@ definitions: properties: channel_id: type: integer - channel_name: + channel_internal_name: type: string content: type: string @@ -426,7 +428,7 @@ definitions: properties: channel_id: type: integer - channel_name: + channel_internal_name: type: string channel_owner_user_id: type: integer @@ -1433,6 +1435,12 @@ paths: name: send_key schema: type: string + - description: Change the cahnnel display-name (only chnages to lowercase/uppercase + are allowed - internal_name must stay the same) + in: body + name: display_name + schema: + type: string responses: "200": description: OK diff --git a/scnserver/test/channel_test.go b/scnserver/test/channel_test.go index 08a9592..41545a0 100644 --- a/scnserver/test/channel_test.go +++ b/scnserver/test/channel_test.go @@ -29,7 +29,8 @@ func TestCreateChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "name") + tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "internal_name") } tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{ @@ -39,7 +40,8 @@ func TestCreateChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) tt.AssertEqual(t, "chan.len", 1, len(clist.Channels)) - tt.AssertMappedSet(t, "channels", []string{"test"}, clist.Channels, "name") + tt.AssertMappedSet(t, "channels", []string{"test"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"test"}, clist.Channels, "internal_name") } tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{ @@ -48,7 +50,8 @@ func TestCreateChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertMappedSet(t, "channels", []string{"asdf", "test"}, clist.Channels, "name") + tt.AssertMappedSet(t, "channels", []string{"asdf", "test"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"asdf", "test"}, clist.Channels, "internal_name") } } @@ -90,8 +93,9 @@ func TestChannelNameNormalization(t *testing.T) { } { - chan0 := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan-count", 0, len(chan0.Channels)) + clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) + tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "internal_name") } tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{ @@ -100,8 +104,8 @@ func TestChannelNameNormalization(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan.len", 1, len(clist.Channels)) - tt.AssertEqual(t, "chan.name", "test", clist.Channels[0]["name"]) + tt.AssertMappedSet(t, "channels", []string{"tESt"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"test"}, clist.Channels, "internal_name") } tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{ @@ -125,14 +129,25 @@ func TestChannelNameNormalization(t *testing.T) { }, 409, apierr.CHANNEL_ALREADY_EXISTS) tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{ - "name": " T e s t ", + "name": "\rTeSt\n", }, 409, apierr.CHANNEL_ALREADY_EXISTS) { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan.len", 1, len(clist.Channels)) - tt.AssertEqual(t, "chan.name", "test", clist.Channels[0]["name"]) + tt.AssertMappedSet(t, "channels", []string{"tESt"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"test"}, clist.Channels, "internal_name") } + + tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{ + "name": " WeiRD_[\uF5FF]\\stUFf\r\n\t ", + }) + + { + clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) + tt.AssertMappedSet(t, "channels", []string{"tESt", "WeiRD_[\uF5FF]\\stUFf"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"test", "weird_[\uF5FF]\\stuff"}, clist.Channels, "internal_name") + } + } func TestListChannelsOwned(t *testing.T) { @@ -147,24 +162,26 @@ func TestListChannelsOwned(t *testing.T) { testdata := map[int][]string{ 0: {"main", "chattingchamber", "unicdhll", "promotions", "reminders"}, - 1: {"promotions"}, - 2: {}, - 3: {}, - 4: {}, - 5: {}, - 6: {}, - 7: {}, - 8: {}, - 9: {}, - 10: {}, - 11: {}, + 1: {"main", "private"}, + 2: {"main", "ü", "ö", "ä"}, + 3: {"main", "innovations", "reminders"}, + 4: {"main"}, + 5: {"main", "test1", "test2", "test3", "test4", "test5"}, + 6: {"main", "security", "lipsum"}, + 7: {"main"}, + 8: {"main"}, + 9: {"main", "manual@chan"}, + 10: {"main"}, + 11: {"promotions"}, 12: {}, 13: {}, + 14: {"", "chan_self_subscribed", "chan_self_unsub"}, //TODO these two have the interesting cases + 15: {"", "chan_other_nosub", "chan_other_request", "chan_other_request", "chan_other_accepted"}, //TODO these two have the interesting cases } for k, v := range testdata { r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels", data.User[k].UID)) - tt.AssertMappedSet(t, fmt.Sprintf("%d->chanlist", k), v, r0.Channels, "name") + tt.AssertMappedSet(t, fmt.Sprintf("%d->chanlist", k), v, r0.Channels, "internal_name") } } diff --git a/scnserver/test/message_test.go b/scnserver/test/message_test.go index 60f0745..c102ca3 100644 --- a/scnserver/test/message_test.go +++ b/scnserver/test/message_test.go @@ -166,7 +166,7 @@ func TestGetMessageFull(t *testing.T) { tt.AssertEqual(t, "msg.title", "Message_1", msgIn["title"]) tt.AssertEqual(t, "msg.content", content, msgIn["content"]) - tt.AssertEqual(t, "msg.channel", "demo-channel-007", msgIn["channel_name"]) + tt.AssertEqual(t, "msg.channel", "demo-channel-007", msgIn["channel_internal_name"]) tt.AssertEqual(t, "msg.msg_id", "580b5055-a9b5-4cee-b53c-28cf304d25b0", msgIn["usr_message_id"]) tt.AssertStrRepEqual(t, "msg.priority", 0, msgIn["priority"]) tt.AssertEqual(t, "msg.sender_name", "unit-test-[TestGetMessageFull]", msgIn["sender_name"]) diff --git a/scnserver/test/send_test.go b/scnserver/test/send_test.go index f9103fc..22a3861 100644 --- a/scnserver/test/send_test.go +++ b/scnserver/test/send_test.go @@ -61,7 +61,7 @@ func TestSendSimpleMessageJSON(t *testing.T) { msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"])) tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_001", msg1Get["title"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"]) } func TestSendSimpleMessageQuery(t *testing.T) { @@ -97,7 +97,7 @@ func TestSendSimpleMessageQuery(t *testing.T) { msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"])) tt.AssertStrRepEqual(t, "msg.title", "Hello World 2134", msg1Get["title"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"]) } func TestSendSimpleMessageForm(t *testing.T) { @@ -137,7 +137,7 @@ func TestSendSimpleMessageForm(t *testing.T) { msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"])) tt.AssertStrRepEqual(t, "msg.title", "Hello World 9999 [$$$]", msg1Get["title"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"]) } func TestSendSimpleMessageFormAndQuery(t *testing.T) { @@ -238,7 +238,7 @@ func TestSendSimpleMessageAlt1(t *testing.T) { msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"])) tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_001", msg1Get["title"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"]) } func TestSendContentMessage(t *testing.T) { @@ -278,12 +278,12 @@ func TestSendContentMessage(t *testing.T) { tt.AssertEqual(t, "len(messages)", 1, len(msgList1.Messages)) tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", msgList1.Messages[0]["title"]) tt.AssertStrRepEqual(t, "msg.content", "I am Content\nasdf", msgList1.Messages[0]["content"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msgList1.Messages[0]["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msgList1.Messages[0]["channel_internal_name"]) msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"])) tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", msg1Get["title"]) tt.AssertStrRepEqual(t, "msg.content", "I am Content\nasdf", msg1Get["content"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"]) } func TestSendWithSendername(t *testing.T) { @@ -326,13 +326,13 @@ func TestSendWithSendername(t *testing.T) { tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_xyz", msgList1.Messages[0]["title"]) tt.AssertStrRepEqual(t, "msg.content", "Unicode: 日本 - yäy\000\n\t\x00...", msgList1.Messages[0]["content"]) tt.AssertStrRepEqual(t, "msg.sender_name", "localhorst", msgList1.Messages[0]["sender_name"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msgList1.Messages[0]["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msgList1.Messages[0]["channel_internal_name"]) msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"])) tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_xyz", msg1Get["title"]) tt.AssertStrRepEqual(t, "msg.content", "Unicode: 日本 - yäy\000\n\t\x00...", msg1Get["content"]) tt.AssertStrRepEqual(t, "msg.sender_name", "localhorst", msg1Get["sender_name"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"]) } func TestSendLongContent(t *testing.T) { @@ -377,20 +377,20 @@ func TestSendLongContent(t *testing.T) { tt.AssertEqual(t, "len(messages)", 1, len(msgList1.Messages)) tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", msgList1.Messages[0]["title"]) tt.AssertNotStrRepEqual(t, "msg.content", longContent, msgList1.Messages[0]["content"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msgList1.Messages[0]["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msgList1.Messages[0]["channel_internal_name"]) tt.AssertStrRepEqual(t, "msg.trimmmed", true, msgList1.Messages[0]["trimmed"]) msgList2 := tt.RequestAuthGet[mglist](t, admintok, baseUrl, "/api/messages?trimmed=false") tt.AssertEqual(t, "len(messages)", 1, len(msgList2.Messages)) tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", msgList2.Messages[0]["title"]) tt.AssertStrRepEqual(t, "msg.content", longContent, msgList2.Messages[0]["content"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msgList2.Messages[0]["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msgList2.Messages[0]["channel_internal_name"]) tt.AssertStrRepEqual(t, "msg.trimmmed", false, msgList2.Messages[0]["trimmed"]) msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"])) tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", msg1Get["title"]) tt.AssertStrRepEqual(t, "msg.titcontentle", longContent, msg1Get["content"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"]) tt.AssertStrRepEqual(t, "msg.trimmmed", false, msg1Get["trimmed"]) } @@ -859,7 +859,7 @@ func TestSendWithTimestamp(t *testing.T) { tt.AssertEqual(t, "len(messages)", 1, len(msgList1.Messages)) tt.AssertStrRepEqual(t, "msg.title", "TTT", msgList1.Messages[0]["title"]) tt.AssertStrRepEqual(t, "msg.content", nil, msgList1.Messages[0]["sender_name"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msgList1.Messages[0]["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msgList1.Messages[0]["channel_internal_name"]) tm1, err := time.Parse(time.RFC3339Nano, msgList1.Messages[0]["timestamp"].(string)) tt.TestFailIfErr(t, err) @@ -868,7 +868,7 @@ func TestSendWithTimestamp(t *testing.T) { msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"])) tt.AssertStrRepEqual(t, "msg.title", "TTT", msg1Get["title"]) tt.AssertStrRepEqual(t, "msg.content", nil, msg1Get["sender_name"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"]) tmg1, err := time.Parse(time.RFC3339Nano, msg1Get["timestamp"].(string)) tt.TestFailIfErr(t, err) @@ -1022,7 +1022,7 @@ func TestSendCompat(t *testing.T) { msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"])) tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_001", msg1Get["title"]) - tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"]) + tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"]) msg2 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/send.php?user_key=%s&user_id=%d&title=%s", sendtok, uid, "HelloWorld_002"), nil) @@ -1098,8 +1098,8 @@ func TestSendToNewChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan.len", 1, len(clist.Channels)) - tt.AssertEqual(t, "chan.name", "main", clist.Channels[0]["name"]) + tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "internal_name") } tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{ @@ -1112,8 +1112,8 @@ func TestSendToNewChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan.len", 1, len(clist.Channels)) - tt.AssertEqual(t, "chan.name", "main", clist.Channels[0]["name"]) + tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "internal_name") } tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{ @@ -1126,9 +1126,8 @@ func TestSendToNewChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan-count", 2, len(clist.Channels)) - tt.AssertArrAny(t, "chan.has('main')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "main" }) - tt.AssertArrAny(t, "chan.has('test')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "test" }) + tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name") } tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{ @@ -1140,9 +1139,8 @@ func TestSendToNewChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan-count", 2, len(clist.Channels)) - tt.AssertArrAny(t, "chan.has('main')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "main" }) - tt.AssertArrAny(t, "chan.has('test')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "test" }) + tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name") } } @@ -1166,8 +1164,9 @@ func TestSendToManualChannel(t *testing.T) { } { - chan0 := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan-count", 0, len(chan0.Channels)) + clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) + tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "internal_name") } tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{ @@ -1178,8 +1177,8 @@ func TestSendToManualChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan.len", 1, len(clist.Channels)) - tt.AssertEqual(t, "chan.name", "main", clist.Channels[0]["name"]) + tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "internal_name") } tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{ @@ -1193,7 +1192,8 @@ func TestSendToManualChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) tt.AssertEqual(t, "chan.len", 1, len(clist.Channels)) - tt.AssertEqual(t, "chan.name", "main", clist.Channels[0]["name"]) + tt.AssertEqual(t, "chan.internal_name", "main", clist.Channels[0]["internal_name"]) + tt.AssertEqual(t, "chan.display_name", "main", clist.Channels[0]["display_name"]) } tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{ @@ -1202,9 +1202,8 @@ func TestSendToManualChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan-count", 2, len(clist.Channels)) - tt.AssertArrAny(t, "chan.has('main')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "main" }) - tt.AssertArrAny(t, "chan.has('test')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "test" }) + tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name") } tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{ @@ -1217,9 +1216,8 @@ func TestSendToManualChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan-count", 2, len(clist.Channels)) - tt.AssertArrAny(t, "chan.has('main')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "main" }) - tt.AssertArrAny(t, "chan.has('test')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "test" }) + tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name") } tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{ @@ -1231,9 +1229,8 @@ func TestSendToManualChannel(t *testing.T) { { clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid)) - tt.AssertEqual(t, "chan-count", 2, len(clist.Channels)) - tt.AssertArrAny(t, "chan.has('main')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "main" }) - tt.AssertArrAny(t, "chan.has('test')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "test" }) + tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name") + tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name") } } diff --git a/scnserver/test/util/common.go b/scnserver/test/util/common.go index 4ffc8cb..348377b 100644 --- a/scnserver/test/util/common.go +++ b/scnserver/test/util/common.go @@ -203,7 +203,14 @@ func AssertMultiNonEmpty(t *testing.T, key string, args ...any) { func AssertMappedSet[T langext.OrderedConstraint](t *testing.T, key string, expected []T, values []gin.H, objkey string) { - actual := langext.ArrMap(values, func(v gin.H) T { return v[objkey].(T) }) + actual := make([]T, 0) + for idx, vv := range values { + if tv, ok := vv[objkey].(T); ok { + actual = append(actual, tv) + } else { + TestFailFmt(t, "[%s]->[%d] is wrong type (expected: %T, actual: %T)", key, idx, *new(T), vv) + } + } langext.Sort(actual) langext.Sort(expected) diff --git a/scnserver/test/util/factory.go b/scnserver/test/util/factory.go index 4ed5da0..e52752a 100644 --- a/scnserver/test/util/factory.go +++ b/scnserver/test/util/factory.go @@ -86,8 +86,10 @@ var userExamples = []userex{ {9, true, "UniqueUnicorn", "Galaxy Quest", "2023.1", "ANDROID", "FCM_TOK_EX_010", ""}, {10, false, "", "", "", "", "", ""}, {11, false, "", "", "", "", "", "ANDROID|v2|PURCHASED:PRO_TOK_002"}, - {12, true, "ChanTester1", "StarfireXX", "1.x", "IOS", "FCM_TOK_EX_012", ""}, - {13, true, "ChanTester2", "StarfireXX", "1.x", "IOS", "FCM_TOK_EX_013", ""}, + {12, true, "NoMessageUser", "Ocean Explorer", "737edc01", "IOS", "FCM_TOK_EX_014", ""}, + {13, false, "EmptyUser", "", "", "", "", ""}, + {14, true, "ChanTester1", "StarfireXX", "1.x", "IOS", "FCM_TOK_EX_012", ""}, + {15, true, "ChanTester2", "StarfireXX", "1.x", "IOS", "FCM_TOK_EX_013", ""}, } var clientExamples = []clientex{ @@ -267,14 +269,14 @@ var messageExamples = []msgex{ {11, "Promotions", "", PX, AKEY, "Join Our VIP Club and Enjoy Exclusive Benefits", "Sign up for our VIP club and enjoy exclusive benefits like early access to sales, special offers, and personalized service. Don't miss out on this exclusive opportunity.", timeext.FromHours(2.32)}, {11, "Promotions", "", P2, SKEY, "Summer Clearance: Save Up to 75% on Your Favorite Products", "It's time for our annual summer clearance sale! Save up to 75% on your favorite products, from clothing and accessories to home decor and more.", timeext.FromHours(1.87)}, - {12, "", "", P0, SKEY, "New Product Launch", "We are excited to announce the launch of our new product, the XYZ widget", 0}, - {12, "chan_self_subscribed", "", P0, SKEY, "Important Update", "We have released a critical update", 0}, - {12, "chan_self_unsub", "", P0, SKEY, "Reminder: Upcoming Maintenance", "", 0}, + {14, "", "", P0, SKEY, "New Product Launch", "We are excited to announce the launch of our new product, the XYZ widget", 0}, + {14, "chan_self_subscribed", "", P0, SKEY, "Important Update", "We have released a critical update", 0}, + {14, "chan_self_unsub", "", P0, SKEY, "Reminder: Upcoming Maintenance", "", 0}, - {13, "", "", P0, SKEY, "New Feature Available", "ability to schedule appointments", 0}, - {13, "chan_other_nosub", "", P0, SKEY, "Account Suspended", "Please contact us", 0}, - {13, "chan_other_request", "", P0, SKEY, "Invitation to Beta Test", "", 0}, - {13, "chan_other_accepted", "", P0, SKEY, "New Blog Post", "Congratulations on your promotion! We are proud", 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_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}, } type DefData struct { @@ -373,6 +375,12 @@ func InitDefaultData(t *testing.T, ws *logic.Application) DefData { RequestPost[gin.H](t, baseUrl, "/", body) } + // create manual channels + + { + RequestAuthPost[Void](t, users[9].AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels", users[9].UID), gin.H{"name": "manual@chan"}) + } + // Sub/Unsub for Users 12+13 { diff --git a/scnserver/test/util/requests.go b/scnserver/test/util/requests.go index b608de0..7c9aa30 100644 --- a/scnserver/test/util/requests.go +++ b/scnserver/test/util/requests.go @@ -163,10 +163,10 @@ func RequestAny[TResult any](t *testing.T, akey string, method string, baseURL s return data } -func RequestAuthAnyShouldFail(t *testing.T, akey string, method string, baseURL string, urlSuffix string, body any, statusCode int, errcode apierr.APIError) { +func RequestAuthAnyShouldFail(t *testing.T, akey string, method string, baseURL string, urlSuffix string, body any, expectedStatusCode int, errcode apierr.APIError) { client := http.Client{} - TPrintf("[-> REQUEST] (%s) %s%s [%s] (should-fail with %d/%d)\n", method, baseURL, urlSuffix, langext.Conditional(akey == "", "NO AUTH", "AUTH"), statusCode, errcode) + TPrintf("[-> REQUEST] (%s) %s%s [%s] (should-fail with %d/%d)\n", method, baseURL, urlSuffix, langext.Conditional(akey == "", "NO AUTH", "AUTH"), expectedStatusCode, errcode) bytesbody := make([]byte, 0) contentType := "" @@ -224,17 +224,17 @@ func RequestAuthAnyShouldFail(t *testing.T, akey string, method string, baseURL TPrintln("") TPrintf("---------------- RESPONSE (%d) ----------------\n", resp.StatusCode) TPrintln(langext.TryPrettyPrintJson(string(respBodyBin))) - if (statusCode != 0 && resp.StatusCode != statusCode) || (statusCode == 0 && resp.StatusCode == 200) { + if (expectedStatusCode != 0 && resp.StatusCode != expectedStatusCode) || (expectedStatusCode == 0 && resp.StatusCode == 200) { TryPrintTraceObj("---------------- -------- ----------------", respBodyBin, "") } TPrintln("---------------- -------- ----------------") TPrintln("") - if statusCode != 0 && resp.StatusCode != statusCode { - TestFailFmt(t, "Statuscode != %d (expected failure)", statusCode) + if expectedStatusCode != 0 && resp.StatusCode != expectedStatusCode { + TestFailFmt(t, "Statuscode != %d (expected failure, but got %d)", expectedStatusCode, resp.StatusCode) } - if statusCode == 0 && resp.StatusCode == 200 { - TestFailFmt(t, "Statuscode == %d (expected failure)", resp.StatusCode) + if expectedStatusCode == 0 && resp.StatusCode == 200 { + TestFailFmt(t, "Statuscode == %d (expected any failure, but got %d)", resp.StatusCode, resp.StatusCode) } var data gin.H