package models import ( "encoding/xml" "testing" ) func TestNewBalanceRequest(t *testing.T) { tests := []struct { name string level int wantError bool wantLevel int }{ { name: "Valid balance level 0", level: 0, wantError: false, wantLevel: 0, }, { name: "Valid balance level +50", level: 50, wantError: false, wantLevel: 50, }, { name: "Valid balance level -50", level: -50, wantError: false, wantLevel: -50, }, { name: "Valid balance level +25", level: 25, wantError: false, wantLevel: 25, }, { name: "Valid balance level -25", level: -25, wantError: false, wantLevel: -25, }, { name: "Invalid balance level +51", level: 51, wantError: true, }, { name: "Invalid balance level -51", level: -51, wantError: true, }, { name: "Invalid balance level +100", level: 100, wantError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req, err := NewBalanceRequest(tt.level) if tt.wantError { if err == nil { t.Errorf("NewBalanceRequest() expected error, got nil") } } else { if err != nil { t.Errorf("NewBalanceRequest() unexpected error: %v", err) } if req.Level != tt.wantLevel { t.Errorf("NewBalanceRequest() level = %d, want %d", req.Level, tt.wantLevel) } } }) } } func TestValidateBalanceLevel(t *testing.T) { tests := []struct { name string level int want bool }{ { name: "Valid minimum level", level: -50, want: true, }, { name: "Valid maximum level", level: 50, want: true, }, { name: "Valid zero level", level: 0, want: true, }, { name: "Valid positive level", level: 25, want: true, }, { name: "Valid negative level", level: -25, want: true, }, { name: "Invalid too high", level: 51, want: false, }, { name: "Invalid too low", level: -51, want: false, }, { name: "Invalid way too high", level: 100, want: false, }, { name: "Invalid way too low", level: -100, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := ValidateBalanceLevel(tt.level); got != tt.want { t.Errorf("ValidateBalanceLevel() = %v, want %v", got, tt.want) } }) } } func TestClampBalanceLevel(t *testing.T) { tests := []struct { name string level int want int }{ { name: "Valid level unchanged", level: 0, want: 0, }, { name: "Valid positive level unchanged", level: 25, want: 25, }, { name: "Valid negative level unchanged", level: -25, want: -25, }, { name: "Maximum level unchanged", level: 50, want: 50, }, { name: "Minimum level unchanged", level: -50, want: -50, }, { name: "Too high clamped to max", level: 51, want: 50, }, { name: "Too low clamped to min", level: -51, want: -50, }, { name: "Way too high clamped to max", level: 100, want: 50, }, { name: "Way too low clamped to min", level: -100, want: -50, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := ClampBalanceLevel(tt.level); got != tt.want { t.Errorf("ClampBalanceLevel() = %v, want %v", got, tt.want) } }) } } func TestGetBalanceLevelName(t *testing.T) { tests := []struct { name string level int want string }{ { name: "Far left balance", level: -50, want: "Far Left", }, { name: "Left balance", level: -20, want: "Left", }, { name: "Slightly left balance", level: -5, want: "Slightly Left", }, { name: "Center balance", level: 0, want: "Center", }, { name: "Slightly right balance", level: 5, want: "Slightly Right", }, { name: "Right balance", level: 20, want: "Right", }, { name: "Far right balance", level: 50, want: "Far Right", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := GetBalanceLevelName(tt.level); got != tt.want { t.Errorf("GetBalanceLevelName() = %v, want %v", got, tt.want) } }) } } func TestGetBalanceLevelCategory(t *testing.T) { tests := []struct { name string level int want string }{ { name: "Left channel negative", level: -25, want: "Left Channel", }, { name: "Left channel minimum", level: -50, want: "Left Channel", }, { name: "Balanced center", level: 0, want: "Balanced", }, { name: "Right channel positive", level: 25, want: "Right Channel", }, { name: "Right channel maximum", level: 50, want: "Right Channel", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := GetBalanceLevelCategory(tt.level); got != tt.want { t.Errorf("GetBalanceLevelCategory() = %v, want %v", got, tt.want) } }) } } func TestBalance_GetMethods(t *testing.T) { balance := &Balance{ TargetBalance: 25, ActualBalance: 20, DeviceID: "1234567890AB", } if got := balance.GetLevel(); got != 25 { t.Errorf("GetLevel() = %v, want %v", got, 25) } if got := balance.GetActualLevel(); got != 20 { t.Errorf("GetActualLevel() = %v, want %v", got, 20) } if got := balance.IsAtTarget(); got != false { t.Errorf("IsAtTarget() = %v, want %v", got, false) } if got := balance.GetBalanceChangeNeeded(); got != 5 { t.Errorf("GetBalanceChangeNeeded() = %v, want %v", got, 5) } } func TestBalance_BooleanMethods(t *testing.T) { tests := []struct { name string balance *Balance wantLeft bool wantRight bool wantBalanced bool wantAtTarget bool }{ { name: "Right balance", balance: &Balance{TargetBalance: 25, ActualBalance: 25}, wantLeft: false, wantRight: true, wantBalanced: false, wantAtTarget: true, }, { name: "Left balance", balance: &Balance{TargetBalance: -15, ActualBalance: -15}, wantLeft: true, wantRight: false, wantBalanced: false, wantAtTarget: true, }, { name: "Center balance", balance: &Balance{TargetBalance: 0, ActualBalance: 0}, wantLeft: false, wantRight: false, wantBalanced: true, wantAtTarget: true, }, { name: "Not at target", balance: &Balance{TargetBalance: 25, ActualBalance: 10}, wantLeft: false, wantRight: true, wantBalanced: false, wantAtTarget: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.balance.IsLeftBalance(); got != tt.wantLeft { t.Errorf("IsLeftBalance() = %v, want %v", got, tt.wantLeft) } if got := tt.balance.IsRightBalance(); got != tt.wantRight { t.Errorf("IsRightBalance() = %v, want %v", got, tt.wantRight) } if got := tt.balance.IsBalanced(); got != tt.wantBalanced { t.Errorf("IsBalanced() = %v, want %v", got, tt.wantBalanced) } if got := tt.balance.IsAtTarget(); got != tt.wantAtTarget { t.Errorf("IsAtTarget() = %v, want %v", got, tt.wantAtTarget) } }) } } func TestBalance_GetLeftRightPercentage(t *testing.T) { tests := []struct { name string balance *Balance wantLeft int wantRight int }{ { name: "Center balance", balance: &Balance{TargetBalance: 0}, wantLeft: 50, wantRight: 50, }, { name: "Right balance +20", balance: &Balance{TargetBalance: 20}, wantLeft: 40, wantRight: 60, }, { name: "Left balance -20", balance: &Balance{TargetBalance: -20}, wantLeft: 60, wantRight: 40, }, { name: "Far right +50", balance: &Balance{TargetBalance: 50}, wantLeft: 25, wantRight: 75, }, { name: "Far left -50", balance: &Balance{TargetBalance: -50}, wantLeft: 75, wantRight: 25, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { left, right := tt.balance.GetLeftRightPercentage() if left != tt.wantLeft { t.Errorf("GetLeftRightPercentage() left = %v, want %v", left, tt.wantLeft) } if right != tt.wantRight { t.Errorf("GetLeftRightPercentage() right = %v, want %v", right, tt.wantRight) } }) } } func TestBalance_String(t *testing.T) { balance := &Balance{ TargetBalance: 15, ActualBalance: 15, } expected := "Balance: 15 (Right)" if got := balance.String(); got != expected { t.Errorf("String() = %v, want %v", got, expected) } } func TestBalance_UnmarshalXML(t *testing.T) { tests := []struct { name string xmlData string wantError bool want Balance }{ { name: "Valid balance XML", xmlData: ` 15 15 `, wantError: false, want: Balance{ DeviceID: "1234567890AB", TargetBalance: 15, ActualBalance: 15, }, }, { name: "Valid negative balance XML", xmlData: ` -25 -25 `, wantError: false, want: Balance{ DeviceID: "1234567890AB", TargetBalance: -25, ActualBalance: -25, }, }, { name: "Valid zero balance XML", xmlData: ` 0 0 `, wantError: false, want: Balance{ DeviceID: "1234567890AB", TargetBalance: 0, ActualBalance: 0, }, }, { name: "Invalid target balance too high", xmlData: ` 75 25 `, wantError: true, }, { name: "Invalid actual balance too low", xmlData: ` 25 -75 `, wantError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var balance Balance err := xml.Unmarshal([]byte(tt.xmlData), &balance) if tt.wantError { if err == nil { t.Errorf("UnmarshalXML() expected error, got nil") } } else { if err != nil { t.Errorf("UnmarshalXML() unexpected error: %v", err) } if balance.DeviceID != tt.want.DeviceID { t.Errorf("DeviceID = %v, want %v", balance.DeviceID, tt.want.DeviceID) } if balance.TargetBalance != tt.want.TargetBalance { t.Errorf("TargetBalance = %v, want %v", balance.TargetBalance, tt.want.TargetBalance) } if balance.ActualBalance != tt.want.ActualBalance { t.Errorf("ActualBalance = %v, want %v", balance.ActualBalance, tt.want.ActualBalance) } } }) } } func TestBalance_MarshalXML(t *testing.T) { tests := []struct { name string balance Balance wantError bool }{ { name: "Valid balance marshal", balance: Balance{ DeviceID: "1234567890AB", TargetBalance: 15, ActualBalance: 15, }, wantError: false, }, { name: "Valid negative balance marshal", balance: Balance{ DeviceID: "1234567890AB", TargetBalance: -25, ActualBalance: -25, }, wantError: false, }, { name: "Valid zero balance marshal", balance: Balance{ DeviceID: "1234567890AB", TargetBalance: 0, ActualBalance: 0, }, wantError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := xml.Marshal(tt.balance) if tt.wantError { if err == nil { t.Errorf("MarshalXML() expected error, got nil") } } else { if err != nil { t.Errorf("MarshalXML() unexpected error: %v", err) } } }) } } func TestBalanceRequest_MarshalXML(t *testing.T) { req := &BalanceRequest{ Level: 25, } data, err := xml.Marshal(req) if err != nil { t.Errorf("MarshalXML() unexpected error: %v", err) } expected := "25" if string(data) != expected { t.Errorf("MarshalXML() = %v, want %v", string(data), expected) } } func TestBalanceConstants(t *testing.T) { if BalanceLevelMin != -50 { t.Errorf("BalanceLevelMin = %v, want %v", BalanceLevelMin, -50) } if BalanceLevelMax != 50 { t.Errorf("BalanceLevelMax = %v, want %v", BalanceLevelMax, 50) } if BalanceLevelDefault != 0 { t.Errorf("BalanceLevelDefault = %v, want %v", BalanceLevelDefault, 0) } } func TestBalanceLevelEdgeCases(t *testing.T) { // Test boundary values t.Run("Minimum boundary", func(t *testing.T) { if !ValidateBalanceLevel(-50) { t.Error("ValidateBalanceLevel(-50) should be true") } if ValidateBalanceLevel(-51) { t.Error("ValidateBalanceLevel(-51) should be false") } }) t.Run("Maximum boundary", func(t *testing.T) { if !ValidateBalanceLevel(50) { t.Error("ValidateBalanceLevel(50) should be true") } if ValidateBalanceLevel(51) { t.Error("ValidateBalanceLevel(51) should be false") } }) t.Run("Zero boundary", func(t *testing.T) { if !ValidateBalanceLevel(0) { t.Error("ValidateBalanceLevel(0) should be true") } }) }