mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 08:36:13 +00:00
test(http-client): pin ignored edges + app/provisioning surface (refs #451)
Two deliberately-unimplemented routes, pinned as "currently ignored" so a future
change to them is conscious:
- GET /v1/blacklist/{deviceId} -> 405 (inline stub)
- POST /alexa/certificate -> 501 (no AWS IoT integration)
App / provisioning surface (app-called, not the speaker data-plane). Shapes come
from the _/mitm capture where one exists, otherwise from the handler (canned /
stub responses):
- GET /streaming/account/{a}/emailaddress -> 200 (<emailAddress>, _/mitm)
- GET /customer/account/{a} -> 200 (<customer> profile, canned)
- POST /customer/account/{a} -> 200 (profile update, stub)
- POST /customer/account/{a}/password -> 200 (password change, stub)
COVERAGE.md gains an app/provisioning section and records the source (mitm vs
handler) for each. make test-http-client: 73 requests, 0 failed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
e91e7d8ad4
commit
d5b298fc43
@@ -59,8 +59,8 @@ Legend: ✅ covered · ⬜ gap · 〰️ partial (some status/variant uncovered)
|
||||
| GET | `/updates/soundtouch` | 200 | `get_soundtouch_updates.http` | ✅ |
|
||||
| GET | `/v1/auth` | 200, 403, 404 | `get_speaker_auth.http` | ✅ (200; 403/404 probe/edge) |
|
||||
| POST | `/v1/scmudc/{d}` | 200 | `post_scmudc_event.http` | ✅ |
|
||||
| GET | `/v1/blacklist/{d}` | 405 | — | ⬜ (edge) |
|
||||
| POST | `/alexa/certificate` | 501 (rare 200) | — | ⬜ (edge) |
|
||||
| GET | `/v1/blacklist/{d}` | 405 | `get_blacklist.http` | ✅ (currently ignored: 405 stub) |
|
||||
| POST | `/alexa/certificate` | 501 (rare 200) | `post_alexa_certificate.http` | ✅ (currently ignored: 501 stub) |
|
||||
| GET | `/bmx/registry/v1/services` | 200 | `get_bmx_services.http` | ✅ |
|
||||
| GET | `/bmx/registry/v1/servicesAvailability` | 200 | `get_bmx_services_availability.http` | ✅ |
|
||||
| POST | `/bmx/tunein/v1/token` | 200 | `tunein_playback_station.http` | ✅ |
|
||||
@@ -77,18 +77,28 @@ Legend: ✅ covered · ⬜ gap · 〰️ partial (some status/variant uncovered)
|
||||
| POST | `/oauth/device/{d}/.../15/token/cs3` | 200 | `post_oauth_token.http` | ✅ |
|
||||
| POST | `/oauth/device/{d}/.../20/token/cs1` | 200 | `post_oauth_token_amazon.http` | ✅ |
|
||||
|
||||
## Not observed from the speaker (lower priority / different audience)
|
||||
## App / provisioning surface (app-called, not the speaker data-plane)
|
||||
|
||||
The SoundTouch app (not the speaker) drives these. They are part of the frozen
|
||||
contract but a different audience; shapes are taken from `_/mitm` where a capture
|
||||
exists, otherwise from the handler (the current responses are canned / stubs).
|
||||
|
||||
| Method | Route | Status | Covered by | Source |
|
||||
|--------|-------|--------|------------|--------|
|
||||
| GET | `/streaming/account/{a}/emailaddress` | 200 | `get_emailaddress.http` | `_/mitm` capture |
|
||||
| GET | `/customer/account/{a}` | 200 | `get_customer_profile.http` | handler (canned profile) |
|
||||
| POST | `/customer/account/{a}` | 200 | `post_customer_profile.http` | handler (stub accept) |
|
||||
| POST | `/customer/account/{a}/password` | 200 | `post_customer_profile.http` | handler (stub accept) |
|
||||
| POST | `/streaming/account/login` | 200 | `create_account.http` | `_/mitm` capture |
|
||||
|
||||
## Not observed (lower priority / no fixture)
|
||||
|
||||
- `/bmx/tunein/v1/navigate`, `/search`, `/search/next` — registered (frozen),
|
||||
but in the corpus the speaker uses `/playback/*`; the search/navigate layer is
|
||||
driven by the app/UI (`/api/tunein/*`), not the speaker. Covered conceptually,
|
||||
no speaker recording to replay (and no upstream fixture yet, see
|
||||
TUNEIN-MOCK-MISSING.md).
|
||||
driven by the app/UI (`/api/tunein/*`), not the speaker. No upstream fixture
|
||||
yet, see TUNEIN-MOCK-MISSING.md.
|
||||
- `/core02/svc-bmx-adapter-siriusxm-*` — registered, but not present in this
|
||||
corpus (no SiriusXM device). Left as a known blank.
|
||||
- App / provisioning surface (`/customer/account*`, account profile/password,
|
||||
`/streaming/account/login` beyond create) — app-called, not the speaker
|
||||
data-plane; out of scope for the speaker-contract net.
|
||||
|
||||
## Remaining gaps
|
||||
|
||||
@@ -97,5 +107,5 @@ Legend: ✅ covered · ⬜ gap · 〰️ partial (some status/variant uncovered)
|
||||
- `/media/tts/{hash}.mp3` — returns 200 only after a TTS has been generated
|
||||
(otherwise a 404 miss). Needs a prior `/setup/tts/speak` step to be a
|
||||
deterministic 200.
|
||||
- Edge statuses (quirky-status pins, add on demand): PUT-device `401`,
|
||||
`/v1/blacklist` `405`, `/alexa/certificate` `501`.
|
||||
- PUT-device `401` (rename with a mismatched/blocked payload) — the only
|
||||
remaining status variant on an otherwise-covered route.
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
### GET /v1/blacklist/{deviceId} (currently ignored: 405 Method Not Allowed)
|
||||
###
|
||||
### The speaker probes a notification blacklist endpoint. AfterTouch does not
|
||||
### implement it and deliberately returns 405 (inline stub in setupRouter).
|
||||
### Pinning the current 405 documents this as a conscious no-op, so any future
|
||||
### behaviour change here is deliberate rather than accidental.
|
||||
GET {{host}}/v1/blacklist/{{deviceId}}
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Accept: */*
|
||||
|
||||
> {%
|
||||
client.test("Blacklist probe is the ignored 405 stub", function() {
|
||||
client.assert(response.status === 405, "Response status is not 405, got " + response.status);
|
||||
});
|
||||
%}
|
||||
@@ -0,0 +1,19 @@
|
||||
### GET /customer/account/{accountId} (app-called account profile)
|
||||
###
|
||||
### App / provisioning surface (not the speaker data-plane). No live recording in
|
||||
### _/mitm for this exact route; the response is a canned profile from
|
||||
### HandleMargeAccountProfile, returned as a <customer> XML document. App user-agent.
|
||||
GET {{host}}/customer/account/{{accountId}}
|
||||
User-Agent: Mozilla/5.0 (Linux; Android 13) SOUNDTOUCH_MOBILE_APP
|
||||
Accept: application/xml
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
> {%
|
||||
client.test("Account profile returned as XML 200", function() {
|
||||
client.assert(response.status === 200, "Response status is not 200, got " + response.status);
|
||||
client.assert(response.body.getElementsByTagName("customer").length > 0,
|
||||
"Response should contain <customer>");
|
||||
client.assert(response.body.getElementsByTagName("accountID").length > 0,
|
||||
"<customer> should contain <accountID>");
|
||||
});
|
||||
%}
|
||||
@@ -0,0 +1,17 @@
|
||||
### GET /streaming/account/{accountId}/emailaddress (app-called account surface)
|
||||
###
|
||||
### The SoundTouch app fetches the account email (captured in _/mitm). Not part of
|
||||
### the speaker data-plane, but part of the frozen app/provisioning contract.
|
||||
### HandleMargeGetEmailAddress returns an <emailAddress> XML body. App user-agent.
|
||||
GET {{host}}/streaming/account/{{accountId}}/emailaddress
|
||||
User-Agent: Mozilla/5.0 (Linux; Android 13) SOUNDTOUCH_MOBILE_APP
|
||||
Accept: application/vnd.bose.streaming-v1.1+xml
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
> {%
|
||||
client.test("Email address returned as XML 200", function() {
|
||||
client.assert(response.status === 200, "Response status is not 200, got " + response.status);
|
||||
client.assert(response.body.getElementsByTagName("emailAddress").length > 0,
|
||||
"Response should contain <emailAddress>");
|
||||
});
|
||||
%}
|
||||
@@ -0,0 +1,20 @@
|
||||
### POST /alexa/certificate (currently ignored: 501 Not Implemented)
|
||||
###
|
||||
### Speakers request an Alexa IoT provisioning certificate. AfterTouch has no AWS
|
||||
### IoT integration, so HandleAlexaCertificate deliberately returns 501 with an
|
||||
### explanatory JSON body. Pinning the current 501 documents this as intentionally
|
||||
### unimplemented. (data is a form field carrying {"device":"..."}.)
|
||||
POST {{host}}/alexa/certificate
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Accept: */*
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
data=%7B%22device%22%3A%22{{deviceId}}%22%7D
|
||||
|
||||
> {%
|
||||
client.test("Alexa certificate provisioning is the ignored 501 stub", function() {
|
||||
client.assert(response.status === 501, "Response status is not 501, got " + response.status);
|
||||
client.assert(response.contentType.mimeType === "application/json",
|
||||
"Expected application/json, got '" + response.contentType.mimeType + "'");
|
||||
});
|
||||
%}
|
||||
@@ -0,0 +1,39 @@
|
||||
### POST /customer/account/{accountId} (update account profile)
|
||||
###
|
||||
### App / provisioning surface. HandleMargeUpdateAccountProfile is a stub that
|
||||
### accepts the update with 200. No live recording; this pins the current accept.
|
||||
POST {{host}}/customer/account/{{accountId}}
|
||||
User-Agent: Mozilla/5.0 (Linux; Android 13) SOUNDTOUCH_MOBILE_APP
|
||||
Content-Type: application/xml
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<customer>
|
||||
<accountID>{{accountId}}</accountID>
|
||||
<firstName>SoundTouch</firstName>
|
||||
<lastName>User</lastName>
|
||||
</customer>
|
||||
|
||||
> {%
|
||||
client.test("Profile update accepted (200)", function() {
|
||||
client.assert(response.status === 200, "Response status is not 200, got " + response.status);
|
||||
});
|
||||
%}
|
||||
|
||||
### POST /customer/account/{accountId}/password (change account password)
|
||||
###
|
||||
### App / provisioning surface. HandleMargeChangePassword is a stub that accepts
|
||||
### the change with 200. No live recording; this pins the current accept.
|
||||
POST {{host}}/customer/account/{{accountId}}/password
|
||||
User-Agent: Mozilla/5.0 (Linux; Android 13) SOUNDTOUCH_MOBILE_APP
|
||||
Content-Type: application/xml
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<password><old>old-secret</old><new>new-secret</new></password>
|
||||
|
||||
> {%
|
||||
client.test("Password change accepted (200)", function() {
|
||||
client.assert(response.status === 200, "Response status is not 200, got " + response.status);
|
||||
});
|
||||
%}
|
||||
Reference in New Issue
Block a user