Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ describe('config command', () => {
let consoleLogSpy: any;
let consoleErrorSpy: any;

const consoleLogOrder = (matcher: RegExp | string) => {
const callIndex = consoleLogSpy.mock.calls.findIndex(([message]: [string]) =>
matcher instanceof RegExp ? matcher.test(message) : message.includes(matcher),
);
expect(callIndex).toBeGreaterThanOrEqual(0);
return consoleLogSpy.mock.invocationCallOrder[callIndex];
};

const firstInputOrder = () => vi.mocked(tui.input).mock.invocationCallOrder[0];

beforeEach(() => {
vi.clearAllMocks();
program = new Command();
Expand Down Expand Up @@ -62,6 +72,21 @@ describe('config command', () => {
expect(configUtils.clearNotificationCredentials).toHaveBeenCalled();
expect(configUtils.setConfig).toHaveBeenCalledWith('notification_service', 'discord');
expect(configUtils.setConfig).toHaveBeenCalledWith('discord_webhook', 'https://discord.com/api/webhooks/123456789/token-here');
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringMatching(/Discord webhook setup/i));
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Integrations > Webhooks'));
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const guideOrder = consoleLogOrder(/Discord webhook setup/i);
expect(guideOrder).toBeLessThan(firstInputOrder());
});

it('should validate Discord webhook URLs during setup', async () => {
vi.mocked(tui.select).mockResolvedValue('discord');
vi.mocked(tui.input).mockResolvedValue('https://discord.com/api/webhooks/123456789/token-here');

await program.parseAsync(['node', 'test', 'config', 'setup']);

const webhookPrompt = vi.mocked(tui.input).mock.calls[0][0];
expect(webhookPrompt.validate('not-a-webhook')).toBe('Must be a valid Discord webhook URL (including ID and Token)');
});

it('should call select, multiple inputs and setConfig on email setup', async () => {
Expand All @@ -79,6 +104,25 @@ describe('config command', () => {
expect(configUtils.setConfig).toHaveBeenCalledWith('email_port', 587);
expect(configUtils.setConfig).toHaveBeenCalledWith('email_user', 'user@test.com');
expect(configUtils.setConfig).toHaveBeenCalledWith('email_to', 'to@test.com');
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringMatching(/Email SMTP setup/i));
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('KDM_SMTP_PASSWORD'));

const guideOrder = consoleLogOrder(/Email SMTP setup/i);
expect(guideOrder).toBeLessThan(firstInputOrder());
});

it('should require an SMTP host during email setup', async () => {
vi.mocked(tui.select).mockResolvedValue('email');
vi.mocked(tui.input)
.mockResolvedValueOnce('smtp.gmail.com')
.mockResolvedValueOnce('587')
.mockResolvedValueOnce('user@test.com')
.mockResolvedValueOnce('to@test.com');

await program.parseAsync(['node', 'test', 'config', 'setup']);

const smtpHostPrompt = vi.mocked(tui.input).mock.calls[0][0];
expect(smtpHostPrompt.validate('')).toBe('Host is required');
});

it('should call setConfig on config set', async () => {
Expand Down
27 changes: 26 additions & 1 deletion src/commands/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export const registerConfigCommand = (program: Command) => {
}

if (choice === 'discord') {
printDiscordWebhookGuide();

const webhook = await input({
message: 'Discord Webhook URL:',
validate: (v) => {
Expand All @@ -43,6 +45,8 @@ export const registerConfigCommand = (program: Command) => {
setConfig('notification_service', 'discord');
console.log(chalk.green('\n✓ Discord Webhook configured.'));
} else if (choice === 'email') {
printEmailSmtpGuide();

const host = await input({
message: 'SMTP Host:',
placeholder: 'smtp.gmail.com',
Expand Down Expand Up @@ -73,7 +77,6 @@ export const registerConfigCommand = (program: Command) => {
setConfig('notification_service', 'email');

console.log(chalk.green('\n✓ Email SMTP configured.'));
console.log(chalk.yellow('! Important: Set your SMTP password in the KDM_SMTP_PASSWORD environment variable for notifications to work.'));
}

console.log(chalk.green(`✓ Notification service set to: ${chalk.bold(choice.toUpperCase())}`));
Expand Down Expand Up @@ -128,3 +131,25 @@ export const registerConfigCommand = (program: Command) => {
console.log(chalk.green('✓ Configuration cleared.'));
});
};

const printDiscordWebhookGuide = () => {
Comment thread
fizyxbt marked this conversation as resolved.
console.log(chalk.gray('\n──────────────────────────────────────────────────'));
console.log(chalk.cyan('Discord webhook setup'));
console.log(chalk.white(' 1. Open your Discord server settings.'));
console.log(chalk.white(' 2. Go to Integrations > Webhooks.'));
console.log(chalk.white(' 3. Create a new webhook and choose the alert channel.'));
console.log(chalk.white(' 4. Copy the webhook URL and paste it below.'));
console.log(chalk.dim(' The URL should start with https://discord.com/api/webhooks/.'));
console.log(chalk.gray('──────────────────────────────────────────────────\n'));
};

const printEmailSmtpGuide = () => {
Comment thread
fizyxbt marked this conversation as resolved.
console.log(chalk.gray('\n──────────────────────────────────────────────────'));
console.log(chalk.cyan('Email SMTP setup'));
console.log(chalk.white(' 1. Find your provider SMTP settings before continuing.'));
console.log(chalk.white(' 2. Common hosts: smtp.gmail.com for Gmail, smtp.office365.com for Outlook.'));
console.log(chalk.white(' 3. Use port 587 for STARTTLS unless your provider says otherwise.'));
console.log(chalk.white(' 4. Set the SMTP password in KDM_SMTP_PASSWORD before sending alerts.'));
console.log(chalk.dim(' Gmail accounts with 2FA usually require an App Password.'));
console.log(chalk.gray('──────────────────────────────────────────────────\n'));
};
Loading