package data import ( "context" "errors" "testing" "kra/internal/modules/system/biz" "gorm.io/gorm" ) type dataScopeRecord struct { ID uint `gorm:"primaryKey"` DeptID uint CreatedBy uint Name string } func (dataScopeRecord) TableName() string { return "business_scope_records" } func newDataScopeTestDB(t *testing.T) *gorm.DB { t.Helper() db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared") if err != nil { t.Fatal(err) } if err := db.AutoMigrate(&dataScopeRecord{}); err != nil { t.Fatal(err) } registerDataScopeCallbacks(db, nil) sqlDB, err := db.DB() if err != nil { t.Fatal(err) } t.Cleanup(func() { _ = sqlDB.Close() }) return db } func TestDataScopeCallbacksFailClosedAndAllowExplicitSystemBypass(t *testing.T) { db := newDataScopeTestDB(t) seed := []dataScopeRecord{ {DeptID: 10, CreatedBy: 7, Name: "visible"}, {DeptID: 20, CreatedBy: 8, Name: "hidden"}, {DeptID: 0, CreatedBy: 9, Name: "unassigned"}, } if err := db.Set("data_scope:skip", true).Create(&seed).Error; err != nil { t.Fatal(err) } var rows []dataScopeRecord if err := db.WithContext(context.Background()).Find(&rows).Error; !errors.Is(err, errDataScopeRequired) { t.Fatalf("query without data scope error = %v", err) } invalidCtx := biz.NewDataScopeContext(context.Background(), biz.DataScope{UserID: 7, AuthorityID: 1, Scope: 1, All: false}) if err := db.WithContext(invalidCtx).Find(&rows).Error; !errors.Is(err, errInvalidDataScope) { t.Fatalf("query with invalid data scope error = %v", err) } scopedCtx := biz.NewDataScopeContext(context.Background(), biz.DataScope{UserID: 7, AuthorityID: 1, Scope: 3, DepartmentIDs: []uint{10}}) rows = nil if err := db.WithContext(scopedCtx).Order("id").Find(&rows).Error; err != nil { t.Fatal(err) } if len(rows) != 1 || rows[0].Name != "visible" { t.Fatalf("scoped query rows = %+v", rows) } emptyCtx := biz.NewDataScopeContext(context.Background(), biz.DataScope{UserID: 7, AuthorityID: 1, Scope: 5}) rows = nil if err := db.WithContext(emptyCtx).Find(&rows).Error; err != nil { t.Fatal(err) } if len(rows) != 0 { t.Fatalf("empty custom scope exposed rows: %+v", rows) } rows = nil if err := db.Set("data_scope:skip", true).Order("id").Find(&rows).Error; err != nil { t.Fatal(err) } if len(rows) != len(seed) { t.Fatalf("explicit system bypass rows = %d, want %d", len(rows), len(seed)) } } func TestDataScopeCreateRequiresIdentityAndStampsOwnership(t *testing.T) { db := newDataScopeTestDB(t) if err := db.Create(&dataScopeRecord{Name: "missing"}).Error; !errors.Is(err, errDataScopeRequired) { t.Fatalf("create without data scope error = %v", err) } ctx := biz.NewDataScopeContext(context.Background(), biz.DataScope{UserID: 7, AuthorityID: 1, Scope: 3, PrimaryDeptID: 10, DepartmentIDs: []uint{10}}) created := dataScopeRecord{Name: "owned", DeptID: 999, CreatedBy: 999} if err := db.WithContext(ctx).Create(&created).Error; err != nil { t.Fatal(err) } var stored dataScopeRecord if err := db.Set("data_scope:skip", true).First(&stored, created.ID).Error; err != nil { t.Fatal(err) } if stored.DeptID != 10 || stored.CreatedBy != 7 { t.Fatalf("stamped ownership = dept:%d user:%d, want 10/7", stored.DeptID, stored.CreatedBy) } }