Add various deleted flags to entities | Add active to subscriptions | Add DeleteUser && DeleteChannel endpoints [skip-tests]
All checks were successful
Build Docker and Deploy / Run Unit-Tests (push) Has been skipped
Build Docker and Deploy / Build Docker Container (push) Successful in 43s
Build Docker and Deploy / Deploy to Server (push) Successful in 16s

This commit is contained in:
2025-04-13 16:12:15 +02:00
parent aac34ef738
commit 8c0f0e3e8f
47 changed files with 2453 additions and 243 deletions

View File

@@ -1,13 +1,14 @@
package test
import (
"blackforestbytes.com/simplecloudnotifier/api/apierr"
tt "blackforestbytes.com/simplecloudnotifier/test/util"
"fmt"
"github.com/gin-gonic/gin"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"strings"
"testing"
"blackforestbytes.com/simplecloudnotifier/api/apierr"
tt "blackforestbytes.com/simplecloudnotifier/test/util"
"github.com/gin-gonic/gin"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
)
func TestCreateChannel(t *testing.T) {
@@ -1233,3 +1234,94 @@ func TestChannelMessageCounter(t *testing.T) {
assertCounter(6, 1, 3)
}
func TestDeleteChannel(t *testing.T) {
ws, baseUrl, stop := tt.StartSimpleWebserver(t)
defer stop()
// Initialize default data set
data := tt.InitDefaultData(t, ws)
// User 16 owns channels, User 1 will subscribe
user16 := data.User[16]
user1 := data.User[1]
// Find channel "Chan2" belonging to user 16
var chan2 tt.ChanData
for _, ch := range user16.Channels {
if ch.InternalName == "Chan2" {
chan2 = ch
break
}
}
tt.AssertNotEqual(t, "Channel Chan2 ID", "", chan2.ChannelID) // Ensure channel was found
// --- Subscribe User 1 to User 16's Chan2 ---
chanInfo := tt.RequestAuthGet[gin.H](t, user16.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s", user16.UID, chan2.ChannelID))
subKey := chanInfo["subscribe_key"].(string)
subReq := tt.RequestAuthPost[gin.H](t, user1.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions?chan_subscribe_key=%s", user1.UID, subKey), gin.H{
"channel_id": chan2.ChannelID, // Provide channel ID for subscription
})
subscriptionID := subReq["subscription_id"].(string)
// Confirm subscription by owner (user 16)
tt.RequestAuthPatch[gin.H](t, user16.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user16.UID, subscriptionID), gin.H{
"confirmed": true,
})
// --- Pre-checks ---
// 1. Check channel exists
tt.RequestAuthGet[gin.H](t, user16.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s", user16.UID, chan2.ChannelID))
// 2. Check channel messages exist (assuming mglist type from previous tests)
type msg struct {
MessageId string `json:"message_id"`
}
type mglist struct {
Messages []msg `json:"messages"`
}
msgs := tt.RequestAuthGet[mglist](t, user16.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s/messages", user16.UID, chan2.ChannelID))
tt.AssertTrue(t, "pre-check messages exist", len(msgs.Messages) > 0)
// 3. Check subscription exists for User 1 (outgoing)
type subobj struct {
SubscriptionId string `json:"subscription_id"`
}
type sublist struct {
Subscriptions []subobj `json:"subscriptions"`
}
subs1 := tt.RequestAuthGet[sublist](t, user1.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions?direction=outgoing", user1.UID))
foundSub1 := langext.ArrAny(subs1.Subscriptions, func(v subobj) bool { return v.SubscriptionId == subscriptionID })
tt.AssertTrue(t, "pre-check user1 subs outgoing", foundSub1)
// 4. Check subscription exists for User 16 (incoming)
subs16 := tt.RequestAuthGet[sublist](t, user16.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions?direction=incoming", user16.UID))
foundSub16 := langext.ArrAny(subs16.Subscriptions, func(v subobj) bool { return v.SubscriptionId == subscriptionID })
tt.AssertTrue(t, "pre-check user16 subs incoming", foundSub16)
// --- Delete Channel ---
tt.RequestAuthDelete[tt.Void](t, user16.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s", user16.UID, chan2.ChannelID), nil)
// --- Post-checks ---
// 1. Check channel fetch fails
tt.RequestAuthGetShouldFail(t, user16.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s", user16.UID, chan2.ChannelID), 404, apierr.CHANNEL_NOT_FOUND)
// 2. Check channel messages fetch fails
tt.RequestAuthGetShouldFail(t, user16.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s/messages", user16.UID, chan2.ChannelID), 404, apierr.CHANNEL_NOT_FOUND)
// Check subscriber cannot fetch messages either
tt.RequestAuthGetShouldFail(t, user1.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s/messages", user16.UID, chan2.ChannelID), 404, apierr.CHANNEL_NOT_FOUND) // Auth fails because subscription is gone
// 3. Check subscription is gone for User 1
subs1After := tt.RequestAuthGet[sublist](t, user1.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions?direction=outgoing", user1.UID))
foundSub1After := langext.ArrAny(subs1After.Subscriptions, func(v subobj) bool { return v.SubscriptionId == subscriptionID })
tt.AssertEqual(t, "post-check user1 subs outgoing", false, foundSub1After)
// 4. Check subscription is gone for User 16
subs16After := tt.RequestAuthGet[sublist](t, user16.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions?direction=incoming", user16.UID))
foundSub16After := langext.ArrAny(subs16After.Subscriptions, func(v subobj) bool { return v.SubscriptionId == subscriptionID })
tt.AssertEqual(t, "post-check user16 subs incoming", false, foundSub16After)
}

View File

@@ -304,6 +304,8 @@ func TestPrimaryDB_Migrate_from_3_to_latest(t *testing.T) {
tt.AssertAny(dbf3)
tt.AssertAny(conf)
schemavers := 3
{
url := fmt.Sprintf("file:%s", dbf1)
@@ -312,8 +314,6 @@ func TestPrimaryDB_Migrate_from_3_to_latest(t *testing.T) {
qqdb := sq.NewDB(xdb, sq.DBOptions{})
schemavers := 3
dbschema := schema.PrimarySchema[schemavers]
_, err = qqdb.Exec(ctx, dbschema.SQL, sq.PP{})
@@ -351,7 +351,7 @@ func TestPrimaryDB_Migrate_from_3_to_latest(t *testing.T) {
schema1, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema1", 3, schema1)
tt.AssertEqual(t, "schema1", schemavers, schema1)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
@@ -400,6 +400,8 @@ func TestPrimaryDB_Migrate_from_4_to_latest(t *testing.T) {
tt.AssertAny(dbf3)
tt.AssertAny(conf)
schemavers := 4
{
url := fmt.Sprintf("file:%s", dbf1)
@@ -408,8 +410,6 @@ func TestPrimaryDB_Migrate_from_4_to_latest(t *testing.T) {
qqdb := sq.NewDB(xdb, sq.DBOptions{})
schemavers := 4
dbschema := schema.PrimarySchema[schemavers]
_, err = qqdb.Exec(ctx, dbschema.SQL, sq.PP{})
@@ -447,7 +447,391 @@ func TestPrimaryDB_Migrate_from_4_to_latest(t *testing.T) {
schema1, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema1", 4, schema1)
tt.AssertEqual(t, "schema1", schemavers, schema1)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
//================================================
{
err = db1.Migrate(ctx)
tt.TestFailIfErr(t, err)
}
//================================================
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schema2, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema2", schema.PrimarySchemaVersion, schema2)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schemHashDB, err := sq.HashSqliteDatabase(tctx, db1.DB())
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schemHashDB", schema.PrimarySchema[schema.PrimarySchemaVersion].Hash, schemHashDB)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
err = db1.Stop(ctx)
tt.TestFailIfErr(t, err)
}
}
func TestPrimaryDB_Migrate_from_5_to_latest(t *testing.T) {
dbf1, dbf2, dbf3, conf, stop := tt.StartSimpleTestspace(t)
defer stop()
ctx := context.Background()
tt.AssertAny(dbf1)
tt.AssertAny(dbf2)
tt.AssertAny(dbf3)
tt.AssertAny(conf)
schemavers := 5
{
url := fmt.Sprintf("file:%s", dbf1)
xdb, err := sqlx.Open("sqlite3", url)
tt.TestFailIfErr(t, err)
qqdb := sq.NewDB(xdb, sq.DBOptions{})
dbschema := schema.PrimarySchema[schemavers]
_, err = qqdb.Exec(ctx, dbschema.SQL, sq.PP{})
tt.TestFailIfErr(t, err)
_, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{
"key": "schema",
"val": schemavers,
})
_, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{
"key": "schema_hash",
"val": dbschema.Hash,
})
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schemHashDB, err := sq.HashSqliteDatabase(tctx, qqdb)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schemHashDB", dbschema.Hash, schemHashDB)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
err = qqdb.Exit()
tt.TestFailIfErr(t, err)
}
{
db1, err := primary.NewPrimaryDatabase(conf)
tt.TestFailIfErr(t, err)
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schema1, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema1", schemavers, schema1)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
//================================================
{
err = db1.Migrate(ctx)
tt.TestFailIfErr(t, err)
}
//================================================
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schema2, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema2", schema.PrimarySchemaVersion, schema2)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schemHashDB, err := sq.HashSqliteDatabase(tctx, db1.DB())
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schemHashDB", schema.PrimarySchema[schema.PrimarySchemaVersion].Hash, schemHashDB)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
err = db1.Stop(ctx)
tt.TestFailIfErr(t, err)
}
}
func TestPrimaryDB_Migrate_from_6_to_latest(t *testing.T) {
dbf1, dbf2, dbf3, conf, stop := tt.StartSimpleTestspace(t)
defer stop()
ctx := context.Background()
tt.AssertAny(dbf1)
tt.AssertAny(dbf2)
tt.AssertAny(dbf3)
tt.AssertAny(conf)
schemavers := 6
{
url := fmt.Sprintf("file:%s", dbf1)
xdb, err := sqlx.Open("sqlite3", url)
tt.TestFailIfErr(t, err)
qqdb := sq.NewDB(xdb, sq.DBOptions{})
dbschema := schema.PrimarySchema[schemavers]
_, err = qqdb.Exec(ctx, dbschema.SQL, sq.PP{})
tt.TestFailIfErr(t, err)
_, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{
"key": "schema",
"val": schemavers,
})
_, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{
"key": "schema_hash",
"val": dbschema.Hash,
})
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schemHashDB, err := sq.HashSqliteDatabase(tctx, qqdb)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schemHashDB", dbschema.Hash, schemHashDB)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
err = qqdb.Exit()
tt.TestFailIfErr(t, err)
}
{
db1, err := primary.NewPrimaryDatabase(conf)
tt.TestFailIfErr(t, err)
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schema1, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema1", schemavers, schema1)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
//================================================
{
err = db1.Migrate(ctx)
tt.TestFailIfErr(t, err)
}
//================================================
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schema2, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema2", schema.PrimarySchemaVersion, schema2)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schemHashDB, err := sq.HashSqliteDatabase(tctx, db1.DB())
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schemHashDB", schema.PrimarySchema[schema.PrimarySchemaVersion].Hash, schemHashDB)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
err = db1.Stop(ctx)
tt.TestFailIfErr(t, err)
}
}
func TestPrimaryDB_Migrate_from_7_to_latest(t *testing.T) {
dbf1, dbf2, dbf3, conf, stop := tt.StartSimpleTestspace(t)
defer stop()
ctx := context.Background()
tt.AssertAny(dbf1)
tt.AssertAny(dbf2)
tt.AssertAny(dbf3)
tt.AssertAny(conf)
schemavers := 7
{
url := fmt.Sprintf("file:%s", dbf1)
xdb, err := sqlx.Open("sqlite3", url)
tt.TestFailIfErr(t, err)
qqdb := sq.NewDB(xdb, sq.DBOptions{})
dbschema := schema.PrimarySchema[schemavers]
_, err = qqdb.Exec(ctx, dbschema.SQL, sq.PP{})
tt.TestFailIfErr(t, err)
_, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{
"key": "schema",
"val": schemavers,
})
_, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{
"key": "schema_hash",
"val": dbschema.Hash,
})
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schemHashDB, err := sq.HashSqliteDatabase(tctx, qqdb)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schemHashDB", dbschema.Hash, schemHashDB)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
err = qqdb.Exit()
tt.TestFailIfErr(t, err)
}
{
db1, err := primary.NewPrimaryDatabase(conf)
tt.TestFailIfErr(t, err)
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schema1, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema1", schemavers, schema1)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
//================================================
{
err = db1.Migrate(ctx)
tt.TestFailIfErr(t, err)
}
//================================================
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schema2, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema2", schema.PrimarySchemaVersion, schema2)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schemHashDB, err := sq.HashSqliteDatabase(tctx, db1.DB())
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schemHashDB", schema.PrimarySchema[schema.PrimarySchemaVersion].Hash, schemHashDB)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
err = db1.Stop(ctx)
tt.TestFailIfErr(t, err)
}
}
func TestPrimaryDB_Migrate_from_8_to_latest(t *testing.T) {
dbf1, dbf2, dbf3, conf, stop := tt.StartSimpleTestspace(t)
defer stop()
ctx := context.Background()
tt.AssertAny(dbf1)
tt.AssertAny(dbf2)
tt.AssertAny(dbf3)
tt.AssertAny(conf)
schemavers := 8
{
url := fmt.Sprintf("file:%s", dbf1)
xdb, err := sqlx.Open("sqlite3", url)
tt.TestFailIfErr(t, err)
qqdb := sq.NewDB(xdb, sq.DBOptions{})
dbschema := schema.PrimarySchema[schemavers]
_, err = qqdb.Exec(ctx, dbschema.SQL, sq.PP{})
tt.TestFailIfErr(t, err)
_, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{
"key": "schema",
"val": schemavers,
})
_, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{
"key": "schema_hash",
"val": dbschema.Hash,
})
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schemHashDB, err := sq.HashSqliteDatabase(tctx, qqdb)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schemHashDB", dbschema.Hash, schemHashDB)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
err = qqdb.Exit()
tt.TestFailIfErr(t, err)
}
{
db1, err := primary.NewPrimaryDatabase(conf)
tt.TestFailIfErr(t, err)
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schema1, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema1", schemavers, schema1)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)

View File

@@ -1,6 +1,8 @@
package test
import (
"database/sql"
"github.com/glebarez/go-sqlite"
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"os"
@@ -12,5 +14,9 @@ func TestMain(m *testing.M) {
exerr.Init(exerr.ErrorPackageConfigInit{ZeroLogErrTraces: langext.PFalse, ZeroLogAllTraces: langext.PFalse})
}
if !langext.InArray("sqlite3", sql.Drivers()) {
sqlite.RegisterAsSQLITE3()
}
os.Exit(m.Run())
}

View File

@@ -1,15 +1,16 @@
package test
import (
"blackforestbytes.com/simplecloudnotifier/api/apierr"
"blackforestbytes.com/simplecloudnotifier/models"
tt "blackforestbytes.com/simplecloudnotifier/test/util"
"fmt"
"github.com/gin-gonic/gin"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"net/url"
"testing"
"time"
"blackforestbytes.com/simplecloudnotifier/api/apierr"
"blackforestbytes.com/simplecloudnotifier/models"
tt "blackforestbytes.com/simplecloudnotifier/test/util"
"github.com/gin-gonic/gin"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
)
func TestSearchMessageFTSSimple(t *testing.T) {
@@ -901,3 +902,293 @@ func TestListMessagesStringSearch(t *testing.T) {
tt.AssertEqual(t, "msgList.filter["+testdata.Name+"].len", testdata.Count, msgList.TotalCount)
}
}
func TestDeactivatedSubscriptionListMessages(t *testing.T) {
ws, baseUrl, stop := tt.StartSimpleWebserver(t)
defer stop()
data := tt.InitDefaultData(t, ws)
type subobj struct {
ChannelId string `json:"channel_id"`
ChannelInternalName string `json:"channel_internal_name"`
ChannelOwnerUserId string `json:"channel_owner_user_id"`
Confirmed bool `json:"confirmed"`
Active bool `json:"active"`
SubscriberUserId string `json:"subscriber_user_id"`
SubscriptionId string `json:"subscription_id"`
TimestampCreated string `json:"timestamp_created"`
}
type msg struct {
Title string `json:"title"`
}
type mglist struct {
Messages []msg `json:"messages"`
}
user14 := data.User[14] // Subscriber
user15 := data.User[15] // Owner
chanName := "chan_other_accepted"
subscriptionID, channelID := tt.FindSubscriptionByChanName(t, baseUrl, user14, user15.UID, chanName)
gsub0 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertTrue(t, "gsub1.active", gsub0.Active)
tt.AssertTrue(t, "gsub1.confirmed", gsub0.Confirmed)
tt.RequestAuthPatch[gin.H](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), gin.H{"active": false})
gsub1 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertFalse(t, "gsub1.active", gsub1.Active)
tt.AssertTrue(t, "gsub1.confirmed", gsub1.Confirmed)
gsub2 := tt.RequestAuthGet[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID))
tt.AssertFalse(t, "gsub2.active", gsub2.Active)
tt.AssertTrue(t, "gsub2.confirmed", gsub2.Confirmed)
newMessageTitle := langext.RandBase62(48)
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
"key": user15.AdminKey,
"user_id": user15.UID,
"channel": chanName,
"title": newMessageTitle,
})
// subscription.active == false && subscription.confirmed == true
{
msgListSub := tt.RequestAuthGet[mglist](t, user14.AdminKey, baseUrl, "/api/v2/messages")
foundActivatedMessageSub := false
for _, m := range msgListSub.Messages {
if m.Title == newMessageTitle {
foundActivatedMessageSub = true
break
}
}
tt.AssertFalse(t, "foundActivatedMessageSub", foundActivatedMessageSub)
msgListDirect := tt.RequestAuthGet[mglist](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s/messages", user15.UID, channelID))
foundActivatedMessageDirect := false
for _, m := range msgListDirect.Messages {
if m.Title == newMessageTitle {
foundActivatedMessageDirect = true
break
}
}
tt.AssertTrue(t, "foundActivatedMessageDirect", foundActivatedMessageDirect)
}
tt.RequestAuthPatch[gin.H](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), gin.H{"active": true})
// subscription.active == true && subscription.confirmed == true
{
msgListSub := tt.RequestAuthGet[mglist](t, user14.AdminKey, baseUrl, "/api/v2/messages")
foundActivatedMessageSub := false
for _, m := range msgListSub.Messages {
if m.Title == newMessageTitle {
foundActivatedMessageSub = true
break
}
}
tt.AssertTrue(t, "foundActivatedMessageSub", foundActivatedMessageSub)
msgListDirect := tt.RequestAuthGet[mglist](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s/messages", user15.UID, channelID))
foundActivatedMessageDirect := false
for _, m := range msgListDirect.Messages {
if m.Title == newMessageTitle {
foundActivatedMessageDirect = true
break
}
}
tt.AssertTrue(t, "foundActivatedMessageDirect", foundActivatedMessageDirect)
}
tt.RequestAuthPatch[gin.H](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID), gin.H{"confirmed": false})
// subscription.active == true && subscription.confirmed == false
{
msgListSub := tt.RequestAuthGet[mglist](t, user14.AdminKey, baseUrl, "/api/v2/messages")
foundActivatedMessageSub := false
for _, m := range msgListSub.Messages {
if m.Title == newMessageTitle {
foundActivatedMessageSub = true
break
}
}
tt.AssertFalse(t, "foundActivatedMessageSub", foundActivatedMessageSub)
tt.RequestAuthGetShouldFail(t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s/messages", user15.UID, channelID), 401, apierr.USER_AUTH_FAILED)
}
tt.RequestAuthPatch[gin.H](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), gin.H{"active": false})
// subscription.active == false && subscription.confirmed == false
{
msgListSub := tt.RequestAuthGet[mglist](t, user14.AdminKey, baseUrl, "/api/v2/messages")
foundActivatedMessageSub := false
for _, m := range msgListSub.Messages {
if m.Title == newMessageTitle {
foundActivatedMessageSub = true
break
}
}
tt.AssertFalse(t, "foundActivatedMessageSub", foundActivatedMessageSub)
tt.RequestAuthGetShouldFail(t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s/messages", user15.UID, channelID), 401, apierr.USER_AUTH_FAILED)
}
tt.RequestAuthPatch[gin.H](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID), gin.H{"confirmed": true})
tt.RequestAuthPatch[gin.H](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), gin.H{"active": true})
// subscription.active == true && subscription.confirmed == true
{
msgListSub := tt.RequestAuthGet[mglist](t, user14.AdminKey, baseUrl, "/api/v2/messages")
foundActivatedMessageSub := false
for _, m := range msgListSub.Messages {
if m.Title == newMessageTitle {
foundActivatedMessageSub = true
break
}
}
tt.AssertTrue(t, "foundActivatedMessageSub", foundActivatedMessageSub)
msgListDirect := tt.RequestAuthGet[mglist](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s/messages", user15.UID, channelID))
foundActivatedMessageDirect := false
for _, m := range msgListDirect.Messages {
if m.Title == newMessageTitle {
foundActivatedMessageDirect = true
break
}
}
tt.AssertTrue(t, "foundActivatedMessageDirect", foundActivatedMessageDirect)
}
tt.RequestAuthDelete[gin.H](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), gin.H{})
// subscription -> deleted
{
msgListSub := tt.RequestAuthGet[mglist](t, user14.AdminKey, baseUrl, "/api/v2/messages")
foundActivatedMessageSub := false
for _, m := range msgListSub.Messages {
if m.Title == newMessageTitle {
foundActivatedMessageSub = true
break
}
}
tt.AssertFalse(t, "foundActivatedMessageSub", foundActivatedMessageSub)
tt.RequestAuthGetShouldFail(t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s/messages", user15.UID, channelID), 401, apierr.USER_AUTH_FAILED)
}
}
func TestActiveSubscriptionListMessages(t *testing.T) {
ws, baseUrl, stop := tt.StartSimpleWebserver(t)
defer stop()
data := tt.InitDefaultData(t, ws)
type subobj struct {
ChannelId string `json:"channel_id"`
ChannelInternalName string `json:"channel_internal_name"`
ChannelOwnerUserId string `json:"channel_owner_user_id"`
Confirmed bool `json:"confirmed"`
Active bool `json:"active"`
SubscriberUserId string `json:"subscriber_user_id"`
SubscriptionId string `json:"subscription_id"`
TimestampCreated string `json:"timestamp_created"`
}
type msg struct {
Title string `json:"title"`
}
type mglist struct {
Messages []msg `json:"messages"`
}
user14 := data.User[14] // Subscriber
user15 := data.User[15] // Owner
chanName := "chan_other_accepted"
subscriptionID, _ := tt.FindSubscriptionByChanName(t, baseUrl, user14, user15.UID, chanName)
gsub1 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertTrue(t, "gsub1.active", gsub1.Active)
newMessageTitle := langext.RandBase62(48)
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
"key": user15.AdminKey,
"user_id": user15.UID,
"channel": chanName,
"title": newMessageTitle,
})
{
msgListSub := tt.RequestAuthGet[mglist](t, user14.AdminKey, baseUrl, "/api/v2/messages")
foundActivatedMessageSub := false
for _, m := range msgListSub.Messages {
if m.Title == newMessageTitle {
foundActivatedMessageSub = true
break
}
}
tt.AssertTrue(t, "foundActivatedMessageSub", foundActivatedMessageSub)
}
}
func TestUnconfirmedSubscriptionListMessages(t *testing.T) {
ws, baseUrl, stop := tt.StartSimpleWebserver(t)
defer stop()
data := tt.InitDefaultData(t, ws)
type subobj struct {
ChannelId string `json:"channel_id"`
ChannelInternalName string `json:"channel_internal_name"`
ChannelOwnerUserId string `json:"channel_owner_user_id"`
Confirmed bool `json:"confirmed"`
Active bool `json:"active"`
SubscriberUserId string `json:"subscriber_user_id"`
SubscriptionId string `json:"subscription_id"`
TimestampCreated string `json:"timestamp_created"`
}
type msg struct {
Title string `json:"title"`
}
type mglist struct {
Messages []msg `json:"messages"`
}
user14 := data.User[14] // Subscriber
user15 := data.User[15] // Owner
chanName := "chan_other_request"
subscriptionID, _ := tt.FindSubscriptionByChanName(t, baseUrl, user14, user15.UID, chanName)
gsub1 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertTrue(t, "gsub1.active", gsub1.Active)
tt.AssertFalse(t, "gsub1.confirmed", gsub1.Confirmed)
newMessageTitle := langext.RandBase62(48)
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
"key": user15.AdminKey,
"user_id": user15.UID,
"channel": chanName,
"title": newMessageTitle,
})
msgList := tt.RequestAuthGet[mglist](t, user14.AdminKey, baseUrl, "/api/v2/messages")
foundActivatedMessage := false
for _, m := range msgList.Messages {
if m.Title == newMessageTitle {
foundActivatedMessage = true
break
}
}
tt.AssertFalse(t, "foundActivatedMessage", foundActivatedMessage)
}

View File

@@ -33,6 +33,7 @@ func TestResponseChannel(t *testing.T) {
"channel_internal_name": "string",
"timestamp_created": "rfc3339",
"confirmed": "bool",
"active": "bool",
},
})
}
@@ -179,6 +180,7 @@ func TestResponseMessage(t *testing.T) {
"sender_user_id": "id",
"channel_internal_name": "string",
"channel_id": "id",
"channel_owner_user_id": "id",
"sender_name": "string",
"sender_ip": "string",
"timestamp": "rfc3339",
@@ -207,6 +209,7 @@ func TestResponseSubscription(t *testing.T) {
"channel_internal_name": "string",
"timestamp_created": "rfc3339",
"confirmed": "bool",
"active": "bool",
})
}

