testserver
A real Elixir + Tidewave server, booted on a unix socket, so tw's subcommands
are tested against actual MCP responses instead of a mock.
server.go is the Go harness. app/ is the fixture application.
Usage
TestMain in main_test.go starts one fixture for the whole package:
srv, err := testserver.Start()
defer srv.Stop()
srv.Socket() // pass to tw via --socket
Start fetches deps and compiles on the first run (~10s), then reuses app/_build
(~0.5s). It waits until the server answers a JSON-RPC ping before returning, so
there is no sleep-and-hope. If mix is not on PATH, Available reports false and
the integration tests skip; any other failure is returned as an error, because a
silent skip is how the previous version of this fixture hid the fact that it never
worked.
Fixture data
app seeds two tables before the listener starts, so data is guaranteed present
once the socket is connectable:
users — Alice, Bob
posts — "Hello World", "Elixir Rocks"
TestApp.User and TestApp.Post are Ecto schemas, which is what
tw schemas reports.
Constraints worth knowing
These are all load-bearing; changing them breaks tests in non-obvious ways.
config :test_app, ecto_repos: [TestApp.Repo] — Tidewave only registers the
execute_sql_query and get_ecto_schemas tools when the host app declares
:ecto_repos. Without it, tw sql and tw schemas fail with "Method not found".
allow_remote_access: true on the Tidewave plug — Tidewave's guard only
accepts loopback IPv4/IPv6 peers, and a unix-domain peer has no address to match.
Access is gated instead by filesystem permissions on the socket, which the harness
puts in a private temp directory.
allowed_origins: ["//localhost"] — Tidewave otherwise derives allowed origins
from a Phoenix endpoint, and this fixture deliberately has none. Tidewave needs
only plug, so Phoenix is not a dependency.
- The listener starts last in
TestApp.Application, after the repo and seed.
- One SQL statement per query in
TestApp.Seed — the SQLite driver rejects
multiple statements in a single call.
- The socket lives under
/tmp — unix socket paths are capped near 104 bytes on
macOS, which the default temp directory can exceed.
mix is a shell wrapper that execs the VM as a child, so Stop signals the
whole process group. Signalling the wrapper alone leaves the VM running.
- Tidewave needs Mix at runtime (
Mix.Project.build_path/0 and friends), hence
mix run --no-halt rather than a release.