Files

131 lines
4.4 KiB
Go

package quota
import (
"sync"
"testing"
"github.com/google/uuid"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
deviceModels "git.ilapage.cn/ila/yovision/Sense/server/app/sense/device/models"
)
func testQuotaDB(t *testing.T, limit int) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open("file:"+uuid.NewString()+"?mode=memory&cache=shared"), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
sqlDB, err := db.DB()
if err != nil {
t.Fatal(err)
}
sqlDB.SetMaxOpenConns(1)
if err = db.AutoMigrate(&deviceModels.Device{}, &Setting{}, &Change{}); err != nil {
t.Fatal(err)
}
if err = db.Create(&Setting{ID: SettingID, Limit: limit, Source: "test", Version: 1}).Error; err != nil {
t.Fatal(err)
}
return db
}
func TestOverviewShowsOccupancyPaginationAndDeliveryBoundaries(t *testing.T) {
db := testQuotaDB(t, 16)
for index, status := range []string{"active", "pending", "disabled"} {
device := deviceModels.Device{ID: uuid.NewString(), Name: "设备", Location: "位置", Modality: "video", CapabilitiesJSON: "[]", Status: status, AdapterStatus: "ready", Version: int64(index + 1)}
if err := db.Create(&device).Error; err != nil {
t.Fatal(err)
}
}
request := PageRequest{}
request.PageIndex, request.PageSize = 1, 64
response, err := (Service{DB: db}).Overview(request)
if err != nil {
t.Fatal(err)
}
if response.Summary.Limit != 16 || response.Summary.Used != 2 || response.Summary.Remaining != 14 || response.Count != 3 || len(response.List) != 3 {
t.Fatalf("unexpected overview: %#v", response)
}
if response.Tiers[0].Validation != "verified" || response.Tiers[1].Validation != "unverified" || !response.Tiers[0].Configured {
t.Fatalf("delivery tiers overpromise capacity: %#v", response.Tiers)
}
}
func TestUnreadableQuotaKeepsReadsAndRejectsWrites(t *testing.T) {
db := testQuotaDB(t, 16)
if err := db.Create(&deviceModels.Device{ID: uuid.NewString(), Name: "已有设备", Modality: "video", CapabilitiesJSON: "[]", Status: "active", AdapterStatus: "ready", Version: 1}).Error; err != nil {
t.Fatal(err)
}
if err := db.Delete(&Setting{}, SettingID).Error; err != nil {
t.Fatal(err)
}
response, err := (Service{DB: db}).Overview(PageRequest{})
if err != nil {
t.Fatal(err)
}
if response.Summary.ReadStatus != ReadStatusUnreadable || response.Summary.Used != 1 || len(response.List) != 1 {
t.Fatalf("read-only fallback failed: %#v", response)
}
if err = WithAvailableSlot(db, func(*gorm.DB) error { return nil }); err != ErrUnavailable {
t.Fatalf("write with unreadable quota error=%v", err)
}
}
func TestUpdateAllowsLowerLimitWithoutStoppingExistingDevices(t *testing.T) {
db := testQuotaDB(t, 16)
for index := 0; index < 2; index++ {
if err := db.Create(&deviceModels.Device{ID: uuid.NewString(), Name: "设备", Modality: "video", CapabilitiesJSON: "[]", Status: "active", AdapterStatus: "ready", Version: 1}).Error; err != nil {
t.Fatal(err)
}
}
summary, err := (Service{DB: db}).Update(UpdateRequest{Limit: 1, Reason: "测试降低配额", Version: 1, UpdateBy: 7})
if err != nil {
t.Fatal(err)
}
if summary.Limit != 1 || summary.Used != 2 || summary.Remaining != 0 {
t.Fatalf("unexpected lowered quota: %#v", summary)
}
var devices int64
if err = db.Model(&deviceModels.Device{}).Count(&devices).Error; err != nil || devices != 2 {
t.Fatalf("existing devices changed: count=%d err=%v", devices, err)
}
var changes int64
if err = db.Model(&Change{}).Count(&changes).Error; err != nil || changes != 1 {
t.Fatalf("audit changes=%d err=%v", changes, err)
}
}
func TestConcurrentSlotReservationDoesNotExceedQuota(t *testing.T) {
db := testQuotaDB(t, 4)
const workers = 12
var wait sync.WaitGroup
var accepted int
var lock sync.Mutex
for index := 0; index < workers; index++ {
wait.Add(1)
go func() {
defer wait.Done()
err := WithAvailableSlot(db, func(tx *gorm.DB) error {
return tx.Create(&deviceModels.Device{ID: uuid.NewString(), Name: "并发设备", Modality: "video", CapabilitiesJSON: "[]", Status: "pending", AdapterStatus: "ready", Version: 1}).Error
})
if err == nil {
lock.Lock()
accepted++
lock.Unlock()
} else if err != ErrExceeded {
t.Errorf("unexpected reservation error: %v", err)
}
}()
}
wait.Wait()
if accepted != 4 {
t.Fatalf("accepted=%d want=4", accepted)
}
var count int64
if err := db.Model(&deviceModels.Device{}).Where("status <> ?", "disabled").Count(&count).Error; err != nil || count != 4 {
t.Fatalf("occupied=%d err=%v", count, err)
}
}