| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- package handlers
- import (
- "net/url"
- "testing"
- "github.com/seaweedfs/seaweedfs/weed/admin/config"
- "github.com/seaweedfs/seaweedfs/weed/worker/tasks"
- "github.com/seaweedfs/seaweedfs/weed/worker/tasks/balance"
- "github.com/seaweedfs/seaweedfs/weed/worker/tasks/base"
- "github.com/seaweedfs/seaweedfs/weed/worker/tasks/erasure_coding"
- "github.com/seaweedfs/seaweedfs/weed/worker/tasks/vacuum"
- )
- func TestParseTaskConfigFromForm_WithEmbeddedStruct(t *testing.T) {
- // Create a maintenance handlers instance for testing
- h := &MaintenanceHandlers{}
- // Test with balance config
- t.Run("Balance Config", func(t *testing.T) {
- // Simulate form data
- formData := url.Values{
- "enabled": {"on"}, // checkbox field
- "scan_interval_seconds_value": {"30"}, // interval field
- "scan_interval_seconds_unit": {"minutes"}, // interval unit
- "max_concurrent": {"2"}, // number field
- "imbalance_threshold": {"0.15"}, // float field
- "min_server_count": {"3"}, // number field
- }
- // Get schema
- schema := tasks.GetTaskConfigSchema("balance")
- if schema == nil {
- t.Fatal("Failed to get balance schema")
- }
- // Create config instance
- config := &balance.Config{}
- // Parse form data
- err := h.parseTaskConfigFromForm(formData, schema, config)
- if err != nil {
- t.Fatalf("Failed to parse form data: %v", err)
- }
- // Verify embedded struct fields were set correctly
- if !config.Enabled {
- t.Errorf("Expected Enabled=true, got %v", config.Enabled)
- }
- if config.ScanIntervalSeconds != 1800 { // 30 minutes * 60
- t.Errorf("Expected ScanIntervalSeconds=1800, got %v", config.ScanIntervalSeconds)
- }
- if config.MaxConcurrent != 2 {
- t.Errorf("Expected MaxConcurrent=2, got %v", config.MaxConcurrent)
- }
- // Verify balance-specific fields were set correctly
- if config.ImbalanceThreshold != 0.15 {
- t.Errorf("Expected ImbalanceThreshold=0.15, got %v", config.ImbalanceThreshold)
- }
- if config.MinServerCount != 3 {
- t.Errorf("Expected MinServerCount=3, got %v", config.MinServerCount)
- }
- })
- // Test with vacuum config
- t.Run("Vacuum Config", func(t *testing.T) {
- // Simulate form data
- formData := url.Values{
- // "enabled" field omitted to simulate unchecked checkbox
- "scan_interval_seconds_value": {"4"}, // interval field
- "scan_interval_seconds_unit": {"hours"}, // interval unit
- "max_concurrent": {"3"}, // number field
- "garbage_threshold": {"0.4"}, // float field
- "min_volume_age_seconds_value": {"2"}, // interval field
- "min_volume_age_seconds_unit": {"days"}, // interval unit
- "min_interval_seconds_value": {"1"}, // interval field
- "min_interval_seconds_unit": {"days"}, // interval unit
- }
- // Get schema
- schema := tasks.GetTaskConfigSchema("vacuum")
- if schema == nil {
- t.Fatal("Failed to get vacuum schema")
- }
- // Create config instance
- config := &vacuum.Config{}
- // Parse form data
- err := h.parseTaskConfigFromForm(formData, schema, config)
- if err != nil {
- t.Fatalf("Failed to parse form data: %v", err)
- }
- // Verify embedded struct fields were set correctly
- if config.Enabled {
- t.Errorf("Expected Enabled=false, got %v", config.Enabled)
- }
- if config.ScanIntervalSeconds != 14400 { // 4 hours * 3600
- t.Errorf("Expected ScanIntervalSeconds=14400, got %v", config.ScanIntervalSeconds)
- }
- if config.MaxConcurrent != 3 {
- t.Errorf("Expected MaxConcurrent=3, got %v", config.MaxConcurrent)
- }
- // Verify vacuum-specific fields were set correctly
- if config.GarbageThreshold != 0.4 {
- t.Errorf("Expected GarbageThreshold=0.4, got %v", config.GarbageThreshold)
- }
- if config.MinVolumeAgeSeconds != 172800 { // 2 days * 86400
- t.Errorf("Expected MinVolumeAgeSeconds=172800, got %v", config.MinVolumeAgeSeconds)
- }
- if config.MinIntervalSeconds != 86400 { // 1 day * 86400
- t.Errorf("Expected MinIntervalSeconds=86400, got %v", config.MinIntervalSeconds)
- }
- })
- // Test with erasure coding config
- t.Run("Erasure Coding Config", func(t *testing.T) {
- // Simulate form data
- formData := url.Values{
- "enabled": {"on"}, // checkbox field
- "scan_interval_seconds_value": {"2"}, // interval field
- "scan_interval_seconds_unit": {"hours"}, // interval unit
- "max_concurrent": {"1"}, // number field
- "quiet_for_seconds_value": {"10"}, // interval field
- "quiet_for_seconds_unit": {"minutes"}, // interval unit
- "fullness_ratio": {"0.85"}, // float field
- "collection_filter": {"test_collection"}, // string field
- "min_size_mb": {"50"}, // number field
- }
- // Get schema
- schema := tasks.GetTaskConfigSchema("erasure_coding")
- if schema == nil {
- t.Fatal("Failed to get erasure_coding schema")
- }
- // Create config instance
- config := &erasure_coding.Config{}
- // Parse form data
- err := h.parseTaskConfigFromForm(formData, schema, config)
- if err != nil {
- t.Fatalf("Failed to parse form data: %v", err)
- }
- // Verify embedded struct fields were set correctly
- if !config.Enabled {
- t.Errorf("Expected Enabled=true, got %v", config.Enabled)
- }
- if config.ScanIntervalSeconds != 7200 { // 2 hours * 3600
- t.Errorf("Expected ScanIntervalSeconds=7200, got %v", config.ScanIntervalSeconds)
- }
- if config.MaxConcurrent != 1 {
- t.Errorf("Expected MaxConcurrent=1, got %v", config.MaxConcurrent)
- }
- // Verify erasure coding-specific fields were set correctly
- if config.QuietForSeconds != 600 { // 10 minutes * 60
- t.Errorf("Expected QuietForSeconds=600, got %v", config.QuietForSeconds)
- }
- if config.FullnessRatio != 0.85 {
- t.Errorf("Expected FullnessRatio=0.85, got %v", config.FullnessRatio)
- }
- if config.CollectionFilter != "test_collection" {
- t.Errorf("Expected CollectionFilter='test_collection', got %v", config.CollectionFilter)
- }
- if config.MinSizeMB != 50 {
- t.Errorf("Expected MinSizeMB=50, got %v", config.MinSizeMB)
- }
- })
- }
- func TestConfigurationValidation(t *testing.T) {
- // Test that config structs can be validated and converted to protobuf format
- taskTypes := []struct {
- name string
- config interface{}
- }{
- {
- "balance",
- &balance.Config{
- BaseConfig: base.BaseConfig{
- Enabled: true,
- ScanIntervalSeconds: 2400,
- MaxConcurrent: 3,
- },
- ImbalanceThreshold: 0.18,
- MinServerCount: 4,
- },
- },
- {
- "vacuum",
- &vacuum.Config{
- BaseConfig: base.BaseConfig{
- Enabled: false,
- ScanIntervalSeconds: 7200,
- MaxConcurrent: 2,
- },
- GarbageThreshold: 0.35,
- MinVolumeAgeSeconds: 86400,
- MinIntervalSeconds: 604800,
- },
- },
- {
- "erasure_coding",
- &erasure_coding.Config{
- BaseConfig: base.BaseConfig{
- Enabled: true,
- ScanIntervalSeconds: 3600,
- MaxConcurrent: 1,
- },
- QuietForSeconds: 900,
- FullnessRatio: 0.9,
- CollectionFilter: "important",
- MinSizeMB: 100,
- },
- },
- }
- for _, test := range taskTypes {
- t.Run(test.name, func(t *testing.T) {
- // Test that configs can be converted to protobuf TaskPolicy
- switch cfg := test.config.(type) {
- case *balance.Config:
- policy := cfg.ToTaskPolicy()
- if policy == nil {
- t.Fatal("ToTaskPolicy returned nil")
- }
- if policy.Enabled != cfg.Enabled {
- t.Errorf("Expected Enabled=%v, got %v", cfg.Enabled, policy.Enabled)
- }
- if policy.MaxConcurrent != int32(cfg.MaxConcurrent) {
- t.Errorf("Expected MaxConcurrent=%v, got %v", cfg.MaxConcurrent, policy.MaxConcurrent)
- }
- case *vacuum.Config:
- policy := cfg.ToTaskPolicy()
- if policy == nil {
- t.Fatal("ToTaskPolicy returned nil")
- }
- if policy.Enabled != cfg.Enabled {
- t.Errorf("Expected Enabled=%v, got %v", cfg.Enabled, policy.Enabled)
- }
- if policy.MaxConcurrent != int32(cfg.MaxConcurrent) {
- t.Errorf("Expected MaxConcurrent=%v, got %v", cfg.MaxConcurrent, policy.MaxConcurrent)
- }
- case *erasure_coding.Config:
- policy := cfg.ToTaskPolicy()
- if policy == nil {
- t.Fatal("ToTaskPolicy returned nil")
- }
- if policy.Enabled != cfg.Enabled {
- t.Errorf("Expected Enabled=%v, got %v", cfg.Enabled, policy.Enabled)
- }
- if policy.MaxConcurrent != int32(cfg.MaxConcurrent) {
- t.Errorf("Expected MaxConcurrent=%v, got %v", cfg.MaxConcurrent, policy.MaxConcurrent)
- }
- default:
- t.Fatalf("Unknown config type: %T", test.config)
- }
- // Test that configs can be validated
- switch cfg := test.config.(type) {
- case *balance.Config:
- if err := cfg.Validate(); err != nil {
- t.Errorf("Validation failed: %v", err)
- }
- case *vacuum.Config:
- if err := cfg.Validate(); err != nil {
- t.Errorf("Validation failed: %v", err)
- }
- case *erasure_coding.Config:
- if err := cfg.Validate(); err != nil {
- t.Errorf("Validation failed: %v", err)
- }
- }
- })
- }
- }
- func TestParseFieldFromForm_EdgeCases(t *testing.T) {
- h := &MaintenanceHandlers{}
- // Test checkbox parsing (boolean fields)
- t.Run("Checkbox Fields", func(t *testing.T) {
- tests := []struct {
- name string
- formData url.Values
- expectedValue bool
- }{
- {"Checked checkbox", url.Values{"test_field": {"on"}}, true},
- {"Unchecked checkbox", url.Values{}, false},
- {"Empty value checkbox", url.Values{"test_field": {""}}, true}, // Present but empty means checked
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- schema := &tasks.TaskConfigSchema{
- Schema: config.Schema{
- Fields: []*config.Field{
- {
- JSONName: "test_field",
- Type: config.FieldTypeBool,
- InputType: "checkbox",
- },
- },
- },
- }
- type TestConfig struct {
- TestField bool `json:"test_field"`
- }
- config := &TestConfig{}
- err := h.parseTaskConfigFromForm(test.formData, schema, config)
- if err != nil {
- t.Fatalf("parseTaskConfigFromForm failed: %v", err)
- }
- if config.TestField != test.expectedValue {
- t.Errorf("Expected %v, got %v", test.expectedValue, config.TestField)
- }
- })
- }
- })
- // Test interval parsing
- t.Run("Interval Fields", func(t *testing.T) {
- tests := []struct {
- name string
- value string
- unit string
- expectedSecs int
- }{
- {"Minutes", "30", "minutes", 1800},
- {"Hours", "2", "hours", 7200},
- {"Days", "1", "days", 86400},
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- formData := url.Values{
- "test_field_value": {test.value},
- "test_field_unit": {test.unit},
- }
- schema := &tasks.TaskConfigSchema{
- Schema: config.Schema{
- Fields: []*config.Field{
- {
- JSONName: "test_field",
- Type: config.FieldTypeInterval,
- InputType: "interval",
- },
- },
- },
- }
- type TestConfig struct {
- TestField int `json:"test_field"`
- }
- config := &TestConfig{}
- err := h.parseTaskConfigFromForm(formData, schema, config)
- if err != nil {
- t.Fatalf("parseTaskConfigFromForm failed: %v", err)
- }
- if config.TestField != test.expectedSecs {
- t.Errorf("Expected %d seconds, got %d", test.expectedSecs, config.TestField)
- }
- })
- }
- })
- }
|