Phoenix-based JSON API for reminders, backed by a small in-memory OTP service.
TestElixir.Reminderscontains pure domain logic.TestElixir.Reminders.Serverowns mutable state in a supervisedGenServer.TestElixirWebis the Phoenix boundary for routing, controllers, and JSON contracts.- Tests are split into domain tests and HTTP contract tests.
cd /Users/mz/sandbox/test-elixir
mix setupWith just:
just setupmix phx.serverOr with IEx:
iex -S mix phx.serverThe server listens on http://localhost:4000.
Opening http://localhost:4000/ in a browser now shows a small landing page
with the available API routes.
For the browser-based Connect Four UI, open:
http://localhost:4000/connect-four
Create a room in the lobby, then open the same room URL in a second browser
window with another player_id.
A third browser can join the same room as a spectator. Spectators receive live updates but cannot drop tokens.
Create a Connect Four room:
curl -X POST http://127.0.0.1:4000/api/connect-four/roomsThen connect players to the returned topic over Phoenix Channels:
ws://127.0.0.1:4000/socket/websocket?vsn=2.0.0&player_id=alice
Reconnect with the same player_id to reclaim the same seat after a disconnect.
Join topic:
["1","1","connect_four:ROOM_ID","phx_join",{}]Drop a token:
["2","2","connect_four:ROOM_ID","drop_token",{"column":0}]List reminders:
curl http://127.0.0.1:4000/api/remindersCreate a reminder:
curl -X POST http://127.0.0.1:4000/api/reminders \
-H 'content-type: application/json' \
-d '{"title":"Pay rent","due_on":"2026-03-15"}'Complete a reminder:
curl -X PATCH http://127.0.0.1:4000/api/reminders/1/completemix qualityThe alias runs:
mix format --check-formattedmix compile --warnings-as-errorsmix test
For static analysis with Dialyzer:
mix typecheckThe first run builds PLTs, so it is significantly slower than normal tests.
Task shortcuts via just:
just quality
just typecheck
just ci
just docker-buildStart the server in one shell:
just serverThen run k6 in another shell:
just bench-http
just bench-channel
just bench-game
just bench-spectator
just bench-soak
just bench-wasmBoth scripts accept k6-style environment overrides:
VUS=20 DURATION=30s just bench-http
VUS=50 DURATION=20s just bench-channel
VUS=10 DURATION=20s just bench-game
VUS=10 DURATION=20s just bench-spectator
just bench-soak
FIB_INPUT=20 BENCH_TIME_S=3 just bench-wasmbench-http measures the landing page, LiveView lobby HTML, reminders API, and
Connect Four room creation. bench-channel measures room creation plus Phoenix
Channel join latency for Connect Four. bench-game creates a room, joins Alice
and Bob over separate WebSockets, and plays a fixed 7-move match until red
wins. bench-spectator adds Carol as a spectator, verifies that spectator
actions are rejected, and measures how quickly match updates fan out to the
spectator socket. bench-soak is a longer-running wrapper around
bench-spectator with defaults tuned for local soak runs.
The spectator benchmarks also emit soak-friendly counters in the default k6 summary:
rooms_created_totalspectator_matches_completed_totalspectator_state_updates_totalspectator_rejections_totalmatch_moves_sent_total
bench-wasm compares two Elixir-side Wasm execution paths against the same
fixture module in priv/wasm/sample.wat:
Wasmex, which embeds Wasmtime through a Rust NIFPort + native wasmtime host, which keeps Wasmtime in an external process
The first run builds the native host under native/wasmtime_host/ with Cargo.
The benchmark prints two sections:
cold_add: start runtime + oneadd/2callhot_add/hot_fib: repeated calls against already-started runtimes
This repository now includes both Fly.io and Linode deployment settings.
Both paths use the same release-oriented Dockerfile.
GET /healthzis available for load balancer and platform health checks.- The current reminders store and Connect Four rooms live in memory.
- Because of that, production should stay on a single app node unless you externalize state or add node-aware room routing.
- Config file: fly.toml
- Release env hooks: rel/env.sh.eex
- Default region:
nrt(Tokyo)
Typical flow:
fly auth login
fly apps create mizchi-test-elixir
fly secrets set SECRET_KEY_BASE="$(mix phx.gen.secret)"
fly deployYou will probably want to change app and PHX_HOST in
fly.toml if mizchi-test-elixir is
already taken.
ENABLE_DISTRIBUTED_ERLANG is intentionally false by default. Turn it on
only after you externalize state or add node-aware room routing, and then set a
shared RELEASE_COOKIE secret.
- Compose setup: deploy/linode/compose.yaml
- Env template: deploy/linode/.env.example
- Deployment notes: deploy/linode/README.md
Typical flow:
cp deploy/linode/.env.example deploy/linode/.env
$EDITOR deploy/linode/.env
docker compose -f deploy/linode/compose.yaml up -d --buildFor Linode NodeBalancer, point HTTP health checks at /healthz. TLS
termination can stay at the balancer while Phoenix serves plain HTTP on port
4000.
Keep ENABLE_DISTRIBUTED_ERLANG=false for the current single-node setup.