From 55235ba90d26196daadc873fd60f50a00f443f17 Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 13 Sep 2026 13:50:13 +0200 Subject: [PATCH] Add song project serialization tests --- .../SongProjectModelTests.swift | 105 ++++++++++++++++++ docs/TASKS.md | 2 +- 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/Tests/MusicAssistantCoreTests/SongProjectModelTests.swift b/Tests/MusicAssistantCoreTests/SongProjectModelTests.swift index fa2be4e..5dd15c8 100644 --- a/Tests/MusicAssistantCoreTests/SongProjectModelTests.swift +++ b/Tests/MusicAssistantCoreTests/SongProjectModelTests.swift @@ -1,3 +1,4 @@ +import Foundation import MusicAssistantCore import XCTest @@ -365,4 +366,108 @@ final class SongProjectModelTests: XCTestCase { XCTAssertNil(project.emotionalArcPoint(for: "arc-chorus")) XCTAssertNil(project.productionDirection(for: "prod-verse")) } + + func testSongProjectRoundTripsThroughJSONSerialization() throws { + let createdAt = Date(timeIntervalSince1970: 1_700_000_000) + let updatedAt = Date(timeIntervalSince1970: 1_700_000_120) + let generatedAt = Date(timeIntervalSince1970: 1_700_000_240) + let section = SongSection( + id: "verse", + type: .verse, + title: "Verse", + order: 0, + startTime: 8, + endTime: 38, + lyrics: "Draft verse", + emotion: "reflective", + energy: 4, + vocalDirection: "close vocal", + productionDirection: "subtle pad" + ) + let instrument = InstrumentTrack( + instrumentId: "oud", + selected: true, + variant: "Arabic oud", + playingStyle: "picked", + role: "lead motif", + autoArrangementEnabled: false, + placements: [ + InstrumentPlacement( + sectionId: section.id, + startTime: section.startTime, + endTime: section.endTime, + direction: "answer the vocal" + ) + ] + ) + var project = SongProject( + id: "serialized-project", + title: "Serializable Song", + idea: "A multilingual cinematic song", + duration: SongDuration(seconds: 210, description: "about three and a half minutes"), + conversationMode: .discuss, + languages: [LanguageSelection(id: "arabic", name: "Arabic")], + dialects: [DialectSelection(id: "levantine", name: "Levantine", languageID: "arabic")], + arabicPronunciationSettings: ArabicPronunciationSettings( + isEnabled: true, + diacritizationPolicy: .pronunciationTargeted, + tanweenPolicy: .preserveUserInput, + userNotes: "Keep dialect phrasing natural", + preservedSpellings: ["habibi"] + ), + genres: [GenreStyle(id: "cinematic-pop", name: "Cinematic Pop", aliases: ["orchestral pop"])], + moods: [MoodTag(id: "yearning", name: "Yearning")], + emotionalArc: [ + EmotionalArcPoint( + id: "arc-verse", + sectionID: section.id, + emotion: "reflective", + energy: 4, + direction: "slowly opening" + ) + ], + bpm: ManualAutoValue(mode: .manual, value: 92), + key: ManualAutoValue(mode: .manual, value: "D"), + scale: ManualAutoValue(mode: .auto), + maqam: ManualAutoValue(mode: .manual, value: "Hijaz"), + sections: [section], + instruments: [instrument], + vocalists: [ + Vocalist( + id: "lead", + label: "Lead", + voiceType: "tenor", + genderSelection: "male", + performanceStyle: "sung", + assignedSections: [section.id] + ) + ], + lyrics: Lyrics(text: "Full lyric draft", sectionTexts: [section.id: "Draft verse"]), + productionDirections: [ + ProductionDirection(id: "prod-verse", sectionID: section.id, text: "keep percussion sparse") + ], + sunoOutput: SunoOutput( + lyricsText: "Full lyric draft", + stylePrompt: "Cinematic pop with oud", + additionalFields: ["style": "cinematic"], + generatedAt: generatedAt + ), + createdAt: createdAt, + updatedAt: updatedAt + ) + project.versions = [ + SongVersion( + id: "version-1", + label: "Initial approved direction", + snapshot: SongProjectSnapshot(project: project), + createdAt: updatedAt + ) + ] + + let encoder = JSONEncoder() + let data = try encoder.encode(project) + let decodedProject = try JSONDecoder().decode(SongProject.self, from: data) + + XCTAssertEqual(decodedProject, project) + } } diff --git a/docs/TASKS.md b/docs/TASKS.md index 92fa2c9..0af7183 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -28,7 +28,7 @@ requirement is missing and blocks implementation, record it in - [x] Model language/dialect and Arabic pronunciation settings. - [x] Model BPM, key, scale and maqam with Manual/Auto state. - [x] Model emotional arc and production directions. -- [ ] Add serialization tests. +- [x] Add serialization tests. ## Phase 2 --- Project Persistence & Versions