Files

122 lines
4.8 KiB
Swift

import MusicAssistantCore
import XCTest
final class AIServiceTests: XCTestCase {
func testProviderIndependentServiceGeneratesStructuredProjectResult() async throws {
let service = MockAIService()
let request = SongProjectGenerationRequest(
context: AIRequestContext(
userInstruction: "Write a hopeful cinematic pop song.",
conversation: [
AIConversationMessage(role: .user, content: "Use piano and violin.")
],
localeIdentifier: "en_US"
),
discussionMode: .discuss
)
let result = try await service.generateSongProject(from: request)
XCTAssertEqual(result.project.title, "Generated Song")
XCTAssertEqual(result.project.idea, request.context.userInstruction)
XCTAssertEqual(result.project.conversationMode, .discuss)
XCTAssertEqual(result.notes, ["Generated by a provider-independent mock."])
XCTAssertEqual(result.followUpQuestions, ["Should the chorus be bigger?"])
}
func testProviderIndependentServiceReturnsDiscussionQuestionsWithoutProjectUpdate() async throws {
let service = MockAIService()
let project = SongProject(
title: "Discussion Project",
idea: "Plan the chorus",
conversationMode: .discuss
)
let request = SongProjectDiscussionRequest(
context: AIRequestContext(userInstruction: "Ask what is still missing."),
project: project
)
let result = try await service.discussSongProject(from: request)
XCTAssertEqual(result.questions, ["Which vocal delivery should lead the chorus?"])
XCTAssertEqual(result.notes, ["Discussion only; no project update was proposed."])
}
func testProviderIndependentServiceRevisesLyricsWithStructuredResult() async throws {
let service = MockAIService()
let project = SongProject(title: "Lyric Project", idea: "Improve words")
let sourceLyrics = Lyrics(text: "rough draft")
let request = LyricsRevisionRequest(
context: AIRequestContext(userInstruction: "Make it stronger."),
project: project,
sourceLyrics: sourceLyrics,
mode: .improve
)
let result = try await service.reviseLyrics(from: request)
XCTAssertEqual(result.lyrics.text, "rough draft\nMake it stronger.")
XCTAssertEqual(result.notes, ["improve"])
}
func testProviderIndependentServiceProposesScopedProjectUpdates() async throws {
let service = MockAIService()
let project = SongProject(title: "Scoped Project", idea: "Add tempo")
let request = SongProjectUpdateRequest(
context: AIRequestContext(userInstruction: "Set tempo manually."),
project: project,
allowedScopes: [.musicalParameters]
)
let result = try await service.proposeProjectUpdate(from: request)
XCTAssertEqual(result.project.bpm, ManualAutoValue(mode: .manual, value: 96))
XCTAssertEqual(result.changedScopes, [.musicalParameters])
XCTAssertEqual(result.notes, ["Updated only requested scopes."])
}
}
private struct MockAIService: AIService {
func generateSongProject(from request: SongProjectGenerationRequest) async throws -> SongProjectGenerationResult {
SongProjectGenerationResult(
project: SongProject(
title: "Generated Song",
idea: request.context.userInstruction,
conversationMode: request.discussionMode
),
notes: ["Generated by a provider-independent mock."],
followUpQuestions: ["Should the chorus be bigger?"]
)
}
func discussSongProject(from request: SongProjectDiscussionRequest) async throws -> SongProjectDiscussionResult {
SongProjectDiscussionResult(
questions: ["Which vocal delivery should lead the chorus?"],
notes: ["Discussion only; no project update was proposed."]
)
}
func reviseLyrics(from request: LyricsRevisionRequest) async throws -> LyricsRevisionResult {
LyricsRevisionResult(
lyrics: Lyrics(text: "\(request.sourceLyrics.text)\n\(request.context.userInstruction)"),
notes: [request.mode.rawValue]
)
}
func proposeProjectUpdate(from request: SongProjectUpdateRequest) async throws -> SongProjectUpdateResult {
var project = request.project
var changedScopes: [SongProjectUpdateScope] = []
if request.allowedScopes.contains(.musicalParameters) {
project.setBPMManually(96)
changedScopes.append(.musicalParameters)
}
return SongProjectUpdateResult(
project: project,
changedScopes: changedScopes,
notes: ["Updated only requested scopes."]
)
}
}