mirror of
https://github.com/hanzoai/base.git
synced 2026-10-21 03:41:50 +00:00
Remove all _hz_ prefixes from collection IDs, internal markers, and test fixtures. The default users collection is now _users_auth_ instead of _hz_users_auth_. Internal SQL parser tokens renamed from __hz_join__/__hz_discard__ to __join__/__discard__. Backup prefix changed from @auto_hz_backup_ to @auto_backup_. Test database updated with correct collection IDs and indexes. JWT tokens in test files regenerated with new collection IDs. Add LogoURL field to MetaConfig so the admin UI logo is configurable via app settings instead of hardcoded. FullPage.svelte and App.svelte sidebar now read logo and app name from the settings store, falling back to defaults when not configured.
847 lines
17 KiB
Go
847 lines
17 KiB
Go
package core_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hanzoai/base/core"
|
|
"github.com/hanzoai/base/tests"
|
|
"github.com/hanzoai/base/tools/mailer"
|
|
)
|
|
|
|
func TestSettingsDelete(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
err := app.Delete(app.Settings())
|
|
if err == nil {
|
|
t.Fatal("Exected settings delete to fail")
|
|
}
|
|
}
|
|
|
|
func TestSettingsMerge(t *testing.T) {
|
|
s1 := &core.Settings{}
|
|
s1.Meta.AppURL = "app_url" // should be unset
|
|
|
|
s2 := &core.Settings{}
|
|
s2.Meta.AppName = "test"
|
|
s2.Logs.MaxDays = 123
|
|
s2.SMTP.Host = "test"
|
|
s2.SMTP.Enabled = true
|
|
s2.S3.Enabled = true
|
|
s2.S3.Endpoint = "test"
|
|
s2.Backups.Cron = "* * * * *"
|
|
s2.Batch.Timeout = 15
|
|
|
|
if err := s1.Merge(s2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s1Encoded, err := json.Marshal(s1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s2Encoded, err := json.Marshal(s2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if string(s1Encoded) != string(s2Encoded) {
|
|
t.Fatalf("Expected the same serialization, got\n%v\nVS\n%v", string(s1Encoded), string(s2Encoded))
|
|
}
|
|
}
|
|
|
|
func TestSettingsClone(t *testing.T) {
|
|
s1 := &core.Settings{}
|
|
s1.Meta.AppName = "test_name"
|
|
|
|
s2, err := s1.Clone()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s1Bytes, err := json.Marshal(s1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s2Bytes, err := json.Marshal(s2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if string(s1Bytes) != string(s2Bytes) {
|
|
t.Fatalf("Expected equivalent serialization, got %v VS %v", string(s1Bytes), string(s2Bytes))
|
|
}
|
|
|
|
// verify that it is a deep copy
|
|
s2.Meta.AppName = "new_test_name"
|
|
if s1.Meta.AppName == s2.Meta.AppName {
|
|
t.Fatalf("Expected s1 and s2 to have different Meta.AppName, got %s", s1.Meta.AppName)
|
|
}
|
|
}
|
|
|
|
func TestSettingsMarshalJSON(t *testing.T) {
|
|
settings := &core.Settings{}
|
|
|
|
// control fields
|
|
settings.Meta.AppName = "test123"
|
|
settings.SMTP.Username = "abc"
|
|
|
|
// secrets
|
|
testSecret := "test_secret"
|
|
settings.SMTP.Password = testSecret
|
|
settings.S3.Secret = testSecret
|
|
settings.Backups.S3.Secret = testSecret
|
|
|
|
raw, err := json.Marshal(settings)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rawStr := string(raw)
|
|
|
|
expected := `{"smtp":{"enabled":false,"port":0,"host":"","username":"abc","authMethod":"","tls":false,"localName":""},"backups":{"cron":"","cronMaxKeep":0,"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","forcePathStyle":false}},"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","forcePathStyle":false},"meta":{"appName":"test123","appURL":"","logoUrl":"","senderName":"","senderAddress":"","hideControls":false},"rateLimits":{"rules":[],"enabled":false},"trustedProxy":{"headers":[],"useLeftmostIP":false},"batch":{"enabled":false,"maxRequests":0,"timeout":0,"maxBodySize":0},"logs":{"maxDays":0,"minLevel":0,"logIP":false,"logAuthId":false}}`
|
|
|
|
if rawStr != expected {
|
|
t.Fatalf("Expected\n%v\ngot\n%v", expected, rawStr)
|
|
}
|
|
}
|
|
|
|
func TestSettingsValidate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
s := app.Settings()
|
|
|
|
// set invalid settings data
|
|
s.Meta.AppName = ""
|
|
s.Logs.MaxDays = -10
|
|
s.SMTP.Enabled = true
|
|
s.SMTP.Host = ""
|
|
s.S3.Enabled = true
|
|
s.S3.Endpoint = "invalid"
|
|
s.Backups.Cron = "invalid"
|
|
s.Backups.CronMaxKeep = -10
|
|
s.Batch.Enabled = true
|
|
s.Batch.MaxRequests = -1
|
|
s.Batch.Timeout = -1
|
|
s.RateLimits.Enabled = true
|
|
s.RateLimits.Rules = nil
|
|
|
|
// check if Validate() is triggering the members validate methods.
|
|
err := app.Validate(s)
|
|
if err == nil {
|
|
t.Fatalf("Expected error, got nil")
|
|
}
|
|
|
|
expectations := []string{
|
|
`"meta":{`,
|
|
`"logs":{`,
|
|
`"smtp":{`,
|
|
`"s3":{`,
|
|
`"backups":{`,
|
|
`"batch":{`,
|
|
`"rateLimits":{`,
|
|
}
|
|
|
|
errBytes, _ := json.Marshal(err)
|
|
jsonErr := string(errBytes)
|
|
for _, expected := range expectations {
|
|
if !strings.Contains(jsonErr, expected) {
|
|
t.Errorf("Expected error key %s in %v", expected, jsonErr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMetaConfigValidate(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
config core.MetaConfig
|
|
expectedErrors []string
|
|
}{
|
|
{
|
|
"zero values",
|
|
core.MetaConfig{},
|
|
[]string{
|
|
"appName",
|
|
"appURL",
|
|
"senderName",
|
|
"senderAddress",
|
|
},
|
|
},
|
|
{
|
|
"invalid data",
|
|
core.MetaConfig{
|
|
AppName: strings.Repeat("a", 300),
|
|
AppURL: "test",
|
|
SenderName: strings.Repeat("a", 300),
|
|
SenderAddress: "invalid_email",
|
|
},
|
|
[]string{
|
|
"appName",
|
|
"appURL",
|
|
"senderName",
|
|
"senderAddress",
|
|
},
|
|
},
|
|
{
|
|
"valid data",
|
|
core.MetaConfig{
|
|
AppName: "test",
|
|
AppURL: "https://example.com",
|
|
SenderName: "test",
|
|
SenderAddress: "[email protected]",
|
|
},
|
|
[]string{},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
result := s.config.Validate()
|
|
|
|
tests.TestValidationErrors(t, result, s.expectedErrors)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLogsConfigValidate(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
config core.LogsConfig
|
|
expectedErrors []string
|
|
}{
|
|
{
|
|
"zero values",
|
|
core.LogsConfig{},
|
|
[]string{},
|
|
},
|
|
{
|
|
"invalid data",
|
|
core.LogsConfig{MaxDays: -1},
|
|
[]string{"maxDays"},
|
|
},
|
|
{
|
|
"valid data",
|
|
core.LogsConfig{MaxDays: 2},
|
|
[]string{},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
result := s.config.Validate()
|
|
|
|
tests.TestValidationErrors(t, result, s.expectedErrors)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSMTPConfigValidate(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
config core.SMTPConfig
|
|
expectedErrors []string
|
|
}{
|
|
{
|
|
"zero values (disabled)",
|
|
core.SMTPConfig{},
|
|
[]string{},
|
|
},
|
|
{
|
|
"zero values (enabled)",
|
|
core.SMTPConfig{Enabled: true},
|
|
[]string{"host", "port"},
|
|
},
|
|
{
|
|
"invalid data",
|
|
core.SMTPConfig{
|
|
Enabled: true,
|
|
Host: "test:test:test",
|
|
Port: -10,
|
|
LocalName: "invalid!",
|
|
AuthMethod: "invalid",
|
|
},
|
|
[]string{"host", "port", "authMethod", "localName"},
|
|
},
|
|
{
|
|
"valid data (no explicit auth method and localName)",
|
|
core.SMTPConfig{
|
|
Enabled: true,
|
|
Host: "example.com",
|
|
Port: 100,
|
|
TLS: true,
|
|
},
|
|
[]string{},
|
|
},
|
|
{
|
|
"valid data (explicit auth method and localName)",
|
|
core.SMTPConfig{
|
|
Enabled: true,
|
|
Host: "example.com",
|
|
Port: 100,
|
|
AuthMethod: mailer.SMTPAuthLogin,
|
|
LocalName: "example.com",
|
|
},
|
|
[]string{},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
result := s.config.Validate()
|
|
|
|
tests.TestValidationErrors(t, result, s.expectedErrors)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestS3ConfigValidate(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
config core.S3Config
|
|
expectedErrors []string
|
|
}{
|
|
{
|
|
"zero values (disabled)",
|
|
core.S3Config{},
|
|
[]string{},
|
|
},
|
|
{
|
|
"zero values (enabled)",
|
|
core.S3Config{Enabled: true},
|
|
[]string{
|
|
"bucket",
|
|
"region",
|
|
"endpoint",
|
|
"accessKey",
|
|
"secret",
|
|
},
|
|
},
|
|
{
|
|
"invalid data",
|
|
core.S3Config{
|
|
Enabled: true,
|
|
Endpoint: "test:test:test",
|
|
},
|
|
[]string{
|
|
"bucket",
|
|
"region",
|
|
"endpoint",
|
|
"accessKey",
|
|
"secret",
|
|
},
|
|
},
|
|
{
|
|
"valid data (url endpoint)",
|
|
core.S3Config{
|
|
Enabled: true,
|
|
Endpoint: "https://localhost:8090",
|
|
Bucket: "test",
|
|
Region: "test",
|
|
AccessKey: "test",
|
|
Secret: "test",
|
|
},
|
|
[]string{},
|
|
},
|
|
{
|
|
"valid data (hostname endpoint)",
|
|
core.S3Config{
|
|
Enabled: true,
|
|
Endpoint: "example.com",
|
|
Bucket: "test",
|
|
Region: "test",
|
|
AccessKey: "test",
|
|
Secret: "test",
|
|
},
|
|
[]string{},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
result := s.config.Validate()
|
|
|
|
tests.TestValidationErrors(t, result, s.expectedErrors)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBackupsConfigValidate(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
config core.BackupsConfig
|
|
expectedErrors []string
|
|
}{
|
|
{
|
|
"zero value",
|
|
core.BackupsConfig{},
|
|
[]string{},
|
|
},
|
|
{
|
|
"invalid cron",
|
|
core.BackupsConfig{
|
|
Cron: "invalid",
|
|
CronMaxKeep: 0,
|
|
},
|
|
[]string{"cron", "cronMaxKeep"},
|
|
},
|
|
{
|
|
"invalid enabled S3",
|
|
core.BackupsConfig{
|
|
S3: core.S3Config{
|
|
Enabled: true,
|
|
},
|
|
},
|
|
[]string{"s3"},
|
|
},
|
|
{
|
|
"valid data",
|
|
core.BackupsConfig{
|
|
S3: core.S3Config{
|
|
Enabled: true,
|
|
Endpoint: "example.com",
|
|
Bucket: "test",
|
|
Region: "test",
|
|
AccessKey: "test",
|
|
Secret: "test",
|
|
},
|
|
Cron: "*/10 * * * *",
|
|
CronMaxKeep: 1,
|
|
},
|
|
[]string{},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
result := s.config.Validate()
|
|
|
|
tests.TestValidationErrors(t, result, s.expectedErrors)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBatchConfigValidate(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
config core.BatchConfig
|
|
expectedErrors []string
|
|
}{
|
|
{
|
|
"zero value",
|
|
core.BatchConfig{},
|
|
[]string{},
|
|
},
|
|
{
|
|
"zero value (enabled)",
|
|
core.BatchConfig{Enabled: true},
|
|
[]string{"maxRequests", "timeout"},
|
|
},
|
|
{
|
|
"invalid data (negative values)",
|
|
core.BatchConfig{
|
|
MaxRequests: -1,
|
|
Timeout: -1,
|
|
MaxBodySize: -1,
|
|
},
|
|
[]string{"maxRequests", "timeout", "maxBodySize"},
|
|
},
|
|
{
|
|
"min fields valid data",
|
|
core.BatchConfig{
|
|
Enabled: true,
|
|
MaxRequests: 1,
|
|
Timeout: 1,
|
|
},
|
|
[]string{},
|
|
},
|
|
{
|
|
"all fields valid data",
|
|
core.BatchConfig{
|
|
Enabled: true,
|
|
MaxRequests: 10,
|
|
Timeout: 1,
|
|
MaxBodySize: 1,
|
|
},
|
|
[]string{},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
result := s.config.Validate()
|
|
|
|
tests.TestValidationErrors(t, result, s.expectedErrors)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRateLimitsConfigValidate(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
config core.RateLimitsConfig
|
|
expectedErrors []string
|
|
}{
|
|
{
|
|
"zero value (disabled)",
|
|
core.RateLimitsConfig{},
|
|
[]string{},
|
|
},
|
|
{
|
|
"zero value (enabled)",
|
|
core.RateLimitsConfig{Enabled: true},
|
|
[]string{"rules"},
|
|
},
|
|
{
|
|
"invalid data",
|
|
core.RateLimitsConfig{
|
|
Enabled: true,
|
|
Rules: []core.RateLimitRule{
|
|
{
|
|
Label: "/123abc/",
|
|
Duration: 1,
|
|
MaxRequests: 2,
|
|
},
|
|
{
|
|
Label: "!abc",
|
|
Duration: -1,
|
|
MaxRequests: -1,
|
|
},
|
|
},
|
|
},
|
|
[]string{"rules"},
|
|
},
|
|
{
|
|
"valid data",
|
|
core.RateLimitsConfig{
|
|
Enabled: true,
|
|
Rules: []core.RateLimitRule{
|
|
{
|
|
Label: "123_abc",
|
|
Duration: 1,
|
|
MaxRequests: 2,
|
|
},
|
|
{
|
|
Label: "/456-abc",
|
|
Duration: 1,
|
|
MaxRequests: 2,
|
|
},
|
|
},
|
|
},
|
|
[]string{},
|
|
},
|
|
{
|
|
"duplicated rules with the same audience",
|
|
core.RateLimitsConfig{
|
|
Enabled: true,
|
|
Rules: []core.RateLimitRule{
|
|
{
|
|
Label: "/a",
|
|
Duration: 1,
|
|
MaxRequests: 2,
|
|
},
|
|
{
|
|
Label: "/a",
|
|
Duration: 2,
|
|
MaxRequests: 3,
|
|
},
|
|
},
|
|
},
|
|
[]string{"rules"},
|
|
},
|
|
{
|
|
"duplicated rule with conflicting audience (A)",
|
|
core.RateLimitsConfig{
|
|
Enabled: true,
|
|
Rules: []core.RateLimitRule{
|
|
{
|
|
Label: "/a",
|
|
Duration: 1,
|
|
MaxRequests: 2,
|
|
},
|
|
{
|
|
Label: "/a",
|
|
Duration: 1,
|
|
MaxRequests: 2,
|
|
Audience: core.RateLimitRuleAudienceGuest,
|
|
},
|
|
},
|
|
},
|
|
[]string{"rules"},
|
|
},
|
|
{
|
|
"duplicated rule with conflicting audience (B)",
|
|
core.RateLimitsConfig{
|
|
Enabled: true,
|
|
Rules: []core.RateLimitRule{
|
|
{
|
|
Label: "/a",
|
|
Duration: 1,
|
|
MaxRequests: 2,
|
|
Audience: core.RateLimitRuleAudienceAuth,
|
|
},
|
|
{
|
|
Label: "/a",
|
|
Duration: 1,
|
|
MaxRequests: 2,
|
|
},
|
|
},
|
|
},
|
|
[]string{"rules"},
|
|
},
|
|
{
|
|
"duplicated rule with non-conflicting audience",
|
|
core.RateLimitsConfig{
|
|
Enabled: true,
|
|
Rules: []core.RateLimitRule{
|
|
{
|
|
Label: "/a",
|
|
Duration: 1,
|
|
MaxRequests: 2,
|
|
Audience: core.RateLimitRuleAudienceAuth,
|
|
},
|
|
{
|
|
Label: "/a",
|
|
Duration: 1,
|
|
MaxRequests: 2,
|
|
Audience: core.RateLimitRuleAudienceGuest,
|
|
},
|
|
},
|
|
},
|
|
[]string{},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
result := s.config.Validate()
|
|
|
|
tests.TestValidationErrors(t, result, s.expectedErrors)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRateLimitsFindRateLimitRule(t *testing.T) {
|
|
limits := core.RateLimitsConfig{
|
|
Rules: []core.RateLimitRule{
|
|
{Label: "abc"},
|
|
{Label: "def", Audience: core.RateLimitRuleAudienceGuest},
|
|
{Label: "/test/a", Audience: core.RateLimitRuleAudienceGuest},
|
|
{Label: "POST /test/a"},
|
|
{Label: "/test/a/", Audience: core.RateLimitRuleAudienceAuth},
|
|
{Label: "POST /test/a/"},
|
|
},
|
|
}
|
|
|
|
scenarios := []struct {
|
|
labels []string
|
|
audience []string
|
|
expected string
|
|
}{
|
|
{[]string{}, []string{}, ""},
|
|
{[]string{"missing"}, []string{}, ""},
|
|
{[]string{"abc"}, []string{}, "abc"},
|
|
{[]string{"abc"}, []string{core.RateLimitRuleAudienceGuest}, ""},
|
|
{[]string{"abc"}, []string{core.RateLimitRuleAudienceAuth}, ""},
|
|
{[]string{"def"}, []string{core.RateLimitRuleAudienceGuest}, "def"},
|
|
{[]string{"def"}, []string{core.RateLimitRuleAudienceAuth}, ""},
|
|
{[]string{"/test"}, []string{}, ""},
|
|
{[]string{"/test/a"}, []string{}, "/test/a"},
|
|
{[]string{"/test/a"}, []string{core.RateLimitRuleAudienceAuth}, "/test/a/"},
|
|
{[]string{"/test/a"}, []string{core.RateLimitRuleAudienceGuest}, "/test/a"},
|
|
{[]string{"GET /test/a"}, []string{}, ""},
|
|
{[]string{"POST /test/a"}, []string{}, "POST /test/a"},
|
|
{[]string{"/test/a/b/c"}, []string{}, "/test/a/"},
|
|
{[]string{"/test/a/b/c"}, []string{core.RateLimitRuleAudienceAuth}, "/test/a/"},
|
|
{[]string{"/test/a/b/c"}, []string{core.RateLimitRuleAudienceGuest}, ""},
|
|
{[]string{"GET /test/a/b/c"}, []string{}, ""},
|
|
{[]string{"POST /test/a/b/c"}, []string{}, "POST /test/a/"},
|
|
{[]string{"/test/a", "abc"}, []string{}, "/test/a"}, // priority checks
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(strings.Join(s.labels, "_")+":"+strings.Join(s.audience, "_"), func(t *testing.T) {
|
|
rule, ok := limits.FindRateLimitRule(s.labels, s.audience...)
|
|
|
|
hasLabel := rule.Label != ""
|
|
if hasLabel != ok {
|
|
t.Fatalf("Expected hasLabel %v, got %v", hasLabel, ok)
|
|
}
|
|
|
|
if rule.Label != s.expected {
|
|
t.Fatalf("Expected rule with label %q, got %q", s.expected, rule.Label)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRateLimitRuleValidate(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
rule core.RateLimitRule
|
|
expectedErrors []string
|
|
}{
|
|
{
|
|
"zero value",
|
|
core.RateLimitRule{},
|
|
[]string{"label", "duration", "maxRequests"},
|
|
},
|
|
{
|
|
"invalid data",
|
|
core.RateLimitRule{
|
|
Label: "@abc",
|
|
Duration: -1,
|
|
MaxRequests: -1,
|
|
Audience: "invalid",
|
|
},
|
|
[]string{"label", "duration", "maxRequests", "audience"},
|
|
},
|
|
{
|
|
"valid data (name)",
|
|
core.RateLimitRule{
|
|
Label: "abc:123",
|
|
Duration: 1,
|
|
MaxRequests: 1,
|
|
},
|
|
[]string{},
|
|
},
|
|
{
|
|
"valid data (name:action)",
|
|
core.RateLimitRule{
|
|
Label: "abc:123",
|
|
Duration: 1,
|
|
MaxRequests: 1,
|
|
},
|
|
[]string{},
|
|
},
|
|
{
|
|
"valid data (*:action)",
|
|
core.RateLimitRule{
|
|
Label: "*:123",
|
|
Duration: 1,
|
|
MaxRequests: 1,
|
|
},
|
|
[]string{},
|
|
},
|
|
{
|
|
"valid data (path /a/b)",
|
|
core.RateLimitRule{
|
|
Label: "/a/b",
|
|
Duration: 1,
|
|
MaxRequests: 1,
|
|
},
|
|
[]string{},
|
|
},
|
|
{
|
|
"valid data (path POST /a/b)",
|
|
core.RateLimitRule{
|
|
Label: "POST /a/b/",
|
|
Duration: 1,
|
|
MaxRequests: 1,
|
|
},
|
|
[]string{},
|
|
},
|
|
{
|
|
"invalid audience",
|
|
core.RateLimitRule{
|
|
Label: "/a/b/",
|
|
Duration: 1,
|
|
MaxRequests: 1,
|
|
Audience: "invalid",
|
|
},
|
|
[]string{"audience"},
|
|
},
|
|
{
|
|
"valid audience - " + core.RateLimitRuleAudienceGuest,
|
|
core.RateLimitRule{
|
|
Label: "POST /a/b/",
|
|
Duration: 1,
|
|
MaxRequests: 1,
|
|
Audience: core.RateLimitRuleAudienceGuest,
|
|
},
|
|
[]string{},
|
|
},
|
|
{
|
|
"valid audience - " + core.RateLimitRuleAudienceAuth,
|
|
core.RateLimitRule{
|
|
Label: "POST /a/b/",
|
|
Duration: 1,
|
|
MaxRequests: 1,
|
|
Audience: core.RateLimitRuleAudienceAuth,
|
|
},
|
|
[]string{},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
result := s.rule.Validate()
|
|
|
|
tests.TestValidationErrors(t, result, s.expectedErrors)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRateLimitRuleDurationTime(t *testing.T) {
|
|
scenarios := []struct {
|
|
rule core.RateLimitRule
|
|
expected time.Duration
|
|
}{
|
|
{core.RateLimitRule{}, 0 * time.Second},
|
|
{core.RateLimitRule{Duration: 1234}, 1234 * time.Second},
|
|
}
|
|
|
|
for i, s := range scenarios {
|
|
t.Run(fmt.Sprintf("%d_%d", i, s.rule.Duration), func(t *testing.T) {
|
|
result := s.rule.DurationTime()
|
|
|
|
if result != s.expected {
|
|
t.Fatalf("Expected duration %d, got %d", s.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRateLimitRuleString(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
rule core.RateLimitRule
|
|
expected string
|
|
}{
|
|
{
|
|
"empty",
|
|
core.RateLimitRule{},
|
|
`{"label":"","audience":"","duration":0,"maxRequests":0}`,
|
|
},
|
|
{
|
|
"all fields",
|
|
core.RateLimitRule{
|
|
Label: "POST /a/b/",
|
|
Duration: 1,
|
|
MaxRequests: 2,
|
|
Audience: core.RateLimitRuleAudienceAuth,
|
|
},
|
|
`{"label":"POST /a/b/","audience":"@auth","duration":1,"maxRequests":2}`,
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
result := s.rule.String()
|
|
|
|
if result != s.expected {
|
|
t.Fatalf("Expected string\n%s\ngot\n%s", s.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|