Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"test:e2e-live": "vitest run --config vitest.e2e-live.config.ts",
"preflight": "unicity-infra-probe --network testnet",
"preflight:json": "unicity-infra-probe --network testnet --format json",
"lint": "eslint .",
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json"
},
Expand All @@ -28,6 +30,7 @@
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^8.24.0",
"@typescript-eslint/parser": "^8.24.0",
"@unicitylabs/infra-probe": "^0.3.0",
"@vitest/coverage-v8": "^3.0.0",
"eslint": "^9.20.0",
"tsup": "^8.4.0",
Expand Down
14 changes: 14 additions & 0 deletions test/e2e-live/global-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Vitest globalSetup for the e2e-live suite.
*
* Runs ONCE before any test file. If the preflight throws, vitest aborts
* the entire run before booting the host-manager binary or spawning any
* tenant containers — saving the multi-minute round-trip we'd otherwise
* eat on a relay outage or unreachable aggregator.
*/

import { runPreflight } from './preflight.js';

export async function setup(): Promise<void> {
await runPreflight();
}
71 changes: 54 additions & 17 deletions test/e2e-live/helpers/contracts.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,55 @@
/**
* E2E-live test infrastructure contracts.
*
* Five worktrees are filling in the helper modules in parallel; this file
* pins down their EXPORTED shapes so each can compile and test against the
* contract while peer impls land. Once all impls are in, this file becomes
* a stable interface registry — useful for understanding the harness without
* This file pins the EXPORTED shapes of the helper modules so each can
* compile and test against the contract independently. It's a stable
* interface registry — useful for understanding the harness without
* reading every helper.
*
* Architecture (the model the user actually wants for partner demos):
* Two architectures coexist in this directory during the in-progress
* migration to HMA-orchestrated tests (see SPHERE-CLI-EXTRACTION-PLAN
* §6.4 in agentic-hosting):
*
* Test → uses -- docker-helpers
* │ │
* ├── tenant-fixture (provisions traders) ─── docker run ────────┘
* │
* └── trader-ctl-driver (drives commands) ─── DM ──→ trader tenant
* ARCHITECTURE A — direct-docker (legacy, scheduled for removal):
*
* Crucially: NO host-manager, NO HMCP. Tests provision containers
* directly via the Docker daemon and drive trading via trader-ctl. This
* matches the production architecture where agentic-hosting only
* orchestrates LIFECYCLE; trading happens controller ↔ tenant directly.
* Test
* ├── tenant-fixture (provisions traders) ─── docker run ─→ container
* └── trader-ctl-driver (drives commands) ─── ACP DM ────→ tenant
*
* The tests in this flavor (basic-roundtrip, multi-agent, surplus-
* refund, etc.) bypass the Host Manager Agent (HMA) and call the
* local Docker daemon directly. This was correct before
* agentic-hosting Phase 5 shipped DM transport for the HMA — the
* HMA simply couldn't be driven over DMs. As of agentic-hosting
* PR #22 (merged 2026-05-03), it can.
*
* ARCHITECTURE B — HMA-orchestrated (target architecture):
*
* Test
* ├── manager-process (spawns HMA) ─── subprocess ─→ HMA
* ├── hma-spawn (sphere host spawn|list|stop) ─── HMCP DM ────→ HMA
* │ │
* │ docker create
* │ ↓
* │ tenant
* └── trader-ctl-driver / sphere trader … ─── ACP DM ────→ tenant
*
* The HMA owns lifecycle; trade ops still go controller→tenant
* directly so tenants stay host-agnostic. Existing direct-docker
* tests are migrated to this flavor incrementally; new tests
* should follow Architecture B (see hma-orchestrated.e2e-live.test.ts).
*
* The interfaces declared in this file (DockerContainer, runContainer,
* etc.) are part of Architecture A and will be deprecated once all
* direct-docker tests are migrated.
*/

// ============================================================================
// docker-helpers.ts — owns: provisioning + lifecycle of a single container
// (Architecture A — direct-docker, deprecated. Use hma-spawn.ts instead.)
// ============================================================================

