Initial implementation of Telegram AI customer support bot
Cloudflare Workers + Hono + D1 + KV + R2 stack with 4 specialized AI agents (onboarding, troubleshoot, asset, billing), OpenAI function calling with 7 tool definitions, human escalation, pending action approval workflow, feedback collection, audit logging, i18n (ko/en), and Workers AI fallback. 43 source files, 45 tests passing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
120
tests/setup.ts
Normal file
120
tests/setup.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Vitest test setup
|
||||
*
|
||||
* Miniflare-based D1 + KV simulation for integration tests.
|
||||
*/
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { beforeAll, afterEach } from 'vitest';
|
||||
import { Miniflare } from 'miniflare';
|
||||
|
||||
let mf: Miniflare | null = null;
|
||||
let db: D1Database | null = null;
|
||||
|
||||
declare global {
|
||||
var getMiniflareBindings: () => {
|
||||
DB: D1Database;
|
||||
RATE_LIMIT_KV: KVNamespace;
|
||||
SESSION_KV: KVNamespace;
|
||||
CACHE_KV: KVNamespace;
|
||||
};
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
mf = new Miniflare({
|
||||
modules: true,
|
||||
script: 'export default { fetch() { return new Response("test"); } }',
|
||||
d1Databases: {
|
||||
DB: '__test_db__',
|
||||
},
|
||||
kvNamespaces: ['RATE_LIMIT_KV', 'SESSION_KV', 'CACHE_KV'],
|
||||
});
|
||||
|
||||
db = await mf.getD1Database('DB');
|
||||
|
||||
(global as any).getMiniflareBindings = () => ({
|
||||
DB: db as D1Database,
|
||||
RATE_LIMIT_KV: {} as KVNamespace,
|
||||
SESSION_KV: {} as KVNamespace,
|
||||
CACHE_KV: {} as KVNamespace,
|
||||
});
|
||||
|
||||
// Schema initialization
|
||||
const schemaPath = join(__dirname, '../schema.sql');
|
||||
const schema = readFileSync(schemaPath, 'utf-8');
|
||||
|
||||
const cleanSchema = schema
|
||||
.split('\n')
|
||||
.filter(line => !line.trim().startsWith('--'))
|
||||
.join('\n');
|
||||
|
||||
const statements = cleanSchema
|
||||
.split(';')
|
||||
.map(s => s.replace(/\s+/g, ' ').trim())
|
||||
.filter(s => s.length > 0);
|
||||
|
||||
try {
|
||||
for (const statement of statements) {
|
||||
await db.exec(statement + ';');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Schema initialization failed:', error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (!db) return;
|
||||
|
||||
// Delete child tables first, then parent tables (FK order)
|
||||
// 1. No FK dependencies between each other
|
||||
await db.exec('DELETE FROM feedback');
|
||||
await db.exec('DELETE FROM pending_actions');
|
||||
await db.exec('DELETE FROM audit_logs');
|
||||
// 2. bank_notifications refs transactions
|
||||
await db.exec('DELETE FROM bank_notifications');
|
||||
// 3. transactions refs users
|
||||
await db.exec('DELETE FROM transactions');
|
||||
// 4. wallets refs users
|
||||
await db.exec('DELETE FROM wallets');
|
||||
// 5. Asset tables ref users
|
||||
await db.exec('DELETE FROM domains');
|
||||
await db.exec('DELETE FROM servers');
|
||||
await db.exec('DELETE FROM services_ddos');
|
||||
await db.exec('DELETE FROM services_vpn');
|
||||
// 6. Conversation tables ref users
|
||||
await db.exec('DELETE FROM conversations');
|
||||
await db.exec('DELETE FROM conversation_archives');
|
||||
// 7. Standalone tables (no FKs)
|
||||
await db.exec('DELETE FROM knowledge_articles');
|
||||
await db.exec('DELETE FROM d2_cache');
|
||||
// 8. Session tables (no FKs to users)
|
||||
await db.exec('DELETE FROM onboarding_sessions');
|
||||
await db.exec('DELETE FROM troubleshoot_sessions');
|
||||
await db.exec('DELETE FROM asset_sessions');
|
||||
await db.exec('DELETE FROM billing_sessions');
|
||||
// 9. users last (parent table)
|
||||
await db.exec('DELETE FROM users');
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a test user and return its auto-incremented id.
|
||||
*/
|
||||
export async function createTestUser(
|
||||
telegramId: string,
|
||||
username?: string
|
||||
): Promise<number> {
|
||||
const bindings = getMiniflareBindings();
|
||||
const result = await bindings.DB.prepare(
|
||||
'INSERT INTO users (telegram_id, username) VALUES (?, ?)'
|
||||
).bind(telegramId, username || null).run();
|
||||
|
||||
return Number(result.meta?.last_row_id || 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test D1Database binding.
|
||||
*/
|
||||
export function getTestDB(): D1Database {
|
||||
return getMiniflareBindings().DB;
|
||||
}
|
||||
Reference in New Issue
Block a user