Files
MusicPlayer/tests/unit/test_music_server_client.gd
T
SansanandClaude Opus 5.5 a735c515bf
Build / test (push) Successful in 1m40s
Build / build (push) Successful in 1m28s
Add Short songs and a choice of how random songs are picked (#30, #51)
Short songs (#30): a quiz settings switch and a Seconds per song row (30 by
default, 10-120 in steps of 5), saved with the quiz settings. Only part of
each quiz song plays, from a random point 10-60% into it, picked once per song
so restart and repeat play the same part. The progress bar, time and seeking
stay within the part, a hidden beginning counts inside it, and its end counts
as the song ending (repeat, stop or go to the next song as set).

Random songs (#51): the low-played switch in Settings is now a Normal / Low
played / Classic choice, stored as Settings.random_mode and not saved.
fetch_random_song(mode) picks /music/rand, /music/rand/low or
/music/rand/classic.

Closes #30
Closes #51

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:21:20 +02:00

668 lines
25 KiB
GDScript

extends GdUnitTestSuite
## MusicServerClient: which paths it asks for, how it handles the answers and how it
## gets and renews its API token.
var _server: FakeHttpTransport
func before_test() -> void:
_server = FakeHttpTransport.new().install()
func after_test() -> void:
_server.uninstall()
func test_fetch_song_list_passes_song_infos() -> void:
var songs: Array = [{"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time"}]
_server.respond_json("/music/list", songs)
var received: Array[SongInfo] = []
MusicServerClient.fetch_song_list(
func(data: Array[SongInfo]) -> void: received.append_array(data))
assert_int(received.size()).is_equal(1)
assert_int(received[0].song_no).is_equal(0)
assert_str(received[0].soundtrack).is_equal("Chrono Trigger")
assert_str(received[0].song).is_equal("Corridors of Time")
func test_fetch_song_info_passes_song_info() -> void:
_server.respond_json("/music/info", {"SongNo": 7, "Soundtrack": "Terranigma",
"Song": "Underworld", "SoundtrackUuid": "st-1", "SongUuid": "so-1"})
var received: Array[SongInfo] = []
MusicServerClient.fetch_song_info(func(info: SongInfo) -> void: received.append(info))
assert_int(received[0].song_no).is_equal(7)
assert_str(received[0].song_uuid).is_equal("so-1")
func test_empty_song_list_sent_as_null_gives_empty_array() -> void:
_server.respond_bytes("/music/list", "null".to_utf8_buffer())
var received: Array = []
MusicServerClient.fetch_song_list(func(data: Array[SongInfo]) -> void: received.append(data))
assert_int(received.size()).is_equal(1)
assert_array(received[0]).is_empty()
func test_json_list_that_is_not_an_array_calls_on_failed() -> void:
_server.respond_json("/music/list", {"error": "not a list"})
var outcome: Array[String] = []
MusicServerClient.fetch_song_list(
func(_data: Array[SongInfo]) -> void: outcome.append("done"),
func() -> void: outcome.append("failed"))
assert_array(outcome).contains_exactly(["failed"])
@warning_ignore("unused_parameter")
func test_fetch_random_song_uses_the_endpoint_of_its_mode(mode: Settings.RandomMode,
path: String, test_parameters: Array = [
[Settings.RandomMode.NORMAL, "/music/rand"],
[Settings.RandomMode.LOW_PLAYED, "/music/rand/low"],
[Settings.RandomMode.CLASSIC, "/music/rand/classic"],
]) -> void:
MusicServerClient.fetch_random_song(mode, func(_body: PackedByteArray) -> void: pass)
assert_array(_server.requested_paths).contains_exactly([path])
func test_fetch_song_passes_raw_bytes() -> void:
var mp3: PackedByteArray = PackedByteArray([0x49, 0x44, 0x33])
_server.respond_bytes("/music?song=4", mp3)
var received: Array[PackedByteArray] = []
MusicServerClient.fetch_song(4, func(body: PackedByteArray) -> void: received.append(body))
assert_array(received).contains_exactly([mp3])
func test_failed_request_does_not_call_back() -> void:
_server.respond_json("/music/list", [], 500)
var called: Array[bool] = [false]
MusicServerClient.fetch_song_list(func(_data: Variant) -> void: called[0] = true)
assert_bool(called[0]).is_false()
func test_invalid_json_does_not_call_back() -> void:
_server.respond_bytes("/music/info", "not json".to_utf8_buffer())
var called: Array[bool] = [false]
MusicServerClient.fetch_song_info(func(_data: SongInfo) -> void: called[0] = true)
assert_bool(called[0]).is_false()
func test_failed_request_calls_on_failed() -> void:
_server.respond_bytes("/music/rand", PackedByteArray(), 503)
var outcome: Array[String] = []
MusicServerClient.fetch_random_song(Settings.RandomMode.NORMAL,
func(_body: PackedByteArray) -> void: outcome.append("done"),
func() -> void: outcome.append("failed"))
assert_array(outcome).contains_exactly(["failed"])
func test_invalid_json_calls_on_failed() -> void:
_server.respond_bytes("/music/info", "not json".to_utf8_buffer())
var outcome: Array[String] = []
MusicServerClient.fetch_song_info(
func(_data: SongInfo) -> void: outcome.append("done"),
func() -> void: outcome.append("failed"))
assert_array(outcome).contains_exactly(["failed"])
func test_successful_request_does_not_call_on_failed() -> void:
_server.respond_bytes("/music/addQue", PackedByteArray())
var outcome: Array[String] = []
MusicServerClient.add_to_queue(
func() -> void: outcome.append("done"),
func() -> void: outcome.append("failed"))
assert_array(outcome).contains_exactly(["done"])
func test_send_calls_back_without_arguments() -> void:
_server.respond_bytes("/music/reset", PackedByteArray())
var called: Array[bool] = [false]
MusicServerClient.reset_playlist(func() -> void: called[0] = true)
assert_bool(called[0]).is_true()
func test_mark_song_played_sends_put() -> void:
_server.respond_bytes("/music/played?song=3", PackedByteArray())
MusicServerClient.mark_song_played(3)
assert_array(_server.requested_paths).contains_exactly(["/music/played?song=3"])
assert_array(_server.requested_methods).contains_exactly([HTTPClient.METHOD_PUT])
func test_character_name_is_url_encoded() -> void:
MusicServerClient.fetch_character_image("Paper Mario.png",
func(_body: PackedByteArray) -> void: pass)
assert_array(_server.requested_paths).contains_exactly(["/character?name=Paper%20Mario.png"])
func test_start_sync_sends_post() -> void:
_server.respond_json("/sync", "Sync started")
var called: Array[bool] = [false]
MusicServerClient.start_sync(func() -> void: called[0] = true)
assert_array(_server.requested_paths).contains_exactly(["/sync"])
assert_array(_server.requested_methods).contains_exactly([HTTPClient.METHOD_POST])
assert_bool(called[0]).is_true()
func test_start_full_sync_sends_post() -> void:
MusicServerClient.start_full_sync(func() -> void: pass)
assert_array(_server.requested_paths).contains_exactly(["/sync/full"])
assert_array(_server.requested_methods).contains_exactly([HTTPClient.METHOD_POST])
func test_sync_already_running_calls_on_failed() -> void:
_server.respond_json("/sync", {"error": "Sync is already running"}, 423)
var outcome: Array[String] = []
MusicServerClient.start_sync(
func() -> void: outcome.append("done"),
func() -> void: outcome.append("failed"))
assert_array(outcome).contains_exactly(["failed"])
func test_requests_go_to_selected_server_under_api_path() -> void:
var previous_path: String = Settings.default_path
Settings.default_path = "http://localhost:8080"
var requested_urls: Array[String] = []
MusicServerClient.transport = _RecordingTransport.new(requested_urls)
MusicServerClient.fetch_song_info(func(_data: SongInfo) -> void: pass)
Settings.default_path = previous_path
assert_array(requested_urls).contains_exactly([
"http://localhost:8080/api/v1/token",
"http://localhost:8080/api/v1/music/info",
])
# Tokens
func test_request_sends_token_in_authorization_header() -> void:
_server.respond_json("/music/info", {})
MusicServerClient.fetch_song_info(func(_data: SongInfo) -> void: pass)
assert_int(_server.token_requests).is_equal(1)
assert_array(_server.requested_headers[0]) \
.contains_exactly(["Authorization: Bearer fake-token-1"])
func test_token_request_sends_password_and_client_type() -> void:
var previous_password: String = Settings.api_password
Settings.api_password = "secret"
MusicServerClient.fetch_song_info(func(_data: SongInfo) -> void: pass)
Settings.api_password = previous_password
assert_dict(_server.last_token_request) \
.is_equal({"password": "secret", "client_type": "godot"})
func test_token_is_reused() -> void:
_server.respond_json("/music/info", {})
_server.respond_json("/music/list", [])
MusicServerClient.fetch_song_info(func(_data: SongInfo) -> void: pass)
MusicServerClient.fetch_song_list(func(_data: Array[SongInfo]) -> void: pass)
MusicServerClient.fetch_song_info(func(_data: SongInfo) -> void: pass)
assert_int(_server.token_requests).is_equal(1)
assert_array(_server.requested_paths) \
.contains_exactly(["/music/info", "/music/list", "/music/info"])
func test_requests_while_getting_a_token_share_it() -> void:
_server.respond_json("/music/info", {})
_server.respond_json("/music/list", [])
_server.hold("/token")
var done: Array[String] = []
MusicServerClient.fetch_song_info(func(_data: SongInfo) -> void: done.append("info"))
MusicServerClient.fetch_song_list(func(_data: Array[SongInfo]) -> void: done.append("list"))
assert_array(_server.requested_paths).is_empty()
_server.release("/token")
assert_int(_server.token_requests).is_equal(1)
assert_array(done).contains_exactly(["info", "list"])
func test_expired_token_is_renewed_and_request_sent_again() -> void:
_server.respond_json("/music/info", {"SongNo": 3})
MusicServerClient.fetch_song_info(func(_data: SongInfo) -> void: pass)
_server.expire_tokens()
var received: Array[SongInfo] = []
MusicServerClient.fetch_song_info(func(info: SongInfo) -> void: received.append(info))
assert_int(_server.token_requests).is_equal(2)
assert_array(_server.requested_paths) \
.contains_exactly(["/music/info", "/music/info", "/music/info"])
assert_array(_server.requested_headers[2]) \
.contains_exactly(["Authorization: Bearer fake-token-2"])
assert_int(received.size()).is_equal(1)
assert_int(received[0].song_no).is_equal(3)
func test_request_is_sent_again_only_once() -> void:
_server.respond_json("/music/info", {"error": "Unauthorized"}, 401)
var outcome: Array[String] = []
MusicServerClient.fetch_song_info(
func(_data: SongInfo) -> void: outcome.append("done"),
func() -> void: outcome.append("failed"))
assert_int(_server.token_requests).is_equal(2)
assert_int(_server.count("/music/info")).is_equal(2)
assert_array(outcome).contains_exactly(["failed"])
func test_refused_password_calls_on_failed_without_sending_request() -> void:
_server.respond_json("/token", {"error": "Invalid password"}, 401)
var outcome: Array[String] = []
MusicServerClient.fetch_song_info(
func(_data: SongInfo) -> void: outcome.append("done"),
func() -> void: outcome.append("failed"))
assert_array(_server.requested_paths).is_empty()
assert_array(outcome).contains_exactly(["failed"])
func test_rate_limited_token_request_calls_on_failed() -> void:
_server.respond_json("/token", {"error": "Too many requests"}, 429)
var outcome: Array[String] = []
MusicServerClient.add_to_queue(
func() -> void: outcome.append("done"),
func() -> void: outcome.append("failed"))
assert_array(outcome).contains_exactly(["failed"])
func test_new_token_after_a_failed_token_request() -> void:
_server.respond_json("/token", {"error": "Too many requests"}, 429)
MusicServerClient.add_to_queue(func() -> void: pass)
_server.respond_json("/token", {"token": "second", "expires_at": "", "client_type": "godot"})
_server.respond_bytes("/music/addQue", PackedByteArray())
_server.expire_tokens()
MusicServerClient.add_to_queue(func() -> void: pass)
assert_int(_server.token_requests).is_equal(2)
assert_array(_server.requested_headers.back()) \
.contains_exactly(["Authorization: Bearer second"])
func test_changing_server_gets_a_new_token() -> void:
var previous_path: String = Settings.default_path
_server.respond_json("/music/info", {})
MusicServerClient.fetch_song_info(func(_data: SongInfo) -> void: pass)
Settings.default_path = "http://another-server:8080"
MusicServerClient.fetch_song_info(func(_data: SongInfo) -> void: pass)
Settings.default_path = previous_path
assert_int(_server.token_requests).is_equal(2)
func test_health_needs_no_token() -> void:
_server.respond_json("/health", {"status": "ok"})
var received: Array = []
MusicServerClient.fetch_health(func(data: Dictionary) -> void: received.append(data))
assert_int(_server.token_requests).is_equal(0)
assert_array(_server.requested_headers[0]).is_empty()
assert_array(received).contains_exactly([{"status": "ok"}])
func test_delete_token_sends_token_and_forgets_it() -> void:
_server.respond_json("/music/info", {})
MusicServerClient.fetch_song_info(func(_data: SongInfo) -> void: pass)
_server.respond_json("/token", {"status": "deleted"})
var called: Array[bool] = [false]
MusicServerClient.delete_token(func() -> void: called[0] = true)
assert_bool(called[0]).is_true()
assert_int(_server.requested_methods.back()).is_equal(HTTPClient.METHOD_DELETE)
assert_array(_server.requested_headers.back()) \
.contains_exactly(["Authorization: Bearer fake-token-1"])
func test_delete_token_without_token_sends_nothing() -> void:
var called: Array[bool] = [false]
MusicServerClient.delete_token(func() -> void: called[0] = true)
assert_bool(called[0]).is_true()
assert_array(_server.requested_paths).is_empty()
# Other endpoints
func test_endpoint_paths_and_methods() -> void:
var noop: Callable = func(_data: Variant = null) -> void: pass
var calls: Array[Array] = [
[func() -> void: MusicServerClient.fetch_next_song(noop),
"/music/next", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_previous_song(noop),
"/music/previous", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_sound_test_song(noop),
"/music/soundTest", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.mark_current_song_played(noop),
"/music/addPlayed", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_all_soundtracks(noop),
"/music/all", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_all_soundtracks_ordered(noop),
"/music/all/order", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_soundtracks(noop),
"/soundtracks", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_soundtrack("a b", noop),
"/soundtracks/a%20b", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_song_by_uuid("so-1", noop),
"/songs/so-1", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_song_file("so-1", noop),
"/songs/so-1/file", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_character_list(noop),
"/character/list", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_sync_progress(noop),
"/sync/progress", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_version(noop),
"/version", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_version_history(noop),
"/version/history", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_latest_client_version(noop),
"/download", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_client_assets(noop),
"/download/list", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.download_client_windows(noop),
"/download/windows", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.download_client_linux(noop),
"/download/linux", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.cleanup_tokens(noop),
"/token/cleanup", HTTPClient.METHOD_POST],
[func() -> void: MusicServerClient.fetch_soundtrack_statistics(noop),
"/statistics/soundtracks", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_statistics_summary(noop),
"/statistics/summary", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_most_played_soundtracks(5, noop),
"/statistics/soundtracks/most-played?limit=5", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_least_played_soundtracks(5, noop),
"/statistics/soundtracks/least-played?limit=5", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_last_played_soundtracks(5, noop),
"/statistics/soundtracks/last-played?limit=5", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_oldest_played_soundtracks(5, noop),
"/statistics/soundtracks/oldest-played?limit=5", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_never_played_soundtracks(noop),
"/statistics/soundtracks/never-played", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_most_played_songs(5, noop),
"/statistics/songs/most-played?limit=5", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_least_played_songs(5, noop),
"/statistics/songs/least-played?limit=5", HTTPClient.METHOD_GET],
]
for entry: Array in calls:
_server.requested_paths.clear()
_server.requested_methods.clear()
entry[0].call()
assert_array(_server.requested_paths).contains_exactly([entry[1]])
assert_array(_server.requested_methods).contains_exactly([entry[2]])
func test_statistics_summary_is_typed() -> void:
_server.respond_json("/statistics/summary", {"total_soundtracks": 120,
"played_soundtracks": 100, "avg_soundtrack_plays": 2.5})
var received: Array[StatisticsSummary] = []
MusicServerClient.fetch_statistics_summary(
func(summary: StatisticsSummary) -> void: received.append(summary))
assert_int(received[0].total_soundtracks).is_equal(120)
assert_int(received[0].played_soundtracks).is_equal(100)
assert_float(received[0].avg_soundtrack_plays).is_equal(2.5)
func test_most_played_soundtracks_are_typed_with_their_songs() -> void:
_server.respond_json("/statistics/soundtracks/most-played?limit=2", [
{"soundtrack_uuid": "st-1", "soundtrack_name": "Chrono Trigger",
"soundtrack_played": 9, "songs": [{"song_name": "Frog's Theme"}]},
{"soundtrack_uuid": "st-2", "soundtrack_name": "Lufia II", "soundtrack_played": 4,
"songs": []},
])
var received: Array[SoundtrackWithSongs] = []
MusicServerClient.fetch_most_played_soundtracks(2,
func(list: Array[SoundtrackWithSongs]) -> void: received.append_array(list))
assert_int(received.size()).is_equal(2)
assert_str(received[0].soundtrack_name).is_equal("Chrono Trigger")
assert_int(received[0].soundtrack_played).is_equal(9)
assert_str(received[0].songs[0].song_name).is_equal("Frog's Theme")
assert_array(received[1].songs).is_empty()
func test_soundtrack_statistics_are_typed() -> void:
_server.respond_json("/statistics/soundtracks", [
{"soundtrack_name": "Chrono Trigger", "soundtrack_played": 2,
"songs": [{"song_name": "Frog's Theme", "times_played": 2}]},
{"soundtrack_name": "Lufia II", "soundtrack_played": 0, "songs": []},
])
var received: Array[SoundtrackWithSongs] = []
MusicServerClient.fetch_soundtrack_statistics(
func(list: Array[SoundtrackWithSongs]) -> void: received.append_array(list))
assert_int(received.size()).is_equal(2)
assert_str(received[0].songs[0].song_name).is_equal("Frog's Theme")
assert_int(received[1].soundtrack_played).is_equal(0)
func test_most_played_songs_are_typed() -> void:
_server.respond_json("/statistics/songs/most-played?limit=1", [
{"song_uuid": "so-1", "song_name": "Dancing Mad", "times_played": 12},
])
var received: Array[SongInfoForStats] = []
MusicServerClient.fetch_most_played_songs(1,
func(list: Array[SongInfoForStats]) -> void: received.append_array(list))
assert_str(received[0].song_name).is_equal("Dancing Mad")
assert_int(received[0].times_played).is_equal(12)
func test_statistics_calls_on_failed_on_server_error() -> void:
var outcome: Array[String] = []
var on_done: Callable = func(_data: Variant) -> void: outcome.append("done")
var on_failed: Callable = func() -> void: outcome.append("failed")
for path: String in ["/statistics/summary", "/statistics/soundtracks",
"/statistics/soundtracks/most-played?limit=3",
"/statistics/soundtracks/least-played?limit=3",
"/statistics/soundtracks/last-played?limit=3",
"/statistics/soundtracks/oldest-played?limit=3",
"/statistics/soundtracks/never-played",
"/statistics/songs/most-played?limit=3",
"/statistics/songs/least-played?limit=3"]:
_server.respond_json(path, {"error": "Internal Server Error"}, 500)
_call_every_statistics_endpoint(on_done, on_failed)
assert_array(outcome).contains_exactly(["failed", "failed", "failed", "failed", "failed",
"failed", "failed", "failed", "failed"])
func test_statistics_calls_on_failed_when_token_is_refused() -> void:
_server.respond_json("/token", {"error": "Invalid password"}, 401)
var outcome: Array[String] = []
_call_every_statistics_endpoint(func(_data: Variant) -> void: outcome.append("done"),
func() -> void: outcome.append("failed"))
assert_array(_server.requested_paths).is_empty()
assert_array(outcome).contains_exactly(["failed", "failed", "failed", "failed", "failed",
"failed", "failed", "failed", "failed"])
func _call_every_statistics_endpoint(on_done: Callable, on_failed: Callable) -> void:
MusicServerClient.fetch_statistics_summary(on_done, on_failed)
MusicServerClient.fetch_soundtrack_statistics(on_done, on_failed)
MusicServerClient.fetch_most_played_soundtracks(3, on_done, on_failed)
MusicServerClient.fetch_least_played_soundtracks(3, on_done, on_failed)
MusicServerClient.fetch_last_played_soundtracks(3, on_done, on_failed)
MusicServerClient.fetch_oldest_played_soundtracks(3, on_done, on_failed)
MusicServerClient.fetch_never_played_soundtracks(on_done, on_failed)
MusicServerClient.fetch_most_played_songs(3, on_done, on_failed)
MusicServerClient.fetch_least_played_songs(3, on_done, on_failed)
# Queue IDs
func test_no_queue_id_is_sent_before_the_server_sends_one() -> void:
_server.respond_json("/music/list", [])
MusicServerClient.fetch_song_list(func(_data: Array[SongInfo]) -> void: pass)
assert_array(_server.requested_headers[0]) \
.contains_exactly(["Authorization: Bearer fake-token-1"])
func test_queue_id_from_a_response_is_sent_with_the_next_requests() -> void:
_server.respond_json("/music/list", [])
_server.respond_bytes("/music/addQue", PackedByteArray())
_server.queue_id = "queue-1"
MusicServerClient.fetch_song_list(func(_data: Array[SongInfo]) -> void: pass)
MusicServerClient.add_to_queue(func() -> void: pass)
assert_str(MusicServerClient.queue_id()).is_equal("queue-1")
assert_array(_server.requested_headers[1]) \
.contains_exactly(["Authorization: Bearer fake-token-1", "X-Queue-Id: queue-1"])
DirAccess.remove_absolute(Settings.saved_settings_path())
func test_new_queue_id_replaces_the_old_one() -> void:
_server.respond_bytes("/music/reset", PackedByteArray())
_server.respond_json("/music/list", [])
_server.queue_id = "queue-1"
MusicServerClient.fetch_song_list(func(_data: Array[SongInfo]) -> void: pass)
_server.queue_id = "queue-2"
MusicServerClient.reset_playlist(func() -> void: pass)
MusicServerClient.fetch_song_list(func(_data: Array[SongInfo]) -> void: pass)
assert_array(_server.requested_headers[1]).contains(["X-Queue-Id: queue-1"])
assert_array(_server.requested_headers[2]).contains(["X-Queue-Id: queue-2"])
DirAccess.remove_absolute(Settings.saved_settings_path())
func test_queue_id_is_kept_per_server() -> void:
var previous_path: String = Settings.default_path
_server.respond_json("/music/list", [])
_server.queue_id = "queue-1"
MusicServerClient.fetch_song_list(func(_data: Array[SongInfo]) -> void: pass)
_server.queue_id = ""
Settings.default_path = "http://another-server:8080"
MusicServerClient.fetch_song_list(func(_data: Array[SongInfo]) -> void: pass)
var other_server_id: String = MusicServerClient.queue_id()
Settings.default_path = previous_path
assert_str(other_server_id).is_empty()
assert_array(_server.requested_headers[1]) \
.not_contains(["X-Queue-Id: queue-1"])
assert_str(MusicServerClient.queue_id()).is_equal("queue-1")
DirAccess.remove_absolute(Settings.saved_settings_path())
func test_queue_id_is_saved_for_the_next_start() -> void:
_server.respond_json("/music/list", [])
_server.queue_id = "queue-1"
MusicServerClient.fetch_song_list(func(_data: Array[SongInfo]) -> void: pass)
Settings.queue_ids.clear()
assert_str(MusicServerClient.queue_id()).is_empty()
Settings.load_saved_settings()
assert_str(MusicServerClient.queue_id()).is_equal("queue-1")
DirAccess.remove_absolute(Settings.saved_settings_path())
func test_queue_id_header_name_is_case_insensitive() -> void:
var transport: _HeaderTransport = _HeaderTransport.new(["x-queue-id: lower-case"])
var previous_transport: HttpTransport = MusicServerClient.transport
MusicServerClient.transport = transport
MusicServerClient.fetch_song_list(func(_data: Array[SongInfo]) -> void: pass)
MusicServerClient.transport = previous_transport
assert_str(MusicServerClient.queue_id()).is_equal("lower-case")
DirAccess.remove_absolute(Settings.saved_settings_path())
# Answers every request with 200, an empty JSON list and the given headers
class _HeaderTransport:
extends HttpTransport
var _headers: PackedStringArray
func _init(headers: PackedStringArray) -> void:
super(null)
_headers = headers
func request(
_url: String,
_method: int,
on_complete: Callable,
_request_headers: PackedStringArray = PackedStringArray(),
_body: String = "",
) -> void:
on_complete.call(200, JSON.stringify({"token": "t"} if _url.ends_with("/token") else [])
.to_utf8_buffer(), _headers)
class _RecordingTransport:
extends HttpTransport
var _urls: Array[String]
func _init(urls: Array[String]) -> void:
super(null)
_urls = urls
func request(
url: String,
_method: int,
on_complete: Callable,
_headers: PackedStringArray = PackedStringArray(),
_body: String = "",
) -> void:
_urls.append(url)
if url.ends_with("/token"):
on_complete.call(200, JSON.stringify({"token": "t"}).to_utf8_buffer(),
PackedStringArray())