init push
This commit is contained in:
340
internal/config/config_test.go
Normal file
340
internal/config/config_test.go
Normal file
@@ -0,0 +1,340 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoadFileManagerUploadPolicyConfigFromEnv_Defaults(t *testing.T) {
|
||||
t.Setenv("FILE_MANAGER_UPLOAD_MAX_FILE_SIZE_BYTES", "")
|
||||
t.Setenv("FILE_MANAGER_UPLOAD_ALLOWED_MIME_TYPES", "")
|
||||
t.Setenv("FILE_MANAGER_UPLOAD_ALLOWED_EXTENSIONS", "")
|
||||
t.Setenv("FILE_MANAGER_UPLOAD_ALLOW_EMPTY_FILE", "")
|
||||
t.Setenv("FILE_MANAGER_UPLOAD_MAX_FILENAME_LENGTH", "")
|
||||
|
||||
cfg := LoadFileManagerUploadPolicyConfigFromEnv()
|
||||
|
||||
if cfg.MaxFileSizeBytes != 4*1024*1024 {
|
||||
t.Fatalf("expected default max file size 4194304, got %d", cfg.MaxFileSizeBytes)
|
||||
}
|
||||
if cfg.AllowEmptyFile {
|
||||
t.Fatalf("expected default allow empty file false")
|
||||
}
|
||||
if cfg.MaxFilenameLength != 255 {
|
||||
t.Fatalf("expected default max filename length 255, got %d", cfg.MaxFilenameLength)
|
||||
}
|
||||
if len(cfg.AllowedMimeTypes) != 0 {
|
||||
t.Fatalf("expected empty default allowed mime types, got %v", cfg.AllowedMimeTypes)
|
||||
}
|
||||
if len(cfg.AllowedExtensions) != 0 {
|
||||
t.Fatalf("expected empty default allowed extensions, got %v", cfg.AllowedExtensions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFileManagerUploadPolicyConfigFromEnv_Normalize(t *testing.T) {
|
||||
t.Setenv("FILE_MANAGER_UPLOAD_MAX_FILE_SIZE_BYTES", "1048576")
|
||||
t.Setenv("FILE_MANAGER_UPLOAD_ALLOWED_MIME_TYPES", " image/PNG ; charset=utf-8,application/pdf,IMAGE/PNG")
|
||||
t.Setenv("FILE_MANAGER_UPLOAD_ALLOWED_EXTENSIONS", " .PDF, png ,PNG")
|
||||
t.Setenv("FILE_MANAGER_UPLOAD_ALLOW_EMPTY_FILE", "true")
|
||||
t.Setenv("FILE_MANAGER_UPLOAD_MAX_FILENAME_LENGTH", "120")
|
||||
|
||||
cfg := LoadFileManagerUploadPolicyConfigFromEnv()
|
||||
|
||||
if cfg.MaxFileSizeBytes != 1048576 {
|
||||
t.Fatalf("expected max file size 1048576, got %d", cfg.MaxFileSizeBytes)
|
||||
}
|
||||
if !cfg.AllowEmptyFile {
|
||||
t.Fatalf("expected allow empty file true")
|
||||
}
|
||||
if cfg.MaxFilenameLength != 120 {
|
||||
t.Fatalf("expected max filename length 120, got %d", cfg.MaxFilenameLength)
|
||||
}
|
||||
|
||||
if len(cfg.AllowedMimeTypes) != 2 || cfg.AllowedMimeTypes[0] != "image/png" || cfg.AllowedMimeTypes[1] != "application/pdf" {
|
||||
t.Fatalf("unexpected normalized mime types: %v", cfg.AllowedMimeTypes)
|
||||
}
|
||||
if len(cfg.AllowedExtensions) != 2 || cfg.AllowedExtensions[0] != "pdf" || cfg.AllowedExtensions[1] != "png" {
|
||||
t.Fatalf("unexpected normalized extensions: %v", cfg.AllowedExtensions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFileManagerTrashPolicyConfigFromEnv_Defaults(t *testing.T) {
|
||||
t.Setenv("FILE_MANAGER_TRASH_AUTO_PURGE_ENABLED", "")
|
||||
t.Setenv("FILE_MANAGER_TRASH_RETENTION", "")
|
||||
t.Setenv("FILE_MANAGER_TRASH_AUTO_PURGE_INTERVAL", "")
|
||||
t.Setenv("FILE_MANAGER_TRASH_AUTO_PURGE_BATCH_SIZE", "")
|
||||
|
||||
cfg := LoadFileManagerTrashPolicyConfigFromEnv()
|
||||
if !cfg.AutoPurgeEnabled {
|
||||
t.Fatalf("expected default auto purge enabled")
|
||||
}
|
||||
if cfg.Retention != 30*24*time.Hour {
|
||||
t.Fatalf("expected default retention 720h, got %s", cfg.Retention)
|
||||
}
|
||||
if cfg.AutoPurgeInterval != time.Hour {
|
||||
t.Fatalf("expected default auto purge interval 1h, got %s", cfg.AutoPurgeInterval)
|
||||
}
|
||||
if cfg.AutoPurgeBatchSize != 200 {
|
||||
t.Fatalf("expected default batch size 200, got %d", cfg.AutoPurgeBatchSize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFileManagerTrashPolicyConfigFromEnv_Custom(t *testing.T) {
|
||||
t.Setenv("FILE_MANAGER_TRASH_AUTO_PURGE_ENABLED", "false")
|
||||
t.Setenv("FILE_MANAGER_TRASH_RETENTION", "168h")
|
||||
t.Setenv("FILE_MANAGER_TRASH_AUTO_PURGE_INTERVAL", "15m")
|
||||
t.Setenv("FILE_MANAGER_TRASH_AUTO_PURGE_BATCH_SIZE", "50")
|
||||
|
||||
cfg := LoadFileManagerTrashPolicyConfigFromEnv()
|
||||
if cfg.AutoPurgeEnabled {
|
||||
t.Fatalf("expected auto purge disabled")
|
||||
}
|
||||
if cfg.Retention != 168*time.Hour {
|
||||
t.Fatalf("expected retention 168h, got %s", cfg.Retention)
|
||||
}
|
||||
if cfg.AutoPurgeInterval != 15*time.Minute {
|
||||
t.Fatalf("expected auto purge interval 15m, got %s", cfg.AutoPurgeInterval)
|
||||
}
|
||||
if cfg.AutoPurgeBatchSize != 50 {
|
||||
t.Fatalf("expected batch size 50, got %d", cfg.AutoPurgeBatchSize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFileManagerLifecycleConfigFromEnv_Defaults(t *testing.T) {
|
||||
t.Setenv("ENABLE_S3_TAGGING", "")
|
||||
t.Setenv("ENABLE_FILE_CLEANUP_JOB", "")
|
||||
t.Setenv("TEMP_UPLOAD_GRACE_HOURS", "")
|
||||
t.Setenv("ORPHAN_GRACE_PERIOD_DAYS", "")
|
||||
t.Setenv("FILE_CLEANUP_BATCH_SIZE", "")
|
||||
t.Setenv("FILE_CLEANUP_DRY_RUN", "")
|
||||
|
||||
cfg := LoadFileManagerLifecycleConfigFromEnv()
|
||||
if !cfg.EnableS3Tagging {
|
||||
t.Fatalf("expected s3 tagging enabled by default")
|
||||
}
|
||||
if cfg.EnableCleanupJob {
|
||||
t.Fatalf("expected cleanup job disabled by default")
|
||||
}
|
||||
if cfg.TempUploadGraceHours != 2 {
|
||||
t.Fatalf("expected temp upload grace 2 hours, got %d", cfg.TempUploadGraceHours)
|
||||
}
|
||||
if cfg.OrphanGraceDays != 30 {
|
||||
t.Fatalf("expected orphan grace 30 days, got %d", cfg.OrphanGraceDays)
|
||||
}
|
||||
if cfg.CleanupBatchSize != 100 {
|
||||
t.Fatalf("expected cleanup batch size 100, got %d", cfg.CleanupBatchSize)
|
||||
}
|
||||
if !cfg.CleanupDryRun {
|
||||
t.Fatalf("expected cleanup dry run enabled by default")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFileManagerLifecycleConfigFromEnv_Custom(t *testing.T) {
|
||||
t.Setenv("ENABLE_S3_TAGGING", "false")
|
||||
t.Setenv("ENABLE_FILE_CLEANUP_JOB", "true")
|
||||
t.Setenv("TEMP_UPLOAD_GRACE_HOURS", "6")
|
||||
t.Setenv("ORPHAN_GRACE_PERIOD_DAYS", "45")
|
||||
t.Setenv("FILE_CLEANUP_BATCH_SIZE", "250")
|
||||
t.Setenv("FILE_CLEANUP_DRY_RUN", "false")
|
||||
|
||||
cfg := LoadFileManagerLifecycleConfigFromEnv()
|
||||
if cfg.EnableS3Tagging {
|
||||
t.Fatalf("expected s3 tagging disabled")
|
||||
}
|
||||
if !cfg.EnableCleanupJob {
|
||||
t.Fatalf("expected cleanup job enabled")
|
||||
}
|
||||
if cfg.TempUploadGraceHours != 6 {
|
||||
t.Fatalf("expected temp upload grace 6 hours, got %d", cfg.TempUploadGraceHours)
|
||||
}
|
||||
if cfg.OrphanGraceDays != 45 {
|
||||
t.Fatalf("expected orphan grace 45 days, got %d", cfg.OrphanGraceDays)
|
||||
}
|
||||
if cfg.CleanupBatchSize != 250 {
|
||||
t.Fatalf("expected cleanup batch size 250, got %d", cfg.CleanupBatchSize)
|
||||
}
|
||||
if cfg.CleanupDryRun {
|
||||
t.Fatalf("expected cleanup dry run disabled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFileQueueConfigFromEnv_Defaults(t *testing.T) {
|
||||
t.Setenv("FILE_QUEUE_MESSAGE_SCHEMA_VERSION", "")
|
||||
t.Setenv("FILE_QUEUE_IDEMPOTENCY_KEY_PREFIX", "")
|
||||
t.Setenv("FILE_QUEUE_WORKER_MONITOR_ADDR", "")
|
||||
t.Setenv("FILE_QUEUE_OUTBOX_ENABLED", "")
|
||||
t.Setenv("FILE_QUEUE_OUTBOX_POLL_INTERVAL", "")
|
||||
t.Setenv("FILE_QUEUE_OUTBOX_BATCH_SIZE", "")
|
||||
t.Setenv("FILE_QUEUE_OUTBOX_DISPATCH_TIMEOUT", "")
|
||||
t.Setenv("FILE_QUEUE_OUTBOX_LOCK_TTL", "")
|
||||
t.Setenv("FILE_QUEUE_OUTBOX_MAX_ATTEMPTS", "")
|
||||
|
||||
cfg := LoadFileQueueConfigFromEnv()
|
||||
if cfg.MessageSchemaVersion != "v1" {
|
||||
t.Fatalf("expected default schema version v1, got %q", cfg.MessageSchemaVersion)
|
||||
}
|
||||
if cfg.IdempotencyKeyPrefix != "queue:idempotency:file:" {
|
||||
t.Fatalf("unexpected default idempotency prefix: %q", cfg.IdempotencyKeyPrefix)
|
||||
}
|
||||
if cfg.WorkerMonitorAddr != "127.0.0.1:9091" {
|
||||
t.Fatalf("unexpected default monitor addr: %q", cfg.WorkerMonitorAddr)
|
||||
}
|
||||
if !cfg.OutboxEnabled {
|
||||
t.Fatalf("expected default file outbox enabled")
|
||||
}
|
||||
if cfg.OutboxPollInterval != time.Second {
|
||||
t.Fatalf("expected default outbox poll interval 1s, got %s", cfg.OutboxPollInterval)
|
||||
}
|
||||
if cfg.OutboxBatchSize != 10 {
|
||||
t.Fatalf("expected default outbox batch size 10, got %d", cfg.OutboxBatchSize)
|
||||
}
|
||||
if cfg.OutboxDispatchTimeout != 10*time.Second {
|
||||
t.Fatalf("expected default outbox dispatch timeout 10s, got %s", cfg.OutboxDispatchTimeout)
|
||||
}
|
||||
if cfg.OutboxLockTTL != time.Minute {
|
||||
t.Fatalf("expected default outbox lock ttl 1m, got %s", cfg.OutboxLockTTL)
|
||||
}
|
||||
if cfg.OutboxMaxAttempts != 20 {
|
||||
t.Fatalf("expected default outbox max attempts 20, got %d", cfg.OutboxMaxAttempts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFileSQSConfigFromEnv(t *testing.T) {
|
||||
t.Setenv("AWS_REGION", "ap-southeast-1")
|
||||
t.Setenv("SQS_ENDPOINT", "http://localhost:4566")
|
||||
t.Setenv("SQS_ALLOW_INSECURE_ENDPOINT", "true")
|
||||
t.Setenv("FILE_SQS_REGION", "")
|
||||
t.Setenv("FILE_SQS_ENDPOINT", "")
|
||||
t.Setenv("FILE_SQS_QUEUE_URL", "http://localhost:4566/000000000000/app-file-queue")
|
||||
t.Setenv("FILE_SQS_QUEUE_TYPE", "standard")
|
||||
t.Setenv("FILE_SQS_MESSAGE_GROUP_ID", "file")
|
||||
t.Setenv("FILE_SQS_MAX_MESSAGES", "8")
|
||||
t.Setenv("FILE_SQS_WAIT_TIME_SECONDS", "15")
|
||||
t.Setenv("FILE_SQS_VISIBILITY_TIMEOUT", "90s")
|
||||
t.Setenv("FILE_SQS_ALLOW_INSECURE_ENDPOINT", "")
|
||||
|
||||
cfg := LoadFileSQSConfigFromEnv()
|
||||
if cfg.Region != "ap-southeast-1" {
|
||||
t.Fatalf("unexpected region: %q", cfg.Region)
|
||||
}
|
||||
if cfg.Endpoint != "http://localhost:4566" {
|
||||
t.Fatalf("unexpected endpoint: %q", cfg.Endpoint)
|
||||
}
|
||||
if cfg.QueueURL == "" {
|
||||
t.Fatalf("expected queue url")
|
||||
}
|
||||
if cfg.MaxMessages != 8 {
|
||||
t.Fatalf("unexpected max messages: %d", cfg.MaxMessages)
|
||||
}
|
||||
if cfg.WaitTimeSeconds != 15 {
|
||||
t.Fatalf("unexpected wait time seconds: %d", cfg.WaitTimeSeconds)
|
||||
}
|
||||
if !cfg.AllowInsecureEndpoint {
|
||||
t.Fatalf("expected insecure endpoint allowed from fallback")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDistinctQueueURLs(t *testing.T) {
|
||||
t.Run("different queue urls", func(t *testing.T) {
|
||||
err := ValidateDistinctQueueURLs(
|
||||
SQSConfig{QueueURL: "http://localhost:4566/000000000000/app-queue"},
|
||||
SQSConfig{QueueURL: "http://localhost:4566/000000000000/app-file-queue"},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("same queue urls with different case and slash", func(t *testing.T) {
|
||||
err := ValidateDistinctQueueURLs(
|
||||
SQSConfig{QueueURL: "HTTP://LOCALHOST:4566/000000000000/app-queue"},
|
||||
SQSConfig{QueueURL: "http://localhost:4566/000000000000/app-queue/"},
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for same queue urls")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("skip when one queue url empty", func(t *testing.T) {
|
||||
err := ValidateDistinctQueueURLs(
|
||||
SQSConfig{QueueURL: "http://localhost:4566/000000000000/app-queue"},
|
||||
SQSConfig{QueueURL: ""},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error when file queue url is empty, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseHTTPTimeoutOverrides(t *testing.T) {
|
||||
t.Run("invalid format ignored", func(t *testing.T) {
|
||||
parsed := parseHTTPTimeoutOverrides("GET /users/:id 100ms,foo")
|
||||
if len(parsed) != 0 {
|
||||
t.Fatalf("expected no parsed entries, got %v", parsed)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid duration ignored", func(t *testing.T) {
|
||||
parsed := parseHTTPTimeoutOverrides("GET /users/:id=not-a-duration")
|
||||
if len(parsed) != 0 {
|
||||
t.Fatalf("expected no parsed entries, got %v", parsed)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("zero and negative duration ignored", func(t *testing.T) {
|
||||
parsed := parseHTTPTimeoutOverrides("GET /users/:id=0s,POST /users=-1s")
|
||||
if len(parsed) != 0 {
|
||||
t.Fatalf("expected no parsed entries, got %v", parsed)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("valid entries parsed", func(t *testing.T) {
|
||||
parsed := parseHTTPTimeoutOverrides("GET /users/:id=120ms, POST /auth/login = 2s")
|
||||
if len(parsed) != 2 {
|
||||
t.Fatalf("expected 2 parsed entries, got %d", len(parsed))
|
||||
}
|
||||
if parsed["GET /users/:id"] != 120*time.Millisecond {
|
||||
t.Fatalf("unexpected GET /users/:id timeout: %s", parsed["GET /users/:id"])
|
||||
}
|
||||
if parsed["POST /auth/login"] != 2*time.Second {
|
||||
t.Fatalf("unexpected POST /auth/login timeout: %s", parsed["POST /auth/login"])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseHTTPEndpointRateLimitOverrides(t *testing.T) {
|
||||
t.Run("invalid entries ignored", func(t *testing.T) {
|
||||
parsed := parseHTTPEndpointRateLimitOverrides("POST /auth/login=abc,1m;foo;GET /x=1")
|
||||
if len(parsed) != 0 {
|
||||
t.Fatalf("expected no parsed entries, got %v", parsed)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("zero or negative values ignored", func(t *testing.T) {
|
||||
parsed := parseHTTPEndpointRateLimitOverrides("POST /auth/login=0,1m;POST /auth/totp/verify=5,0s")
|
||||
if len(parsed) != 0 {
|
||||
t.Fatalf("expected no parsed entries, got %v", parsed)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("valid entries parsed", func(t *testing.T) {
|
||||
parsed := parseHTTPEndpointRateLimitOverrides("POST /auth/login=5,1m; POST /auth/totp/verify = 3,30s")
|
||||
if len(parsed) != 2 {
|
||||
t.Fatalf("expected 2 parsed entries, got %d", len(parsed))
|
||||
}
|
||||
login, ok := parsed["POST /auth/login"]
|
||||
if !ok {
|
||||
t.Fatalf("expected POST /auth/login to be parsed")
|
||||
}
|
||||
if login.Max != 5 || login.Window != time.Minute {
|
||||
t.Fatalf("unexpected login rule: %+v", login)
|
||||
}
|
||||
totp, ok := parsed["POST /auth/totp/verify"]
|
||||
if !ok {
|
||||
t.Fatalf("expected POST /auth/totp/verify to be parsed")
|
||||
}
|
||||
if totp.Max != 3 || totp.Window != 30*time.Second {
|
||||
t.Fatalf("unexpected totp rule: %+v", totp)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user