/**
* Unit tests for detect_ai_music tool
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { detectAiMusicTool, handleDetectAiMusic } from '../../../src/tools/detect-ai-music.js';
// Mock the http module
vi.mock('../../../src/utils/http.js', () => ({
httpPost: vi.fn(),
buildApiUrl: vi.fn((endpoint) => `https://api.ircamamplify.io/v1${endpoint}`),
}));
// Mock auth
vi.mock('../../../src/utils/auth.js', () => ({
getApiKey: vi.fn(() => 'test-api-key'),
hasApiKey: vi.fn(() => true),
createAuthHeaders: vi.fn(() => ({
Authorization: 'Bearer test-api-key',
'Content-Type': 'application/json',
})),
API_KEY_ENV_VAR: 'IRCAM_AMPLIFY_API_KEY',
}));
import { httpPost } from '../../../src/utils/http.js';
describe('detect_ai_music tool', () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
vi.resetAllMocks();
});
describe('tool definition', () => {
it('should have correct name', () => {
expect(detectAiMusicTool.name).toBe('detect_ai_music');
});
it('should have description about AI detection', () => {
expect(detectAiMusicTool.description).toBeTruthy();
expect(detectAiMusicTool.description).toContain('AI');
expect(detectAiMusicTool.description).toContain('confidence');
});
it('should require audio_url parameter', () => {
expect(detectAiMusicTool.inputSchema).toBeDefined();
expect(detectAiMusicTool.inputSchema.required).toContain('audio_url');
});
});
describe('handleDetectAiMusic', () => {
it('should classify as ai_generated for high AI probability', async () => {
const mockResponse = {
ok: true,
status: 200,
data: {
result: {
ai_probability: 85,
human_probability: 15,
classification: 'ai',
},
},
};
vi.mocked(httpPost).mockResolvedValue(mockResponse);
const result = await handleDetectAiMusic({
audio_url: 'https://example.com/ai-song.mp3',
});
expect(result.classification).toBe('ai_generated');
expect(result.confidence).toBe(85);
});
it('should classify as human_made for low AI probability', async () => {
const mockResponse = {
ok: true,
status: 200,
data: {
result: {
ai_probability: 20,
human_probability: 80,
classification: 'human',
},
},
};
vi.mocked(httpPost).mockResolvedValue(mockResponse);
const result = await handleDetectAiMusic({
audio_url: 'https://example.com/human-song.mp3',
});
expect(result.classification).toBe('human_made');
expect(result.confidence).toBe(80);
});
it('should classify as uncertain for mid-range probability', async () => {
const mockResponse = {
ok: true,
status: 200,
data: {
result: {
ai_probability: 50,
human_probability: 50,
classification: 'uncertain',
},
},
};
vi.mocked(httpPost).mockResolvedValue(mockResponse);
const result = await handleDetectAiMusic({
audio_url: 'https://example.com/ambiguous.mp3',
});
expect(result.classification).toBe('uncertain');
expect(result.confidence).toBe(50);
});
it('should use 70% threshold for ai_generated', async () => {
const mockResponse = {
ok: true,
status: 200,
data: {
result: {
ai_probability: 70,
human_probability: 30,
classification: 'ai',
},
},
};
vi.mocked(httpPost).mockResolvedValue(mockResponse);
const result = await handleDetectAiMusic({
audio_url: 'https://example.com/test.mp3',
});
expect(result.classification).toBe('ai_generated');
});
it('should use 30% threshold for human_made', async () => {
const mockResponse = {
ok: true,
status: 200,
data: {
result: {
ai_probability: 30,
human_probability: 70,
classification: 'human',
},
},
};
vi.mocked(httpPost).mockResolvedValue(mockResponse);
const result = await handleDetectAiMusic({
audio_url: 'https://example.com/test.mp3',
});
expect(result.classification).toBe('human_made');
});
it('should throw error for invalid URL', async () => {
await expect(
handleDetectAiMusic({ audio_url: 'not-a-url' })
).rejects.toMatchObject({
code: 'INVALID_URL',
});
});
it('should throw error when API returns error', async () => {
const mockError = {
ok: false,
status: 400,
error: {
code: 'UNSUPPORTED_FORMAT',
message: 'Unsupported format',
suggestion: 'Use supported format',
},
};
vi.mocked(httpPost).mockResolvedValue(mockError);
await expect(
handleDetectAiMusic({ audio_url: 'https://example.com/song.aac' })
).rejects.toMatchObject({
code: 'UNSUPPORTED_FORMAT',
});
});
it('should call API with audio URL in body', async () => {
const mockResponse = {
ok: true,
status: 200,
data: {
result: {
ai_probability: 50,
human_probability: 50,
classification: 'uncertain',
},
},
};
vi.mocked(httpPost).mockResolvedValue(mockResponse);
await handleDetectAiMusic({
audio_url: 'https://example.com/test.mp3',
});
// Verify httpPost was called with audio URL in body
expect(httpPost).toHaveBeenCalled();
const calls = vi.mocked(httpPost).mock.calls;
expect(calls[0][1]).toEqual({ url: 'https://example.com/test.mp3' });
});
});
});