Skip to content

Commit 50e1104

Browse files
committed
test: add the boot harness as npm test and run it in CI
test/harness.js does what main.js does without logging in: it unions the intents, builds the shared command registry through all three attach() calls, serializes the exact rest.put payload (33 commands, no duplicates, valid names, 4 context menus), and dispatches mock interactions through the real InteractionCreate listeners. It also verifies that every gcfg.*_ROLE_ID used anywhere in the code exists in core/config.js and looks like a snowflake, exercises the Collection surface discord.js relies on, and round trips the points system. data/points.json is restored afterwards, so the run is safe on a machine holding real data. 58 checks. The final section makes one unauthenticated request to Discord: it logs in with an invalid token and asserts Discord rejects it, which proves the REST stack works end to end. That is the check that caught undici 8 breaking @discordjs/rest. SKIP_NETWORK_TESTS=1 skips it for offline work. The header documents what the harness cannot cover: an invalid token fails at REST authentication, so the gateway WebSocket never opens and @discordjs/ws is never exercised. An override of that package passed 47 of 47 checks here and still crashed the bot on startup. Gateway changes need a real login. Both READMEs gain a Testing section and a short note on why there are no overrides, and the dependency table was still listing discord.js ^14.26.4 and dotenv ^16.6.1.
1 parent 7c3f32c commit 50e1104

5 files changed

Lines changed: 570 additions & 5 deletions

File tree

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ jobs:
5959
done
6060
echo "$FILES" | wc -l | xargs printf '✅ %s files parsed without errors.\n'
6161
62+
# Boots every bot module against a fake client, serializes the exact
63+
# payload main.js sends to Discord and dispatches mock interactions
64+
# through the real listeners. Also makes one unauthenticated request to
65+
# Discord to prove the REST stack works, which is what catches a broken
66+
# undici. See the header of test/harness.js for what it cannot cover.
67+
- name: Boot harness
68+
run: npm test
69+
6270
# points_config.json is read at runtime, a broken one takes the bot down.
6371
- name: Validate tracked JSON files
6472
run: |

README.md

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,34 @@ Point values per game are configurable in `bots/minigames/points_config.json`.
261261

262262
---
263263

264+
## Testing
265+
266+
```bash
267+
npm test
268+
```
269+
270+
`test/harness.js` boots every bot module against a fake client, exactly the way
271+
`main.js` does but without logging in. It unions the intents, builds the shared
272+
command registry through all three `attach()` calls, serializes the exact
273+
payload that goes to Discord, and dispatches mock interactions through the real
274+
`InteractionCreate` listeners. It also round trips the points system and
275+
restores `data/points.json` afterwards, so it is safe to run on a machine that
276+
holds real data.
277+
278+
The last section makes one unauthenticated request to Discord: it logs in with
279+
an obviously invalid token and asserts that Discord rejects it. That proves the
280+
REST stack works end to end. Set `SKIP_NETWORK_TESTS=1` to skip it offline.
281+
282+
**What it cannot cover.** An invalid token fails at REST authentication, so the
283+
gateway WebSocket is never opened and `@discordjs/ws` never runs. Anything that
284+
touches the gateway needs a real login with a real token. This is not a
285+
theoretical gap: an `@discordjs/ws` override once passed this harness 47 checks
286+
out of 47 and still crashed the bot on startup.
287+
288+
The harness runs in CI on every pull request.
289+
290+
---
291+
264292
## Project Structure
265293

