import Foundation import Testing @testable import MusicShared struct RemoteProtocolTests { private let encoder: JSONEncoder = { let e = JSONEncoder() e.outputFormatting = [.sortedKeys] return e }() private let decoder = JSONDecoder() // MARK: - Helpers /// Encode then decode a value, returning the decoded copy. private func roundTrip(_ value: T) throws -> T { let data = try encoder.encode(value) return try decoder.decode(T.self, from: data) } // MARK: - RemoteCommand round-trip tests // Each test encodes a RemoteCommand case to JSON and decodes it back, // verifying the decoded value equals the original. @Test func remoteCommandRoundTrip_play() throws { let cmd = RemoteCommand.play(trackId: 42, queueIds: [42, 43, 44, 45]) #expect(try roundTrip(cmd) == cmd) } @Test func remoteCommandRoundTrip_pause() throws { let cmd = RemoteCommand.pause #expect(try roundTrip(cmd) == cmd) } @Test func remoteCommandRoundTrip_resume() throws { let cmd = RemoteCommand.resume #expect(try roundTrip(cmd) == cmd) } @Test func remoteCommandRoundTrip_next() throws { let cmd = RemoteCommand.next #expect(try roundTrip(cmd) == cmd) } @Test func remoteCommandRoundTrip_previous() throws { let cmd = RemoteCommand.previous #expect(try roundTrip(cmd) == cmd) } @Test func remoteCommandRoundTrip_seek() throws { let cmd = RemoteCommand.seek(position: 123.456) #expect(try roundTrip(cmd) == cmd) } @Test func remoteCommandRoundTrip_setVolume() throws { let cmd = RemoteCommand.setVolume(level: 0.75) #expect(try roundTrip(cmd) == cmd) } @Test func remoteCommandRoundTrip_toggleShuffle() throws { let cmd = RemoteCommand.toggleShuffle #expect(try roundTrip(cmd) == cmd) } @Test func remoteCommandRoundTrip_refreshDB() throws { let cmd = RemoteCommand.refreshDB #expect(try roundTrip(cmd) == cmd) } // MARK: - HostEvent round-trip tests // Each test encodes a HostEvent case to JSON and decodes it back, // verifying the decoded value equals the original. @Test func hostEventRoundTrip_playbackState() throws { let payload = PlaybackStatePayload( trackId: 7, isPlaying: true, currentTime: 42.5, duration: 210.0, volume: 0.8, isShuffled: false ) let event = HostEvent.playbackState(payload) #expect(try roundTrip(event) == event) } @Test func hostEventRoundTrip_dbReady() throws { let event = HostEvent.dbReady #expect(try roundTrip(event) == event) } @Test func hostEventRoundTrip_error() throws { let event = HostEvent.error(message: "Something went wrong") #expect(try roundTrip(event) == event) } // MARK: - HandshakeMessage round-trip test // Verifies HandshakeMessage survives JSON encoding and decoding. @Test func handshakeMessageRoundTrip() throws { let msg = HandshakeMessage(protocolVersion: RemoteProtocolVersion, appVersion: "1.2.3") #expect(try roundTrip(msg) == msg) } // MARK: - Wire format decode tests // Verify that hand-crafted JSON strings matching the expected wire format // decode correctly, ensuring the Codable implementation matches the spec. @Test func wireFormatDecode_playCommand() throws { let json = """ {"type":"play","payload":{"trackId":42,"queueIds":[42,43,44,45]}} """ let decoded = try decoder.decode(RemoteCommand.self, from: Data(json.utf8)) #expect(decoded == .play(trackId: 42, queueIds: [42, 43, 44, 45])) } @Test func wireFormatDecode_playbackStateEvent() throws { let json = """ {"type":"playbackState","payload":{"trackId":7,"isPlaying":true,"currentTime":42.5,"duration":210.0,"volume":0.8,"isShuffled":false}} """ let decoded = try decoder.decode(HostEvent.self, from: Data(json.utf8)) let expected = HostEvent.playbackState(PlaybackStatePayload( trackId: 7, isPlaying: true, currentTime: 42.5, duration: 210.0, volume: 0.8, isShuffled: false )) #expect(decoded == expected) } }