| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | package core_test | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/pocketbase/pocketbase/core" | 
					
						
							|  |  |  | 	"github.com/pocketbase/pocketbase/tests" | 
					
						
							|  |  |  | 	"github.com/pocketbase/pocketbase/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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-11 20:25:21 +08:00
										 |  |  | 	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":"","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}}` | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	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: "test@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 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{}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-11-09 00:04:13 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			"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{}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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"}, | 
					
						
							| 
									
										
										
										
											2024-11-18 20:46:06 +08:00
										 |  |  | 			{Label: "def", Audience: core.RateLimitRuleAudienceGuest}, | 
					
						
							|  |  |  | 			{Label: "/test/a", Audience: core.RateLimitRuleAudienceGuest}, | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 			{Label: "POST /test/a"}, | 
					
						
							| 
									
										
										
										
											2024-11-18 20:46:06 +08:00
										 |  |  | 			{Label: "/test/a/", Audience: core.RateLimitRuleAudienceAuth}, | 
					
						
							|  |  |  | 			{Label: "POST /test/a/"}, | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	scenarios := []struct { | 
					
						
							|  |  |  | 		labels   []string | 
					
						
							| 
									
										
										
										
											2024-11-18 20:46:06 +08:00
										 |  |  | 		audience []string | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 		expected string | 
					
						
							|  |  |  | 	}{ | 
					
						
							| 
									
										
										
										
											2024-11-18 20:46:06 +08:00
										 |  |  | 		{[]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
 | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, s := range scenarios { | 
					
						
							| 
									
										
										
										
											2024-11-18 20:46:06 +08:00
										 |  |  | 		t.Run(strings.Join(s.labels, "_")+":"+strings.Join(s.audience, "_"), func(t *testing.T) { | 
					
						
							|  |  |  | 			rule, ok := limits.FindRateLimitRule(s.labels, s.audience...) | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			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 | 
					
						
							|  |  |  | 		config         core.RateLimitRule | 
					
						
							|  |  |  | 		expectedErrors []string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			"zero value", | 
					
						
							|  |  |  | 			core.RateLimitRule{}, | 
					
						
							|  |  |  | 			[]string{"label", "duration", "maxRequests"}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			"invalid data", | 
					
						
							|  |  |  | 			core.RateLimitRule{ | 
					
						
							|  |  |  | 				Label:       "@abc", | 
					
						
							|  |  |  | 				Duration:    -1, | 
					
						
							|  |  |  | 				MaxRequests: -1, | 
					
						
							| 
									
										
										
										
											2024-11-09 00:04:13 +08:00
										 |  |  | 				Audience:    "invalid", | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2024-11-09 00:04:13 +08:00
										 |  |  | 			[]string{"label", "duration", "maxRequests", "audience"}, | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			"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{}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-11-09 00:04:13 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			"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{}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, s := range scenarios { | 
					
						
							|  |  |  | 		t.Run(s.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			result := s.config.Validate() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			tests.TestValidationErrors(t, result, s.expectedErrors) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestRateLimitRuleDurationTime(t *testing.T) { | 
					
						
							|  |  |  | 	scenarios := []struct { | 
					
						
							|  |  |  | 		config   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.config.Duration), func(t *testing.T) { | 
					
						
							|  |  |  | 			result := s.config.DurationTime() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if result != s.expected { | 
					
						
							|  |  |  | 				t.Fatalf("Expected duration %d, got %d", s.expected, result) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |