clawdie-ai/setup/telegram-auth.test.ts
Clawdie AI 118e91d4b2 test: add status, registry, and telegram-auth test suites
- src/upstream/status.test.ts: 16 tests for getUpstreamStatus() covering
  disabled, not_configured, up_to_date, and updates_available paths
- src/channels/registry.test.ts: 6 tests for registerChannel() and
  getAllChannelFactories() module-level registry operations
- setup/telegram-auth.test.ts: 12 tests for run() covering missing env,
  missing token, invalid token, valid token, and parseEnv edge cases

Total: 91 test files / 1527 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---
Build: FAIL | Tests: pass — Tests  1527 passed (1527)
2026-04-15 05:45:35 +00:00

209 lines
6.8 KiB
TypeScript

/**
* setup/telegram-auth.test.ts — telegram-auth run() unit tests.
*
* Tests env-missing, token-missing, invalid-token, and valid-token paths
* by mocking fs, fetch, emitStatus, and logger.
*
* Run with: npx vitest run setup/telegram-auth.test.ts
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
// ---------------------------------------------------------------------------
// Mocks
// ---------------------------------------------------------------------------
vi.mock('../src/logger.js', () => ({
logger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
}));
const emitStatusMock = vi.hoisted(() => vi.fn());
vi.mock('./status.js', () => ({ emitStatus: emitStatusMock }));
const fsMocks = vi.hoisted(() => ({
existsSync: vi.fn<[string], boolean>(() => false),
readFileSync: vi.fn<[string, string], string>(() => ''),
}));
vi.mock('fs', async () => {
const actual = await vi.importActual<typeof import('fs')>('fs');
const mocked = { ...actual, ...fsMocks };
return { ...mocked, default: mocked };
});
// Capture process.exit calls without terminating
const exitMock = vi.spyOn(process, 'exit').mockImplementation((_code?: number) => {
throw new Error(`process.exit(${_code})`);
});
import { run } from './telegram-auth.js';
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function makeSuccessFetch(username = 'mybot'): typeof fetch {
return vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ ok: true, result: { username } }),
status: 200,
}) as unknown as typeof fetch;
}
function makeFailFetch(description = 'Unauthorized'): typeof fetch {
return vi.fn().mockResolvedValue({
ok: false,
json: async () => ({ ok: false, description }),
status: 401,
}) as unknown as typeof fetch;
}
// ---------------------------------------------------------------------------
// env missing
// ---------------------------------------------------------------------------
describe('run — env file missing', () => {
beforeEach(() => {
emitStatusMock.mockReset();
fsMocks.existsSync.mockReset().mockReturnValue(false);
fsMocks.readFileSync.mockReset();
exitMock.mockClear();
});
it('calls emitStatus with STATUS=failed', async () => {
await expect(run([])).rejects.toThrow('process.exit');
expect(emitStatusMock).toHaveBeenCalledWith(
'AUTH_TELEGRAM',
expect.objectContaining({ STATUS: 'failed', ERROR: 'env_missing' }),
);
});
it('calls process.exit(4)', async () => {
await expect(run([])).rejects.toThrow('process.exit(4)');
});
});
// ---------------------------------------------------------------------------
// token missing
// ---------------------------------------------------------------------------
describe('run — token missing from .env', () => {
beforeEach(() => {
emitStatusMock.mockReset();
fsMocks.existsSync.mockReset().mockReturnValue(true);
fsMocks.readFileSync.mockReset().mockReturnValue('SOME_OTHER_KEY=value\n');
exitMock.mockClear();
});
it('calls emitStatus with missing_telegram_bot_token error', async () => {
await expect(run([])).rejects.toThrow('process.exit');
expect(emitStatusMock).toHaveBeenCalledWith(
'AUTH_TELEGRAM',
expect.objectContaining({ STATUS: 'failed', ERROR: 'missing_telegram_bot_token' }),
);
});
it('calls process.exit(4)', async () => {
await expect(run([])).rejects.toThrow('process.exit(4)');
});
});
// ---------------------------------------------------------------------------
// invalid token
// ---------------------------------------------------------------------------
describe('run — invalid Telegram token', () => {
beforeEach(() => {
emitStatusMock.mockReset();
fsMocks.existsSync.mockReset().mockReturnValue(true);
fsMocks.readFileSync.mockReset().mockReturnValue('TELEGRAM_BOT_TOKEN=badtoken\n');
exitMock.mockClear();
vi.stubGlobal('fetch', makeFailFetch('Unauthorized'));
});
it('calls emitStatus with AUTH_STATUS=invalid', async () => {
await expect(run([])).rejects.toThrow('process.exit');
expect(emitStatusMock).toHaveBeenCalledWith(
'AUTH_TELEGRAM',
expect.objectContaining({ STATUS: 'failed', AUTH_STATUS: 'invalid' }),
);
});
it('calls process.exit(1)', async () => {
await expect(run([])).rejects.toThrow('process.exit(1)');
});
});
// ---------------------------------------------------------------------------
// valid token
// ---------------------------------------------------------------------------
describe('run — valid Telegram token', () => {
beforeEach(() => {
emitStatusMock.mockReset();
fsMocks.existsSync.mockReset().mockReturnValue(true);
fsMocks.readFileSync.mockReset().mockReturnValue('TELEGRAM_BOT_TOKEN=goodtoken123\n');
exitMock.mockClear();
vi.stubGlobal('fetch', makeSuccessFetch('clawdiebot'));
});
it('calls emitStatus with STATUS=success', async () => {
await run([]);
expect(emitStatusMock).toHaveBeenCalledWith(
'AUTH_TELEGRAM',
expect.objectContaining({ STATUS: 'success', AUTH_STATUS: 'verified' }),
);
});
it('includes bot username in status', async () => {
await run([]);
expect(emitStatusMock).toHaveBeenCalledWith(
'AUTH_TELEGRAM',
expect.objectContaining({ BOT_USERNAME: 'clawdiebot' }),
);
});
it('does not call process.exit', async () => {
await run([]);
expect(exitMock).not.toHaveBeenCalled();
});
it('parses quoted token values correctly', async () => {
fsMocks.readFileSync.mockReturnValue("TELEGRAM_BOT_TOKEN='quotedtoken'\n");
await run([]);
expect(emitStatusMock).toHaveBeenCalledWith(
'AUTH_TELEGRAM',
expect.objectContaining({ STATUS: 'success' }),
);
});
});
// ---------------------------------------------------------------------------
// parseEnv edge cases (tested via run() behaviour)
// ---------------------------------------------------------------------------
describe('run — parseEnv edge cases', () => {
beforeEach(() => {
emitStatusMock.mockReset();
fsMocks.existsSync.mockReset().mockReturnValue(true);
exitMock.mockClear();
vi.stubGlobal('fetch', makeSuccessFetch('bot'));
});
it('ignores comment lines in .env', async () => {
fsMocks.readFileSync.mockReturnValue('# This is a comment\nTELEGRAM_BOT_TOKEN=tok123\n');
await run([]);
expect(emitStatusMock).toHaveBeenCalledWith(
'AUTH_TELEGRAM',
expect.objectContaining({ STATUS: 'success' }),
);
});
it('ignores blank lines in .env', async () => {
fsMocks.readFileSync.mockReturnValue('\n\nTELEGRAM_BOT_TOKEN=tok456\n\n');
await run([]);
expect(emitStatusMock).toHaveBeenCalledWith(
'AUTH_TELEGRAM',
expect.objectContaining({ STATUS: 'success' }),
);
});
});