8.8 KiB
Testing
This document describes the test setup, the run commands, and the known gaps.
Unit tests
The pure daemon logic has unit tests. The tests cover the ring buffers, the mention helpers, the
command parser, the poll parser, the trust helpers, the context assembly, the redaction, and the
pacing policy (planSend). The cache tests cover merge, dedupe, trimming, and eviction. The audio
tests cover the VAD, the WAV writer, the transcript store, and a local STT command.
cd poc
pnpm test:comm
pnpm test:cache
pnpm test:audio
pnpm test:media
The files are poc/src/test-communication.ts, poc/src/test-cache.ts, and the media archive
tests media-scrape/test/run.ts (engine) and media-scrape/test/slopbot.ts (bot mapping). The
tests do not need Discord or Vesktop.
Type checks
cd poc && pnpm check # daemon and spike code
cd .. && pnpm testTypes # Vesktop core
Both commands must pass before a change is complete.
Build
pnpm build
The build writes the application to dist/.
Mock core test
The mock core runs the daemon without Electron.
# Terminal 1: mock core
VESKSLOP_SOCKET=/tmp/vesktop-puppet-mock.sock pnpm control:mock
# Terminal 2: daemon
VESKSLOP_SOCKET=/tmp/vesktop-puppet-mock.sock pnpm control:bridge
# Terminal 3: inject an owner command
VESKSLOP_SOCKET=/tmp/vesktop-puppet-mock.sock TEST_TRIGGER_CONTENT="whitelist list" pnpm test:trigger
Expected result: the daemon log shows one trusted entry with the parsed command. The mock log shows
the reply (message.send). A first reply after a quiet period waits 30 to 200 ms. Further replies
in the same burst wait 500 to 2800 ms.
The mock also covers tiers, continuation, polls, reactions, and the action log. Set config values
with set_puppet_config before a test. Inject a reaction with
TEST_TRIGGER_REACTION=1 TEST_REACTION_MESSAGE_ID=msg-1 TEST_REACTION_EMOJI="1️⃣" pnpm test:trigger.
Web UI smoke test
The web UI starts with the daemon. The mock core is enough for this test.
# Terminal 1: mock core
VESKSLOP_SOCKET=/tmp/vesktop-puppet-mock.sock pnpm control:mock
# Terminal 2: daemon (web UI on a free port)
VESKSLOP_SOCKET=/tmp/vesktop-puppet-mock.sock WEBUI_PORT=18789 pnpm control:bridge
# Terminal 3: check the API
curl -s http://127.0.0.1:18789/api/health
curl -s http://127.0.0.1:18789/api/state
curl -s "http://127.0.0.1:18789/api/users?q=zz"
curl -s http://127.0.0.1:18789/api/dms
curl -s "http://127.0.0.1:18789/api/voice/overview?guildId=100000000000000001"
curl -s http://127.0.0.1:18789/api/events # server-sent events
The page itself can be rendered headless for a visual check. With Xvfb running:
DISPLAY=:99 node_modules/.bin/electron /tmp/check.js --no-sandbox
where check.js loads http://127.0.0.1:18789/ in a BrowserWindow and logs page errors. This
was used to verify the 7a shell, the locked/unlocked composer, and the Settings slide-over.
The voice join and leave routes return 501 until the voice backend lands. See webui.md.
Process control and login work without the mock core:
curl -s http://127.0.0.1:18789/api/core/status
curl -s http://127.0.0.1:18789/api/auth
curl -s -X POST http://127.0.0.1:18789/api/auth/qr
curl -s -X POST http://127.0.0.1:18789/api/auth/qr/cancel
Starting /api/auth/qr opens a real remote-auth gateway session. Cancel it if you do not intend to
log in. Approving a login replaces the token file.
To test start and stop without Electron, run the daemon with a stand-in command:
VESKSLOP_SOCKET=/tmp/vesktop-puppet-mock.sock VESKSLOP_CORE_CMD="sleep 300" WEBUI_PORT=18789 pnpm control:bridge
curl -s -X POST http://127.0.0.1:18789/api/core/start
curl -s -X POST http://127.0.0.1:18789/api/core/stop
A real client start needs a display and the Electron shared libraries. On Linux, start Xvfb first.
The container needs libasound.so.2 for Electron. Extract the library locally when the system
package is not available.
CDP probe
The probe checks the CDP fallback transport. Start the client with
--remote-debugging-port=9222 first.
cd poc
pnpm cdp:probe
# Run one expression in the client.
CDP_EXPRESSION='Vencord.Webpack.Common.FluxDispatcher.dispatch({...})' pnpm cdp:dispatch
A good result shows the Vencord global, the Flux dispatcher, the current user, and one binding event.
Live test setup
The daemon can start the client for you, including a virtual display. This is the easy path.
cd poc
pnpm control:bridge
# Open http://<host>:8789/ and press Start in Settings → Core process.
# Or from the shell:
curl -s -X POST http://127.0.0.1:8789/api/core/start
curl -s http://127.0.0.1:8789/api/core/status
The daemon starts Xvfb when DISPLAY is unset, spawns electron ., and connects the bridge. The
token file seeds the login. The Settings slide-over shows the process state, the last exit code,
and the last 20 output lines. Set VESKSLOP_XVFB=0 to turn off auto-Xvfb.
Manual setup (no auto-start):
# 1. Start a virtual display. On Linux, install Xvfb or extract it locally.
Xvfb :99 -screen 0 1280x720x24 &
# 2. Start the stock client with a pinned profile and remote debugging.
DISPLAY=:99 \
VENCORD_USER_DATA_DIR=~/.config/veskslop \
electron . --no-sandbox --disable-gpu --disable-dev-shm-usage --remote-debugging-port=9222
# 3. Start the daemon.
cd poc && pnpm control:bridge
# 4. Check the health endpoint.
curl http://127.0.0.1:8788/health
Notes:
VENCORD_USER_DATA_DIRpins the Electron profile. The profile holds the client session.- Write
{"firstLaunch": false}to<DATA_DIR>/state.jsonbefore the first start. This step skips the first-launch window. TheVESKSLOP_HEADLESS=1flag does the same job. - The container needs
libasound.so.2for Electron. Installlibasound2or extract the library locally and pointLD_LIBRARY_PATHat it. - The live start was verified on 2026-09-13 through the web UI route.
Web UI browser smoke test
Optional. playwright-core is an optional dependency and the test needs a Chromium (PW_CHROMIUM,
or a Playwright cache under ~/.cache/ms-playwright). The daemon must be running with media data.
cd poc
pnpm test:ui
VESKSLOP_UI_URL=https://192.168.0.178:8789/ pnpm test:ui
It opens the Media tab, loads tiles, verifies no broken images, applies the cached-only filter,
checks the zoom slider, opens the Bot tab and asserts the backfill totals, the Bot/Puppet split and
the three settings groups render,
checks that the zoom slider changes the grid density, presses Reset, and fails on any console
error. A screenshot lands in /tmp/opencode/ui-smoke.png.
Slopbot live check
The bot core needs a real token and an invite to the test guild. The check logs in, lists guilds, and fetches one history page without printing the token:
cd poc
VESKSLOP_SLOPBOT_ENABLED=1 VESKSLOP_SLOPBOT_STATE=/path/to/slopbot.json \
pnpm test:slopbot:live <channelId> [guildId]
# Search total for a channel or range, without scraping:
VESKSLOP_SLOPBOT_ENABLED=1 VESKSLOP_SLOPBOT_STATE=/path/to/slopbot.json \
pnpm test:slopbot:count <channelId> <guildId> [afterIso] [beforeIso]
Sanctioned live target
The only permitted live-send target is user zzib (177117296140091393). Send a test message
with this command:
cd poc
pnpm poc:dm
Live results
The core bridge was verified live on 2026-09-11:
get_statereturned the real account and 7 guilds.send_dmdelivered a real DM to zzib.- A synthetic owner message flowed through the real event path. The daemon log held the entry with the parsed command.
- The client ran headless under Xvfb with the token seeded from the token file.
Core process control and the web QR session were verified live on 2026-09-13:
POST /api/core/startstarted the client, including auto-Xvfb, and the bridge connected.state.getreturned the real account and guilds.POST /api/core/stopexited with code 0 and released Xvfb.- The QR session reached
pendingwith a scannable QR and cancelled cleanly. A full approval needs a phone and was not run.
The CDP transport was verified earlier with the same checks.
Known gaps
| Gap | Effect |
|---|---|
| Live trigger tests need an incoming message. | Use the mock core, or send from zzib's account. |
reaction.add and the message ID return are mock-verified only. |
Confirm Discord's internal addReaction lookup on the first live run. |
| Polls and other chat commands are mock-verified only. | Poll is paused; the tests still cover the parser. |
message.read is cache-only. |
A channel that the client never opened returns zero messages. |
VOICE_STATE_UPDATE normalization is mock-verified only. |
Confirm the event fields on the first live run. |
| The web UI has no authentication. | Keep it on LAN or Tailscale only. |
| Voice actions, audio, and video are not started. | No coverage. |