/** @deprecated Architecture A. Use `SpawnedTenant` from `hma-spawn.ts`. */
export interface DockerRunOptions {
/** Fully-qualified image ref, e.g. ghcr.io/vrogojin/agentic-hosting/trader:v0.1 */
image: string;
Expand All @@ -42,6 +67,7 @@ export interface DockerRunOptions {
startTimeoutMs?: number;
}

/** @deprecated Architecture A. Use `SpawnedTenant` from `hma-spawn.ts`. */
export interface DockerContainer {
/** Docker container ID (sha256-ish, 64 chars). */
id: string;
Expand All @@ -55,28 +81,33 @@ export interface DockerContainer {
* Spawn a container with the given options. Returns once the daemon has
* accepted the run (NOT once the app inside is ready — provisioning code
* upstream polls for that).
* @deprecated Architecture A. Use `hostSpawn` from `hma-spawn.ts`.
*/
export type RunContainer = (opts: DockerRunOptions) => Promise<DockerContainer>;

/**
* Stop with SIGTERM, fall back to SIGKILL after `timeoutMs`. Idempotent —
* stopping an already-stopped container is a no-op.
* @deprecated Architecture A. Use `hostStop` from `hma-spawn.ts`.
*/
export type StopContainer = (id: string, timeoutMs?: number) => Promise<void>;

/** Remove the container record. Throws if container is still running. */
/** @deprecated Architecture A. Use `hostStop` from `hma-spawn.ts`. */
export type RemoveContainer = (id: string) => Promise<void>;

/** Read the last `lines` of stdout+stderr for diagnostic output on failure. */
/** @deprecated Architecture A. Read tenant logs via the HMA's `hm.inspect` instead. */
export type GetContainerLogs = (id: string, lines?: number) => Promise<string>;

/** Resolve true once container is RUNNING per `docker inspect`, else false on `timeoutMs` elapse. */
/** @deprecated Architecture A. Use `hostList` from `hma-spawn.ts`. */
export type WaitForContainerRunning = (id: string, timeoutMs?: number) => Promise<boolean>;

// ============================================================================
// trader-ctl-driver.ts — owns: invoke the canonical CLI as a subprocess
// (Architecture A — direct ACP DM. Use `sphere trader …` from sphere-cli
// via `runSphere()` from `helpers/sphere-cli.ts` once PR-C lands.)
// ============================================================================

/** @deprecated Architecture A. PR-C migrates trade-ops to `sphere trader …`. */
export interface TraderCtlOptions {
/** Tenant address: @nametag, DIRECT://hex, or 64-char hex pubkey. */
tenant: string;
Expand All @@ -90,6 +121,7 @@ export interface TraderCtlOptions {
json?: boolean;
}

/** @deprecated Architecture A. PR-C migrates trade-ops to `sphere trader …`. */
export interface TraderCtlResult {
/** Always 0 on success; see `error` field on non-zero. */
exitCode: number;
Expand All @@ -103,6 +135,7 @@ export interface TraderCtlResult {
* Run `trader-ctl <command> [args]` as a subprocess. Uses the bundled
* trader-ctl from this repo (../bin/trader-ctl). Throws ONLY on subprocess
* launch failure; non-zero exit codes are returned as `result.exitCode`.
* @deprecated Architecture A. PR-C migrates trade-ops to `sphere trader …`.
*/
export type RunTraderCtl = (
command: string,
Expand All @@ -112,8 +145,10 @@ export type RunTraderCtl = (

// ============================================================================
// tenant-fixture.ts — owns: provision N trader tenants ready to trade
// (Architecture A — direct-docker, deprecated. Use hma-spawn.ts instead.)
// ============================================================================

/** @deprecated Architecture A. Use `HostSpawnOpts` from `hma-spawn.ts`. */
export interface ProvisionTraderOptions {
/** Operator-friendly label, used in container name and logs. */
label: string;
Expand All @@ -131,6 +166,7 @@ export interface ProvisionTraderOptions {
readyTimeoutMs?: number;
}

/** @deprecated Architecture A. Use `SpawnedTenant` from `hma-spawn.ts`. */
export interface ProvisionedTenant {
/** trader-ctl-targetable address. Either DIRECT://hex or 64-char hex. */
address: string;
Expand All @@ -147,6 +183,7 @@ export interface ProvisionedTenant {
* wait until reachable. Returns a fully-armed tenant.
*
* On any step failure, partial resources are cleaned up before throwing.
* @deprecated Architecture A. Use `hostSpawn` from `hma-spawn.ts`.
*/
export type ProvisionTrader = (opts: ProvisionTraderOptions) => Promise<ProvisionedTenant>;

Expand Down
Loading
Loading