ArangoTestClientsCommand extends Kernel uses ArangoClientTestTrait
Live end-to-end integration test for the new `api/src/oihana/arango/clients/` client.
Talks to a real ArangoDB server (whose connection settings come from
the project's [arango] config, with CLI overrides) and exercises
the full surface of the client: connection, database lifecycle,
collection lifecycle, document CRUD, edges, AQL + cursor, indexes,
and error mapping.
Every step runs on its own ephemeral database
(arango_clients_test_<random>) created at setup and dropped at
cleanup — production data is never touched. The cleanup is in a
finally block so the database is dropped even on unexpected
exception. Pass --no-cleanup to keep the database around for
post-mortem inspection.
Coverage matrix:
| Step | Surface |
|---|---|
| 0 | (setup) build client, create test database |
| 1 | server: version + time + availability + listDatabases |
| 2 | database: exists, collections() |
| 3 | collection: create/properties/rename/drop |
| 4 | documents: insert/get/exists/count/update/replace/remove/truncate |
| 5 | edge collection: create + inEdges/outEdges/edges |
| 6 | AQL + Cursor (single batch + lazy multi-batch) + pipeline + explain / parse |
| 7 | indexes: PersistentIndex (unique/sparse) + TtlIndex + drop |
| 8 | error mapping: HttpException on 404, ConflictException on 409 |
| 9 | auth: login / useBearerAuth / useBasicAuth |
| 10 | import: bulk JSON Lines + onDuplicate + overwrite + details |
| 11 | transactions: begin / commit / abort / status / exists / step / list / withTransaction |
| 12 | graphs: create / get / exists / vertex+edge collection mgmt / edge definition mgmt / drop |
| 13 | analyzers: 4 types CRUD (identity/text/norm/stem) + listing + get round-trip + drop force |
| 14 | views (arangosearch): create + links + AQL SEARCH/PHRASE/BM25 + properties round-trip + drop |
Usage:
composer test:clients
composer test:clients -- --step=1-3
composer test:clients -- --step=4
composer test:clients -- --no-cleanup --endpoint=tcp://127.0.0.1:8529
# Long form (equivalent — bypasses Composer):
php bin/console.php command:arango:test:clients --step=4
Tags
Table of Contents
Constants
- ARANGO_CONFIG : string = 'arangoConfig'
- DI key carrying the `[arango]` configuration array.
- MAX_STEP : int = 14
- Total number of business steps exposed by the `--step` option.
- NAME : string = 'command:arango:test:clients'
- The default name of the command.
- OPTION_DATABASE : string = 'database'
- OPTION_ENDPOINT : string = 'endpoint'
- OPTION_NO_CLEANUP : string = 'no-cleanup'
- OPTION_PASSWORD : string = 'password'
- OPTION_STEP : string = 'step'
- Name of the `--step` option used to select a subset of steps to run.
- OPTION_USER : string = 'user'
- TEST_DB_PREFIX : string = 'arango_clients_test_'
- Prefix of the ephemeral database created for the run.
Properties
- $arangoConfig : array<string, mixed>
- Resolved configuration array, captured at construction time.
Methods
- __construct() : mixed
- buildArangoClient() : ArangoClient|null
- Builds an {@see ArangoClient} from the trait's stored config, applying any CLI override from `$input`. Reports the resolved endpoint / user / database to `$io` for transparency. Returns null when the config is missing or incomplete.
- check() : array{0: int, 1: int}
- Reports a single assertion to `$io` and returns the updated `[ passed , errors ]` counters.
- configure() : void
- Configures the current command.
- configureArangoTestOptions() : void
- Adds the trait's CLI options to the consuming command.
- execute() : int
- Executes the current command.
- initializeArangoTestClient() : void
- Captures the `[arango]` configuration injected through the command's `init` array. Called from the consuming command's constructor.
- runCleanup() : void
-
runSetup()
: array{0: int, 1: int, 2: array
} -
runStep1()
: array{0: int, 1: int, 2: array
} - runStep10() : array<string|int, mixed>
- Exercises {@see \oihana\arango\clients\collection\Collection::import()} — the bulk-load fast path that streams JSON Lines to the dedicated `/_api/import` endpoint.
- runStep11() : array<string|int, mixed>
- Exercises the streaming-transaction surface shipped in Lot 7.0b:
- runStep12() : array<string|int, mixed>
- Exercises the named-graph surface shipped in Lot 7.1a:
- runStep13() : array<string|int, mixed>
- Exercises the ArangoSearch analyzer surface shipped in Lot 7.2a:
- runStep14() : array<string|int, mixed>
- Exercises the ArangoSearch view surface shipped in Lot 7.2b plus the resulting full-text query path through AQL `SEARCH`:
- runStep2() : array<string|int, mixed>
- runStep3() : array<string|int, mixed>
- runStep4() : array<string|int, mixed>
- runStep5() : array<string|int, mixed>
- runStep6() : array<string|int, mixed>
- runStep7() : array<string|int, mixed>
- runStep8() : array<string|int, mixed>
- runStep9() : array<string|int, mixed>
- Exercises the runtime auth surface added in Lot 6.2c: - `login(user, password)` against `/_open/auth` returns a JWT and makes the transport carry it on subsequent requests, - a second {@see ArangoClient} built bearer-only (no basic credentials) can talk to the server through the same JWT, - `useBearerAuth(null)` falls back to the configured basic credentials, - `useBasicAuth(user, password)` switches the identity at runtime.
- shouldCleanup() : bool
- Returns true when `--no-cleanup` is NOT set.
- stringOption() : string|null
- Reads a string CLI option, returning null when missing or empty.
- waitForSearchHits() : array<int, string>
- Polls an AQL `SEARCH` query until it returns the expected number of rows or the deadline elapses (~2 seconds in 10 attempts of 200 ms).
Constants
ARANGO_CONFIG
DI key carrying the `[arango]` configuration array.
public
string
ARANGO_CONFIG
= 'arangoConfig'
Forwarded through the init array at construction time. The
trait reads it via initializeArangoTestClient() and keeps
the resolved options for later builds.
MAX_STEP
Total number of business steps exposed by the `--step` option.
public
int
MAX_STEP
= 14
NAME
The default name of the command.
public
string
NAME
= 'command:arango:test:clients'
OPTION_DATABASE
public
string
OPTION_DATABASE
= 'database'
OPTION_ENDPOINT
public
string
OPTION_ENDPOINT
= 'endpoint'
OPTION_NO_CLEANUP
public
string
OPTION_NO_CLEANUP
= 'no-cleanup'
OPTION_PASSWORD
public
string
OPTION_PASSWORD
= 'password'
OPTION_STEP
Name of the `--step` option used to select a subset of steps to run.
public
string
OPTION_STEP
= 'step'
OPTION_USER
public
string
OPTION_USER
= 'user'
TEST_DB_PREFIX
Prefix of the ephemeral database created for the run.
private
string
TEST_DB_PREFIX
= 'arango_clients_test_'
Properties
$arangoConfig
Resolved configuration array, captured at construction time.
protected
array<string, mixed>
$arangoConfig
= []
Methods
__construct()
public
__construct(string|null $name[, Container|null $container = null ][, array<string|int, mixed> $init = [] ]) : mixed
Parameters
- $name : string|null
- $container : Container|null = null
- $init : array<string|int, mixed> = []
Tags
buildArangoClient()
Builds an {@see ArangoClient} from the trait's stored config, applying any CLI override from `$input`. Reports the resolved endpoint / user / database to `$io` for transparency. Returns null when the config is missing or incomplete.
protected
buildArangoClient(InputInterface $input, SymfonyStyle $io) : ArangoClient|null
Parameters
- $input : InputInterface
- $io : SymfonyStyle
Return values
ArangoClient|nullcheck()
Reports a single assertion to `$io` and returns the updated `[ passed , errors ]` counters.
protected
check(SymfonyStyle $io, mixed $condition, string $label, int $passed, int $errors) : array{0: int, 1: int}
Same contract as the auth:test:* ApiAssertionsTrait::check(),
inlined here so the new arango command does not depend on the
auth namespace.
Parameters
- $io : SymfonyStyle
- $condition : mixed
- $label : string
- $passed : int
- $errors : int
Return values
array{0: int, 1: int}configure()
Configures the current command.
protected
configure() : void
configureArangoTestOptions()
Adds the trait's CLI options to the consuming command.
protected
configureArangoTestOptions() : void
execute()
Executes the current command.
protected
execute(InputInterface $input, OutputInterface $output) : int
Parameters
- $input : InputInterface
- $output : OutputInterface
Tags
Return values
intinitializeArangoTestClient()
Captures the `[arango]` configuration injected through the command's `init` array. Called from the consuming command's constructor.
protected
initializeArangoTestClient(array<string, mixed> $init) : void
Parameters
- $init : array<string, mixed>
runCleanup()
protected
runCleanup(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state) : void
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
runSetup()
protected
runSetup(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array{0: int, 1: int, 2: array}
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array{0: int, 1: int, 2: arrayrunStep1()
protected
runStep1(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array{0: int, 1: int, 2: array}
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array{0: int, 1: int, 2: arrayrunStep10()
Exercises {@see \oihana\arango\clients\collection\Collection::import()} — the bulk-load fast path that streams JSON Lines to the dedicated `/_api/import` endpoint.
protected
runStep10(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
- 3 documents imported through a plain
import()populate the collection and returncreated: 3, every other counter at 0, onDuplicate: ignorelets the same batch be re-imported with the duplicates surfaced through theignoredcounter,onDuplicate: updatepatches the existing documents (the server bumpsupdated) and the change is observable through a subsequentdocument()round-trip,onDuplicate: error+details: truesurfaces every duplicate as a server-side error message in ImportResult::$details,overwrite: truetruncates the collection before importing — Collection::count() confirms the previous content is gone,- an empty input still hits the server (no client-side short-circuit) and produces a zeroed ImportResult.
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep11()
Exercises the streaming-transaction surface shipped in Lot 7.0b:
protected
runStep11(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Database::beginTransaction()returns aTransactionwhoseidis the server-assigned id and whose initialstatus()isrunning.Transaction::exists()is true for the live trx and false for a bogus id.Transaction::step()propagates thex-arango-trx-idheader transparently: aCollection::insert()called inside the callback is part of the transaction (an outside reader does NOT see the pending row through the server-side count — this would only be testable with strict isolation; we instead verify the row IS visible inside the transaction).Transaction::commit()makes the staged write durable.- A second transaction with
abort()discards its staged write — the count is unchanged after the abort. Database::listTransactions()includes the running trx while it is open.
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep12()
Exercises the named-graph surface shipped in Lot 7.1a:
protected
runStep12(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Database::createGraph()posts a graph with edge definitions and returns aGraphhandle.Graph::exists()/get()/edgeDefinitions()/vertexCollections()/edgeCollections()/orphanCollections()reflect the server state.Database::graphs()/listGraphs()include the freshly created graph.- Vertex collection management (
addVertexCollection/removeVertexCollection) lives on its own (the collection becomes an "orphan" until referenced by an edge definition). - Edge definition management (
addEdgeDefinition/replaceEdgeDefinition/removeEdgeDefinition). Graph::drop(dropCollections: true)wipes the graph AND its underlying vertex/edge collections.
The vertex/edge CRUD (insert/get/replace/update/remove via
gharial endpoints) lands separately on GraphVertexCollection /
GraphEdgeCollection in Lot 7.1b — Step 12 will be enriched
then.
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep13()
Exercises the ArangoSearch analyzer surface shipped in Lot 7.2a:
protected
runStep13(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Database::createAnalyzer()posts an analyzer with typed options and returns anAnalyzerhandle.- All four V1 must-have types (
identity,text,norm,stem) round-trip throughAnalyzer::get()with theirpropertiespreserved server-side. Analyzer::exists()distinguishes a created analyzer from a missing one (404 swallowed cleanly).Database::listAnalyzers()andDatabase::analyzers()include the user-created entries (each name prefixed with the test database name server-side, e.g.mydb::raw).Analyzer::drop()removes the analyzer, with and without theforceflag (force is harmless when no view / inverted index references the analyzer).
The arangosearch View + AQL SEARCH live coverage lands
separately in Step 14 with Lot 7.2b.
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep14()
Exercises the ArangoSearch view surface shipped in Lot 7.2b plus the resulting full-text query path through AQL `SEARCH`:
protected
runStep14(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Database::createView()posts an arangosearch view withArangoSearchLinktyped links and returns a View handle.View::get()returns the simple description (4 top-level fields);View::properties()returns the full per-view configuration including the normalisedlinksecho.Database::views()/listViews()include the freshly created view.- The actual indexing path: insert documents in a linked
collection, then run AQL
SEARCH ANALYZER(...)andSEARCH PHRASE(...)queries through the view; both must return the expected documents.BM25()scoring round-trips too. View::updateProperties()(PATCH) bumps thecleanupIntervalStep;properties()echoes the new value.View::drop()removes the view (the source collection itself is untouched).
ArangoSearch indexes documents asynchronously — the view
created here is configured with short commitIntervalMsec /
consolidationIntervalMsec so the queries can be polled
inside a short window. waitForSearchHits() retries the
SEARCH up to ~2 seconds before giving up.
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep2()
protected
runStep2(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep3()
protected
runStep3(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep4()
protected
runStep4(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep5()
protected
runStep5(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep6()
protected
runStep6(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep7()
protected
runStep7(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep8()
protected
runStep8(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>runStep9()
Exercises the runtime auth surface added in Lot 6.2c: - `login(user, password)` against `/_open/auth` returns a JWT and makes the transport carry it on subsequent requests, - a second {@see ArangoClient} built bearer-only (no basic credentials) can talk to the server through the same JWT, - `useBearerAuth(null)` falls back to the configured basic credentials, - `useBasicAuth(user, password)` switches the identity at runtime.
protected
runStep9(SymfonyStyle $io, ArangoClient $client, array<string|int, mixed> $state, int $passed, int $errors) : array<string|int, mixed>
Skipped (with a single passing assertion explaining why) when no
basic credentials are configured — /_open/auth requires them.
Parameters
- $io : SymfonyStyle
- $client : ArangoClient
- $state : array<string|int, mixed>
- $passed : int
- $errors : int
Tags
Return values
array<string|int, mixed>shouldCleanup()
Returns true when `--no-cleanup` is NOT set.
protected
shouldCleanup(InputInterface $input) : bool
Parameters
- $input : InputInterface
Return values
boolstringOption()
Reads a string CLI option, returning null when missing or empty.
private
stringOption(InputInterface $input, string $name) : string|null
Parameters
- $input : InputInterface
- $name : string
Return values
string|nullwaitForSearchHits()
Polls an AQL `SEARCH` query until it returns the expected number of rows or the deadline elapses (~2 seconds in 10 attempts of 200 ms).
private
waitForSearchHits(Database $db, AqlQuery $query, int $expectedCount) : array<int, string>
ArangoSearch indexes documents asynchronously: a freshly
inserted doc is not immediately visible to SEARCH. The
view is configured with short commit / consolidation
intervals in Step 14, but a small wait is still needed.
Parameters
Tags
Return values
array<int, string> —The result rows (typically _key strings).