Releases: PT-Perkasa-Pilar-Utama/testate
Release list
1.2.0
The query console completes what you type. Released 11 September 2026; the full record is in the CHANGELOG.
New
- SQL completion and highlighting. The console is a CodeMirror 6 editor. It completes keywords in the engine's dialect, PostgreSQL, MySQL or MariaDB, and the adapter's tables and columns under their schema, so
public.usersand bareusersboth complete. Strings, numbers, keywords and comments are coloured in the app's own tokens, in both themes. - Mongo completion. The filter and the pipeline complete operators and stages on
$and the chosen collection's fields on a bare word. Fields come from the collection's own documents once the name in the Collection box matches one, and that box offers the collection names. - Nothing waits on it. The schema loads beside the editor, never in front of it, and the editor itself is a separate chunk fetched the first time a console is opened. The app's own bundle stays the size it was.
Changed
- The compose MongoDB for development publishes on 17017, not 27017, beside Postgres on 15432 and MySQL on 13306, so it no longer collides with any other MongoDB on the machine. MongoDB's own default of 27017 stays in the adapter form and the protocol defaults. Only
deploy/compose.engines.ymlusers are affected; a production adapter keeps whatever port it was given.
Upgrade
docker pull ghcr.io/pt-perkasa-pilar-utama/testate:1.2.0No migration, no new setting. Compare v1.1.1...v1.2.0.
Credits
1.1.1
One fix, released 9 September 2026, for pointing a Testate container at a Supabase database. The full record is in the CHANGELOG.
Fixed
- An IPv6-only cloud host names the IPv4 way out. The direct Supabase host,
db.<ref>.supabase.co, has only an IPv6 address. Docker Desktop's resolver drops it on a machine without IPv6, so the probe said the name does not resolve and pointed at container names andhost.docker.internal, neither of which applies. A dotted name that fails to resolve inside a container now leads with the IPv6-only cause and the provider's IPv4 endpoint: on Supabase, the Session pooler on port 5432 with userpostgres.<ref>. - A missing IPv6 route is an unreachable host, not a failed batch. Where the resolver does return the address, Linux Docker fails the connect with
ENETUNREACH. Postgres and MySQL now report that as unreachable, like a refused connection. - The connecting guide had Supabase backwards. It said to use the direct host and avoid the pooler. Section C now gives the Session pooler values and keeps the transaction pooler on 6543 as the one to avoid.
Upgrade
docker pull ghcr.io/pt-perkasa-pilar-utama/testate:1.1.1No migration, no new setting. Compare v1.1.0...v1.1.1.
Credits
v1.1.0
v1.0.1
v1.0.0
Full Changelog: v1.0.0-beta...v1.0.0
v1.0.0-beta
What's Changed
- build(deps): bump docker/login-action from 4.4.0 to 4.6.0 by @dependabot[bot] in #1
- build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 by @dependabot[bot] in #2
- build(deps): bump github/codeql-action/upload-sarif from 4.37.0 to 4.37.9 by @dependabot[bot] in #3
- build(deps): bump github/codeql-action/analyze from 4.37.0 to 4.37.9 by @dependabot[bot] in #4
- build(deps): bump github/codeql-action/init from 4.37.0 to 4.37.9 by @dependabot[bot] in #5
- build(deps): bump the actions group with 2 updates by @dependabot[bot] in #6
New Contributors
- @dependabot[bot] made their first contribution in #1
Full Changelog: v1.1.0-alpha...v1.0.0-beta
1.1.0-alpha
Released 2026-08-31. Testate stops fighting your other projects for ports, the sidebar folds away, and a table's actions stay reachable however far right you scroll. Full record in the changelog, and the compare against the previous release is here.
Breaking changes
The default port moves from 3000 to 7378. Port 3000 belongs to Next, Rails, Express and half the JavaScript world, so Testate collided with whatever else you had running.
Nothing changes if you already set PORT. If you rely on the default, the container's internal port moved and the mapping has to move with it:
- ports: ["3000:3000"]
+ ports: ["7378:7378"]Two ways out if that is inconvenient: map 3000:7378 to keep the outside address you already publish, or set PORT=3000 and change nothing at all.
The dev server moved from 5173 to 7379 for the same reason. That one only affects you if you run Testate from source.
New
- The sidebar folds away: it took 15rem whether or not you were using it, which on a grid with twenty columns is the difference between reading a row and scrolling for it. The choice is remembered per browser.
- Action columns stay put: Edit and Delete used to scroll off the right edge of a wide table, so you found the row you wanted and then scrolled back to act on it. The column is frozen against the right edge in all thirteen tables that have one, with the row's hover carried across the seam.
Fixed
- The engine compose stack no longer fails to start at random. Its databases published on host ports inside Linux's ephemeral range, which begins at 32768, so an unrelated outgoing connection could be holding one at the moment Docker tried to bind it. They publish below the range now. This only ever affected running the test stack, never a deployment.
- The FTP fixture container survives a cold start. It quits if its daemon is slow to come up, which it often is when eight containers start at once, and it always works on the next attempt. It is told to make that attempt now.
Upgrading
docker pull ghcr.io/pt-perkasa-pilar-utama/testate:1.1.0-alphaChange your port mapping as above. Data lives on the volume and the image swap does not touch it.
Still true
The limits have not moved: databases are reset one after another rather than together, Testate only touches the databases you add to it, and microservices are untested. Read What it does not do before installing.
The one-line patch to @solidjs/signals is still in place, and still upstream as solidjs/solid#3143.
1.0.2-alpha
Released 2026-08-31. Testate now ships for linux/arm64 as well as linux/amd64, so Apple Silicon and ARM servers run it natively instead of under emulation. Full record in the changelog, and the compare against the previous release is here.
Performance
Cold start on an Apple M-series host, from docker run to a 204 on /api/v1/health/live, against an empty volume. Same machine, same command, median of three runs each.
| Cold start (Apple Silicon, empty volume) | 1.0.1-alpha | 1.0.2-alpha | Change |
|---|---|---|---|
| time to first healthy response | 2478 ms | 1249 ms | ~50% faster |
| image size | 39 MB | 39 MB | unchanged |
| platforms in the tag | amd64 | amd64, arm64 | arm64 added |
The speed is not an optimisation. It is the cost of emulation going away: 1.0.1-alpha had no arm64 image, so an ARM host ran the amd64 one through translation. On an amd64 host nothing changed.
New
linux/arm64images: the tag is a manifest list, sodocker pullpicks the right one and the command in the README is unchanged. Apple Silicon no longer printsWARNING: The requested image's platform (linux/amd64) does not match the detected host platform.- Both architectures are built natively: one job per architecture on a runner of that architecture, so nothing is cross-built or emulated during the build either. Bun and SQLite under emulation are a source of failures, not just delay.
- Each image has to boot before it is published: every build job starts its own slimmed image on a fresh volume and waits for a 204 before pushing anything. Nothing between the build and the registry used to start the container, which is how 1.0.0-alpha shipped unable to boot.
Fixed
- A half-finished deploy can no longer publish a single-platform image at the release tag. Per-architecture images push under a staging tag; the release tag and
latestare only written once both architectures exist, by a job that then re-reads the published manifest and fails if either is missing.
Upgrading
Nothing to do. Pull the new tag:
docker pull ghcr.io/pt-perkasa-pilar-utama/testate:1.0.2-alphaData lives on the volume and is untouched by the image swap. If you are on 1.0.0-alpha, that image cannot boot at all and this is the upgrade off it.
Still true from 1.0.1-alpha
The limits have not moved: databases are reset one after another rather than together, Testate only touches the databases you add to it, and microservices are untested. Read What it does not do before installing.
The one-line patch to @solidjs/signals is still in place, and still upstream as solidjs/solid#3143.
1.0.1-alpha
Released 2026-08-31. Testate is a self-hosted tool for QA teams. It takes data-only snapshots of the databases behind a system under test, restores them on demand, diffs them, imports fixtures, and lets an AI agent read them. One container, one volume, on your own network. Overview at https://pt-perkasa-pilar-utama.github.io/testate/, full record in the changelog.
This is the first usable release. 1.0.0-alpha was published and cannot boot: docker-slim removed /data from the image, so the first run against an empty volume refused with TESTATE_DATA_DIR is not writable. Use this version instead.
Install
docker run -d --name testate -p 3000:3000 -v testate-data:/data \
-e TESTATE_SECRETS_ACTIVE_KEY="$(openssl rand -base64 32)" \
-e TESTATE_ADMIN_PASSWORD=change-me-now-1234 \
ghcr.io/pt-perkasa-pilar-utama/testate:1.0.1-alphaFor a deployment you keep, use Compose and an .env file: How to install. To run from source instead, copy apps/api/.env.example to apps/api/.env and use bun run dev.
Quick start
Open http://localhost:3000 and sign in as admin with the password you set. Testate makes you change it on the first login.
The API answers as soon as the container is up:
$ curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/api/v1/health/live
204
$ curl -s -X POST http://localhost:3000/api/v1/auth/login \
-H 'content-type: application/json' -H 'X-Testate-Request: 1' \
-d '{"username":"admin","password":"change-me-now-1234"}'
{"data":{"user":{"id":"01a05603-8398-7668-975f-69b40b380d6a","username":"admin","display_name":"admin","role":"admin"},"must_change_password":true}}Then add a database under Adapters, snapshot it under States, break something, and put it back under Checkouts. Where to point the adapter's host is the one thing that catches people out, so read Connecting to a database first.
What it does
- Snapshots that hold data, not schema: one state covers every database in a project, and a checkout restores them and reports what happened per database.
- Diffs you can read: compare two states, or a state against the live database. The dialog leads with the totals and hides the tables that did not move, so a change in one table out of two hundred is the first thing you see.
- A grid that behaves like a database client: filter, sort, page by keyset, follow a foreign key into the next table. Write mode stashes the table before the first write.
- Fixtures with a dry run: upload a CSV or XLSX, map the columns, dry-run it, then import. Rejected rows come back as a file you can fix and re-import.
- Read-only agent access over MCP: point Claude or any MCP client at
/api/v1/mcpwith an agent token. It getslist_tables,describe_table,page_rows,run_readonly_query,extract_fixtureand more, capped at 1000 rows and 15 seconds a query. There is no write tool, and column policies mask values before the agent sees them. - An agent that arrives knowing how to use it: the first tool in
tools/listishelpand the first resource istestate://guide, both serving the same document: how the pieces relate, the order to call things in, and the limits that refuse a call. Every tool carries its own description, so an agent reading the list knows what each one answers. - A REST API under every screen:
POST /projects/{slug}/checkoutsresets a database before a test run. The contract is served at/api/v1/openapi.jsonby a running instance.
Engines
| Tier | Engines | What you get |
|---|---|---|
| Tabular | PostgreSQL, MySQL, MariaDB | view, snapshot, checkout, diff, extract, edit, import |
| Document | MongoDB | view, snapshot, checkout, diff, extract |
| Files | S3, SFTP, FTP | view, download |
| REST | any HTTP API | saved requests, hooks around checkouts |
Every engine in that table is real. None is a stand-in, and each has a contract suite that runs against the actual server rather than a mock.
Known limits
Read What it does not do before you install this. The three that bite hardest:
- Databases go one at a time: even inside one project, Testate snapshots and restores them in sequence. Each is correct alone, the set is not guaranteed to line up. Keep the app idle while it runs.
- It only resets what you add: an untracked database survives a reset and Testate cannot warn you, because it has never heard of it.
- Microservices are untested: every limit above compounds. Stay inside one service, and add the rest as read-only adapters.
It also ships a one-line patch to @solidjs/signals, because a stale transaction stamp made the scheduler spin forever on the data grid. The evidence is in patches/ and the same fix is upstream as solidjs/solid#3143. It goes away when a Solid release carries it.
Credits
Built on Bun, Hono (MIT), SolidJS (MIT), valibot (MIT), Cloudflare Kumo (MIT), ssh2 (MIT), basic-ftp (MIT), and the MongoDB Node driver (Apache-2.0). The interface follows GitHub's design language and uses Mona Sans (OFL-1.1). Images are slimmed with SlimToolkit.
Testate is MIT licensed.