forked from baron/baron-sso
Merge branch 'dev' into feature/tenant-user-list-ui-improvement
This commit is contained in:
@@ -3,11 +3,15 @@ package repository
|
||||
import (
|
||||
"baron-sso-backend/internal/domain"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/lib/pq"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func TestCheckDataIntegrityDetectsTenantAndUserProblems(t *testing.T) {
|
||||
@@ -60,7 +64,18 @@ func TestCheckDataIntegrityDetectsTenantAndUserProblems(t *testing.T) {
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
}
|
||||
deletedLoginUser := domain.User{
|
||||
ID: uuid.NewString(),
|
||||
Email: "deleted-login-user-" + suffix + "@example.com",
|
||||
Name: "Deleted Login User",
|
||||
Role: domain.RoleUser,
|
||||
TenantID: &child.ID,
|
||||
Status: domain.UserStatusActive,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
}
|
||||
require.NoError(t, testDB.Create(&orphanUser).Error)
|
||||
require.NoError(t, testDB.Create(&deletedLoginUser).Error)
|
||||
require.NoError(t, testDB.Create(&domain.UserLoginID{
|
||||
ID: uuid.NewString(),
|
||||
UserID: orphanUser.ID,
|
||||
@@ -68,8 +83,14 @@ func TestCheckDataIntegrityDetectsTenantAndUserProblems(t *testing.T) {
|
||||
FieldKey: "emp_id",
|
||||
LoginID: "EMP-" + suffix,
|
||||
}).Error)
|
||||
// Missing UserID for UserLoginID cannot be inserted due to FK constraint fk_users_user_login_ids.
|
||||
// So we don't test orphan_user_login_id_users here.
|
||||
require.NoError(t, testDB.Create(&domain.UserLoginID{
|
||||
ID: uuid.NewString(),
|
||||
UserID: deletedLoginUser.ID,
|
||||
TenantID: child.ID,
|
||||
FieldKey: "emp_id",
|
||||
LoginID: "MISSING-" + suffix,
|
||||
}).Error)
|
||||
require.NoError(t, testDB.Delete(&domain.User{}, "id = ?", deletedLoginUser.ID).Error)
|
||||
|
||||
report, err := CheckDataIntegrity(ctx, testDB)
|
||||
require.NoError(t, err)
|
||||
@@ -80,7 +101,69 @@ func TestCheckDataIntegrityDetectsTenantAndUserProblems(t *testing.T) {
|
||||
requireIntegrityCheck(t, report, "tenant_integrity", "orphan_tenant_parents", domain.DataIntegrityStatusFail, 1)
|
||||
requireIntegrityCheck(t, report, "user_integrity", "orphan_user_tenant_memberships", domain.DataIntegrityStatusFail, 1)
|
||||
requireIntegrityCheck(t, report, "user_integrity", "orphan_user_login_id_tenants", domain.DataIntegrityStatusFail, 1)
|
||||
requireIntegrityCheck(t, report, "user_integrity", "orphan_user_login_id_users", domain.DataIntegrityStatusPass, 0)
|
||||
requireIntegrityCheck(t, report, "user_integrity", "orphan_user_login_id_users", domain.DataIntegrityStatusFail, 1)
|
||||
}
|
||||
|
||||
func TestCheckDataIntegrityDetectsHardOrphanUserLoginIDRows(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
suffix := uuid.NewString()
|
||||
rollback := errors.New("rollback hard orphan fixture")
|
||||
|
||||
err := testDB.Transaction(func(tx *gorm.DB) error {
|
||||
var constraintNames []string
|
||||
if err := tx.Raw(`
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'user_login_ids'::regclass
|
||||
AND contype = 'f'
|
||||
`).Scan(&constraintNames).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, constraintName := range constraintNames {
|
||||
statement := fmt.Sprintf("ALTER TABLE user_login_ids DROP CONSTRAINT %s", pq.QuoteIdentifier(constraintName))
|
||||
if err := tx.Exec(statement).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
before, err := CheckDataIntegrity(ctx, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
beforeTenantCount, err := integrityCheckCount(before, "user_integrity", "orphan_user_login_id_tenants")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
beforeUserCount, err := integrityCheckCount(before, "user_integrity", "orphan_user_login_id_users")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Create(&domain.UserLoginID{
|
||||
ID: uuid.NewString(),
|
||||
UserID: uuid.NewString(),
|
||||
TenantID: uuid.NewString(),
|
||||
FieldKey: "emp_id",
|
||||
LoginID: "HARD-ORPHAN-" + suffix,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
report, err := CheckDataIntegrity(ctx, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := expectIntegrityCheck(report, "user_integrity", "orphan_user_login_id_tenants", domain.DataIntegrityStatusFail, beforeTenantCount+1); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := expectIntegrityCheck(report, "user_integrity", "orphan_user_login_id_users", domain.DataIntegrityStatusFail, beforeUserCount+1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return rollback
|
||||
})
|
||||
require.ErrorIs(t, err, rollback)
|
||||
}
|
||||
|
||||
func TestListAndDeleteOrphanUserLoginIDsOnlyDeletesRevalidatedTargets(t *testing.T) {
|
||||
@@ -189,17 +272,41 @@ func TestListAndDeleteOrphanUserLoginIDsOnlyDeletesRevalidatedTargets(t *testing
|
||||
|
||||
func requireIntegrityCheck(t *testing.T, report domain.DataIntegrityReport, sectionKey, checkKey string, status domain.DataIntegrityStatus, count int64) {
|
||||
t.Helper()
|
||||
require.NoError(t, expectIntegrityCheck(report, sectionKey, checkKey, status, count))
|
||||
}
|
||||
|
||||
func expectIntegrityCheck(report domain.DataIntegrityReport, sectionKey, checkKey string, status domain.DataIntegrityStatus, count int64) error {
|
||||
check, ok := findIntegrityCheck(report, sectionKey, checkKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("integrity check %s/%s not found", sectionKey, checkKey)
|
||||
}
|
||||
if check.Status != status {
|
||||
return fmt.Errorf("integrity check %s/%s status = %s, want %s", sectionKey, checkKey, check.Status, status)
|
||||
}
|
||||
if check.Count != count {
|
||||
return fmt.Errorf("integrity check %s/%s count = %d, want %d", sectionKey, checkKey, check.Count, count)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func integrityCheckCount(report domain.DataIntegrityReport, sectionKey, checkKey string) (int64, error) {
|
||||
check, ok := findIntegrityCheck(report, sectionKey, checkKey)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("integrity check %s/%s not found", sectionKey, checkKey)
|
||||
}
|
||||
return check.Count, nil
|
||||
}
|
||||
|
||||
func findIntegrityCheck(report domain.DataIntegrityReport, sectionKey, checkKey string) (domain.DataIntegrityCheck, bool) {
|
||||
for _, section := range report.Sections {
|
||||
if section.Key != sectionKey {
|
||||
continue
|
||||
}
|
||||
for _, check := range section.Checks {
|
||||
if check.Key == checkKey {
|
||||
require.Equal(t, status, check.Status)
|
||||
require.Equal(t, count, check.Count)
|
||||
return
|
||||
return check, true
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Fatalf("integrity check %s/%s not found", sectionKey, checkKey)
|
||||
return domain.DataIntegrityCheck{}, false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user