From 3152858aacf395ba5a7b03a25830ce0d7e6713f7 Mon Sep 17 00:00:00 2001 From: whekin Date: Sun, 8 Mar 2026 20:14:36 +0400 Subject: [PATCH] feat(test): add local billing smoke test --- apps/bot/src/purchase-topic-ingestion.test.ts | 9 +++ apps/bot/src/purchase-topic-ingestion.ts | 8 +- .../HOUSEBOT-061-local-e2e-smoke-tests.md | 77 +++++++++++++++++++ scripts/e2e/billing-flow.ts | 14 ++++ 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 docs/specs/HOUSEBOT-061-local-e2e-smoke-tests.md diff --git a/apps/bot/src/purchase-topic-ingestion.test.ts b/apps/bot/src/purchase-topic-ingestion.test.ts index 9a941eb..e51e969 100644 --- a/apps/bot/src/purchase-topic-ingestion.test.ts +++ b/apps/bot/src/purchase-topic-ingestion.test.ts @@ -50,4 +50,13 @@ describe('extractPurchaseTopicCandidate', () => { expect(record).toBeNull() }) + + test('skips slash commands in purchase topic', () => { + const record = extractPurchaseTopicCandidate( + candidate({ rawText: '/statement 2026-03' }), + config + ) + + expect(record).toBeNull() + }) }) diff --git a/apps/bot/src/purchase-topic-ingestion.ts b/apps/bot/src/purchase-topic-ingestion.ts index 0c5bada..37e0af6 100644 --- a/apps/bot/src/purchase-topic-ingestion.ts +++ b/apps/bot/src/purchase-topic-ingestion.ts @@ -36,6 +36,10 @@ export function extractPurchaseTopicCandidate( value: PurchaseTopicCandidate, config: PurchaseTopicIngestionConfig ): PurchaseTopicRecord | null { + if (value.rawText.trim().startsWith('/')) { + return null + } + if (value.chatId !== config.householdChatId) { return null } @@ -195,14 +199,16 @@ export function registerPurchaseTopicIngestion( llmFallback?: PurchaseParserLlmFallback } = {} ): void { - bot.on('message:text', async (ctx) => { + bot.on('message:text', async (ctx, next) => { const candidate = toCandidateFromContext(ctx) if (!candidate) { + await next() return } const record = extractPurchaseTopicCandidate(candidate, config) if (!record) { + await next() return } diff --git a/docs/specs/HOUSEBOT-061-local-e2e-smoke-tests.md b/docs/specs/HOUSEBOT-061-local-e2e-smoke-tests.md new file mode 100644 index 0000000..ce03d6f --- /dev/null +++ b/docs/specs/HOUSEBOT-061-local-e2e-smoke-tests.md @@ -0,0 +1,77 @@ +# HOUSEBOT-061: Local End-to-End Smoke Tests for Billing Flow + +## Summary + +Add a pragmatic local smoke test that exercises the main billing path against a real database with deterministic assertions. + +## Goals + +- Provide `bun run test:e2e` for local pre-deploy confidence. +- Cover purchase ingestion, manual utility entry, and statement generation in one flow. +- Ensure smoke data is isolated and cleaned up automatically. + +## Non-goals + +- Full browser or Telegram API end-to-end automation. +- Running destructive write tests in the default CI quality matrix. +- Comprehensive scenario coverage for every finance edge case. + +## Scope + +- In: write-gated smoke script, docs, typed env for the smoke test, deterministic assertions, cleanup. +- Out: full staging environment orchestration. + +## Interfaces and Contracts + +- Command: `bun run test:e2e` +- Required env: + - `DATABASE_URL` + - `E2E_SMOKE_ALLOW_WRITE=true` +- Script behavior: + - creates temporary household/member/cycle data + - simulates Telegram topic purchase ingestion + - simulates finance commands for rent, utilities, and statements + - deletes created data in `finally` + +## Domain Rules + +- Use integer minor units only. +- Statement totals must match deterministic settlement behavior. +- Purchase-topic ingestion must not swallow non-purchase slash commands. + +## Data Model Changes + +- None. + +## Security and Privacy + +- Test writes are disabled unless `E2E_SMOKE_ALLOW_WRITE=true`. +- No production secrets are logged by the smoke script. + +## Observability + +- Script prints a single success line on pass. +- Failures surface assertion or runtime errors with non-zero exit code. + +## Edge Cases and Failure Modes + +- Missing `DATABASE_URL`: fail fast in env validation. +- Missing explicit write guard: fail fast before DB writes. +- Middleware ordering regression: smoke test should fail when commands stop emitting statements. + +## Test Plan + +- Unit: parser/topic candidate tests cover slash-command exclusion. +- Integration: `bun run test:e2e` against a migrated dev database. +- E2E: same smoke script verifies purchase ingestion -> statement -> recalculated statement after utility update. + +## Acceptance Criteria + +- [ ] `bun run test:e2e` executes locally with deterministic output. +- [ ] Purchase ingestion and utility updates are both covered in the same smoke flow. +- [ ] Docs explain required env and safety guard. + +## Rollout Plan + +- Keep the smoke test local-first. +- Consider adding an opt-in CI job later once a dedicated disposable database is available. diff --git a/scripts/e2e/billing-flow.ts b/scripts/e2e/billing-flow.ts index ef36a40..11dedc0 100644 --- a/scripts/e2e/billing-flow.ts +++ b/scripts/e2e/billing-flow.ts @@ -127,6 +127,20 @@ async function run(): Promise { const bot = createTelegramBot('000000:test-token') const replies: string[] = [] + bot.botInfo = { + id: 999000, + is_bot: true, + first_name: 'Household Test Bot', + username: 'household_test_bot', + can_join_groups: true, + can_read_all_group_messages: false, + supports_inline_queries: false, + can_connect_to_business: false, + has_main_web_app: false, + has_topics_enabled: true, + allows_users_to_create_topics: false + } + bot.api.config.use(async (_prev, method, payload) => { if (method === 'sendMessage') { const p = payload as any