266 lines
8.3 KiB
Go
266 lines
8.3 KiB
Go
package system
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"kra/internal/data/migration"
|
|
)
|
|
|
|
func TestMigrateAllUsesVersionTableAndIsIdempotent(t *testing.T) {
|
|
db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sqlDB, err := db.DB()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = sqlDB.Close() })
|
|
|
|
if err = migrateAll(db); err != nil {
|
|
t.Fatalf("first migration: %v", err)
|
|
}
|
|
if err = migrateAll(db); err != nil {
|
|
t.Fatalf("second migration: %v", err)
|
|
}
|
|
if !db.Migrator().HasTable(migration.TableName) {
|
|
t.Fatalf("missing migration table %q", migration.TableName)
|
|
}
|
|
var count int64
|
|
if err = db.Table(migration.TableName).Count(&count).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if count != 2 {
|
|
t.Fatalf("migration rows = %d, want 2", count)
|
|
}
|
|
}
|
|
|
|
func TestEnsurePaymentAdminSurfaceIsIdempotent(t *testing.T) {
|
|
db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sqlDB, err := db.DB()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = sqlDB.Close() })
|
|
if err = db.AutoMigrate(&menuPO{}, &authorityMenuPO{}, &authorityPO{}, &apiPO{}, &casbinRulePO{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rootParentID := uint(0)
|
|
if err = db.Create(&authorityPO{AuthorityID: 888, AuthorityName: "root", ParentID: &rootParentID}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.Create(&menuPO{Name: "extensions", Path: "legacy-extensions", Title: "旧扩展"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.Create(&menuPO{Name: "paymentOrders", Component: "legacy.vue", Title: "旧支付订单"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.Create(&apiPO{Path: "/payment/configs", Method: "GET", APIGroup: "legacy"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for i := 0; i < 2; i++ {
|
|
if err = ensurePaymentAdminSurface(db); err != nil {
|
|
t.Fatalf("ensure payment admin surface pass %d: %v", i+1, err)
|
|
}
|
|
}
|
|
|
|
var parent menuPO
|
|
if err = db.Where("name = ?", "extensions").First(&parent).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for name, component := range map[string]string{
|
|
"paymentOrders": "view/systemTools/payment/orders.vue",
|
|
"paymentConfig": "view/systemTools/payment/config.vue",
|
|
} {
|
|
var menu menuPO
|
|
if err = db.Where("name = ?", name).First(&menu).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if menu.ParentID != parent.ID || menu.Component != component {
|
|
t.Fatalf("menu %s = %#v", name, menu)
|
|
}
|
|
var linkCount int64
|
|
if err = db.Model(&authorityMenuPO{}).Where("sys_authority_authority_id = ? AND sys_base_menu_id = ?", 888, menu.ID).Count(&linkCount).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if linkCount != 1 {
|
|
t.Fatalf("menu %s root links = %d, want 1", name, linkCount)
|
|
}
|
|
}
|
|
for _, item := range []struct{ method, path string }{
|
|
{"GET", "/payment/configs"}, {"POST", "/payment/config"},
|
|
{"GET", "/payment/orders"}, {"POST", "/payment/order"},
|
|
} {
|
|
var apiCount int64
|
|
if err = db.Model(&apiPO{}).Where("path = ? AND method = ?", item.path, item.method).Count(&apiCount).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if apiCount != 1 {
|
|
t.Fatalf("API %s %s rows = %d, want 1", item.method, item.path, apiCount)
|
|
}
|
|
var policyCount int64
|
|
if err = policyScope(db).Where("v0 = ? AND v1 = ? AND v2 = ?", "888", item.path, item.method).Count(&policyCount).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if policyCount != 1 {
|
|
t.Fatalf("policy %s %s rows = %d, want 1", item.method, item.path, policyCount)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMigrateLegacyAuthorityAPIsWithoutDeletedAt(t *testing.T) {
|
|
db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sqlDB, err := db.DB()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = sqlDB.Close() })
|
|
|
|
for _, statement := range []string{
|
|
`CREATE TABLE sys_apis (id integer primary key, path text, method text)`,
|
|
`CREATE TABLE sys_authority_apis (authority_id integer, api_id integer)`,
|
|
`INSERT INTO sys_apis (id, path, method) VALUES (1, '/legacy', 'GET')`,
|
|
`INSERT INTO sys_authority_apis (authority_id, api_id) VALUES (888, 1)`,
|
|
} {
|
|
if err = db.Exec(statement).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err = db.AutoMigrate(&casbinRulePO{}, &errorRecordPO{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
legacyError := errorRecordPO{Status: "未解决"}
|
|
if err = db.Create(&legacyError).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = migrateLegacyAuthorityAPIsToCasbinRules(db); err != nil {
|
|
t.Fatalf("legacy migration failed without sys_apis.deleted_at: %v", err)
|
|
}
|
|
if err = normalizeErrorRecordStatuses(db); err != nil {
|
|
t.Fatalf("following migration inherited the legacy table alias: %v", err)
|
|
}
|
|
|
|
var count int64
|
|
if err = db.Model(&casbinRulePO{}).
|
|
Where("ptype = ? AND v0 = ? AND v1 = ? AND v2 = ?", "p", "888", "/legacy", "GET").
|
|
Count(&count).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if count != 1 {
|
|
t.Fatalf("migrated policy count = %d, want 1", count)
|
|
}
|
|
if err = db.First(&legacyError, legacyError.ID).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if legacyError.Status != "未处理" {
|
|
t.Fatalf("normalized status = %q, want 未处理", legacyError.Status)
|
|
}
|
|
}
|
|
|
|
func TestReconcileRootAuthorityAPIsUsesIndependentQueries(t *testing.T) {
|
|
db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sqlDB, err := db.DB()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = sqlDB.Close() })
|
|
|
|
if err = db.AutoMigrate(&authorityPO{}, &apiPO{}, &ignoredAPIPO{}, &casbinRulePO{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.Create(&authorityPO{AuthorityID: 888, AuthorityName: "root"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.Create(&apiPO{Path: "/allowed", Method: "GET"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.Create(&apiPO{Path: "/ignored", Method: "POST"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.Create(&ignoredAPIPO{Path: "/ignored", Method: "POST"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Deliberately pass a handle carrying an unrelated model and predicate.
|
|
// Migration queries must not inherit either state.
|
|
dirty := db.Model(&authorityPO{}).Where("authority_id = ?", 999)
|
|
if err = reconcileRootAuthorityAPIs(dirty); err != nil {
|
|
t.Fatalf("reconcile root policies with dirty DB state: %v", err)
|
|
}
|
|
|
|
var rules []casbinRulePO
|
|
if err = db.Where("ptype = ? AND v0 = ?", "p", "888").Find(&rules).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(rules) != 1 || rules[0].V1 != "/allowed" || rules[0].V2 != "GET" {
|
|
t.Fatalf("root rules = %#v, want only GET /allowed", rules)
|
|
}
|
|
}
|
|
|
|
func TestMigrateLegacyAuthorityDepartmentColumns(t *testing.T) {
|
|
db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sqlDB, err := db.DB()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = sqlDB.Close() })
|
|
|
|
for _, statement := range []string{
|
|
`CREATE TABLE sys_authority_departments (authority_id integer, department_id integer, PRIMARY KEY (authority_id, department_id))`,
|
|
`INSERT INTO sys_authority_departments (authority_id, department_id) VALUES (888, 7)`,
|
|
} {
|
|
if err = db.Exec(statement).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err = migrateLegacyAuthorityDepartmentColumns(db); err != nil {
|
|
t.Fatalf("migrate legacy authority-department columns: %v", err)
|
|
}
|
|
if err = db.AutoMigrate(&authorityDepartmentPO{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var relation authorityDepartmentPO
|
|
if err = db.First(&relation).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if relation.AuthorityID != 888 || relation.DepartmentID != 7 {
|
|
t.Fatalf("migrated relation = %#v", relation)
|
|
}
|
|
for _, column := range []string{"sys_authority_authority_id", "sys_department_id"} {
|
|
if !tableHasColumn(db, "sys_authority_departments", column) {
|
|
t.Fatalf("missing compatible column %s", column)
|
|
}
|
|
}
|
|
hasPrimaryKey, err := tableHasPrimaryKey(db, "sys_authority_departments")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if hasPrimaryKey {
|
|
t.Fatal("authority-department table retained the legacy primary key")
|
|
}
|
|
if err = db.Create(&authorityDepartmentPO{AuthorityID: 888, DepartmentID: 7}).Error; err != nil {
|
|
t.Fatalf("duplicate administration relation was rejected: %v", err)
|
|
}
|
|
var count int64
|
|
if err = db.Model(&authorityDepartmentPO{}).Where("sys_authority_authority_id = ? AND sys_department_id = ?", 888, 7).Count(&count).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if count != 2 {
|
|
t.Fatalf("duplicate relation count = %d, want 2", count)
|
|
}
|
|
}
|