Files
MusicPlayer/tests/unit/test_music_server_client.gd
T
Sansan f67dccbf52 Retry failed song downloads instead of getting stuck
MusicServerClient functions now take an optional on_failed callback,
called when a request fails or returns invalid JSON. Before, a failed
request was silently dropped, so a failure while preloading a song left
Playlist.is_song_downloading set forever and no more songs were fetched.

Song fetching retries the step that failed (download, queue or info)
every song_fetch_retry_seconds, so the server and the game keep the same
playlist.
2026-10-06 08:32:56 +02:00

147 lines
4.8 KiB
GDScript

extends GdUnitTestSuite
var _server: FakeHttpTransport
func before_test() -> void:
_server = FakeHttpTransport.new().install()
func after_test() -> void:
_server.uninstall()
func test_fetch_song_list_passes_parsed_json() -> void:
var songs: Array = [{"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time"}]
_server.respond_json("/music/list", songs)
var received: Array = []
MusicServerClient.fetch_song_list(func(data: Array) -> void: received.append_array(data))
assert_int(received.size()).is_equal(1)
assert_int(int(received[0].SongNo)).is_equal(0)
assert_str(received[0].Soundtrack).is_equal("Chrono Trigger")
func test_fetch_random_song_uses_normal_endpoint() -> void:
MusicServerClient.fetch_random_song(false, func(_body: PackedByteArray) -> void: pass)
assert_array(_server.requested_paths).contains_exactly(["/music/rand"])
func test_fetch_random_song_uses_low_played_endpoint() -> void:
MusicServerClient.fetch_random_song(true, func(_body: PackedByteArray) -> void: pass)
assert_array(_server.requested_paths).contains_exactly(["/music/rand/low"])
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: Variant) -> 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(false,
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: Variant) -> 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_requests_go_to_selected_server() -> void:
var previous_path: String = Settings.default_path
Settings.default_path = "http://localhost:8080"
var requested_urls: Array[String] = []
var recording_transport: HttpTransport = _RecordingTransport.new(requested_urls)
MusicServerClient.transport = recording_transport
MusicServerClient.fetch_song_info(func(_data: Variant) -> void: pass)
Settings.default_path = previous_path
assert_array(requested_urls).contains_exactly(["http://localhost:8080/music/info"])
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) -> void:
_urls.append(url)