mirror of
https://github.com/MiniMax-AI/cli.git
synced 2026-09-18 08:48:06 +08:00
294 lines
8.2 KiB
TypeScript
294 lines
8.2 KiB
TypeScript
import { describe, it, expect } from 'bun:test';
|
|
import { renderQuotaTable } from '../../src/output/quota-table';
|
|
import type { Config } from '../../src/config/schema';
|
|
import type { QuotaModelRemain } from '../../src/types/api';
|
|
|
|
const WHITE_ANSI = '\x1b[38;2;255;255;255m';
|
|
|
|
function createConfig(): Config {
|
|
return {
|
|
region: 'global',
|
|
baseUrl: 'https://api.minimax.io',
|
|
output: 'text',
|
|
timeout: 10_000,
|
|
verbose: false,
|
|
quiet: false,
|
|
noColor: false,
|
|
yes: false,
|
|
dryRun: false,
|
|
nonInteractive: true,
|
|
async: false,
|
|
};
|
|
}
|
|
|
|
function createModel(): QuotaModelRemain {
|
|
return {
|
|
model_name: 'MiniMax-M2',
|
|
start_time: Date.UTC(2026, 3, 18, 0, 0, 0),
|
|
end_time: Date.UTC(2026, 3, 18, 12, 0, 0),
|
|
remains_time: 3 * 60 * 60 * 1000,
|
|
current_interval_total_count: 1500,
|
|
current_interval_usage_count: 80,
|
|
current_weekly_total_count: 15000,
|
|
current_weekly_usage_count: 666,
|
|
weekly_start_time: Date.UTC(2026, 3, 12, 0, 0, 0),
|
|
weekly_end_time: Date.UTC(2026, 3, 19, 0, 0, 0),
|
|
weekly_remains_time: 3 * 60 * 60 * 1000,
|
|
};
|
|
}
|
|
|
|
function createCodingPlanModels(): QuotaModelRemain[] {
|
|
return [
|
|
{
|
|
model_name: 'general',
|
|
start_time: Date.UTC(2026, 4, 31, 0, 0, 0),
|
|
end_time: Date.UTC(2026, 4, 31, 2, 0, 0),
|
|
remains_time: 2 * 60 * 60 * 1000,
|
|
current_interval_total_count: 0,
|
|
current_interval_usage_count: 0,
|
|
current_interval_remaining_percent: 94,
|
|
current_weekly_total_count: 0,
|
|
current_weekly_usage_count: 0,
|
|
current_weekly_remaining_percent: 98,
|
|
weekly_start_time: Date.UTC(2026, 4, 31, 0, 0, 0),
|
|
weekly_end_time: Date.UTC(2026, 5, 7, 0, 0, 0),
|
|
weekly_remains_time: 6 * 24 * 60 * 60 * 1000,
|
|
},
|
|
{
|
|
model_name: 'video',
|
|
start_time: Date.UTC(2026, 4, 31, 0, 0, 0),
|
|
end_time: Date.UTC(2026, 5, 1, 0, 0, 0),
|
|
remains_time: 6 * 60 * 60 * 1000,
|
|
current_interval_total_count: 3,
|
|
current_interval_usage_count: 3,
|
|
current_interval_remaining_percent: 100,
|
|
current_weekly_total_count: 21,
|
|
current_weekly_usage_count: 21,
|
|
current_weekly_remaining_percent: 100,
|
|
weekly_start_time: Date.UTC(2026, 4, 31, 0, 0, 0),
|
|
weekly_end_time: Date.UTC(2026, 5, 7, 0, 0, 0),
|
|
weekly_remains_time: 6 * 24 * 60 * 60 * 1000,
|
|
},
|
|
];
|
|
}
|
|
|
|
describe('renderQuotaTable', () => {
|
|
it('does not force model names to white in color mode', () => {
|
|
const lines: string[] = [];
|
|
const originalLog = console.log;
|
|
const ttyDescriptor = Object.getOwnPropertyDescriptor(process.stdout, 'isTTY');
|
|
|
|
console.log = (message?: unknown) => {
|
|
lines.push(String(message ?? ''));
|
|
};
|
|
Object.defineProperty(process.stdout, 'isTTY', {
|
|
value: true,
|
|
configurable: true,
|
|
});
|
|
|
|
try {
|
|
renderQuotaTable([createModel()], createConfig());
|
|
} finally {
|
|
console.log = originalLog;
|
|
if (ttyDescriptor) {
|
|
Object.defineProperty(process.stdout, 'isTTY', ttyDescriptor);
|
|
}
|
|
}
|
|
|
|
const output = lines.join('\n');
|
|
|
|
expect(output).toContain('MiniMax-M2');
|
|
expect(output).not.toContain(WHITE_ANSI);
|
|
});
|
|
|
|
it('renders coding plan remaining quotas without deriving counts from percent', () => {
|
|
const lines: string[] = [];
|
|
const originalLog = console.log;
|
|
|
|
console.log = (message?: unknown) => {
|
|
lines.push(String(message ?? ''));
|
|
};
|
|
|
|
try {
|
|
renderQuotaTable(createCodingPlanModels(), {
|
|
...createConfig(),
|
|
region: 'cn',
|
|
noColor: true,
|
|
});
|
|
} finally {
|
|
console.log = originalLog;
|
|
}
|
|
|
|
const output = lines.join('\n');
|
|
|
|
expect(output).toContain('通用');
|
|
expect(output).toContain('剩余 [█████████.] 94%');
|
|
expect(output).toContain('周剩余 [██████████] 98%');
|
|
expect(output).toContain('视频');
|
|
expect(output).toContain('3 / 3');
|
|
expect(output).toContain('21 / 21');
|
|
expect(output).not.toContain('0 / 3');
|
|
});
|
|
|
|
it('applies weekly_boost_permille (1500 ⇒ up to 150%) when rendering weekly percent', () => {
|
|
const lines: string[] = [];
|
|
const originalLog = console.log;
|
|
|
|
console.log = (message?: unknown) => {
|
|
lines.push(String(message ?? ''));
|
|
};
|
|
|
|
try {
|
|
renderQuotaTable(
|
|
[
|
|
{
|
|
...createModel(),
|
|
current_weekly_total_count: 0,
|
|
current_weekly_usage_count: 0,
|
|
current_weekly_remaining_percent: 100,
|
|
weekly_boost_permille: 1500,
|
|
},
|
|
],
|
|
{ ...createConfig(), noColor: true },
|
|
);
|
|
} finally {
|
|
console.log = originalLog;
|
|
}
|
|
|
|
const output = lines.join('\n');
|
|
expect(output).toContain('Wk left [██████████] 150%');
|
|
});
|
|
|
|
it('clamps boosted weekly percent at MAX_DISPLAY_PCT (200)', () => {
|
|
const lines: string[] = [];
|
|
const originalLog = console.log;
|
|
|
|
console.log = (message?: unknown) => {
|
|
lines.push(String(message ?? ''));
|
|
};
|
|
|
|
try {
|
|
renderQuotaTable(
|
|
[
|
|
{
|
|
...createModel(),
|
|
current_weekly_total_count: 0,
|
|
current_weekly_usage_count: 0,
|
|
current_weekly_remaining_percent: 100,
|
|
weekly_boost_permille: 3000,
|
|
},
|
|
],
|
|
{ ...createConfig(), noColor: true },
|
|
);
|
|
} finally {
|
|
console.log = originalLog;
|
|
}
|
|
|
|
const output = lines.join('\n');
|
|
expect(output).toContain('200%');
|
|
expect(output).not.toContain('300%');
|
|
});
|
|
|
|
it('renders "无限" for weekly when status=3 (CN region)', () => {
|
|
const lines: string[] = [];
|
|
const originalLog = console.log;
|
|
|
|
console.log = (message?: unknown) => {
|
|
lines.push(String(message ?? ''));
|
|
};
|
|
|
|
try {
|
|
renderQuotaTable(
|
|
[
|
|
{
|
|
...createModel(),
|
|
current_weekly_total_count: 0,
|
|
current_weekly_usage_count: 0,
|
|
current_weekly_remaining_percent: 100,
|
|
current_weekly_status: 3,
|
|
weekly_boost_permille: 1500,
|
|
},
|
|
],
|
|
{ ...createConfig(), region: 'cn', noColor: true },
|
|
);
|
|
} finally {
|
|
console.log = originalLog;
|
|
}
|
|
|
|
const output = lines.join('\n');
|
|
expect(output).toContain('[██████████]');
|
|
expect(output).toContain('周剩余');
|
|
expect(output).toContain('无限');
|
|
expect(output).not.toContain('150%');
|
|
});
|
|
|
|
it('renders "unlimited" for weekly when status=3 (global region)', () => {
|
|
const lines: string[] = [];
|
|
const originalLog = console.log;
|
|
|
|
console.log = (message?: unknown) => {
|
|
lines.push(String(message ?? ''));
|
|
};
|
|
|
|
try {
|
|
renderQuotaTable(
|
|
[
|
|
{
|
|
...createModel(),
|
|
current_weekly_total_count: 0,
|
|
current_weekly_usage_count: 0,
|
|
current_weekly_remaining_percent: 100,
|
|
current_weekly_status: 3,
|
|
},
|
|
],
|
|
{ ...createConfig(), noColor: true },
|
|
);
|
|
} finally {
|
|
console.log = originalLog;
|
|
}
|
|
|
|
const output = lines.join('\n');
|
|
expect(output).toContain('[██████████]');
|
|
expect(output).toContain('Wk left');
|
|
expect(output).toContain('unlimited');
|
|
expect(output).not.toContain('100%');
|
|
});
|
|
|
|
it('renders a zero-allocation model as unavailable instead of unlimited', () => {
|
|
const lines: string[] = [];
|
|
const originalLog = console.log;
|
|
|
|
console.log = (message?: unknown) => {
|
|
lines.push(String(message ?? ''));
|
|
};
|
|
|
|
try {
|
|
renderQuotaTable(
|
|
[
|
|
{
|
|
...createModel(),
|
|
model_name: 'video',
|
|
current_interval_total_count: 0,
|
|
current_interval_usage_count: 0,
|
|
current_interval_remaining_percent: 100,
|
|
current_interval_status: 3,
|
|
current_weekly_total_count: 0,
|
|
current_weekly_usage_count: 0,
|
|
current_weekly_remaining_percent: 100,
|
|
current_weekly_status: 3,
|
|
},
|
|
],
|
|
{ ...createConfig(), noColor: true },
|
|
);
|
|
} finally {
|
|
console.log = originalLog;
|
|
}
|
|
|
|
const output = lines.join('\n');
|
|
expect(output).toContain('not in plan');
|
|
expect(output).toContain('Reset —');
|
|
expect(output).not.toContain('unlimited');
|
|
expect(output).not.toContain('100%');
|
|
});
|
|
});
|