Files
Tobias GesellchenandClaude Opus 4.7 feadc478d5 test: sweep example data in test files to RFC-5737 + placeholders
Mirrors the .md/.txt sweep across all tracked _test.go, testdata XML,
and .http integration files. Test files are self-contained (producer
+ assertion in the same file), so the matched-pair swap stays green
under `go test ./...`.

Mapping applied:
  192.168.178.[0-9]+   → 192.0.2.[same]
  192.168.1.[0-9]+     → 192.0.2.[same]
  Sound Machinechen    → Living Room SoundTouch
  A Sound Machine      → Kitchen SoundTouch
  A81B6A536A98 + case/separator variants → AABBCCDDEEFF (etc.)
  A81B6A849D99         → AABBCCDDEE01
  A81B6A849D88         → AABBCCDDEE03
  A81B6A536A09         → AABBCCDDEE04
  884AEAEEBD27         → AABBCCDDEE02
  3230304              → 1000001
  9569497              → 1000002

Two semantic fixes alongside the bulk swap:

- pkg/service/zeroconf/zeroconf_test.go: the "private 192" and
  "strips query" cases pin acceptance of RFC-1918 192.168/16. They
  must use a real 192.168 value; doc-range IPs would (correctly) be
  rejected by validateZcBaseURL. Switched to 192.168.10.10 — generic
  enough not to match any home LAN default, real enough for the
  validator. Added a comment explaining why this single test still
  carries a 192.168 literal.

- pkg/service/setup/setup_test.go: TestTestDNSRedirection mocks the
  device's `od -An -tu1` byte output, which is space-separated
  octets ("192 168 1 100"). My sed only matched the dot-separated
  form, so the mock was returning the old IP while the test
  assertions had moved to the doc range. Updated to " 192 0 2 100".

go build ./... clean. go test ./... clean (only TestDocsConsistency
remains failing, which is a pre-existing/untracked-file issue).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 22:05:13 +02:00

438 lines
12 KiB
Go

