dev/e2e: disposable server for app e2e / agent UI testing

`dev/e2e/e2e.sh up` runs an isolated ImageApi (port 8090, own build dir,
db, thumbnails, HLS) next to a real local server, serving a copy of a
synthetic fixture library. Every fixture image has its path, date and
scenario burned into the pixels; manifest.json records expected dates,
GPS, cameras, tags and counts. `reset` restores the baseline.

- LOCAL_BIND_URL makes the hardcoded localhost:8088 listener
  configurable (default unchanged; empty disables it) so a second
  instance can start.
- create_user bin: the server has no registration route; idempotent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cameron CordesandClaude Opus 5.5 committed 2026-09-29 13:02:49 -04:00
1 parent da3901c564
commit f00a309a24
7 files changed
+719 -6

No files matched your search

+40
View File
@@ -0,0 +1,40 @@
//! Create a login user. The server has no registration route, so this is
//! how a fresh database (e.g. the `dev/e2e` harness) gets its first account.
//!
//! Idempotent: an existing username is left untouched and reported, so the
//! harness can re-run it on every reset. Runs pending migrations first so it
//! works against a database the server has never opened.
//!
//! Usage: `create_user <username> <password>` (reads `DATABASE_URL` from
//! `.env` / the environment like the server does).
use anyhow::{Result, bail};
use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations};
use image_api::connect;
use image_api::database::{SqliteUserDao, UserDao};
const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
fn main() -> Result<()> {
dotenv::dotenv().ok();
let args: Vec<String> = std::env::args().skip(1).collect();
let [username, password] = args.as_slice() else {
bail!("usage: create_user <username> <password>");
};
connect()
.run_pending_migrations(MIGRATIONS)
.map_err(|e| anyhow::anyhow!("failed to run migrations: {e}"))?;
let mut dao = SqliteUserDao::new();
if dao.user_exists(username) {
println!("user '{username}' already exists; leaving it unchanged");
return Ok(());
}
match dao.create_user(username, password) {
Some(_) => println!("created user '{username}'"),
None => bail!("failed to create user '{username}'"),
}
Ok(())
}