95 lines
3.8 KiB
Swift
95 lines
3.8 KiB
Swift
import MusicAssistantCore
|
|
import XCTest
|
|
|
|
final class SongProjectDiscussionDirectorTests: XCTestCase {
|
|
func testDiscussBuildsARequestAndReturnsTrimmedQuestionsWithoutChangingProject() async throws {
|
|
let project = SongProject(
|
|
id: "discussion-project",
|
|
title: "Discussion Project",
|
|
idea: "Plan a cinematic chorus",
|
|
conversationMode: .discuss,
|
|
createdAt: Date(timeIntervalSince1970: 10),
|
|
updatedAt: Date(timeIntervalSince1970: 20)
|
|
)
|
|
let aiService = RecordingDiscussionAIService(
|
|
result: SongProjectDiscussionResult(
|
|
questions: [" Should the chorus use a choir? ", " "],
|
|
notes: ["Awaiting the user's choice."]
|
|
)
|
|
)
|
|
let director = SongProjectDiscussionDirector(aiService: aiService)
|
|
let conversation = [AIConversationMessage(role: .assistant, content: "What mood should lead?")]
|
|
|
|
let result = try await director.discuss(
|
|
project: project,
|
|
userMessage: " Keep the verses intimate. ",
|
|
conversation: conversation,
|
|
localeIdentifier: "en_US"
|
|
)
|
|
let request = await aiService.recordedRequests.first
|
|
|
|
XCTAssertEqual(request?.context.userInstruction, "Keep the verses intimate.")
|
|
XCTAssertEqual(request?.context.conversation, conversation)
|
|
XCTAssertEqual(request?.context.localeIdentifier, "en_US")
|
|
XCTAssertEqual(request?.project, project)
|
|
XCTAssertEqual(result.questions, ["Should the chorus use a choir?"])
|
|
XCTAssertEqual(result.notes, ["Awaiting the user's choice."])
|
|
}
|
|
|
|
func testDiscussRejectsAutoModeAndBlankMessagesBeforeCallingAI() async throws {
|
|
let aiService = RecordingDiscussionAIService(
|
|
result: SongProjectDiscussionResult(questions: ["Unused"])
|
|
)
|
|
let director = SongProjectDiscussionDirector(aiService: aiService)
|
|
|
|
do {
|
|
_ = try await director.discuss(
|
|
project: SongProject(title: "Auto", idea: "Auto", conversationMode: .auto),
|
|
userMessage: "Ask a question."
|
|
)
|
|
XCTFail("Expected discussion mode validation to fail.")
|
|
} catch let error as SongProjectDiscussionDirectorError {
|
|
XCTAssertEqual(error, .discussionModeNotEnabled)
|
|
}
|
|
|
|
do {
|
|
_ = try await director.discuss(
|
|
project: SongProject(title: "Discuss", idea: "Discuss", conversationMode: .discuss),
|
|
userMessage: " \n "
|
|
)
|
|
XCTFail("Expected blank message validation to fail.")
|
|
} catch let error as SongProjectDiscussionDirectorError {
|
|
XCTAssertEqual(error, .emptyUserMessage)
|
|
}
|
|
|
|
let requestCount = await aiService.recordedRequests.count
|
|
XCTAssertEqual(requestCount, 0)
|
|
}
|
|
}
|
|
|
|
private actor RecordingDiscussionAIService: AIService {
|
|
private(set) var recordedRequests: [SongProjectDiscussionRequest] = []
|
|
private let result: SongProjectDiscussionResult
|
|
|
|
init(result: SongProjectDiscussionResult) {
|
|
self.result = result
|
|
}
|
|
|
|
func generateSongProject(from request: SongProjectGenerationRequest) async throws -> SongProjectGenerationResult {
|
|
SongProjectGenerationResult(project: request.seedProject ?? SongProject(title: "Unused", idea: "Unused"))
|
|
}
|
|
|
|
func discussSongProject(from request: SongProjectDiscussionRequest) async throws -> SongProjectDiscussionResult {
|
|
recordedRequests.append(request)
|
|
return result
|
|
}
|
|
|
|
func reviseLyrics(from request: LyricsRevisionRequest) async throws -> LyricsRevisionResult {
|
|
LyricsRevisionResult(lyrics: request.sourceLyrics)
|
|
}
|
|
|
|
func proposeProjectUpdate(from request: SongProjectUpdateRequest) async throws -> SongProjectUpdateResult {
|
|
SongProjectUpdateResult(project: request.project)
|
|
}
|
|
}
|