Add Spotify mock server and integration tests

This commit is contained in:
Tobias Gesellchen
2026-04-06 15:05:44 +02:00
parent 382567d67d
commit c4cf078d2a
11 changed files with 269 additions and 21 deletions
@@ -12,6 +12,7 @@
"macAddress2": "B05ECAFF",
"accountId": "7654321",
"deviceName": "SoundTouch-11",
"spotifyProviderID": "15",
"spotifyUserId": "13570",
"spotifyToken": "example-spotify-token",
"spotifyRefreshToken": "example-spotify-refresh-token",
@@ -32,6 +33,7 @@
"macAddress2": "B05ECAFF",
"accountId": "7654321",
"deviceName": "SoundTouch-11",
"spotifyProviderID": "15",
"spotifyUserId": "13570",
"spotifyToken": "example-spotify-token",
"spotifyRefreshToken": "example-spotify-refresh-token",
@@ -6,7 +6,7 @@ Accept: application/vnd.bose.streaming-v1.1+xml
Authorization: Bearer {{token}}
Content-Type: application/vnd.bose.streaming-v1.1+xml
<?xml version="1.0" encoding="UTF-8"?><source><username>{{spotifyUserId}}</username><sourceproviderid>15</sourceproviderid><credential type="token_version_3">{{spotifyToken}}</credential><sourcename>{{spotifyDisplayName}}</sourcename></source>
<?xml version="1.0" encoding="UTF-8"?><source><username>{{spotifyUserId}}</username><sourceproviderid>{{spotifyProviderID}}</sourceproviderid><credential type="token_version_3">{{spotifyToken}}</credential><sourcename>{{spotifyDisplayName}}</sourcename></source>
> {%
client.test("Response is 201 Created", function() {
@@ -15,10 +15,11 @@ Content-Type: application/vnd.bose.streaming-v1.1+xml
client.test("Response body is a source", function() {
const doc = response.body;
const spotifySourceProviderID = client.variables.environment.get("spotifyProviderID");
const sourceID = doc.getElementsByTagName("sourceID")[0].textContent;
client.assert(sourceID !== "", "Response body does not contain a non-empty <sourceID>");
client.global.set("sourceID", sourceID);
client.assert(doc.getElementsByTagName("sourceProviderID")[0].textContent === "15", "Response body does not contain <sourceProviderID>15</sourceProviderID>");
client.assert(doc.getElementsByTagName("sourceProviderID")[0].textContent === spotifySourceProviderID, "Response body does not contain <sourceProviderID>{{spotifySourceProviderID}}</sourceProviderID>");
});
%}
@@ -47,10 +48,11 @@ Content-Type: application/vnd.bose.streaming-v1.2+xml
client.assert(doc.getElementsByTagName("contentItemType")[0].textContent === "tracklisturl", "Response body does not contain <contentItemType>tracklisturl</contentItemType>");
const source = doc.getElementsByTagName("source")[0];
const spotifySourceProviderID = client.variables.environment.get("spotifyProviderID");
client.assert(source.getAttribute("id") === client.global.get("sourceID"), "Response body does not contain source id=\"" + client.global.get("sourceID") + "\"");
client.assert(source.getAttribute("type") === "Audio", "Response body does not contain source type=\"Audio\"");
client.assert(source.getAttribute("displayName") === null, "Response body should NOT contain displayName attribute in source");
client.assert(doc.getElementsByTagName("sourceproviderid")[0].textContent === "15", "Response body does not contain <sourceproviderid>15</sourceproviderid>");
client.assert(doc.getElementsByTagName("sourceproviderid")[0].textContent === spotifySourceProviderID, "Response body does not contain <sourceproviderid>{{spotifySourceProviderID}}</sourceproviderid>");
client.assert(source.getElementsByTagName("username")[0].textContent === "", "Response body does not contain empty <username> in source, found: " + source.getElementsByTagName("username")[0].textContent);
client.assert(doc.getElementsByTagName("username")[1].textContent === "For Lovers, Not Killers", "Response body does not contain <username>For Lovers, Not Killers</username> in preset, found: " + doc.getElementsByTagName("username")[1].textContent);
});
@@ -0,0 +1,36 @@
### Spotify Initial Registration Flow
# @name Init Spotify Flow
POST {{host}}/mgmt/spotify/init
Authorization: Basic admin change_me!
> {%
client.global.set("redirectUrl", response.body.redirectUrl);
%}
###
# @name Spotify Callback
# Simulates the user being redirected back from Spotify with a code
GET {{host}}/mgmt/spotify/callback?code=mock-auth-code
###
# @name Verify Accounts
GET {{host}}/mgmt/spotify/accounts
Authorization: Basic admin change_me!
> {%
client.test("Account exists", function() {
client.assert(response.body.accounts.length > 0, "No accounts found");
var found = false;
for (var i = 0; i < response.body.accounts.length; i++) {
if (response.body.accounts[i].user_id === "mock-user-id") {
found = true;
client.assert(response.body.accounts[i].display_name === "Mock User", "Display name mismatch");
break;
}
}
client.assert(found, "Account 'mock-user-id' not found in " + JSON.stringify(response.body.accounts));
});
%}
+40
View File
@@ -0,0 +1,40 @@
// Package mocks provides mock implementations for external services during testing.
package mocks
import (
"net/http/httptest"
"github.com/gesellix/bose-soundtouch/pkg/testutils/spotify"
)
// SpotifyMock simulates Spotify API responses for OAuth and profile interactions.
type SpotifyMock struct {
server *httptest.Server
}
// NewSpotifyMock creates and starts a new Spotify mock server.
func NewSpotifyMock() *SpotifyMock {
return &SpotifyMock{
server: httptest.NewServer(spotify.NewSpotifyHandler()),
}
}
// URL returns the base URL of the mock server.
func (m *SpotifyMock) URL() string {
return m.server.URL
}
// TokenURL returns the OAuth token endpoint URL.
func (m *SpotifyMock) TokenURL() string {
return m.server.URL + "/api/token"
}
// APIBase returns the base API URL.
func (m *SpotifyMock) APIBase() string {
return m.server.URL
}
// Close stops the mock server.
func (m *SpotifyMock) Close() {
m.server.Close()
}
+9 -1
View File
@@ -1,4 +1,12 @@
{
"mock-user-id": {
"user_id": "mock-user-id",
"display_name": "Mock User",
"email": "mock@example.com",
"access_token": "mock-access-token",
"refresh_token": "mock-refresh-token",
"expires_at": 1775483553
},
"test-user": {
"user_id": "test-user",
"display_name": "Integration Test User",
@@ -7,4 +15,4 @@
"refresh_token": "mock-refresh-token",
"expires_at": 2147483647
}
}
}