305 lines
7.7 KiB
Go
305 lines
7.7 KiB
Go
// Copyright 2024 The Forgejo Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package quota_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
quota_model "code.gitea.io/gitea/models/quota"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func makeFullyUsed() quota_model.Used {
|
|
return quota_model.Used{
|
|
Size: quota_model.UsedSize{
|
|
Repos: quota_model.UsedSizeRepos{
|
|
Public: 1024,
|
|
Private: 1024,
|
|
},
|
|
Git: quota_model.UsedSizeGit{
|
|
LFS: 1024,
|
|
},
|
|
Assets: quota_model.UsedSizeAssets{
|
|
Attachments: quota_model.UsedSizeAssetsAttachments{
|
|
Issues: 1024,
|
|
Releases: 1024,
|
|
},
|
|
Artifacts: 1024,
|
|
Packages: quota_model.UsedSizeAssetsPackages{
|
|
All: 1024,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func makePartiallyUsed() quota_model.Used {
|
|
return quota_model.Used{
|
|
Size: quota_model.UsedSize{
|
|
Repos: quota_model.UsedSizeRepos{
|
|
Public: 1024,
|
|
},
|
|
Assets: quota_model.UsedSizeAssets{
|
|
Attachments: quota_model.UsedSizeAssetsAttachments{
|
|
Releases: 1024,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func setUsed(used quota_model.Used, subject quota_model.LimitSubject, value int64) *quota_model.Used {
|
|
switch subject {
|
|
case quota_model.LimitSubjectSizeReposPublic:
|
|
used.Size.Repos.Public = value
|
|
return &used
|
|
case quota_model.LimitSubjectSizeReposPrivate:
|
|
used.Size.Repos.Private = value
|
|
return &used
|
|
case quota_model.LimitSubjectSizeGitLFS:
|
|
used.Size.Git.LFS = value
|
|
return &used
|
|
case quota_model.LimitSubjectSizeAssetsAttachmentsIssues:
|
|
used.Size.Assets.Attachments.Issues = value
|
|
return &used
|
|
case quota_model.LimitSubjectSizeAssetsAttachmentsReleases:
|
|
used.Size.Assets.Attachments.Releases = value
|
|
return &used
|
|
case quota_model.LimitSubjectSizeAssetsArtifacts:
|
|
used.Size.Assets.Artifacts = value
|
|
return &used
|
|
case quota_model.LimitSubjectSizeAssetsPackagesAll:
|
|
used.Size.Assets.Packages.All = value
|
|
return &used
|
|
case quota_model.LimitSubjectSizeWiki:
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func assertEvaluation(t *testing.T, rule quota_model.Rule, used quota_model.Used, subject quota_model.LimitSubject, expected bool) {
|
|
t.Helper()
|
|
|
|
t.Run(subject.String(), func(t *testing.T) {
|
|
ok, has := rule.Evaluate(used, subject)
|
|
assert.True(t, has)
|
|
assert.Equal(t, expected, ok)
|
|
})
|
|
}
|
|
|
|
func TestQuotaRuleNoEvaluation(t *testing.T) {
|
|
rule := quota_model.Rule{
|
|
Limit: 1024,
|
|
Subjects: quota_model.LimitSubjects{
|
|
quota_model.LimitSubjectSizeAssetsAttachmentsAll,
|
|
},
|
|
}
|
|
used := quota_model.Used{}
|
|
used.Size.Repos.Public = 4096
|
|
|
|
_, has := rule.Evaluate(used, quota_model.LimitSubjectSizeReposAll)
|
|
|
|
// We have a rule for "size:assets:attachments:all", and query for
|
|
// "size:repos:all". We don't cover that subject, so the evaluation returns
|
|
// with no rules found.
|
|
assert.False(t, has)
|
|
}
|
|
|
|
func TestQuotaRuleDirectEvaluation(t *testing.T) {
|
|
// This function is meant to test direct rule evaluation: cases where we set
|
|
// a rule for a subject, and we evaluate against the same subject.
|
|
|
|
runTest := func(t *testing.T, subject quota_model.LimitSubject, limit, used int64, expected bool) {
|
|
t.Helper()
|
|
|
|
rule := quota_model.Rule{
|
|
Limit: limit,
|
|
Subjects: quota_model.LimitSubjects{
|
|
subject,
|
|
},
|
|
}
|
|
usedObj := setUsed(quota_model.Used{}, subject, used)
|
|
if usedObj == nil {
|
|
return
|
|
}
|
|
|
|
assertEvaluation(t, rule, *usedObj, subject, expected)
|
|
}
|
|
|
|
t.Run("limit:0", func(t *testing.T) {
|
|
// With limit:0, nothing used is fine.
|
|
t.Run("used:0", func(t *testing.T) {
|
|
for subject := quota_model.LimitSubjectFirst; subject <= quota_model.LimitSubjectLast; subject++ {
|
|
runTest(t, subject, 0, 0, true)
|
|
}
|
|
})
|
|
// With limit:0, any usage will fail evaluation
|
|
t.Run("used:512", func(t *testing.T) {
|
|
for subject := quota_model.LimitSubjectFirst; subject <= quota_model.LimitSubjectLast; subject++ {
|
|
runTest(t, subject, 0, 512, false)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("limit:unlimited", func(t *testing.T) {
|
|
// With no limits, any usage will succeed evaluation
|
|
t.Run("used:512", func(t *testing.T) {
|
|
for subject := quota_model.LimitSubjectFirst; subject <= quota_model.LimitSubjectLast; subject++ {
|
|
runTest(t, subject, -1, 512, true)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("limit:1024", func(t *testing.T) {
|
|
// With a set limit, usage below the limit succeeds
|
|
t.Run("used:512", func(t *testing.T) {
|
|
for subject := quota_model.LimitSubjectFirst; subject <= quota_model.LimitSubjectLast; subject++ {
|
|
runTest(t, subject, 1024, 512, true)
|
|
}
|
|
})
|
|
|
|
// With a set limit, usage above the limit fails
|
|
t.Run("used:2048", func(t *testing.T) {
|
|
for subject := quota_model.LimitSubjectFirst; subject <= quota_model.LimitSubjectLast; subject++ {
|
|
runTest(t, subject, 1024, 2048, false)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestQuotaRuleCombined(t *testing.T) {
|
|
rule := quota_model.Rule{
|
|
Limit: 1024,
|
|
Subjects: quota_model.LimitSubjects{
|
|
quota_model.LimitSubjectSizeGitLFS,
|
|
quota_model.LimitSubjectSizeAssetsAttachmentsReleases,
|
|
quota_model.LimitSubjectSizeAssetsPackagesAll,
|
|
},
|
|
}
|
|
used := quota_model.Used{
|
|
Size: quota_model.UsedSize{
|
|
Repos: quota_model.UsedSizeRepos{
|
|
Public: 4096,
|
|
},
|
|
Git: quota_model.UsedSizeGit{
|
|
LFS: 256,
|
|
},
|
|
Assets: quota_model.UsedSizeAssets{
|
|
Attachments: quota_model.UsedSizeAssetsAttachments{
|
|
Issues: 2048,
|
|
Releases: 256,
|
|
},
|
|
Packages: quota_model.UsedSizeAssetsPackages{
|
|
All: 2560,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
expectationMap := map[quota_model.LimitSubject]bool{
|
|
quota_model.LimitSubjectSizeGitLFS: false,
|
|
quota_model.LimitSubjectSizeAssetsAttachmentsReleases: false,
|
|
quota_model.LimitSubjectSizeAssetsPackagesAll: false,
|
|
}
|
|
|
|
for subject := quota_model.LimitSubjectFirst; subject <= quota_model.LimitSubjectLast; subject++ {
|
|
t.Run(subject.String(), func(t *testing.T) {
|
|
evalOk, evalHas := rule.Evaluate(used, subject)
|
|
expected, expectedHas := expectationMap[subject]
|
|
|
|
assert.Equal(t, expectedHas, evalHas)
|
|
if expectedHas {
|
|
assert.Equal(t, expected, evalOk)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestQuotaRuleSizeAll(t *testing.T) {
|
|
runTests := func(t *testing.T, rule quota_model.Rule, expected bool) {
|
|
t.Helper()
|
|
|
|
subject := quota_model.LimitSubjectSizeAll
|
|
|
|
t.Run("used:0", func(t *testing.T) {
|
|
used := quota_model.Used{}
|
|
|
|
assertEvaluation(t, rule, used, subject, true)
|
|
})
|
|
|
|
t.Run("used:some-each", func(t *testing.T) {
|
|
used := makeFullyUsed()
|
|
|
|
assertEvaluation(t, rule, used, subject, expected)
|
|
})
|
|
|
|
t.Run("used:some", func(t *testing.T) {
|
|
used := makePartiallyUsed()
|
|
|
|
assertEvaluation(t, rule, used, subject, expected)
|
|
})
|
|
}
|
|
|
|
// With all limits set to 0, evaluation always fails if usage > 0
|
|
t.Run("rule:0", func(t *testing.T) {
|
|
rule := quota_model.Rule{
|
|
Limit: 0,
|
|
Subjects: quota_model.LimitSubjects{
|
|
quota_model.LimitSubjectSizeAll,
|
|
},
|
|
}
|
|
|
|
runTests(t, rule, false)
|
|
})
|
|
|
|
// With no limits, evaluation always succeeds
|
|
t.Run("rule:unlimited", func(t *testing.T) {
|
|
rule := quota_model.Rule{
|
|
Limit: -1,
|
|
Subjects: quota_model.LimitSubjects{
|
|
quota_model.LimitSubjectSizeAll,
|
|
},
|
|
}
|
|
|
|
runTests(t, rule, true)
|
|
})
|
|
|
|
// With a specific, very generous limit, evaluation succeeds if the limit isn't exhausted
|
|
t.Run("rule:generous", func(t *testing.T) {
|
|
rule := quota_model.Rule{
|
|
Limit: 102400,
|
|
Subjects: quota_model.LimitSubjects{
|
|
quota_model.LimitSubjectSizeAll,
|
|
},
|
|
}
|
|
|
|
runTests(t, rule, true)
|
|
|
|
t.Run("limit exhaustion", func(t *testing.T) {
|
|
used := quota_model.Used{
|
|
Size: quota_model.UsedSize{
|
|
Repos: quota_model.UsedSizeRepos{
|
|
Public: 204800,
|
|
},
|
|
},
|
|
}
|
|
|
|
assertEvaluation(t, rule, used, quota_model.LimitSubjectSizeAll, false)
|
|
})
|
|
})
|
|
|
|
// With a specific, small limit, evaluation fails
|
|
t.Run("rule:limited", func(t *testing.T) {
|
|
rule := quota_model.Rule{
|
|
Limit: 512,
|
|
Subjects: quota_model.LimitSubjects{
|
|
quota_model.LimitSubjectSizeAll,
|
|
},
|
|
}
|
|
|
|
runTests(t, rule, false)
|
|
})
|
|
}
|