fix(order): prevent duplicate subscriptions and repair invite gifts
Build docker and publish / build (20.15.1) (push) Successful in 5m6s
Build docker and publish / build (20.15.1) (push) Successful in 5m6s
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
package orderLogic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
modelOrder "github.com/perfect-panel/server/internal/model/order"
|
||||
"github.com/perfect-panel/server/internal/model/subscribe"
|
||||
"github.com/perfect-panel/server/internal/model/user"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
func setupActivationEligibilityDB(t *testing.T) *gorm.DB {
|
||||
t.Helper()
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
sqls := []string{
|
||||
`CREATE TABLE IF NOT EXISTS "user" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
password VARCHAR(100) NOT NULL DEFAULT '',
|
||||
created_at DATETIME,
|
||||
updated_at DATETIME,
|
||||
deleted_at DATETIME DEFAULT NULL
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS "user_device" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL DEFAULT 0,
|
||||
identifier VARCHAR(255) NOT NULL DEFAULT '' UNIQUE,
|
||||
created_at DATETIME,
|
||||
updated_at DATETIME
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS "user_family" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
owner_user_id INTEGER NOT NULL DEFAULT 0,
|
||||
status TINYINT NOT NULL DEFAULT 1,
|
||||
deleted_at DATETIME DEFAULT NULL
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS "user_family_member" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
family_id INTEGER NOT NULL DEFAULT 0,
|
||||
user_id INTEGER NOT NULL DEFAULT 0,
|
||||
role TINYINT NOT NULL DEFAULT 0,
|
||||
status TINYINT NOT NULL DEFAULT 0,
|
||||
join_source VARCHAR(32) NOT NULL DEFAULT '',
|
||||
deleted_at DATETIME DEFAULT NULL
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS "order" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL DEFAULT 0,
|
||||
order_no VARCHAR(255) NOT NULL DEFAULT '' UNIQUE,
|
||||
type TINYINT NOT NULL DEFAULT 1,
|
||||
status TINYINT NOT NULL DEFAULT 1,
|
||||
subscribe_id INTEGER NOT NULL DEFAULT 0,
|
||||
quantity INTEGER NOT NULL DEFAULT 1,
|
||||
created_at DATETIME,
|
||||
updated_at DATETIME
|
||||
)`,
|
||||
}
|
||||
for _, sql := range sqls {
|
||||
require.NoError(t, db.Exec(sql).Error)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func insertActivationUser(t *testing.T, db *gorm.DB, userID int64, createdAt time.Time) {
|
||||
t.Helper()
|
||||
require.NoError(t, db.Exec(
|
||||
`INSERT INTO "user" (id, created_at, updated_at) VALUES (?, ?, datetime('now'))`,
|
||||
userID,
|
||||
createdAt.UTC().Format("2006-01-02 15:04:05"),
|
||||
).Error)
|
||||
}
|
||||
|
||||
func insertActivationDevice(t *testing.T, db *gorm.DB, userID int64, identifier string, createdAt time.Time) {
|
||||
t.Helper()
|
||||
require.NoError(t, db.Exec(
|
||||
`INSERT INTO "user_device" (user_id, identifier, created_at, updated_at) VALUES (?, ?, ?, datetime('now'))`,
|
||||
userID,
|
||||
identifier,
|
||||
createdAt.UTC().Format("2006-01-02 15:04:05"),
|
||||
).Error)
|
||||
}
|
||||
|
||||
func insertActivationFamily(t *testing.T, db *gorm.DB, familyID, ownerUserID int64) {
|
||||
t.Helper()
|
||||
require.NoError(t, db.Exec(
|
||||
`INSERT INTO "user_family" (id, owner_user_id, status) VALUES (?, ?, 1)`,
|
||||
familyID,
|
||||
ownerUserID,
|
||||
).Error)
|
||||
}
|
||||
|
||||
func insertActivationFamilyMember(t *testing.T, db *gorm.DB, familyID, userID int64, role, status uint8, joinSource string) {
|
||||
t.Helper()
|
||||
require.NoError(t, db.Exec(
|
||||
`INSERT INTO "user_family_member" (family_id, user_id, role, status, join_source) VALUES (?, ?, ?, ?, ?)`,
|
||||
familyID,
|
||||
userID,
|
||||
role,
|
||||
status,
|
||||
joinSource,
|
||||
).Error)
|
||||
}
|
||||
|
||||
func insertActivationOrder(t *testing.T, db *gorm.DB, orderNo string, userID, subscribeID int64, status uint8) {
|
||||
t.Helper()
|
||||
require.NoError(t, db.Exec(
|
||||
`INSERT INTO "order" (user_id, order_no, type, status, subscribe_id, quantity, created_at, updated_at)
|
||||
VALUES (?, ?, 1, ?, ?, 1, datetime('now'), datetime('now'))`,
|
||||
userID,
|
||||
orderNo,
|
||||
status,
|
||||
subscribeID,
|
||||
).Error)
|
||||
}
|
||||
|
||||
func TestValidateNewUserOnlyEligibilityAtActivation_UsesEarliestBoundDeviceTime(t *testing.T) {
|
||||
db := setupActivationEligibilityDB(t)
|
||||
|
||||
const (
|
||||
ownerUserID = int64(1)
|
||||
memberUserID = int64(2)
|
||||
familyID = int64(10)
|
||||
subscribeID = int64(100)
|
||||
)
|
||||
|
||||
insertActivationUser(t, db, ownerUserID, time.Now().Add(-1*time.Hour))
|
||||
insertActivationUser(t, db, memberUserID, time.Now().Add(-72*time.Hour))
|
||||
insertActivationDevice(t, db, memberUserID, "activation-old-device", time.Now().Add(-72*time.Hour))
|
||||
insertActivationFamily(t, db, familyID, ownerUserID)
|
||||
insertActivationFamilyMember(t, db, familyID, ownerUserID, user.FamilyRoleOwner, user.FamilyMemberActive, "owner_init")
|
||||
insertActivationFamilyMember(t, db, familyID, memberUserID, user.FamilyRoleMember, user.FamilyMemberActive, "bind_email_with_verification")
|
||||
|
||||
err := validateNewUserOnlyEligibilityAtActivation(
|
||||
context.Background(),
|
||||
db,
|
||||
&modelOrder.Order{
|
||||
UserId: ownerUserID,
|
||||
OrderNo: "activation-check-old-device",
|
||||
Type: OrderTypeSubscribe,
|
||||
Quantity: 1,
|
||||
SubscribeId: subscribeID,
|
||||
},
|
||||
&subscribe.Subscribe{
|
||||
Id: subscribeID,
|
||||
Discount: `[{"quantity":1,"discount":90,"new_user_only":true}]`,
|
||||
},
|
||||
)
|
||||
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "is not a new user")
|
||||
}
|
||||
|
||||
func TestValidateNewUserOnlyEligibilityAtActivation_SharesHistoryAcrossBoundScope(t *testing.T) {
|
||||
db := setupActivationEligibilityDB(t)
|
||||
|
||||
const (
|
||||
ownerUserID = int64(11)
|
||||
memberUserID = int64(12)
|
||||
familyID = int64(20)
|
||||
subscribeID = int64(200)
|
||||
)
|
||||
|
||||
insertActivationUser(t, db, ownerUserID, time.Now().Add(-1*time.Hour))
|
||||
insertActivationUser(t, db, memberUserID, time.Now().Add(-2*time.Hour))
|
||||
insertActivationDevice(t, db, memberUserID, "activation-shared-device", time.Now().Add(-2*time.Hour))
|
||||
insertActivationFamily(t, db, familyID, ownerUserID)
|
||||
insertActivationFamilyMember(t, db, familyID, ownerUserID, user.FamilyRoleOwner, user.FamilyMemberActive, "owner_init")
|
||||
insertActivationFamilyMember(t, db, familyID, memberUserID, user.FamilyRoleMember, user.FamilyMemberActive, "bind_email_with_verification")
|
||||
insertActivationOrder(t, db, "previous-finished-order", memberUserID, subscribeID, OrderStatusFinished)
|
||||
|
||||
err := validateNewUserOnlyEligibilityAtActivation(
|
||||
context.Background(),
|
||||
db,
|
||||
&modelOrder.Order{
|
||||
UserId: ownerUserID,
|
||||
OrderNo: "current-paid-order",
|
||||
Type: OrderTypeSubscribe,
|
||||
Quantity: 1,
|
||||
SubscribeId: subscribeID,
|
||||
},
|
||||
&subscribe.Subscribe{
|
||||
Id: subscribeID,
|
||||
Discount: `[{"quantity":1,"discount":90,"new_user_only":true}]`,
|
||||
},
|
||||
)
|
||||
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "already activated")
|
||||
}
|
||||
Reference in New Issue
Block a user