mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-19 00:56:16 +00:00
main
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6e6e4838e6 |
fix(setup): fire <sourcesUpdated/> after data sync to recover post-factory-reset sources
Closes the AfterTouch-side half of issue #234. After a factory reset the speaker's /sources only lists the always-on local entries (AUX, BLUETOOTH, AIRPLAY, NOTIFICATION, QPLAY, plus a SpotifyConnectUserName placeholder); TUNEIN, LOCAL_INTERNET_RADIO, DEEZER, and linked Spotify accounts are absent until the device receives the <sourcesUpdated/> notification the reporter ran by hand. SyncDeviceData now POSTs that notification as the final step, so users get the visible-source-list recovery for free when they click Data Sync. The other half — re-creating Marge.xml so playback resumes — is already handled by the wizard's pair-account flow: it detects an empty <margeAccountUUID/> in /info and prompts the user to pick a known account or generate a new one. The wizard's pairing UI is deliberately user-driven (the user picks the ID); the notification nudge is purely automatic because there's no choice to make. Implementation routes through the existing client surface rather than reinventing it. setup.notifySpeakerSourcesUpdated delegates to pkg/client.Client.NotifySourcesUpdated — the same path handlers_mgmt.go already uses after music-service account changes (handlers_mgmt.go:304, :637). The wire shape lives in one place (pkg/models.NewSourcesUpdatedNotification). Fire-and-forget: a notification failure logs but doesn't fail the sync. Adjacent UX changes: - docs/guides/TROUBLESHOOTING.md: new section "Presets flash then revert to 'Select a preset' after a factory reset". Names the symptom, the Marge.xml + reduced-/sources cause, and walks the user through re-opening the Migration tab + Data Sync. - pkg/service/handlers/web/js/script.js: devices list now renders a "⚠ Not paired — re-pair" badge in the account-ID column for speakers whose live /info reports an empty margeAccountUUID. Clicking it opens the Migration tab pre-filled with that device, surfacing the wizard's existing "Not paired (factory-reset or never paired)" flow without making users discover it cold. - pkg/service/testing/fakespeaker/testdata/info.xml: demo speaker now reports margeAccountUUID=1234567 instead of the misleading 0000000 (which AfterTouch happens to accept as syntactically valid but is not a documented sentinel anywhere — the convention is empty for factory-reset, a real 7-digit number otherwise, matching pkg/client/testdata/info_response_st{10,20}.xml). Screenshots regenerated accordingly. Test scaffolding: - fakespeaker grows a POST /notification recorder that captures body + Content-Type; tests assert on s.Notifications(). - TestIssue234_FactoryResetSpeakerSyncsReducedSources now drives SyncDeviceData end-to-end (exercises the wiring) and asserts the notification fires with the right deviceID and shape. - TestFakeSpeakerNotificationRecorder pins the recorder contract and the POST-only method gate. Refs #234. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
0e10bfcb14 |
test(setup): wire issue #235 — Spotify Connect /now_playing reports IsPresetable=false
Two-part iteration. First, the fakespeaker grows a `/now_playing` route with a default STANDBY fixture — issue #235 is the first one in this series that needs to override /now_playing, and adding the route on its own would be infrastructure noise; bundled here it has an immediate consumer. The regression test then locks in the device-side signal at the heart of #235: when a SoundTouch is targeted by Spotify Connect (Spotify app sends audio to the speaker), the speaker's /now_playing reports - source = SPOTIFY - sourceAccount = SpotifyConnectUserName (the marker) - ContentItem.location = /playback/container/<base64 spotify:...> — a perfectly resolvable URI - **ContentItem.isPresetable = false** The contradiction (resolvable location + isPresetable=false) is the reason the CLI's storeCurrentPreset at cmd/soundtouch-cli/cmd_preset.go:41 refuses to act and emits "current content cannot be preset" — exactly the reporter's symptom. The test base64-decodes the location to surface the contradiction explicitly: it should yield a `spotify:` URI. When AfterTouch grows a fallback path (CLI --force, or service-side resolution to the device's own Spotify integration via the SoundTouch Spotify source provider), the assertion here stays sound — it tests what the device emits, not what the CLI decides — but a sibling test should assert the new fallback path produces a successful preset. Fixture pattern matches the rest of the issue series: testdata/issue235/ next to the test, fakespeaker driven via FixtureOverrides, doc-comment naming what would have to change for the assertion to flip. Refs #235. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
2fabdece64 |
test(fakespeaker): wire issue-specific payloads via Config.FixtureOverrides
Introduces a per-route fixture-override hook on fakespeaker.Config so open issues with concrete device-side payloads can become repeatable regression tests, then demonstrates the pattern by wiring issue #218. Foundation. Config grows a single optional field: FixtureOverrides map[string][]byte Routes named in the map (e.g. "/presets", "/sources", "/info") return the supplied bytes; routes not in the map fall through to the embedded testdata defaults the screenshot pipeline relies on. Stateful handlers (/getGroup, /addGroup, /updateGroup, /removeGroup) are unaffected because they're code-driven, not fixture-driven. The override slice is snapshotted at construction so later mutations of the caller's slice don't change the served body. Zero-value Config keeps the existing behaviour, so cmd/dummy-speaker + scripts/screenshots are untouched. Iteration zero — issue #218. pkg/service/setup/issue218_regression_test.go starts a fakespeaker serving the reporter's LOCAL_INTERNET_RADIO preset XML verbatim (URL: content.api.bose.io/core02/svc-bmx-adapter-orion/prod/orion/station?…), runs Manager.syncPresets against it, then asserts the persisted Presets.xml retains the Bose cloud URL prefix. This locks in the "location preserved through sync" contract; when AfterTouch starts rewriting the URL to its own base (the eventual fix for #218), the assertion flips and the fixture stays unchanged — the test is the carrier for the decision. Pattern reference for future issue regression tests: this exemplar mirrors pkg/service/marge/recents_sourceproviderid_regression_test.go's style (issue link, trigger chain in the doc-comment, locked-in assertion) but is the first one to drive the device side via fakespeaker rather than an inline httptest.NewServer. Subsequent issues with device-side payloads (#234 factory-reset state, #235 Spotify-as-preset, …) can reuse the FixtureOverrides hook without further infrastructure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
abae685a85 |
fix(screenshots): widen fakespeaker coverage and stabilize the pipeline
make screenshots was producing artifacts: a ghost Spotify pill on
ui-devices, empty Plan-card URL inputs on ui-migration with cascading
"localhost" warnings, and "Checking configuration…" placeholder text
instead of "❌ Not configured" on ui-settings. Two root causes, fixed
together so the run is deterministic again.
1. Fakespeaker too thin for the post-wizard inspect pipeline. The new
migration wizard probes /supportedURLs and reads /networkInfo and
/sources alongside the existing /info, /presets, /recents. Those
routes now exist with sanitized fixtures (deviceID DEADBEEFCAFE,
loopback IPs, no real MACs or account IDs). The full group endpoint
set is also wired: /getGroup and /removeGroup return the empty
<group/> shape a real un-paired device emits; /addGroup and
/updateGroup echo the posted body with <status>GROUP_OK</status>
inserted before </group>, matching the success path documented in
issue #252. /supportedURLs lists everything the fake now serves so
any caller that probes capabilities first (e.g. marge_pairing.go)
sees a coherent picture. Tests cover the GET routes' XML roots, the
POST echo + GROUP_OK insertion contract, and /removeGroup's
GET-only contract (405 with Allow: GET on other methods).
2. run.sh seed hit a DNS cliff. The :443 preflight shipped in
|
||
|
|
bb71253690 |
feat(screenshots): add headless-Chrome capture pipeline with fake speaker
Refreshes docs/images/ui-{settings,devices,sync,migration}.png by
driving the web UI in chromedp against a synthetic speaker, so
documentation can be regenerated without real hardware and without
leaking personal data from the local network.
Three independent pieces:
- pkg/service/testing/fakespeaker — embeddable library serving the
HTTP and telnet surface the migration wizard probes (/info,
/presets, /recents and a getpdo CurrentSystemConfiguration reply
that places the device on the unmigrated happy path).
- cmd/dummy-speaker — thin CLI wrapping the library; self-registers
with a running service via POST /setup/devices.
- scripts/screenshots — chromedp runner driven by a JSON manifest;
decoupled from speaker/service setup so it can target any backend
URL. run.sh orchestrates a one-shot end-to-end capture and seeds
settings.json with a generic hostname plus discovery disabled to
keep real-network state out of the captures.
Captures are at DPR=2 for retina-sharp text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|