mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 00:26:29 +00:00
test(http-client): pin the ETag conditional-GET (304) contract (refs #451)
The speaker re-polls /full and the device presets with the ETag it last saw and expects 304 Not Modified when nothing changed. Two self-contained flows capture the current ETag and replay it via If-None-Match, asserting 304. This pins the conditional-GET behaviour and the case-sensitive ETag header path (CLAUDE.md). make test-http-client: 65 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
6efad165f6
commit
a974862b07
@@ -200,6 +200,7 @@ test-http-client:
|
||||
/workdir/get_bmx_icon.http \
|
||||
/workdir/set_preset_6.http \
|
||||
/workdir/get_presets.http \
|
||||
/workdir/get_presets_conditional.http \
|
||||
/workdir/delete_preset_6.http \
|
||||
/workdir/set_preset_5.http \
|
||||
/workdir/post_recent.http \
|
||||
@@ -210,6 +211,7 @@ test-http-client:
|
||||
/workdir/get_api_versions.http \
|
||||
/workdir/post_musicprovider_is_eligible.http \
|
||||
/workdir/get_full_account.http \
|
||||
/workdir/get_full_account_conditional.http \
|
||||
/workdir/create_group.http \
|
||||
/workdir/get_group.http \
|
||||
/workdir/delete_group.http \
|
||||
|
||||
@@ -25,7 +25,7 @@ Legend: ✅ covered · ⬜ gap · 〰️ partial (some status/variant uncovered)
|
||||
|
||||
| Method | Route | Status(es) observed | Covered by | State |
|
||||
|--------|-------|--------------------|------------|-------|
|
||||
| GET | `/streaming/account/{a}/full` | 200, 304 | `get_full_account.http` | 〰️ (304 conditional gap) |
|
||||
| GET | `/streaming/account/{a}/full` | 200, 304 | `get_full_account.http`, `get_full_account_conditional.http` | ✅ |
|
||||
| GET | `/streaming/account/{a}/devices` | 200 | `get_account_devices.http` | ✅ |
|
||||
| GET | `/streaming/account/{a}/sources` | 200 | `get_account_sources.http` | ✅ |
|
||||
| GET | `/streaming/account/{a}/presets/all` | 200 | `get_account_presets.http` | ✅ |
|
||||
@@ -35,7 +35,7 @@ Legend: ✅ covered · ⬜ gap · 〰️ partial (some status/variant uncovered)
|
||||
| PUT | `/streaming/account/{a}/device/{d}` | 200, 401 | `rename_device.http` | 〰️ (401 gap) |
|
||||
| DELETE | `/streaming/account/{a}/device/{d}` | 200 | `unregister_device.http` | ✅ |
|
||||
| GET | `/streaming/account/{a}/device/{d}/group/` | 200 | `get_group.http` | ✅ |
|
||||
| GET | `/streaming/account/{a}/device/{d}/presets` | 200, 304 | `get_presets.http` | 〰️ (304 conditional gap) |
|
||||
| GET | `/streaming/account/{a}/device/{d}/presets` | 200, 304 | `get_presets.http`, `get_presets_conditional.http` | ✅ |
|
||||
| PUT | `/streaming/account/{a}/device/{d}/preset/{n}` | 200 | `set_preset_5/6.http` | ✅ |
|
||||
| DELETE | `/streaming/account/{a}/device/{d}/preset/{n}` | 200 | `delete_preset_6.http` | ✅ |
|
||||
| POST | `/streaming/account/{a}/device/{d}/recent` | 201 | `post_recent.http` | ✅ |
|
||||
@@ -93,6 +93,6 @@ Legend: ✅ covered · ⬜ gap · 〰️ partial (some status/variant uncovered)
|
||||
`/media/bmx-icons/{...}`, group delete lifecycle.
|
||||
2. **Medium (TuneIn live dep, like the existing playback test):**
|
||||
`/bmx/tunein/v1/playback/episode(s)/{id}`, `/bmx/tunein/v1/favorite/{id}`.
|
||||
3. **Low / edge:** 304 conditional GETs (`/full`, `/presets`), PUT-device 401,
|
||||
`/v1/blacklist` 405, `/ced/*` 404, `/alexa/certificate` 501,
|
||||
`/media/tts/{hash}`.
|
||||
3. **Low / edge (still open):** PUT-device 401, `/v1/blacklist` 405,
|
||||
`/ced/*` 404, `/alexa/certificate` 501, `/media/tts/{hash}`. Quirky-status
|
||||
pins; add on demand.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
### GET /streaming/account/{accountId}/full then re-GET with If-None-Match -> 304
|
||||
###
|
||||
### The speaker polls /full with the ETag it last saw; when nothing changed the
|
||||
### service answers 304 Not Modified so the speaker skips re-parsing. This pins
|
||||
### the ETag / conditional-GET contract (the ETag header is case-sensitive on the
|
||||
### wire; see CLAUDE.md). Self-contained: capture the current ETag, then replay.
|
||||
|
||||
### Prime: fetch /full and capture its ETag
|
||||
GET {{host}}/streaming/account/{{accountId}}/full
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Accept: application/vnd.bose.streaming-v1.2+xml
|
||||
|
||||
> {%
|
||||
client.test("Initial /full is 200 with an ETag", function() {
|
||||
client.assert(response.status === 200, "Response status is not 200, got " + response.status);
|
||||
client.assert(response.headers.valueOf("ETag") !== null, "Response should carry an ETag header");
|
||||
});
|
||||
|
||||
client.global.set("fullEtag", response.headers.valueOf("ETag"));
|
||||
%}
|
||||
|
||||
### Conditional GET with the captured ETag -> 304 Not Modified
|
||||
GET {{host}}/streaming/account/{{accountId}}/full
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Accept: application/vnd.bose.streaming-v1.2+xml
|
||||
If-None-Match: {{fullEtag}}
|
||||
|
||||
> {%
|
||||
client.test("Conditional /full returns 304 Not Modified", function() {
|
||||
client.assert(response.status === 304, "Response status is not 304, got " + response.status);
|
||||
});
|
||||
%}
|
||||
@@ -0,0 +1,31 @@
|
||||
### GET .../device/{deviceId}/presets then re-GET with If-None-Match -> 304
|
||||
###
|
||||
### Same conditional-GET contract as /full, for the device presets resource: the
|
||||
### speaker re-polls with its last ETag and the service answers 304 when the
|
||||
### preset list is unchanged. Self-contained: capture the current ETag, replay.
|
||||
|
||||
### Prime: fetch presets and capture the ETag
|
||||
GET {{host}}/streaming/account/{{accountId}}/device/{{deviceId}}/presets
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Accept: application/vnd.bose.streaming-v1.2+xml
|
||||
|
||||
> {%
|
||||
client.test("Initial presets fetch is 200 with an ETag", function() {
|
||||
client.assert(response.status === 200, "Response status is not 200, got " + response.status);
|
||||
client.assert(response.headers.valueOf("ETag") !== null, "Response should carry an ETag header");
|
||||
});
|
||||
|
||||
client.global.set("presetsEtag", response.headers.valueOf("ETag"));
|
||||
%}
|
||||
|
||||
### Conditional GET with the captured ETag -> 304 Not Modified
|
||||
GET {{host}}/streaming/account/{{accountId}}/device/{{deviceId}}/presets
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Accept: application/vnd.bose.streaming-v1.2+xml
|
||||
If-None-Match: {{presetsEtag}}
|
||||
|
||||
> {%
|
||||
client.test("Conditional presets fetch returns 304 Not Modified", function() {
|
||||
client.assert(response.status === 304, "Response status is not 304, got " + response.status);
|
||||
});
|
||||
%}
|
||||
Reference in New Issue
Block a user