| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- package base
- import (
- "reflect"
- "testing"
- )
- // Test structs that mirror the actual configuration structure
- type TestBaseConfig struct {
- Enabled bool `json:"enabled"`
- ScanIntervalSeconds int `json:"scan_interval_seconds"`
- MaxConcurrent int `json:"max_concurrent"`
- }
- type TestTaskConfig struct {
- TestBaseConfig
- TaskSpecificField float64 `json:"task_specific_field"`
- AnotherSpecificField string `json:"another_specific_field"`
- }
- type TestNestedConfig struct {
- TestBaseConfig
- NestedStruct struct {
- NestedField string `json:"nested_field"`
- } `json:"nested_struct"`
- TaskField int `json:"task_field"`
- }
- func TestStructToMap_WithEmbeddedStruct(t *testing.T) {
- // Test case 1: Basic embedded struct
- config := &TestTaskConfig{
- TestBaseConfig: TestBaseConfig{
- Enabled: true,
- ScanIntervalSeconds: 1800,
- MaxConcurrent: 3,
- },
- TaskSpecificField: 0.25,
- AnotherSpecificField: "test_value",
- }
- result := StructToMap(config)
- // Verify all fields are present
- expectedFields := map[string]interface{}{
- "enabled": true,
- "scan_interval_seconds": 1800,
- "max_concurrent": 3,
- "task_specific_field": 0.25,
- "another_specific_field": "test_value",
- }
- if len(result) != len(expectedFields) {
- t.Errorf("Expected %d fields, got %d. Result: %+v", len(expectedFields), len(result), result)
- }
- for key, expectedValue := range expectedFields {
- if actualValue, exists := result[key]; !exists {
- t.Errorf("Missing field: %s", key)
- } else if !reflect.DeepEqual(actualValue, expectedValue) {
- t.Errorf("Field %s: expected %v (%T), got %v (%T)", key, expectedValue, expectedValue, actualValue, actualValue)
- }
- }
- }
- func TestStructToMap_WithNestedStruct(t *testing.T) {
- config := &TestNestedConfig{
- TestBaseConfig: TestBaseConfig{
- Enabled: false,
- ScanIntervalSeconds: 3600,
- MaxConcurrent: 1,
- },
- NestedStruct: struct {
- NestedField string `json:"nested_field"`
- }{
- NestedField: "nested_value",
- },
- TaskField: 42,
- }
- result := StructToMap(config)
- // Verify embedded struct fields are included
- if enabled, exists := result["enabled"]; !exists || enabled != false {
- t.Errorf("Expected enabled=false from embedded struct, got %v", enabled)
- }
- if scanInterval, exists := result["scan_interval_seconds"]; !exists || scanInterval != 3600 {
- t.Errorf("Expected scan_interval_seconds=3600 from embedded struct, got %v", scanInterval)
- }
- if maxConcurrent, exists := result["max_concurrent"]; !exists || maxConcurrent != 1 {
- t.Errorf("Expected max_concurrent=1 from embedded struct, got %v", maxConcurrent)
- }
- // Verify regular fields are included
- if taskField, exists := result["task_field"]; !exists || taskField != 42 {
- t.Errorf("Expected task_field=42, got %v", taskField)
- }
- // Verify nested struct is included as a whole
- if nestedStruct, exists := result["nested_struct"]; !exists {
- t.Errorf("Missing nested_struct field")
- } else {
- // The nested struct should be included as-is, not flattened
- if nested, ok := nestedStruct.(struct {
- NestedField string `json:"nested_field"`
- }); !ok || nested.NestedField != "nested_value" {
- t.Errorf("Expected nested_struct with NestedField='nested_value', got %v", nestedStruct)
- }
- }
- }
- func TestMapToStruct_WithEmbeddedStruct(t *testing.T) {
- // Test data with all fields including embedded struct fields
- data := map[string]interface{}{
- "enabled": true,
- "scan_interval_seconds": 2400,
- "max_concurrent": 5,
- "task_specific_field": 0.15,
- "another_specific_field": "updated_value",
- }
- config := &TestTaskConfig{}
- err := MapToStruct(data, config)
- if err != nil {
- t.Fatalf("MapToStruct failed: %v", err)
- }
- // Verify embedded struct fields were set
- if config.Enabled != true {
- t.Errorf("Expected Enabled=true, got %v", config.Enabled)
- }
- if config.ScanIntervalSeconds != 2400 {
- t.Errorf("Expected ScanIntervalSeconds=2400, got %v", config.ScanIntervalSeconds)
- }
- if config.MaxConcurrent != 5 {
- t.Errorf("Expected MaxConcurrent=5, got %v", config.MaxConcurrent)
- }
- // Verify regular fields were set
- if config.TaskSpecificField != 0.15 {
- t.Errorf("Expected TaskSpecificField=0.15, got %v", config.TaskSpecificField)
- }
- if config.AnotherSpecificField != "updated_value" {
- t.Errorf("Expected AnotherSpecificField='updated_value', got %v", config.AnotherSpecificField)
- }
- }
- func TestMapToStruct_PartialData(t *testing.T) {
- // Test with only some fields present (simulating form data)
- data := map[string]interface{}{
- "enabled": false,
- "max_concurrent": 2,
- "task_specific_field": 0.30,
- }
- // Start with some initial values
- config := &TestTaskConfig{
- TestBaseConfig: TestBaseConfig{
- Enabled: true,
- ScanIntervalSeconds: 1800,
- MaxConcurrent: 1,
- },
- TaskSpecificField: 0.20,
- AnotherSpecificField: "initial_value",
- }
- err := MapToStruct(data, config)
- if err != nil {
- t.Fatalf("MapToStruct failed: %v", err)
- }
- // Verify updated fields
- if config.Enabled != false {
- t.Errorf("Expected Enabled=false (updated), got %v", config.Enabled)
- }
- if config.MaxConcurrent != 2 {
- t.Errorf("Expected MaxConcurrent=2 (updated), got %v", config.MaxConcurrent)
- }
- if config.TaskSpecificField != 0.30 {
- t.Errorf("Expected TaskSpecificField=0.30 (updated), got %v", config.TaskSpecificField)
- }
- // Verify unchanged fields remain the same
- if config.ScanIntervalSeconds != 1800 {
- t.Errorf("Expected ScanIntervalSeconds=1800 (unchanged), got %v", config.ScanIntervalSeconds)
- }
- if config.AnotherSpecificField != "initial_value" {
- t.Errorf("Expected AnotherSpecificField='initial_value' (unchanged), got %v", config.AnotherSpecificField)
- }
- }
- func TestRoundTripSerialization(t *testing.T) {
- // Test complete round-trip: struct -> map -> struct
- original := &TestTaskConfig{
- TestBaseConfig: TestBaseConfig{
- Enabled: true,
- ScanIntervalSeconds: 3600,
- MaxConcurrent: 4,
- },
- TaskSpecificField: 0.18,
- AnotherSpecificField: "round_trip_test",
- }
- // Convert to map
- dataMap := StructToMap(original)
- // Convert back to struct
- roundTrip := &TestTaskConfig{}
- err := MapToStruct(dataMap, roundTrip)
- if err != nil {
- t.Fatalf("Round-trip MapToStruct failed: %v", err)
- }
- // Verify all fields match
- if !reflect.DeepEqual(original.TestBaseConfig, roundTrip.TestBaseConfig) {
- t.Errorf("BaseConfig mismatch:\nOriginal: %+v\nRound-trip: %+v", original.TestBaseConfig, roundTrip.TestBaseConfig)
- }
- if original.TaskSpecificField != roundTrip.TaskSpecificField {
- t.Errorf("TaskSpecificField mismatch: %v != %v", original.TaskSpecificField, roundTrip.TaskSpecificField)
- }
- if original.AnotherSpecificField != roundTrip.AnotherSpecificField {
- t.Errorf("AnotherSpecificField mismatch: %v != %v", original.AnotherSpecificField, roundTrip.AnotherSpecificField)
- }
- }
- func TestStructToMap_EmptyStruct(t *testing.T) {
- config := &TestTaskConfig{}
- result := StructToMap(config)
- // Should still include all fields, even with zero values
- expectedFields := []string{"enabled", "scan_interval_seconds", "max_concurrent", "task_specific_field", "another_specific_field"}
- for _, field := range expectedFields {
- if _, exists := result[field]; !exists {
- t.Errorf("Missing field: %s", field)
- }
- }
- }
- func TestStructToMap_NilPointer(t *testing.T) {
- var config *TestTaskConfig = nil
- result := StructToMap(config)
- if len(result) != 0 {
- t.Errorf("Expected empty map for nil pointer, got %+v", result)
- }
- }
- func TestMapToStruct_InvalidInput(t *testing.T) {
- data := map[string]interface{}{
- "enabled": "not_a_bool", // Wrong type
- }
- config := &TestTaskConfig{}
- err := MapToStruct(data, config)
- if err == nil {
- t.Errorf("Expected error for invalid input type, but got none")
- }
- }
- func TestMapToStruct_NonPointer(t *testing.T) {
- data := map[string]interface{}{
- "enabled": true,
- }
- config := TestTaskConfig{} // Not a pointer
- err := MapToStruct(data, config)
- if err == nil {
- t.Errorf("Expected error for non-pointer input, but got none")
- }
- }
- // Benchmark tests to ensure performance is reasonable
- func BenchmarkStructToMap(b *testing.B) {
- config := &TestTaskConfig{
- TestBaseConfig: TestBaseConfig{
- Enabled: true,
- ScanIntervalSeconds: 1800,
- MaxConcurrent: 3,
- },
- TaskSpecificField: 0.25,
- AnotherSpecificField: "benchmark_test",
- }
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- _ = StructToMap(config)
- }
- }
- func BenchmarkMapToStruct(b *testing.B) {
- data := map[string]interface{}{
- "enabled": true,
- "scan_interval_seconds": 1800,
- "max_concurrent": 3,
- "task_specific_field": 0.25,
- "another_specific_field": "benchmark_test",
- }
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- config := &TestTaskConfig{}
- _ = MapToStruct(data, config)
- }
- }
- func BenchmarkRoundTrip(b *testing.B) {
- original := &TestTaskConfig{
- TestBaseConfig: TestBaseConfig{
- Enabled: true,
- ScanIntervalSeconds: 1800,
- MaxConcurrent: 3,
- },
- TaskSpecificField: 0.25,
- AnotherSpecificField: "benchmark_test",
- }
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- dataMap := StructToMap(original)
- roundTrip := &TestTaskConfig{}
- _ = MapToStruct(dataMap, roundTrip)
- }
- }
|