View File

@@ -7,6 +7,7 @@ import (
tt "blackforestbytes.com/simplecloudnotifier/test/util"
"fmt"
"github.com/gin-gonic/gin"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"math/rand/v2"
"net/url"
"strings"
@@ -1807,3 +1808,176 @@ func TestSendWithPermissionSendKey(t *testing.T) {
func TestSendDeliveryRetry(t *testing.T) {
t.SkipNow() //TODO
}
func TestDeactivatedSubscriptionReceiveMessage(t *testing.T) {
ws, baseUrl, stop := tt.StartSimpleWebserver(t)
defer stop()
data := tt.InitDefaultData(t, ws)
type subobj struct {
ChannelId string `json:"channel_id"`
ChannelInternalName string `json:"channel_internal_name"`
ChannelOwnerUserId string `json:"channel_owner_user_id"`
Confirmed bool `json:"confirmed"`
Active bool `json:"active"`
SubscriberUserId string `json:"subscriber_user_id"`
SubscriptionId string `json:"subscription_id"`
TimestampCreated string `json:"timestamp_created"`
}
pusher := ws.Pusher.(*push.TestSink)
pusher.Clear()
user14 := data.User[14] // Subscriber
user15 := data.User[15] // Owner
chanName := "chan_other_accepted"
subscriptionID, _ := tt.FindSubscriptionByChanName(t, baseUrl, user14, user15.UID, chanName)
gsub0 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertTrue(t, "gsub0.active", gsub0.Active)
tt.AssertTrue(t, "gsub0.confirmed", gsub0.Confirmed)
tt.RequestAuthPatch[gin.H](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), gin.H{"active": false})
gsub1 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertFalse(t, "gsub1.active", gsub1.Active)
tt.AssertTrue(t, "gsub1.confirmed", gsub1.Confirmed)
// sub is active=false && confirmed=true
{
newMessageTitle := langext.RandBase62(48)
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
"key": user15.AdminKey,
"user_id": user15.UID,
"channel": chanName,
"title": newMessageTitle,
})
pushObj := langext.ArrFirstOrNil(pusher.Data, func(d push.SinkData) bool { return d.Client.UserID.String() == user14.UID })
tt.AssertNil(t, "pushObj", pushObj)
pusher.Clear()
}
tt.RequestAuthPatch[gin.H](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), gin.H{"active": true})
gsub2 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertTrue(t, "gsub1.active", gsub2.Active)
tt.AssertTrue(t, "gsub1.confirmed", gsub2.Confirmed)
// sub is active=true && confirmed=true
{
newMessageTitle := langext.RandBase62(48)
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
"key": user15.AdminKey,
"user_id": user15.UID,
"channel": chanName,
"title": newMessageTitle,
})
pushObj := langext.ArrFirstOrNil(pusher.Data, func(d push.SinkData) bool { return d.Client.UserID.String() == user14.UID })
tt.AssertNotNil(t, "pushObj", pushObj)
tt.AssertStrRepEqual(t, "msg.title", newMessageTitle, pushObj.Message.Title)
tt.AssertStrRepEqual(t, "msg.content", nil, pushObj.Message.Content)
tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pushObj.Message.MessageID)
tt.AssertStrRepEqual(t, "msg.scn_msg_id", user14.UID, pushObj.Client.UserID)
pusher.Clear()
}
tt.RequestAuthPatch[gin.H](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID), gin.H{"confirmed": false})
gsub3 := tt.RequestAuthGet[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID))
tt.AssertTrue(t, "gsub1.active", gsub3.Active)
tt.AssertFalse(t, "gsub1.confirmed", gsub3.Confirmed)
// sub is active=true && confirmed=false
{
newMessageTitle := langext.RandBase62(48)
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
"key": user15.AdminKey,
"user_id": user15.UID,
"channel": chanName,
"title": newMessageTitle,
})
pushObj := langext.ArrFirstOrNil(pusher.Data, func(d push.SinkData) bool { return d.Client.UserID.String() == user14.UID })
tt.AssertNil(t, "pushObj", pushObj)
pusher.Clear()
}
tt.RequestAuthDelete[gin.H](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID), gin.H{})
tt.RequestAuthGetShouldFail(t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), 404, apierr.SUBSCRIPTION_NOT_FOUND)
tt.RequestAuthGetShouldFail(t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID), 404, apierr.SUBSCRIPTION_NOT_FOUND)
// sub is deleted
{
newMessageTitle := langext.RandBase62(48)
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
"key": user15.AdminKey,
"user_id": user15.UID,
"channel": chanName,
"title": newMessageTitle,
})
pushObj := langext.ArrFirstOrNil(pusher.Data, func(d push.SinkData) bool { return d.Client.UserID.String() == user14.UID })
tt.AssertNil(t, "pushObj", pushObj)
pusher.Clear()
}
}
func TestActiveSubscriptionReceiveMessage(t *testing.T) {
ws, baseUrl, stop := tt.StartSimpleWebserver(t)
defer stop()
data := tt.InitDefaultData(t, ws)
type subobj struct {
ChannelId string `json:"channel_id"`
ChannelInternalName string `json:"channel_internal_name"`
ChannelOwnerUserId string `json:"channel_owner_user_id"`
Confirmed bool `json:"confirmed"`
Active bool `json:"active"`
SubscriberUserId string `json:"subscriber_user_id"`
SubscriptionId string `json:"subscription_id"`
TimestampCreated string `json:"timestamp_created"`
}
pusher := ws.Pusher.(*push.TestSink)
pusher.Clear()
user14 := data.User[14] // Subscriber
user15 := data.User[15] // Owner
chanName := "chan_other_accepted"
subscriptionID, _ := tt.FindSubscriptionByChanName(t, baseUrl, user14, user15.UID, chanName)
gsub1 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertTrue(t, "gsub1.active", gsub1.Active)
tt.AssertTrue(t, "gsub1.confirmed", gsub1.Confirmed)
newMessageTitle := langext.RandBase62(48)
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
"key": user15.AdminKey,
"user_id": user15.UID,
"channel": chanName,
"title": newMessageTitle,
})
pushObj := langext.ArrFirstOrNil(pusher.Data, func(d push.SinkData) bool { return d.Client.UserID.String() == user14.UID })
tt.AssertNotNil(t, "pushObj", pushObj)
tt.AssertStrRepEqual(t, "msg.title", newMessageTitle, pushObj.Message.Title)
tt.AssertStrRepEqual(t, "msg.content", nil, pushObj.Message.Content)
tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pushObj.Message.MessageID)
tt.AssertStrRepEqual(t, "msg.scn_msg_id", user14.UID, pushObj.Client.UserID)
pusher.Clear()
}

