mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 14:04:04 +00:00
feat(test): add local billing smoke test
This commit is contained in:
@@ -50,4 +50,13 @@ describe('extractPurchaseTopicCandidate', () => {
|
|||||||
|
|
||||||
expect(record).toBeNull()
|
expect(record).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('skips slash commands in purchase topic', () => {
|
||||||
|
const record = extractPurchaseTopicCandidate(
|
||||||
|
candidate({ rawText: '/statement 2026-03' }),
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(record).toBeNull()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ export function extractPurchaseTopicCandidate(
|
|||||||
value: PurchaseTopicCandidate,
|
value: PurchaseTopicCandidate,
|
||||||
config: PurchaseTopicIngestionConfig
|
config: PurchaseTopicIngestionConfig
|
||||||
): PurchaseTopicRecord | null {
|
): PurchaseTopicRecord | null {
|
||||||
|
if (value.rawText.trim().startsWith('/')) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
if (value.chatId !== config.householdChatId) {
|
if (value.chatId !== config.householdChatId) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -195,14 +199,16 @@ export function registerPurchaseTopicIngestion(
|
|||||||
llmFallback?: PurchaseParserLlmFallback
|
llmFallback?: PurchaseParserLlmFallback
|
||||||
} = {}
|
} = {}
|
||||||
): void {
|
): void {
|
||||||
bot.on('message:text', async (ctx) => {
|
bot.on('message:text', async (ctx, next) => {
|
||||||
const candidate = toCandidateFromContext(ctx)
|
const candidate = toCandidateFromContext(ctx)
|
||||||
if (!candidate) {
|
if (!candidate) {
|
||||||
|
await next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const record = extractPurchaseTopicCandidate(candidate, config)
|
const record = extractPurchaseTopicCandidate(candidate, config)
|
||||||
if (!record) {
|
if (!record) {
|
||||||
|
await next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
77
docs/specs/HOUSEBOT-061-local-e2e-smoke-tests.md
Normal file
77
docs/specs/HOUSEBOT-061-local-e2e-smoke-tests.md
Normal file
@@ -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.
|
||||||
@@ -127,6 +127,20 @@ async function run(): Promise<void> {
|
|||||||
const bot = createTelegramBot('000000:test-token')
|
const bot = createTelegramBot('000000:test-token')
|
||||||
const replies: string[] = []
|
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) => {
|
bot.api.config.use(async (_prev, method, payload) => {
|
||||||
if (method === 'sendMessage') {
|
if (method === 'sendMessage') {
|
||||||
const p = payload as any
|
const p = payload as any
|
||||||
|
|||||||
Reference in New Issue
Block a user