Skip to content

Commit c388d5e

Browse files
NianJiuZstclaude
andcommitted
test: add unit tests for music cover command
Cover validation rules, request construction, format acceptance, option flags, and examples. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b9a3a23 commit c388d5e

1 file changed

Lines changed: 205 additions & 0 deletions

File tree

test/commands/music/cover.test.ts

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import { describe, it, expect } from 'bun:test';
2+
import { default as coverCommand } from '../../../src/commands/music/cover';
3+
4+
const baseConfig = {
5+
apiKey: 'test-key',
6+
region: 'global' as const,
7+
baseUrl: 'https://api.mmx.io',
8+
output: 'text' as const,
9+
timeout: 10,
10+
verbose: false,
11+
quiet: false,
12+
noColor: true,
13+
yes: false,
14+
dryRun: false,
15+
nonInteractive: true,
16+
async: false,
17+
};
18+
19+
const baseFlags = {
20+
quiet: false,
21+
verbose: false,
22+
noColor: true,
23+
yes: false,
24+
dryRun: false,
25+
help: false,
26+
nonInteractive: true,
27+
async: false,
28+
};
29+
30+
describe('music cover command', () => {
31+
it('has correct name', () => {
32+
expect(coverCommand.name).toBe('music cover');
33+
});
34+
35+
it('requires --prompt', async () => {
36+
await expect(
37+
coverCommand.execute(baseConfig, baseFlags),
38+
).rejects.toThrow('--prompt is required');
39+
});
40+
41+
it('requires either --audio or --audio-file', async () => {
42+
await expect(
43+
coverCommand.execute(
44+
baseConfig,
45+
{ ...baseFlags, prompt: 'Indie folk cover' },
46+
),
47+
).rejects.toThrow('One of --audio <url> or --audio-file <path> is required');
48+
});
49+
50+
it('rejects using both --audio and --audio-file', async () => {
51+
await expect(
52+
coverCommand.execute(
53+
baseConfig,
54+
{ ...baseFlags, prompt: 'Indie folk', audio: 'https://example.com/song.mp3', audioFile: '/tmp/ref.mp3' },
55+
),
56+
).rejects.toThrow('Use either --audio or --audio-file, not both');
57+
});
58+
59+
it('builds correct request body in dry-run with --audio', async () => {
60+
let captured = '';
61+
const origLog = console.log;
62+
console.log = (msg: string) => { captured += msg; };
63+
64+
try {
65+
await coverCommand.execute(
66+
{ ...baseConfig, dryRun: true, output: 'json' as const },
67+
{
68+
...baseFlags,
69+
dryRun: true,
70+
prompt: 'Jazz cover',
71+
audio: 'https://example.com/ref.mp3',
72+
},
73+
);
74+
} catch {
75+
// dry-run may resolve or reject
76+
}
77+
78+
console.log = origLog;
79+
const parsed = JSON.parse(captured);
80+
expect(parsed.request.model).toMatch(/^music-cover/);
81+
expect(parsed.request.prompt).toBe('Jazz cover');
82+
expect(parsed.request.audio_url).toBe('https://example.com/ref.mp3');
83+
expect(parsed.request.output_format).toBe('hex');
84+
});
85+
86+
it('accepts optional --lyrics', async () => {
87+
let captured = '';
88+
const origLog = console.log;
89+
console.log = (msg: string) => { captured += msg; };
90+
91+
try {
92+
await coverCommand.execute(
93+
{ ...baseConfig, dryRun: true, output: 'json' as const },
94+
{
95+
...baseFlags,
96+
dryRun: true,
97+
prompt: 'Pop cover',
98+
audio: 'https://example.com/ref.mp3',
99+
lyrics: 'New lyrics here',
100+
},
101+
);
102+
} catch {
103+
// dry-run may resolve or reject
104+
}
105+
106+
console.log = origLog;
107+
const parsed = JSON.parse(captured);
108+
expect(parsed.request.lyrics).toBe('New lyrics here');
109+
});
110+
111+
it('accepts optional --seed', async () => {
112+
let captured = '';
113+
const origLog = console.log;
114+
console.log = (msg: string) => { captured += msg; };
115+
116+
try {
117+
await coverCommand.execute(
118+
{ ...baseConfig, dryRun: true, output: 'json' as const },
119+
{
120+
...baseFlags,
121+
dryRun: true,
122+
prompt: 'Rock cover',
123+
audio: 'https://example.com/ref.mp3',
124+
seed: 42,
125+
},
126+
);
127+
} catch {
128+
// dry-run may resolve or reject
129+
}
130+
131+
console.log = origLog;
132+
const parsed = JSON.parse(captured);
133+
expect(parsed.request.seed).toBe(42);
134+
});
135+
136+
it('rejects invalid model', async () => {
137+
await expect(
138+
coverCommand.execute(
139+
{ ...baseConfig, dryRun: true },
140+
{
141+
...baseFlags,
142+
prompt: 'Folk cover',
143+
audio: 'https://example.com/ref.mp3',
144+
model: 'music-2.6',
145+
},
146+
),
147+
).rejects.toThrow('Invalid model');
148+
});
149+
150+
it('rejects invalid audio format', async () => {
151+
await expect(
152+
coverCommand.execute(
153+
{ ...baseConfig, dryRun: true },
154+
{
155+
...baseFlags,
156+
prompt: 'Folk cover',
157+
audio: 'https://example.com/ref.mp3',
158+
format: 'opus',
159+
},
160+
),
161+
).rejects.toThrow('Invalid audio format');
162+
});
163+
164+
it.each(['mp3', 'wav', 'pcm'])(
165+
'accepts %s format in dry-run',
166+
async (fmt) => {
167+
let captured = '';
168+
const origLog = console.log;
169+
console.log = (msg: string) => { captured += msg; };
170+
try {
171+
await coverCommand.execute(
172+
{ ...baseConfig, dryRun: true, output: 'json' as const },
173+
{
174+
...baseFlags,
175+
dryRun: true,
176+
prompt: 'Folk cover',
177+
audio: 'https://example.com/ref.mp3',
178+
format: fmt,
179+
},
180+
);
181+
const parsed = JSON.parse(captured);
182+
expect(parsed.request.audio_setting.format).toBe(fmt);
183+
} finally {
184+
console.log = origLog;
185+
}
186+
},
187+
);
188+
189+
it('has expected options', () => {
190+
const flags = coverCommand.options?.map(o => o.flag) ?? [];
191+
expect(flags.some(f => f.startsWith('--audio'))).toBe(true);
192+
expect(flags.some(f => f.startsWith('--audio-file'))).toBe(true);
193+
expect(flags.some(f => f.startsWith('--lyrics'))).toBe(true);
194+
expect(flags.some(f => f.startsWith('--seed'))).toBe(true);
195+
expect(flags.some(f => f.startsWith('--format'))).toBe(true);
196+
expect(flags.some(f => f.startsWith('--out'))).toBe(true);
197+
});
198+
199+
it('has examples with --audio and --audio-file usage', () => {
200+
const examples = coverCommand.examples ?? [];
201+
const joined = examples.join(' ');
202+
expect(joined).toContain('--audio');
203+
expect(joined).toContain('--audio-file');
204+
});
205+
});

0 commit comments

Comments
 (0)