View File

@@ -1,12 +1,13 @@
package test
import (
"fmt"
"testing"
"blackforestbytes.com/simplecloudnotifier/api/apierr"
tt "blackforestbytes.com/simplecloudnotifier/test/util"
"fmt"
"github.com/gin-gonic/gin"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"testing"
)
func TestListSubscriptionsOfUser(t *testing.T) {
@@ -1240,3 +1241,106 @@ func TestCancelOutgoingSubscription(t *testing.T) {
tt.RequestAuthGetShouldFail(t, data1.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", data1.UID, sub1.SubscriptionId), 404, apierr.SUBSCRIPTION_NOT_FOUND)
}
}
func TestSubscriptionDeactivate(t *testing.T) {
ws, baseUrl, stop := tt.StartSimpleWebserver(t)
defer stop()
data := tt.InitDefaultData(t, ws)
user14 := data.User[14]
user15 := data.User[15]
chanName := "chan_other_accepted"
subscriptionID, _ := tt.FindSubscriptionByChanName(t, baseUrl, user14, user15.UID, chanName)
type subobj struct {
SubscriptionId string `json:"subscription_id"`
Active bool `json:"active"`
}
initialSub := tt.RequestAuthGet[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID))
tt.AssertTrue(t, "initialSub.Active", initialSub.Active)
// subscriber deactivates
{
tt.RequestAuthPatch[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), gin.H{
"active": false,
})
sub1 := tt.RequestAuthGet[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID))
tt.AssertEqual(t, "sub1.Active", false, sub1.Active)
sub2 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertEqual(t, "sub2.Active", false, sub2.Active)
}
// subscriber activates
{
tt.RequestAuthPatch[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), gin.H{
"active": true,
})
sub1 := tt.RequestAuthGet[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID))
tt.AssertEqual(t, "sub1.Active", true, sub1.Active)
sub2 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertEqual(t, "sub2.Active", true, sub2.Active)
}
// owner deactivates
{
tt.RequestAuthPatch[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID), gin.H{
"active": false,
})
sub1 := tt.RequestAuthGet[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID))
tt.AssertEqual(t, "sub1.Active", false, sub1.Active)
sub2 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertEqual(t, "sub2.Active", false, sub2.Active)
}
// owner activates
{
tt.RequestAuthPatch[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID), gin.H{
"active": true,
})
sub1 := tt.RequestAuthGet[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID))
tt.AssertEqual(t, "sub1.Active", true, sub1.Active)
sub2 := tt.RequestAuthGet[subobj](t, user15.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user15.UID, subscriptionID))
tt.AssertEqual(t, "sub2.Active", true, sub2.Active)
}
}
func TestSubscriptionActivate(t *testing.T) {
ws, baseUrl, stop := tt.StartSimpleWebserver(t)
defer stop()
data := tt.InitDefaultData(t, ws)
user14 := data.User[14]
user15 := data.User[15]
chanName := "chan_other_accepted"
subscriptionID, _ := tt.FindSubscriptionByChanName(t, baseUrl, user14, user15.UID, chanName)
type subobj struct {
SubscriptionId string `json:"subscription_id"`
Active bool `json:"active"`
}
tt.RequestAuthPatch[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), gin.H{
"active": false,
})
deactivatedSub := tt.RequestAuthGet[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID))
tt.AssertEqual(t, "deactivatedSub.Active", false, deactivatedSub.Active)
tt.RequestAuthPatch[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID), gin.H{
"active": true,
})
finalSub := tt.RequestAuthGet[subobj](t, user14.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", user14.UID, subscriptionID))
tt.AssertTrue(t, "finalSub.Active", finalSub.Active)
}

