chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
package iot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
|
||||
)
|
||||
|
||||
func channelDisplayTestContext(tenant string, dept int64) context.Context {
|
||||
return contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{
|
||||
ID: 1, TenantId: tenant, DeptId: dept,
|
||||
Roles: []model.Role{{RoleId: consts.SuperAdminRoleId, DataScope: consts.SysRoleDataScopeAll}},
|
||||
}}})
|
||||
}
|
||||
|
||||
func TestChannelDisplayScope(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
dept *entity.SysDept
|
||||
err error
|
||||
want bool
|
||||
}{
|
||||
{name: "top organization", dept: &entity.SysDept{DeptId: 100, ParentId: 0}, want: true},
|
||||
{name: "top organization legacy ancestors", dept: &entity.SysDept{DeptId: 100, ParentId: 1, Ancestors: "0"}, want: true},
|
||||
{name: "downstream super admin still uses alias", dept: &entity.SysDept{DeptId: 100, ParentId: 50, Ancestors: "0,50"}},
|
||||
{name: "missing organization"},
|
||||
{name: "different organization", dept: &entity.SysDept{DeptId: 999, ParentId: 0}},
|
||||
{name: "lookup failure", err: errors.New("lookup failed")},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := channelDisplayTestContext("tenant-A", 100)
|
||||
got := resolveChannelDisplayScope(ctx, func() (*entity.SysDept, error) { return tc.dept, tc.err })
|
||||
if got != tc.want {
|
||||
t.Fatalf("full name = %v, want %v", got, tc.want)
|
||||
}
|
||||
if got := canViewChannelFullName(ctx); got != tc.want {
|
||||
t.Fatalf("cached scope = %v", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, ctx := range []context.Context{context.Background(), channelDisplayTestContext("", 100), channelDisplayTestContext("tenant-A", 0)} {
|
||||
if resolveChannelDisplayScope(ctx, func() (*entity.SysDept, error) {
|
||||
t.Fatal("invalid identity should not query organizations")
|
||||
return nil, nil
|
||||
}) {
|
||||
t.Fatal("invalid identity got full names")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelDisplayScopeCacheIdentity(t *testing.T) {
|
||||
ctx := channelDisplayTestContext("tenant-A", 100)
|
||||
calls := 0
|
||||
load := func() (*entity.SysDept, error) {
|
||||
calls++
|
||||
return &entity.SysDept{DeptId: contexts.GetDeptId(ctx), ParentId: 0}, nil
|
||||
}
|
||||
if !resolveChannelDisplayScope(ctx, load) || !resolveChannelDisplayScope(ctx, load) || calls != 1 {
|
||||
t.Fatal("scope not cached for same identity")
|
||||
}
|
||||
contexts.GetUser(ctx).DeptId = 200
|
||||
resolveChannelDisplayScope(ctx, load)
|
||||
contexts.GetUser(ctx).TenantId = "tenant-B"
|
||||
resolveChannelDisplayScope(ctx, load)
|
||||
if calls != 3 {
|
||||
t.Fatalf("scope reused across organization or tenant: %d calls", calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelDisplayAliasAndExport(t *testing.T) {
|
||||
ctx := channelDisplayTestContext("tenant-A", 200)
|
||||
resolveChannelDisplayScope(ctx, func() (*entity.SysDept, error) {
|
||||
return &entity.SysDept{DeptId: 200, ParentId: 100, Ancestors: "0,100"}, nil
|
||||
})
|
||||
list := []*model.TSimCardInfoListModel{
|
||||
{ChanName: "PRIVATE UPSTREAM NAME", ChanAlias: " 中国移动223 "},
|
||||
{ChanName: "PRIVATE UPSTREAM NAME", ChanAlias: " "},
|
||||
nil,
|
||||
}
|
||||
applyCardChannelDisplayNames(ctx, list)
|
||||
if list[0].ChanName != "中国移动223" || list[1].ChanName != "--" {
|
||||
t.Fatalf("name leak: %+v %+v", list[0], list[1])
|
||||
}
|
||||
for _, card := range list[:2] {
|
||||
row := cancellationExportRow(&model.TSimCardCancellationListModel{ChanName: card.ChanName})
|
||||
if row[7] != card.ChanName {
|
||||
t.Fatalf("export name differs: %v", row[7])
|
||||
}
|
||||
}
|
||||
if channelDisplayName(true, "full name", "alias") != "full name" {
|
||||
t.Fatal("top organization name changed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelDisplaySQL(t *testing.T) {
|
||||
db, err := gdb.New(gdb.ConfigNode{Type: "sqlite", Link: "sqlite::@file(:memory:)", MaxOpenConnCount: 1})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = db.Close(context.Background()) })
|
||||
if _, err = db.Exec(context.Background(), "CREATE TABLE channels(chan_name TEXT,chan_alias TEXT)"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, alias := range []interface{}{"中国移动223", "", " ", nil} {
|
||||
if _, err = db.Exec(context.Background(), "INSERT INTO channels VALUES(?,?)", "PRIVATE UPSTREAM NAME", alias); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, top := range []bool{false, true} {
|
||||
ctx := channelDisplayTestContext("tenant-A", 100)
|
||||
parent := int64(50)
|
||||
if top {
|
||||
parent = 0
|
||||
}
|
||||
resolveChannelDisplayScope(ctx, func() (*entity.SysDept, error) {
|
||||
return &entity.SysDept{DeptId: 100, ParentId: parent, Ancestors: "0,50"}, nil
|
||||
})
|
||||
values, err := db.GetArray(ctx, "SELECT "+channelDisplayNameSQL(ctx, "channels")+" FROM channels ORDER BY rowid")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i, v := range values {
|
||||
want := "--"
|
||||
if i == 0 {
|
||||
want = "中国移动223"
|
||||
}
|
||||
if top {
|
||||
want = "PRIVATE UPSTREAM NAME"
|
||||
}
|
||||
if v.String() != want {
|
||||
t.Fatalf("top=%v row=%d got=%q want=%q", top, i, v.String(), want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exercise the real list query and export formatter against an isolated database.
|
||||
func TestChannelDisplayCancellationList(t *testing.T) {
|
||||
previous := gdb.GetConfig("default")
|
||||
if err := gdb.SetConfig(gdb.Config{"default": gdb.ConfigGroup{{Type: "sqlite", Link: "sqlite::@file(:memory:)", MaxOpenConnCount: 1}}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db := g.DB()
|
||||
t.Cleanup(func() { _ = db.Close(context.Background()); _ = gdb.SetConfig(gdb.Config{"default": previous}) })
|
||||
statements := []string{
|
||||
"CREATE TABLE sys_dept(dept_id INTEGER,tenant_id TEXT,parent_id INTEGER,ancestors TEXT,dept_name TEXT,deleted_at TEXT)",
|
||||
"INSERT INTO sys_dept VALUES(100,'tenant-A',0,'0','Top',NULL),(200,'tenant-A',100,'0,100','Customer',NULL)",
|
||||
"CREATE TABLE t_sim_channels(id INTEGER,temp_key TEXT,chan_key TEXT,tenant_id TEXT,chan_name TEXT,chan_alias TEXT)",
|
||||
"CREATE TABLE t_sim_channel_template(temp_key TEXT,temp_name TEXT,temp_model TEXT)",
|
||||
"INSERT INTO t_sim_channels(id,chan_key,tenant_id,chan_name,chan_alias) VALUES(1,'SHARED','tenant-A','PRIVATE NAME A','中国移动223'),(2,'SHARED','tenant-B','PRIVATE NAME B','Other tenant alias')",
|
||||
"CREATE TABLE t_sim_flow_package_info(package_code TEXT,package_name TEXT,tenant_id TEXT)",
|
||||
"INSERT INTO t_sim_flow_package_info VALUES('PKG','套餐A','tenant-A'),('PKG','套餐B','tenant-B')",
|
||||
"CREATE TABLE t_sim_card_cancellation(id INTEGER,iccid TEXT,msisdn TEXT,imsi TEXT,imei TEXT,dept_id INTEGER,chan_key TEXT,cancel_batch_no TEXT,cancelled_at TEXT,boss_card_status INTEGER,current_package_code TEXT,inbound_batch_no TEXT,outbound_batch_no TEXT,allocation_batch_no TEXT,card_type INTEGER,net_model INTEGER,remark TEXT,created_by INTEGER,tenant_id TEXT)",
|
||||
"INSERT INTO t_sim_card_cancellation(id,iccid,dept_id,chan_key,created_by,tenant_id,current_package_code) VALUES(1,'CARD-A',200,'SHARED',0,'tenant-A','PKG'),(2,'CARD-B',200,'SHARED',0,'tenant-B','PKG')",
|
||||
}
|
||||
for _, sql := range statements {
|
||||
if _, err := db.Exec(context.Background(), sql); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, dept := range []int64{200, 100} {
|
||||
ctx := channelDisplayTestContext("tenant-A", dept)
|
||||
contexts.GetUser(ctx).Roles = []model.Role{{RoleId: 99, DataScope: consts.SysRoleDataScopeAll}}
|
||||
in := &model.TSimCardCancellationListParam{}
|
||||
in.Page = 1
|
||||
in.PageSize = 20
|
||||
list, total, err := NewIotTSimCardCancellation().List(ctx, in)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if total != 1 || len(list) != 1 || list[0].Iccid != "CARD-A" {
|
||||
t.Fatalf("cross tenant rows: total=%d list=%+v", total, list)
|
||||
}
|
||||
want := "中国移动223"
|
||||
if dept == 100 {
|
||||
want = "PRIVATE NAME A"
|
||||
}
|
||||
if list[0].PackageName != "套餐A" {
|
||||
t.Fatalf("cross tenant package: %q", list[0].PackageName)
|
||||
}
|
||||
if list[0].ChanName != want {
|
||||
t.Fatalf("dept=%d name=%q want=%q", dept, list[0].ChanName, want)
|
||||
}
|
||||
if row := cancellationExportRow(list[0]); row[7] != want {
|
||||
t.Fatalf("export name=%v", row[7])
|
||||
}
|
||||
channelQuery := &model.TSimChannelsListParam{}
|
||||
channelQuery.Page = 1
|
||||
channelQuery.PageSize = 20
|
||||
channelQuery.ChanName = want
|
||||
channels, count, err := NewIotTSimChannels().List(ctx, channelQuery)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if count != 1 || len(channels) != 1 || channels[0].ChanName != want {
|
||||
t.Fatalf("channel options: count=%d rows=%+v", count, channels)
|
||||
}
|
||||
stockNames, err := loadStockChannelNames(ctx, []string{"SHARED"})
|
||||
if err != nil || stockNames["SHARED"] != want {
|
||||
t.Fatalf("stock channel name=%v err=%v", stockNames, err)
|
||||
}
|
||||
channelView, err := NewIotTSimChannels().View(ctx, &model.TSimChannelsViewParam{Id: 1})
|
||||
if err != nil || channelView == nil || channelView.ChanName != want {
|
||||
t.Fatalf("channel view=%+v err=%v", channelView, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelDisplayCancellationMissingAlias(t *testing.T) {
|
||||
ctx := channelDisplayTestContext("tenant-A", 200)
|
||||
resolveChannelDisplayScope(ctx, func() (*entity.SysDept, error) { return &entity.SysDept{DeptId: 200, ParentId: 100}, nil })
|
||||
for _, alias := range []string{"", " "} {
|
||||
name := channelDisplayName(canViewChannelFullName(ctx), "PRIVATE NAME", alias)
|
||||
encoded, err := json.Marshal(&model.TSimCardCancellationListModel{ChanName: name})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(string(encoded), "PRIVATE NAME") || name != "--" {
|
||||
t.Fatalf("response exposes full name: %s", encoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user