131 lines
4.4 KiB
Go
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)
|
|
}
|
|
}
|