package models
import (
"encoding/xml"
"testing"
"time"
)
func TestName_UnmarshalXML(t *testing.T) {
xmlData := `<?xml version="1.0" encoding="UTF-8" ?><name>Living Room SoundTouch</name>`
var name Name
err := xml.Unmarshal([]byte(xmlData), &name)
if err != nil {
t.Fatalf("Failed to unmarshal XML: %v", err)
}
if name.Value != "Living Room SoundTouch" {
t.Errorf("Expected name 'Living Room SoundTouch', got '%s'", name.Value)
}
if name.GetName() != "Living Room SoundTouch" {
t.Errorf("Expected GetName() 'Living Room SoundTouch', got '%s'", name.GetName())
}
if name.String() != "Living Room SoundTouch" {
t.Errorf("Expected String() 'Living Room SoundTouch', got '%s'", name.String())
}
if name.IsEmpty() {
t.Error("Expected IsEmpty() to return false for non-empty name")
}
}
func TestName_EmptyName(t *testing.T) {
xmlData := `<?xml version="1.0" encoding="UTF-8" ?><name></name>`
var name Name
err := xml.Unmarshal([]byte(xmlData), &name)
if err != nil {
t.Fatalf("Failed to unmarshal XML: %v", err)
}
if !name.IsEmpty() {
t.Error("Expected IsEmpty() to return true for empty name")
}
if name.GetName() != "" {
t.Errorf("Expected GetName() to return empty string, got '%s'", name.GetName())
}
}
func TestCapabilities_UnmarshalXML(t *testing.T) {
xmlData := `<?xml version="1.0" encoding="UTF-8" ?>
<capabilities deviceID="AABBCCDDEEFF">
<networkConfig>
<dualMode>true</dualMode>
<wsapiproxy>true</wsapiproxy>
<allInterfacesSupported />
<wlanInterfaces />
<security />
</networkConfig>
<dspCapabilities>
<dspMonoStereo available="false" />
</dspCapabilities>
<lightswitch>false</lightswitch>
<clockDisplay>false</clockDisplay>
<capability name="systemtimeout" url="/systemtimeout" info="" />
<capability name="rebroadcastlatencymode" url="/rebroadcastlatencymode" info="" />
<lrStereoCapable>true</lrStereoCapable>
<bcoresetCapable>false</bcoresetCapable>
<disablePowerSaving>true</disablePowerSaving>
</capabilities>`
var capabilities Capabilities
err := xml.Unmarshal([]byte(xmlData), &capabilities)
if err != nil {
t.Fatalf("Failed to unmarshal XML: %v", err)
}
// Test basic fields
if capabilities.DeviceID != "AABBCCDDEEFF" {
t.Errorf("Expected DeviceID 'AABBCCDDEEFF', got '%s'", capabilities.DeviceID)
}
// Test boolean capabilities
if capabilities.HasLightswitch() {
t.Error("Expected HasLightswitch() to return false")
}
if capabilities.HasClockDisplay() {
t.Error("Expected HasClockDisplay() to return false")
}
if !capabilities.HasLRStereoCapability() {
t.Error("Expected HasLRStereoCapability() to return true")
}
if capabilities.HasBCOResetCapability() {
t.Error("Expected HasBCOResetCapability() to return false")
}
if !capabilities.HasPowerSavingDisabled() {
t.Error("Expected HasPowerSavingDisabled() to return true")
}
// Test network capabilities
if !capabilities.HasDualModeNetwork() {
t.Error("Expected HasDualModeNetwork() to return true")
}
if !capabilities.HasWSAPIProxy() {
t.Error("Expected HasWSAPIProxy() to return true")
}
// Test DSP capabilities
if capabilities.HasDSPMonoStereo() {
t.Error("Expected HasDSPMonoStereo() to return false")
}
// Test capability by name
if !capabilities.HasCapability("systemtimeout") {
t.Error("Expected to have systemtimeout capability")
}
if capabilities.HasCapability("nonexistent") {
t.Error("Expected to not have nonexistent capability")
}
// Test capability names
capNames := capabilities.GetCapabilityNames()
if len(capNames) != 2 {
t.Errorf("Expected 2 capability names, got %d", len(capNames))
}
// Test summaries
networkCaps := capabilities.GetNetworkCapabilities()
expectedNetworkCaps := []string{"Dual Mode", "WSAPI Proxy"}
if len(networkCaps) != len(expectedNetworkCaps) {
t.Errorf("Expected %d network capabilities, got %d", len(expectedNetworkCaps), len(networkCaps))
}
audioCaps := capabilities.GetAudioCapabilities()
expectedAudioCaps := []string{"L/R Stereo"}
if len(audioCaps) != len(expectedAudioCaps) {
t.Errorf("Expected %d audio capabilities, got %d", len(expectedAudioCaps), len(audioCaps))
}
systemCaps := capabilities.GetSystemCapabilities()
expectedSystemCaps := []string{"Power Saving Disabled"}
if len(systemCaps) != len(expectedSystemCaps) {
t.Errorf("Expected %d system capabilities, got %d", len(expectedSystemCaps), len(systemCaps))
}
}
func TestCapabilities_HostedWifiConfig(t *testing.T) {
xmlData := `<?xml version="1.0" encoding="UTF-8" ?>
<capabilities deviceID="1234567890AB">
<networkConfig>
<hostedWifiConfigWebPage hostedBy="BCO" generation="1" port="80">true</hostedWifiConfigWebPage>
<wsapiproxy>false</wsapiproxy>
<allInterfacesSupported />
<wlanInterfaces />
<security />
</networkConfig>
<lightswitch>true</lightswitch>
<clockDisplay>true</clockDisplay>
</capabilities>`
var capabilities Capabilities
err := xml.Unmarshal([]byte(xmlData), &capabilities)
if err != nil {
t.Fatalf("Failed to unmarshal XML: %v", err)
}
if !capabilities.HasHostedWifiConfig() {
t.Error("Expected HasHostedWifiConfig() to return true")
}
if capabilities.GetHostedWifiPort() != "80" {
t.Errorf("Expected hosted wifi port '80', got '%s'", capabilities.GetHostedWifiPort())
}
if capabilities.GetHostedWifiHostedBy() != "BCO" {
t.Errorf("Expected hosted wifi hosted by 'BCO', got '%s'", capabilities.GetHostedWifiHostedBy())
}
if !capabilities.HasLightswitch() {
t.Error("Expected HasLightswitch() to return true")
}
if !capabilities.HasClockDisplay() {
t.Error("Expected HasClockDisplay() to return true")
}
}
func TestPresets_UnmarshalXML(t *testing.T) {
xmlData := `<?xml version="1.0" encoding="UTF-8" ?>
<presets>
<preset id="1" createdOn="1719128436" updatedOn="1728740382">
<ContentItem source="SPOTIFY" type="tracklisturl" location="/playback/container/abc123" sourceAccount="user@example.com" isPresetable="true">
<itemName>My Playlist</itemName>
<containerArt>https://i.scdn.co/image/ab67616d00001e025ff75c5d082fc50a3a74ad7b</containerArt>
</ContentItem>
</preset>
<preset id="2" createdOn="1703353552" updatedOn="1743615710">
<ContentItem source="SPOTIFY" type="tracklisturl" location="/playback/container/def456" sourceAccount="user@example.com" isPresetable="true">
<itemName>Chill Music Collection</itemName>
<containerArt>https://i.scdn.co/image/ab67616d0000b273e07c8adc6fb49168dc8b7a2f</containerArt>
</ContentItem>
</preset>
<preset id="3">
<ContentItem source="TUNEIN" type="stationurl" location="http://stream.example.com" isPresetable="true">
<itemName>Radio Station</itemName>
</ContentItem>
</preset>
</presets>`
var presets Presets
err := xml.Unmarshal([]byte(xmlData), &presets)
if err != nil {
t.Fatalf("Failed to unmarshal XML: %v", err)
}
// Test basic structure
if presets.GetPresetCount() != 3 {
t.Errorf("Expected 3 presets, got %d", presets.GetPresetCount())
}
// Test first preset
preset1 := presets.GetPresetByID(1)
if preset1 == nil {
t.Fatal("Expected to find preset with ID 1")
}
if preset1.GetDisplayName() != "My Playlist" {
t.Errorf("Expected display name 'My Playlist', got '%s'", preset1.GetDisplayName())
}
if !preset1.IsSpotifyPreset() {
t.Error("Expected preset 1 to be a Spotify preset")
}
if preset1.GetSource() != "SPOTIFY" {
t.Errorf("Expected source 'SPOTIFY', got '%s'", preset1.GetSource())
}
if preset1.GetSourceAccount() != "user@example.com" {
t.Errorf("Expected source account 'user@example.com', got '%s'", preset1.GetSourceAccount())
}
if !preset1.HasTimestamps() {
t.Error("Expected preset 1 to have timestamps")
}
// Test timestamps
expectedCreated := time.Unix(1719128436, 0)
if !preset1.GetCreatedTime().Equal(expectedCreated) {
t.Errorf("Expected created time %v, got %v", expectedCreated, preset1.GetCreatedTime())
}
expectedUpdated := time.Unix(1728740382, 0)
if !preset1.GetUpdatedTime().Equal(expectedUpdated) {
t.Errorf("Expected updated time %v, got %v", expectedUpdated, preset1.GetUpdatedTime())
}
// Test third preset (TuneIn radio)
preset3 := presets.GetPresetByID(3)
if preset3 == nil {
t.Fatal("Expected to find preset with ID 3")
}
if preset3.IsSpotifyPreset() {
t.Error("Expected preset 3 to not be a Spotify preset")
}
if preset3.GetSource() != "TUNEIN" {
t.Errorf("Expected source 'TUNEIN', got '%s'", preset3.GetSource())
}
if preset3.HasTimestamps() {
t.Error("Expected preset 3 to not have timestamps")
}
// Test filtering methods
spotifyPresets := presets.GetSpotifyPresets()
if len(spotifyPresets) != 2 {
t.Errorf("Expected 2 Spotify presets, got %d", len(spotifyPresets))
}
tuneinPresets := presets.GetPresetsBySource("TUNEIN")
if len(tuneinPresets) != 1 {
t.Errorf("Expected 1 TuneIn preset, got %d", len(tuneinPresets))
}
// Test empty slots
emptySlots := presets.GetEmptyPresetSlots()
expectedEmpty := []int{4, 5, 6}
if len(emptySlots) != len(expectedEmpty) {
t.Errorf("Expected %d empty slots, got %d", len(expectedEmpty), len(emptySlots))
}
// Test used slots
usedSlots := presets.GetUsedPresetSlots()
expectedUsed := []int{1, 2, 3}
if len(usedSlots) != len(expectedUsed) {
t.Errorf("Expected %d used slots, got %d", len(expectedUsed), len(usedSlots))
}
// Test summary
summary := presets.GetPresetsSummary()
if summary["total"] != 3 {
t.Errorf("Expected total 3, got %d", summary["total"])
}
if summary["used"] != 3 {
t.Errorf("Expected used 3, got %d", summary["used"])
}
if summary["spotify"] != 2 {
t.Errorf("Expected spotify 2, got %d", summary["spotify"])
}
if summary["SPOTIFY"] != 2 {
t.Errorf("Expected SPOTIFY 2, got %d", summary["SPOTIFY"])
}
if summary["TUNEIN"] != 1 {
t.Errorf("Expected TUNEIN 1, got %d", summary["TUNEIN"])
}
// Test most recent preset
mostRecent := presets.GetMostRecentPreset()
if mostRecent == nil {
t.Fatal("Expected to find most recent preset")
}
if mostRecent.ID != 2 {
t.Errorf("Expected most recent preset ID 2, got %d", mostRecent.ID)
}
// Test oldest preset
oldest := presets.GetOldestPreset()
if oldest == nil {
t.Fatal("Expected to find oldest preset")
}
if oldest.ID != 2 {
t.Errorf("Expected oldest preset ID 2, got %d", oldest.ID)
}
}
func TestPresets_EmptyPresets(t *testing.T) {
xmlData := `<?xml version="1.0" encoding="UTF-8" ?><presets></presets>`
var presets Presets
err := xml.Unmarshal([]byte(xmlData), &presets)
if err != nil {
t.Fatalf("Failed to unmarshal XML: %v", err)
}
if presets.HasPresets() {
t.Error("Expected HasPresets() to return false for empty presets")
}
if presets.GetPresetCount() != 0 {
t.Errorf("Expected preset count 0, got %d", presets.GetPresetCount())
}
emptySlots := presets.GetEmptyPresetSlots()
expectedSlots := []int{1, 2, 3, 4, 5, 6}
if len(emptySlots) != len(expectedSlots) {
t.Errorf("Expected %d empty slots, got %d", len(expectedSlots), len(emptySlots))
}
if presets.GetMostRecentPreset() != nil {
t.Error("Expected GetMostRecentPreset() to return nil for empty presets")
}
if presets.GetOldestPreset() != nil {
t.Error("Expected GetOldestPreset() to return nil for empty presets")
}
}
func TestPreset_EdgeCases(t *testing.T) {
// Test preset without ContentItem
emptyPreset := Preset{ID: 1}
if !emptyPreset.IsEmpty() {
t.Error("Expected IsEmpty() to return true for preset without ContentItem")
}
if emptyPreset.GetDisplayName() != "Preset 1" {
t.Errorf("Expected display name 'Preset 1', got '%s'", emptyPreset.GetDisplayName())
}
if emptyPreset.GetSource() != "" {
t.Errorf("Expected empty source, got '%s'", emptyPreset.GetSource())
}
if emptyPreset.GetArtworkURL() != "" {
t.Errorf("Expected empty artwork URL, got '%s'", emptyPreset.GetArtworkURL())
}
if emptyPreset.IsSpotifyPreset() {
t.Error("Expected IsSpotifyPreset() to return false for empty preset")
}
// Test preset with ContentItem but no artwork
presetNoArt := Preset{
ID: 2,
ContentItem: &ContentItem{
Source: "TUNEIN",
ItemName: "Test Station",
},
}
if presetNoArt.GetArtworkURL() != "" {
t.Errorf("Expected empty artwork URL, got '%s'", presetNoArt.GetArtworkURL())
}
if presetNoArt.GetDisplayName() != "Test Station" {
t.Errorf("Expected display name 'Test Station', got '%s'", presetNoArt.GetDisplayName())
}
}