diff --git a/Tests/MusicAssistantCoreTests/InstrumentSelectionPersistenceTests.swift b/Tests/MusicAssistantCoreTests/InstrumentSelectionPersistenceTests.swift new file mode 100644 index 0000000..5f6bc1d --- /dev/null +++ b/Tests/MusicAssistantCoreTests/InstrumentSelectionPersistenceTests.swift @@ -0,0 +1,65 @@ +import Foundation +import MusicAssistantCore +import XCTest + +final class InstrumentSelectionPersistenceTests: XCTestCase { + func testSelectedInstrumentsRoundTripThroughSongProjectSerialization() throws { + let section = SongSection(id: "intro", type: .intro, title: "Intro") + let project = SongProject( + id: "serialized-instrument-selection", + title: "Serialized Instrument Selection", + idea: "Preserve selected instruments", + sections: [section], + instruments: [ + InstrumentTrack( + instrumentId: "oud", + selected: true, + variant: "Arabic Oud", + playingStyle: "tremolo", + role: "lead motif", + autoArrangementEnabled: false, + placements: [InstrumentPlacement(sectionId: section.id, direction: "Opening")] + ), + InstrumentTrack( + instrumentId: "violin", + selected: false, + variant: "Violin" + ) + ] + ) + + let encodedProject = try JSONEncoder().encode(project) + let decodedProject = try JSONDecoder().decode(SongProject.self, from: encodedProject) + + XCTAssertEqual(decodedProject.instruments, project.instruments) + XCTAssertEqual(decodedProject.selectedInstrumentIDs, ["oud"]) + XCTAssertEqual(decodedProject.instrumentTrack(for: "oud")?.placements.first?.sectionId, section.id) + } + + func testSelectedInstrumentsPersistWhenProjectIsSavedAndReopened() async throws { + let directoryURL = FileManager.default.temporaryDirectory + .appendingPathComponent("music-assistant-instrument-selection-tests") + .appendingPathComponent(UUID().uuidString) + defer { try? FileManager.default.removeItem(at: directoryURL) } + + let store = LocalSongProjectStore(directoryURL: directoryURL) + var project = SongProject( + id: "persisted-instrument-selection", + title: "Persisted Instrument Selection", + idea: "Keep catalog selections" + ) + + project.setInstrumentSelected(id: "oud", isSelected: true, variant: "Oud") + project.setInstrumentSelected(id: "violin", isSelected: true, variant: "Violin") + project.setInstrumentSelected(id: "oud", isSelected: false) + + try await store.create(project) + + let reopenedProject = try await store.open(id: project.id) + + XCTAssertFalse(reopenedProject.isInstrumentSelected(id: "oud")) + XCTAssertTrue(reopenedProject.isInstrumentSelected(id: "violin")) + XCTAssertEqual(reopenedProject.selectedInstrumentIDs, ["violin"]) + XCTAssertEqual(reopenedProject.instrumentTrack(for: "oud")?.variant, "Oud") + } +} diff --git a/docs/TASKS.md b/docs/TASKS.md index 46f1c6e..59a2c43 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -91,7 +91,7 @@ requirement is missing and blocks implementation, record it in logic for arrangement, roles, entry/exit timing, relevant structure decisions and Suno Style Prompt generation. - [x] Preserve existing Manual/Auto arrangement behavior. -- [ ] Add tests for catalog search, category/region filtering, +- [x] Add tests for catalog search, category/region filtering, selection persistence and SongProject serialization. ## Phase 7 --- Prompt Compiler