mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-04-28 12:20:42 +00:00
08ff72985a
Telegram commands /semantle, /semantle_new, /semantle_giveup, /semantle_stats. Round starts with /random pick from hosted word2sim; each guess scored via /similarity. Unlimited guesses; solve on case-insensitive exact match. New env var WORD2SIM_API_URL (wrangler.toml, .env.deploy). Includes module README and 90 unit tests covering api-client, state, format, render, and handlers.
114 lines
4.3 KiB
Markdown
114 lines
4.3 KiB
Markdown
# Phase 3 — Tests & Documentation
|
|
|
|
Ship-ready polish: unit-test coverage with fake KV + stubbed fetch, module README,
|
|
`/help` integration verification.
|
|
|
|
## Context links
|
|
|
|
- Overview: `./plan.md`
|
|
- Prior phases: `./phase-01-foundation.md`, `./phase-02-gameplay.md`
|
|
- Test pattern: `tests/modules/wordle/` and `tests/modules/trading/` (fetch-stubbing examples)
|
|
- Fakes: `tests/fakes/{fake-kv-namespace,fake-bot,fake-modules}.js`
|
|
|
|
## Files to create
|
|
|
|
### `tests/modules/semantle/api-client.test.js` (~50 LOC)
|
|
Stub `global.fetch` with `vi.fn()`:
|
|
- asserts query-string building (rank filters, URL encoding)
|
|
- asserts `Word2SimError` on non-2xx
|
|
- asserts AbortController timeout path (simulate slow upstream)
|
|
|
|
### `tests/modules/semantle/state.test.js` (~60 LOC)
|
|
Using `fake-kv-namespace`:
|
|
- load/save round-trip preserves shape
|
|
- clearGame removes entry
|
|
- recordResult increments played/solved/totalGuesses correctly
|
|
- bestGuessCount is `min(prev, current)` only when solved
|
|
- loadStats returns defaults when empty
|
|
|
|
### `tests/modules/semantle/format.test.js` (~25 LOC)
|
|
Pure-function coverage:
|
|
- formatWarmth signed/rounded
|
|
- warmthEmoji buckets boundary cases
|
|
|
|
### `tests/modules/semantle/render.test.js` (~40 LOC)
|
|
- renderBoard empty state renders "round ready" prompt
|
|
- renderBoard sorts by similarity desc (verify first row = highest score)
|
|
- renderBoard caps to top 15 when >15 guesses
|
|
- renderGuess escapes HTML-unsafe chars in the word
|
|
|
|
### `tests/modules/semantle/handlers.test.js` (~130 LOC)
|
|
Integration-ish: fake KV + stubbed client (not real fetch).
|
|
- happy path: round start → guess → guess → solve (case-insensitive) → stats updated
|
|
- OOV guess: not appended, no state mutation
|
|
- _giveup reveals target and clears game
|
|
- _new abandons prior round + clears, records non-solve
|
|
- error from client surfaces a user-friendly message; state unchanged
|
|
- case sensitivity: guess "APPLE" against target "apple" solves the round
|
|
|
|
## Files to create (docs)
|
|
|
|
### `src/modules/semantle/README.md`
|
|
Mirror `src/modules/loldle/README.md` shape. Sections:
|
|
- **Commands** table (from `plan.md`)
|
|
- **Data source** — point at `tiennm99/word2sim` hosted instance
|
|
- **Architecture** — list of files and what each does
|
|
- **Storage** — KV layout table (`game:<subject>`, `stats:<subject>`)
|
|
- **Config** — `WORD2SIM_API_URL` env var
|
|
- **Credits** — word2vec / GoogleNews pretrained vectors
|
|
|
|
## Files to edit
|
|
|
|
### `docs/adding-a-module.md`
|
|
No change expected unless the word2sim env-var pattern is the first of its kind.
|
|
If so, add a short note about `[vars]` config for external API bases.
|
|
|
|
### `scripts/register.js`
|
|
Verify `setMyCommands` picks up the new public commands automatically (no code
|
|
change expected — registry is the source of truth).
|
|
|
|
## Todo
|
|
|
|
- [ ] `api-client.test.js`
|
|
- [ ] `state.test.js`
|
|
- [ ] `format.test.js`
|
|
- [ ] `render.test.js`
|
|
- [ ] `handlers.test.js`
|
|
- [ ] `src/modules/semantle/README.md`
|
|
- [ ] Run `npm test` — all pass
|
|
- [ ] Run `npm run lint && npm run format` — clean
|
|
- [ ] `npm run register:dry` — confirms `/semantle`, `/semantle_giveup`, `/semantle_new`,
|
|
`/semantle_stats` appear in the command payload
|
|
- [ ] Optional: `docs/adding-a-module.md` one-paragraph addition if env-var
|
|
pattern is novel
|
|
|
|
## Success criteria
|
|
|
|
- Test coverage for all new files (>80% line coverage, pragmatic thresholds).
|
|
- No biome/eslint warnings (project enforces 100-char line width, sorted imports,
|
|
trailing commas).
|
|
- `npm run register:dry` output includes all public `/semantle*` commands.
|
|
- `src/modules/semantle/README.md` parallel to loldle's in shape and depth.
|
|
|
|
## Risk
|
|
|
|
- **Fetch stub drift** — if word2sim adds fields, tests may not catch breaking
|
|
response-shape changes in prod. Mitigation: add a single optional integration
|
|
test (guarded by env flag) that hits the real URL; skip by default.
|
|
- **Flaky timing tests** — avoid real `setTimeout` in the AbortController test;
|
|
use `vi.useFakeTimers()` if needed.
|
|
|
|
## Security
|
|
|
|
- Tests must not commit real TELEGRAM tokens or call Telegram.
|
|
- `.dev.vars` is gitignored; never add secrets to `.dev.vars.example`.
|
|
|
|
## Next
|
|
|
|
- Deploy via `npm run deploy` (handles webhook + setMyCommands).
|
|
- Monitor the first day of usage via CF Worker logs; look for upstream errors.
|
|
- Potential follow-ups (separate plans):
|
|
- Daily shared secret (Semantle-classic mode)
|
|
- Leaderboard module integration
|
|
- Webhook-side warm-up cron to keep word2sim hot
|