266294
```
@@ -275,6 +303,8 @@ discord_multibot/
275303
│ ├── codeql.yml ← Code scanning
276304
│ ├── deploy.yml ← SSH deploy + systemd restart on push to main
277305
│ └── release.yml ← GitHub release on v* tags
306+
├── test/
307+
│ └── harness.js ← Boot harness, run via npm test
278308
├── core/
279309
│ ├── config.js ← Environment config
280310
│ ├── utils.js ← Shared helpers (makeEmbed, readJson, …)
@@ -323,11 +353,28 @@ discord_multibot/
323353

324354
| Package | Version |
325355
|---|---|
326-
| [discord.js](https://discord.js.org) | `^14.26.4` |
327-
| [dotenv](https://github.com/motdotla/dotenv) | `^16.6.1` |
356+
| [discord.js](https://discord.js.org) | `^14.27.0` |
357+
| [dotenv](https://github.com/motdotla/dotenv) | `^17.4.2` |
328358

329359
Dependency updates are monitored automatically via [Dependabot](.github/dependabot.yml) (weekly, grouped).
330360

361+
### No overrides, on purpose
362+
363+
`npm outdated --all` reports major updates for `@discordjs/collection`,
364+
`@discordjs/ws` and `undici`. They belong to the discord.js 15 line and are
365+
deliberately left alone. Both attempts to force them failed:
366+
367+
- **`undici@8`** kills the REST layer. `@discordjs/rest` puts Node's
368+
`sensitiveHeaders` symbol into the request headers and undici 8 rejects it
369+
(`Key Symbol(sensitiveHeaders) in init is a symbol, which cannot be converted
370+
to a ByteString`). No command registration, no interaction reply. `npm test`
371+
catches this one.
372+
- **`@discordjs/ws@2.0.4`** passed every test and crashed the bot on startup in
373+
production. Note that discord.js 14.27.0 pins `@discordjs/collection` to
374+
exactly `1.5.3`, without a caret.
375+
376+
Patch level updates inside the existing ranges are fine and need no override.
377+
331378
---
332379

333380
## Links

README_DE.md

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,36 @@ Die Punktwerte pro Spiel sind in `bots/minigames/points_config.json` konfigurier
262262

263263
---
264264

265+
## Tests
266+
267+
```bash
268+
npm test
269+
```
270+
271+
`test/harness.js` bootet alle Bot-Module gegen einen Fake-Client, genau wie
272+
`main.js`, nur ohne Login. Es vereinigt die Intents, baut die gemeinsame
273+
Command-Registry über alle drei `attach()`-Aufrufe, serialisiert exakt das
274+
Payload, das an Discord geht, und schickt Mock-Interactions durch die echten
275+
`InteractionCreate`-Listener. Das Punktesystem wird ebenfalls durchgespielt und
276+
`data/points.json` danach wiederhergestellt, der Lauf ist also auch auf einer
277+
Maschine mit echten Daten unbedenklich.
278+
279+
Der letzte Abschnitt stellt eine unauthentifizierte Anfrage an Discord: Login
280+
mit offensichtlich ungültigem Token, und die Erwartung, dass Discord ihn
281+
ablehnt. Das beweist, dass die REST-Schicht durchgängig funktioniert. Mit
282+
`SKIP_NETWORK_TESTS=1` bleibt der Test offline außen vor.
283+
284+
**Was er nicht abdeckt.** Ein ungültiges Token scheitert an der
285+
REST-Authentifizierung, der Gateway-WebSocket wird also nie geöffnet und
286+
`@discordjs/ws` nie ausgeführt. Alles, was den Gateway betrifft, braucht einen
287+
echten Login mit echtem Token. Das ist keine theoretische Lücke: ein
288+
`@discordjs/ws`-Override hat diesen Harness mit 47 von 47 bestanden und den Bot
289+
trotzdem beim Start zum Absturz gebracht.
290+
291+
Der Harness läuft in der CI bei jedem Pull Request.
292+
293+
---
294+
265295
## Projektstruktur
266296

267297
```
@@ -276,6 +306,8 @@ discord_multibot/
276306
│ ├── codeql.yml ← Code Scanning
277307
│ ├── deploy.yml ← SSH-Deploy + systemd-Restart bei Push auf main
278308
│ └── release.yml ← GitHub-Release bei v*-Tags
309+
├── test/
310+
│ └── harness.js ← Boot-Harness, Aufruf über npm test
279311
├── core/
280312
│ ├── config.js ← Umgebungskonfiguration
281313
│ ├── utils.js ← Gemeinsame Helfer (makeEmbed, readJson, …)
@@ -324,11 +356,29 @@ discord_multibot/
324356

325357
| Paket | Version |
326358
|---|---|
327-
| [discord.js](https://discord.js.org) | `^14.26.4` |
328-
| [dotenv](https://github.com/motdotla/dotenv) | `^16.6.1` |
359+
| [discord.js](https://discord.js.org) | `^14.27.0` |
360+
| [dotenv](https://github.com/motdotla/dotenv) | `^17.4.2` |
329361

330362
Dependency-Updates werden automatisch über [Dependabot](.github/dependabot.yml) überwacht (wöchentlich, gruppiert).
331363

364+
### Bewusst keine Overrides
365+
366+
`npm outdated --all` meldet Major-Updates für `@discordjs/collection`,
367+
`@discordjs/ws` und `undici`. Die gehören zur discord.js-15-Linie und bleiben
368+
bewusst liegen. Beide Versuche, sie zu erzwingen, sind gescheitert:
369+
370+
- **`undici@8`** zerlegt die REST-Schicht. `@discordjs/rest` reicht Nodes
371+
`sensitiveHeaders`-Symbol in die Request-Header, undici 8 lehnt das ab (`Key
372+
Symbol(sensitiveHeaders) in init is a symbol, which cannot be converted to a
373+
ByteString`). Keine Command-Registrierung, keine Interaction-Antwort. Das
374+
findet `npm test`.
375+
- **`@discordjs/ws@2.0.4`** hat jeden Test bestanden und den Bot in Produktion
376+
beim Start zum Absturz gebracht. discord.js 14.27.0 pinnt
377+
`@discordjs/collection` übrigens auf exakt `1.5.3`, ohne Caret.
378+
379+
Patch-Updates innerhalb der bestehenden Ranges sind unproblematisch und
380+
brauchen keinen Override.
381+
332382
---
333383

334384
## Links

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"description": "MSK Scripts Discord Multi-Bot",
55
"main": "main.js",
66
"scripts": {
7-
"start": "node main.js"
7+
"start": "node main.js",
8+
"test": "node test/harness.js"
89
},
910
"dependencies": {
1011
"discord.js": "^14.27.0",

0 commit comments

Comments
 (0)