View File

@@ -185,8 +185,6 @@ func TestFailedUgradeUserToPro(t *testing.T) {
}
func TestDeleteUser(t *testing.T) {
t.SkipNow() // TODO DeleteUser Not implemented
_, baseUrl, stop := tt.StartSimpleWebserver(t)
defer stop()
@@ -199,15 +197,18 @@ func TestDeleteUser(t *testing.T) {
uid := fmt.Sprintf("%v", r0["user_id"])
admintok := r0["admin_key"].(string)
readtok := r0["read_key"].(string)
sendtok := r0["send_key"].(string)
tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid)
tt.RequestAuthDeleteShouldFail(t, admintok, baseUrl, "/api/v2/users/"+uid, nil, 401, apierr.USER_AUTH_FAILED)
tt.RequestAuthDeleteShouldFail(t, readtok, baseUrl, "/api/v2/users/"+uid, nil, 401, apierr.USER_AUTH_FAILED)
tt.RequestAuthDeleteShouldFail(t, sendtok, baseUrl, "/api/v2/users/"+uid, nil, 401, apierr.USER_AUTH_FAILED)
tt.RequestAuthDelete[tt.Void](t, admintok, baseUrl, "/api/v2/users/"+uid, nil)
tt.RequestAuthGetShouldFail(t, admintok, baseUrl, "/api/v2/users/"+uid, 404, apierr.USER_NOT_FOUND)
tt.RequestAuthGetShouldFail(t, admintok, baseUrl, "/api/v2/users/"+uid, 401, apierr.USER_AUTH_FAILED)
}
func TestCreateProUser(t *testing.T) {

View File

@@ -177,6 +177,14 @@ func AssertTrue(t *testing.T, key string, v bool) {
}
}
func AssertFalse(t *testing.T, key string, v bool) {
if v {
t.Errorf("AssertFalse(%s) failed", key)
t.Error(string(debug.Stack()))
t.FailNow()
}
}
func AssertNotDefault[T comparable](t *testing.T, key string, v T) {
if v == *new(T) {
t.Errorf("AssertNotDefault(%s) failed", key)
@@ -307,7 +315,7 @@ func AssertAny(v any) {
}
func AssertNil(t *testing.T, key string, v any) {
if v != nil {
if !langext.IsNil(v) {
t.Errorf("AssertNil(%s) failed - actual value:\n%+v", key, v)
t.Error(string(debug.Stack()))
t.FailNow()
@@ -315,7 +323,7 @@ func AssertNil(t *testing.T, key string, v any) {
}
func AssertNotNil(t *testing.T, key string, v any) {
if v == nil {
if langext.IsNil(v) {
t.Errorf("AssertNotNil(%s) failed", key)
t.Error(string(debug.Stack()))
t.FailNow()

View File

@@ -467,8 +467,10 @@ func InitDefaultData(t *testing.T, ws *logic.Application) DefData {
users[i].Subscriptions = langext.ArrMap(r0.Subs, func(v ssub) string { return v.ID })
}
// Sub/Unsub for Users 12+13
// Sub/Unsub for Users 14+15
// - User 14 is not subscribed to (own) channel "chan_self_unsub"
// - User 14 has an unconfirmed request to User15's channel "chan_other_request"
// - User 14 has a confirmed+active subscription to User15's channel "chan_other_accepted"
{
doUnsubscribe(t, baseUrl, users[14], users[14], "chan_self_unsub")
doSubscribe(t, baseUrl, users[14], users[15], "chan_other_request")

View File

@@ -0,0 +1,34 @@
package util
import (
"fmt"
"testing"
)
func FindSubscriptionByChanName(t *testing.T, baseUrl string, subscriber Userdat, ownerUID string, chanName string) (subscriptionID string, channelID string) {
type subobj struct {
SubscriptionId string `json:"subscription_id"`
ChannelId string `json:"channel_id"`
ChannelInternalName string `json:"channel_internal_name"`
ChannelOwnerUserId string `json:"channel_owner_user_id"`
Confirmed bool `json:"confirmed"`
Active bool `json:"active"`
}
type sublist struct {
Subscriptions []subobj `json:"subscriptions"`
}
subs := RequestAuthGet[sublist](t, subscriber.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions?direction=outgoing", subscriber.UID))
for _, sub := range subs.Subscriptions {
if sub.ChannelOwnerUserId == ownerUID && sub.ChannelInternalName == chanName {
fullSub := RequestAuthGet[subobj](t, subscriber.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/subscriptions/%s", subscriber.UID, sub.SubscriptionId))
if fullSub.ChannelOwnerUserId == ownerUID && fullSub.ChannelInternalName == chanName {
return fullSub.SubscriptionId, fullSub.ChannelId
}
}
}
t.Fatalf("Could not find subscription for user %s to channel %s owned by %s", subscriber.UID, chanName, ownerUID)
return "", ""
}