diff --git a/.github/workflows/testkit.yml b/.github/workflows/testkit.yml index 0f60231b..523f1dfc 100644 --- a/.github/workflows/testkit.yml +++ b/.github/workflows/testkit.yml @@ -68,11 +68,5 @@ jobs: - name: Run integration tests run: | - docker compose up -d --remove-orphans --wait --no-build \ - server1 \ - server2 \ - server3 \ - server4 \ - testkit_backend - - docker compose run --rm testkit ./testkit.sh + docker compose up -d --remove-orphans --wait --no-build testkit_backend neo4j + docker compose up testkit diff --git a/docker-compose.yml b/docker-compose.yml index abf14b8d..59efc216 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -119,6 +119,7 @@ services: testkit: image: python:3.13 + command: ./testkit.sh volumes: - .:/opt/project working_dir: /opt/project/testkit-backend @@ -131,6 +132,9 @@ services: TEST_DRIVER_NAME: php TEST_DRIVER_REPO: /opt/project TEST_BACKEND_HOST: testkit_backend + TEST_STUB_HOST: testkit + depends_on: + - testkit_backend testkit_backend: <<: *common-php @@ -141,7 +145,5 @@ services: - neo4j extra_hosts: - "host.docker.internal:host-gateway" - depends_on: - - neo4j ports: - "9876:9876" diff --git a/src/Authentication/BasicAuth.php b/src/Authentication/BasicAuth.php index a1d7bdcc..a3f0939a 100644 --- a/src/Authentication/BasicAuth.php +++ b/src/Authentication/BasicAuth.php @@ -13,6 +13,8 @@ namespace Laudis\Neo4j\Authentication; +use Bolt\enum\Signature; +use Bolt\protocol\Response; use Bolt\protocol\V4_4; use Bolt\protocol\V5; use Bolt\protocol\V5_1; @@ -22,8 +24,8 @@ use Exception; use Laudis\Neo4j\Bolt\BoltMessageFactory; use Laudis\Neo4j\Common\Neo4jLogger; -use Laudis\Neo4j\Common\ResponseHelper; use Laudis\Neo4j\Contracts\AuthenticateInterface; +use Laudis\Neo4j\Exception\Neo4jException; use Psr\Http\Message\UriInterface; /** @@ -51,7 +53,7 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $ $helloMetadata = ['user_agent' => $userAgent]; $factory->createHelloMessage($helloMetadata)->send(); - $response = ResponseHelper::getResponse($protocol); + $response = self::getResponse($protocol); $credentials = [ 'scheme' => 'basic', @@ -60,7 +62,7 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $ ]; $factory->createLogonMessage($credentials)->send(); - ResponseHelper::getResponse($protocol); + self::getResponse($protocol); /** @var array{server: string, connection_id: string, hints: list} */ return $response->content; @@ -74,9 +76,10 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $ ]; $factory->createHelloMessage($helloMetadata)->send(); + $response = self::getResponse($protocol); /** @var array{server: string, connection_id: string, hints: list} */ - return ResponseHelper::getResponse($protocol)->content; + return $response->content; } /** @@ -86,7 +89,17 @@ public function logoff(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): void { $factory = $this->createMessageFactory($protocol); $factory->createLogoffMessage()->send(); - ResponseHelper::getResponse($protocol); + $protocol->getResponse(); + } + + public static function getResponse(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): Response + { + $response = $protocol->getResponse(); + if ($response->signature === Signature::FAILURE) { + throw Neo4jException::fromBoltResponse($response); + } + + return $response; } public function toString(UriInterface $uri): string diff --git a/src/Authentication/KerberosAuth.php b/src/Authentication/KerberosAuth.php index e50a27dc..7e524e70 100644 --- a/src/Authentication/KerberosAuth.php +++ b/src/Authentication/KerberosAuth.php @@ -13,6 +13,8 @@ namespace Laudis\Neo4j\Authentication; +use Bolt\enum\Signature; +use Bolt\protocol\Response; use Bolt\protocol\V4_4; use Bolt\protocol\V5; use Bolt\protocol\V5_1; @@ -22,8 +24,8 @@ use Exception; use Laudis\Neo4j\Bolt\BoltMessageFactory; use Laudis\Neo4j\Common\Neo4jLogger; -use Laudis\Neo4j\Common\ResponseHelper; use Laudis\Neo4j\Contracts\AuthenticateInterface; +use Laudis\Neo4j\Exception\Neo4jException; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\UriInterface; use Psr\Log\LogLevel; @@ -62,7 +64,7 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $ $factory->createHelloMessage(['user_agent' => $userAgent])->send(); - $response = ResponseHelper::getResponse($protocol); + $response = self::getResponse($protocol); $this->logger?->log(LogLevel::DEBUG, 'LOGON', ['scheme' => 'kerberos', 'principal' => '']); @@ -72,7 +74,7 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $ 'credentials' => $this->token, ])->send(); - ResponseHelper::getResponse($protocol); + self::getResponse($protocol); /** * @var array{server: string, connection_id: string, hints: list} @@ -80,6 +82,16 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $ return $response->content; } + public static function getResponse(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): Response + { + $response = $protocol->getResponse(); + if ($response->signature === Signature::FAILURE) { + throw Neo4jException::fromBoltResponse($response); + } + + return $response; + } + public function toString(UriInterface $uri): string { return sprintf('Kerberos %s@%s:%s', $this->token, $uri->getHost(), $uri->getPort() ?? ''); diff --git a/src/Authentication/NoAuth.php b/src/Authentication/NoAuth.php index 80b1b1b9..eaff0693 100644 --- a/src/Authentication/NoAuth.php +++ b/src/Authentication/NoAuth.php @@ -13,6 +13,8 @@ namespace Laudis\Neo4j\Authentication; +use Bolt\enum\Signature; +use Bolt\protocol\Response; use Bolt\protocol\V4_4; use Bolt\protocol\V5; use Bolt\protocol\V5_1; @@ -22,8 +24,8 @@ use Exception; use Laudis\Neo4j\Bolt\BoltMessageFactory; use Laudis\Neo4j\Common\Neo4jLogger; -use Laudis\Neo4j\Common\ResponseHelper; use Laudis\Neo4j\Contracts\AuthenticateInterface; +use Laudis\Neo4j\Exception\Neo4jException; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\UriInterface; use Psr\Log\LogLevel; @@ -57,10 +59,10 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $ $helloMetadata = ['user_agent' => $userAgent]; $factory->createHelloMessage($helloMetadata)->send(); - $response = ResponseHelper::getResponse($protocol); + $response = self::getResponse($protocol); $factory->createLogonMessage(['scheme' => 'none'])->send(); - ResponseHelper::getResponse($protocol); + self::getResponse($protocol); /** @var array{server: string, connection_id: string, hints: list} */ return $response->content; @@ -74,14 +76,24 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $ $factory->createHelloMessage($helloMetadata)->send(); /** @var array{server: string, connection_id: string, hints: list} */ - return ResponseHelper::getResponse($protocol)->content; + return self::getResponse($protocol)->content; + } + + public static function getResponse(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): Response + { + $response = $protocol->getResponse(); + if ($response->signature === Signature::FAILURE) { + throw Neo4jException::fromBoltResponse($response); + } + + return $response; } public function logoff(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): void { $factory = $this->createMessageFactory($protocol); $factory->createLogoffMessage()->send(); - ResponseHelper::getResponse($protocol); + $protocol->getResponse(); } public function toString(UriInterface $uri): string diff --git a/src/Authentication/OpenIDConnectAuth.php b/src/Authentication/OpenIDConnectAuth.php index bb7a134f..19045690 100644 --- a/src/Authentication/OpenIDConnectAuth.php +++ b/src/Authentication/OpenIDConnectAuth.php @@ -22,7 +22,6 @@ use Exception; use Laudis\Neo4j\Bolt\BoltMessageFactory; use Laudis\Neo4j\Common\Neo4jLogger; -use Laudis\Neo4j\Common\ResponseHelper; use Laudis\Neo4j\Contracts\AuthenticateInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\UriInterface; @@ -59,7 +58,7 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $ $factory->createHelloMessage(['user_agent' => $userAgent])->send(); - $response = ResponseHelper::getResponse($protocol); + $response = $protocol->getResponse(); $this->logger?->log(LogLevel::DEBUG, 'LOGON', ['scheme' => 'bearer']); @@ -68,7 +67,7 @@ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $ 'credentials' => $this->token, ])->send(); - ResponseHelper::getResponse($protocol); + $protocol->getResponse(); /** * @var array{server: string, connection_id: string, hints: list} diff --git a/src/Bolt/BoltUnmanagedTransaction.php b/src/Bolt/BoltUnmanagedTransaction.php index f01daf84..216adcc0 100644 --- a/src/Bolt/BoltUnmanagedTransaction.php +++ b/src/Bolt/BoltUnmanagedTransaction.php @@ -21,7 +21,7 @@ use Laudis\Neo4j\Databags\SummarizedResult; use Laudis\Neo4j\Databags\TransactionConfiguration; use Laudis\Neo4j\Enum\TransactionState; -use Laudis\Neo4j\Exception\ClientException; +use Laudis\Neo4j\Exception\TransactionException; use Laudis\Neo4j\Formatter\SummarizedResultFormatter; use Laudis\Neo4j\ParameterHelper; use Laudis\Neo4j\Types\CypherList; @@ -58,7 +58,7 @@ public function __construct( /** * @param iterable $statements * - * @throws ClientException|Throwable + * @throws TransactionException|Throwable * * @return CypherList */ @@ -66,15 +66,15 @@ public function commit(iterable $statements = []): CypherList { if ($this->isFinished()) { if ($this->state === TransactionState::TERMINATED) { - throw new ClientException("Can't commit, transaction has been terminated"); + throw new TransactionException("Can't commit, transaction has been terminated"); } if ($this->state === TransactionState::COMMITTED) { - throw new ClientException("Can't commit, transaction has already been committed"); + throw new TransactionException("Can't commit, transaction has already been committed"); } if ($this->state === TransactionState::ROLLED_BACK) { - throw new ClientException("Can't commit, transaction has already been rolled back"); + throw new TransactionException("Can't commit, transaction has already been rolled back"); } } @@ -93,16 +93,20 @@ public function commit(iterable $statements = []): CypherList public function rollback(): void { if ($this->isFinished()) { - if ($this->state === TransactionState::TERMINATED) { - throw new ClientException("Can't rollback, transaction has been terminated"); - } - if ($this->state === TransactionState::COMMITTED) { - throw new ClientException("Can't rollback, transaction has already been committed"); + throw new TransactionException("Can't rollback, transaction has already been committed"); } if ($this->state === TransactionState::ROLLED_BACK) { - throw new ClientException("Can't rollback, transaction has already been rolled back"); + // Already rolled back, throw a TransactionException to be wrapped by DriverErrorResponse + throw new TransactionException('Transaction has already been rolled back'); + } + + if ($this->state === TransactionState::TERMINATED) { + // Transaction failed, allow rollback as a no-op + $this->state = TransactionState::ROLLED_BACK; + + return; } } @@ -115,6 +119,20 @@ public function rollback(): void */ public function run(string $statement, iterable $parameters = []): SummarizedResult { + if ($this->isFinished()) { + if ($this->state === TransactionState::TERMINATED) { + throw new TransactionException("Can't rollback, transaction has been terminated"); + } + + if ($this->state === TransactionState::COMMITTED) { + throw new TransactionException("Can't rollback, transaction has already been committed"); + } + + if ($this->state === TransactionState::ROLLED_BACK) { + throw new TransactionException("Can't rollback, transaction has already been rolled back"); + } + } + return $this->runStatement(new Statement($statement, $parameters)); } diff --git a/src/Common/ResponseHelper.php b/src/Common/ResponseHelper.php deleted file mode 100644 index 9fb6a094..00000000 --- a/src/Common/ResponseHelper.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Laudis\Neo4j\Common; - -use Bolt\enum\Signature; -use Bolt\protocol\Response; -use Bolt\protocol\V4_4; -use Bolt\protocol\V5; -use Bolt\protocol\V5_1; -use Bolt\protocol\V5_2; -use Bolt\protocol\V5_3; -use Bolt\protocol\V5_4; -use Laudis\Neo4j\Exception\Neo4jException; - -class ResponseHelper -{ - public static function getResponse(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): Response - { - $response = $protocol->getResponse(); - if ($response->signature === Signature::FAILURE) { - throw Neo4jException::fromBoltResponse($response); - } - - return $response; - } -} diff --git a/src/Exception/ClientException.php b/src/Exception/TransactionException.php similarity index 91% rename from src/Exception/ClientException.php rename to src/Exception/TransactionException.php index 81639c5a..161378a7 100644 --- a/src/Exception/ClientException.php +++ b/src/Exception/TransactionException.php @@ -23,7 +23,7 @@ * * @psalm-suppress MutableDependency */ -final class ClientException extends RuntimeException +final class TransactionException extends RuntimeException { public function __construct(string $message, ?Throwable $previous = null) { diff --git a/testkit-backend/src/Backend.php b/testkit-backend/src/Backend.php index 08f36702..6a6af3b1 100644 --- a/testkit-backend/src/Backend.php +++ b/testkit-backend/src/Backend.php @@ -23,14 +23,17 @@ use const JSON_THROW_ON_ERROR; use JsonException; +use Laudis\Neo4j\Exception\Neo4jException; use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; use Laudis\Neo4j\TestkitBackend\Responses\BackendErrorResponse; +use Laudis\Neo4j\TestkitBackend\Responses\DriverErrorResponse; use const PHP_EOL; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +use Symfony\Component\Uid\Uuid; use Throwable; use UnexpectedValueException; @@ -88,9 +91,13 @@ public function handle(): void [$handler, $request] = $this->extractRequest($message); try { $this->properSendoff($handler->handle($request)); + } catch (Neo4jException $e) { + $this->logger->error($e->__toString()); + // Convert Neo4jException to DriverErrorResponse for testkit protocol + $this->properSendoff(new DriverErrorResponse(Uuid::v4(), $e)); } catch (Throwable $e) { $this->logger->error($e->__toString()); - + // All other exceptions become BackendErrorResponse $this->properSendoff(new BackendErrorResponse($e->getMessage())); } } diff --git a/testkit-backend/src/Handlers/AbstractRunner.php b/testkit-backend/src/Handlers/AbstractRunner.php index 81bd004c..c02ef9ea 100644 --- a/testkit-backend/src/Handlers/AbstractRunner.php +++ b/testkit-backend/src/Handlers/AbstractRunner.php @@ -18,6 +18,7 @@ use Laudis\Neo4j\Databags\SummarizedResult; use Laudis\Neo4j\Databags\TransactionConfiguration; use Laudis\Neo4j\Exception\Neo4jException; +use Laudis\Neo4j\Exception\TransactionException; use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; use Laudis\Neo4j\TestkitBackend\MainRepository; use Laudis\Neo4j\TestkitBackend\Requests\SessionRunRequest; @@ -47,7 +48,7 @@ public function __construct(MainRepository $repository, LoggerInterface $logger) $this->logger = $logger; } - public function handle($request): ResultResponse + public function handle($request): ResultResponse|DriverErrorResponse { $session = $this->getRunner($request); $id = Uuid::v4(); @@ -78,14 +79,16 @@ public function handle($request): ResultResponse $this->repository->addRecords($id, $result); return new ResultResponse($id, $result->isEmpty() ? [] : $result->first()->keys()); - } catch (Neo4jException $exception) { + } catch (Neo4jException|TransactionException $exception) { $this->logger->debug($exception->__toString()); - $this->repository->addRecords($id, new DriverErrorResponse( + + $driverErrorResponse = new DriverErrorResponse( $this->getId($request), $exception - )); + ); + $this->repository->addRecords($id, $driverErrorResponse); - return new ResultResponse($id, []); + return $driverErrorResponse; } // NOTE: all other exceptions will be caught in the Backend } diff --git a/testkit-backend/src/Handlers/SessionBeginTransaction.php b/testkit-backend/src/Handlers/SessionBeginTransaction.php index fe2647ed..600821da 100644 --- a/testkit-backend/src/Handlers/SessionBeginTransaction.php +++ b/testkit-backend/src/Handlers/SessionBeginTransaction.php @@ -21,6 +21,9 @@ use Laudis\Neo4j\TestkitBackend\Requests\SessionBeginTransactionRequest; use Laudis\Neo4j\TestkitBackend\Responses\DriverErrorResponse; use Laudis\Neo4j\TestkitBackend\Responses\TransactionResponse; +use Laudis\Neo4j\Types\AbstractCypherObject; +use Laudis\Neo4j\Types\CypherList; +use Laudis\Neo4j\Types\CypherMap; use Psr\Log\LoggerInterface; use Symfony\Component\Uid\Uuid; @@ -52,7 +55,14 @@ public function handle($request): TestkitResponseInterface } if ($request->getTxMeta()) { - $config = $config->withMetaData($request->getTxMeta()); + $metaData = $request->getTxMeta(); + $actualMeta = []; + if ($metaData !== null) { + foreach ($metaData as $key => $meta) { + $actualMeta[$key] = $this->decodeToValue($meta); + } + } + $config = $config->withMetaData($actualMeta); } // TODO - Create beginReadTransaction and beginWriteTransaction @@ -70,4 +80,45 @@ public function handle($request): TestkitResponseInterface return new TransactionResponse($id); } + + /** + * @param array{name: string, data: array{value: iterable|scalar|null}} $param + * + * @return scalar|AbstractCypherObject|iterable|null + */ + private function decodeToValue(array $param) + { + $value = $param['data']['value']; + if (is_iterable($value)) { + if ($param['name'] === 'CypherMap') { + /** @psalm-suppress MixedArgumentTypeCoercion */ + $map = []; + /** + * @var numeric $k + * @var mixed $v + */ + foreach ($value as $k => $v) { + /** @psalm-suppress MixedArgument */ + $map[(string) $k] = $this->decodeToValue($v); + } + + return new CypherMap($map); + } + + if ($param['name'] === 'CypherList') { + $list = []; + /** + * @var mixed $v + */ + foreach ($value as $v) { + /** @psalm-suppress MixedArgument */ + $list[] = $this->decodeToValue($v); + } + + return new CypherList($list); + } + } + + return $value; + } } diff --git a/testkit-backend/src/Handlers/TransactionCommit.php b/testkit-backend/src/Handlers/TransactionCommit.php index 203487c2..ca6d1029 100644 --- a/testkit-backend/src/Handlers/TransactionCommit.php +++ b/testkit-backend/src/Handlers/TransactionCommit.php @@ -15,6 +15,7 @@ use Laudis\Neo4j\Contracts\UnmanagedTransactionInterface; use Laudis\Neo4j\Exception\Neo4jException; +use Laudis\Neo4j\Exception\TransactionException; use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; use Laudis\Neo4j\TestkitBackend\MainRepository; @@ -48,7 +49,7 @@ public function handle($request): TestkitResponseInterface try { $tsx->commit(); - } catch (Neo4jException $e) { + } catch (Neo4jException|TransactionException $e) { return new DriverErrorResponse($request->getTxId(), $e); } diff --git a/testkit-backend/src/Handlers/TransactionRollback.php b/testkit-backend/src/Handlers/TransactionRollback.php index ac40879d..08e4f6bd 100644 --- a/testkit-backend/src/Handlers/TransactionRollback.php +++ b/testkit-backend/src/Handlers/TransactionRollback.php @@ -15,6 +15,7 @@ use Laudis\Neo4j\Contracts\TransactionInterface; use Laudis\Neo4j\Exception\Neo4jException; +use Laudis\Neo4j\Exception\TransactionException; use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; use Laudis\Neo4j\TestkitBackend\MainRepository; @@ -48,7 +49,7 @@ public function handle($request): TestkitResponseInterface try { $tsx->rollback(); - } catch (Neo4jException $e) { + } catch (Neo4jException|TransactionException $e) { return new DriverErrorResponse($request->getTxId(), $e); } diff --git a/testkit-backend/src/MainRepository.php b/testkit-backend/src/MainRepository.php index 848ca3b7..273b5b89 100644 --- a/testkit-backend/src/MainRepository.php +++ b/testkit-backend/src/MainRepository.php @@ -125,9 +125,11 @@ public function addRecords(Uuid $id, $result): void { $this->records[$id->toRfc4122()] = $result; if ($result instanceof SummarizedResult) { - /** @var SummarizedResult> $result */ - $this->recordIterators[$id->toRfc4122()] = (function () use ($result) { - foreach ($result as $row) { + // Fully buffer this result set so multiple results don't interfere + $buffered = iterator_to_array($result, false); + + $this->recordIterators[$id->toRfc4122()] = (function () use ($buffered) { + foreach ($buffered as $row) { yield $row; } })(); diff --git a/testkit-backend/src/Requests/SessionBeginTransactionRequest.php b/testkit-backend/src/Requests/SessionBeginTransactionRequest.php index 8e9025d3..a8242624 100644 --- a/testkit-backend/src/Requests/SessionBeginTransactionRequest.php +++ b/testkit-backend/src/Requests/SessionBeginTransactionRequest.php @@ -13,7 +13,6 @@ namespace Laudis\Neo4j\TestkitBackend\Requests; -use Laudis\Neo4j\Databags\TransactionConfiguration; use Symfony\Component\Uid\Uuid; final class SessionBeginTransactionRequest @@ -49,8 +48,8 @@ public function getTxMeta(): iterable return $this->txMeta ?? []; } - public function getTimeout(): int + public function getTimeout(): ?int { - return (int) ($this->timeout ?? TransactionConfiguration::DEFAULT_TIMEOUT); + return $this->timeout; } } diff --git a/testkit-backend/src/Responses/DriverErrorResponse.php b/testkit-backend/src/Responses/DriverErrorResponse.php index 781effa4..f471e74f 100644 --- a/testkit-backend/src/Responses/DriverErrorResponse.php +++ b/testkit-backend/src/Responses/DriverErrorResponse.php @@ -14,6 +14,7 @@ namespace Laudis\Neo4j\TestkitBackend\Responses; use Laudis\Neo4j\Exception\Neo4jException; +use Laudis\Neo4j\Exception\TransactionException; use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; use Symfony\Component\Uid\Uuid; @@ -23,9 +24,9 @@ final class DriverErrorResponse implements TestkitResponseInterface { private Uuid $id; - private Neo4jException $exception; + private Neo4jException|TransactionException $exception; - public function __construct(Uuid $id, Neo4jException $exception) + public function __construct(Uuid $id, Neo4jException|TransactionException $exception) { $this->id = $id; $this->exception = $exception; @@ -33,12 +34,23 @@ public function __construct(Uuid $id, Neo4jException $exception) public function jsonSerialize(): array { + if ($this->exception instanceof Neo4jException) { + return [ + 'name' => 'DriverError', + 'data' => [ + 'id' => $this->id->toRfc4122(), + 'code' => $this->exception->getNeo4jCode(), + 'msg' => $this->exception->getNeo4jMessage(), + ], + ]; + } + return [ 'name' => 'DriverError', 'data' => [ 'id' => $this->id->toRfc4122(), - 'code' => $this->exception->getNeo4jCode(), - 'msg' => $this->exception->getNeo4jMessage(), + 'code' => $this->exception->getCode(), + 'msg' => $this->exception->getMessage(), ], ]; } diff --git a/testkit-backend/testkit.sh b/testkit-backend/testkit.sh index 5e317dfe..061ad7ac 100755 --- a/testkit-backend/testkit.sh +++ b/testkit-backend/testkit.sh @@ -23,9 +23,10 @@ if [ ! -d testkit ]; then if [ "$(cd testkit && git branch --show-current)" != "${TESTKIT_VERSION}" ]; then (cd testkit && git checkout ${TESTKIT_VERSION}) fi -else - (cd testkit && git pull) fi +#else +# (cd testkit && git pull) +#fi cd testkit || (echo 'cannot cd into testkit' && exit 1) python3 -m venv venv @@ -38,49 +39,75 @@ echo "Starting tests..." EXIT_CODE=0 #neo4j -#test_authentication -python3 -m unittest tests.neo4j.test_authentication.TestAuthenticationBasic || EXIT_CODE=1 - -#test_bookmarks +##test_authentication +python3 -m unittest tests.neo4j.test_authentication.TestAuthenticationBasic|| EXIT_CODE=1 +## +####test_bookmarks python3 -m unittest tests.neo4j.test_bookmarks.TestBookmarks || EXIT_CODE=1 - -#test_session_run -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_iteration_smaller_than_fetch_size -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_can_return_node -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_can_return_relationship -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_can_return_path -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_autocommit_transactions_should_support_metadata -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_regex_in_parameter -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_regex_inline -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_iteration_larger_than_fetch_size -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_partial_iteration -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_simple_query -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_session_reuse -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_iteration_nested -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_recover_from_invalid_query -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_recover_from_fail_on_streaming -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_updates_last_bookmark -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_fails_on_bad_syntax -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_fails_on_missing_parameter -python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_long_string - -#test_direct_driver +### +####test_session_run +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_iteration_smaller_than_fetch_size || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_can_return_node || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_can_return_relationship || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_can_return_path || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_autocommit_transactions_should_support_metadata || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_regex_in_parameter || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_regex_inline || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_iteration_larger_than_fetch_size || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_partial_iteration || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_simple_query || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_session_reuse || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_iteration_nested || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_recover_from_invalid_query || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_updates_last_bookmark || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_fails_on_bad_syntax || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_fails_on_missing_parameter || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_session_run.TestSessionRun.test_long_string || EXIT_CODE=1 +## +###test_direct_driver +#### test_direct_driver python3 -m unittest tests.neo4j.test_direct_driver.TestDirectDriver.test_custom_resolver|| EXIT_CODE=1 python3 -m unittest tests.neo4j.test_direct_driver.TestDirectDriver.test_fail_nicely_when_using_http_port|| EXIT_CODE=1 python3 -m unittest tests.neo4j.test_direct_driver.TestDirectDriver.test_supports_multi_db|| EXIT_CODE=1 -python3 -m unittest tests.neo4j.test_direct_driver.TestDirectDriver.test_multi_db_non_existing -python3 -m unittest tests.neo4j.test_direct_driver.TestDirectDriver.test_multi_db|| EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_direct_driver.TestDirectDriver.test_multi_db_non_existing || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_direct_driver.TestDirectDriver.test_multi_db || EXIT_CODE=1 python3 -m unittest tests.neo4j.test_direct_driver.TestDirectDriver.test_multi_db_various_databases|| EXIT_CODE=1 - -#test_summary -python3 -m unittest tests.neo4j.test_summary.TestSummary +## +###test_summary +python3 -m unittest tests.neo4j.test_summary.TestSummary || EXIT_CODE=1 ####stub ####test-basic-query -python3 -m unittest tests.stub.basic_query.test_basic_query.TestBasicQuery.test_5x0_populates_path_element_ids_with_string -python3 -m unittest tests.stub.basic_query.test_basic_query.TestBasicQuery.test_4x4_populates_node_element_id_with_id -python3 -m unittest tests.stub.basic_query.test_basic_query.TestBasicQuery.test_5x0_populates_node_element_id_with_string -python3 -m unittest tests.stub.basic_query.test_basic_query.TestBasicQuery.test_4x4_populates_rel_element_id_with_id -python3 -m unittest tests.stub.basic_query.test_basic_query.TestBasicQuery.test_4x4_populates_path_element_ids_with_long +python3 -m unittest tests.stub.basic_query.test_basic_query.TestBasicQuery.test_5x0_populates_path_element_ids_with_string || EXIT_CODE=1 +python3 -m unittest tests.stub.basic_query.test_basic_query.TestBasicQuery.test_4x4_populates_node_element_id_with_id || EXIT_CODE=1 +python3 -m unittest tests.stub.basic_query.test_basic_query.TestBasicQuery.test_5x0_populates_node_element_id_with_string || EXIT_CODE=1 +python3 -m unittest tests.stub.basic_query.test_basic_query.TestBasicQuery.test_4x4_populates_rel_element_id_with_id || EXIT_CODE=1 +python3 -m unittest tests.stub.basic_query.test_basic_query.TestBasicQuery.test_4x4_populates_path_element_ids_with_long || EXIT_CODE=1 + +##test_tx_run +#tx_run +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_simple_query || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_can_commit_transaction || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_can_rollback_transaction || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_updates_last_bookmark_on_commit || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_does_not_update_last_bookmark_on_rollback || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_does_not_update_last_bookmark_on_failure || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_should_be_able_to_rollback_a_failure || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_should_not_commit_a_failure || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_should_not_rollback_a_rollbacked_tx || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_should_not_rollback_a_commited_tx || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_should_not_commit_a_commited_tx || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_should_not_allow_run_on_a_commited_tx || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_should_not_allow_run_on_a_rollbacked_tx || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_should_not_run_valid_query_in_invalid_tx || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_should_fail_run_in_a_commited_tx || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_should_fail_run_in_a_rollbacked_tx || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_should_fail_to_run_query_for_invalid_bookmark || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_broken_transaction_should_not_break_session || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_tx_configuration || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_consume_after_commit || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_parallel_queries || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_interwoven_queries || EXIT_CODE=1 +python3 -m unittest tests.neo4j.test_tx_run.TestTxRun.test_unconsumed_result || EXIT_CODE=1 exit $EXIT_CODE diff --git a/tests/Integration/TransactionIntegrationTest.php b/tests/Integration/TransactionIntegrationTest.php index 4d8707cc..2417b6b2 100644 --- a/tests/Integration/TransactionIntegrationTest.php +++ b/tests/Integration/TransactionIntegrationTest.php @@ -14,8 +14,8 @@ namespace Laudis\Neo4j\Tests\Integration; use Laudis\Neo4j\Databags\Statement; -use Laudis\Neo4j\Exception\ClientException; use Laudis\Neo4j\Exception\Neo4jException; +use Laudis\Neo4j\Exception\TransactionException; use Laudis\Neo4j\Tests\EnvironmentAwareIntegrationTest; use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; @@ -227,14 +227,14 @@ public function testCommitInvalid(): void $exception = null; try { $tsx->commit(); - } catch (ClientException|Neo4jException $e) { + } catch (TransactionException|Neo4jException $e) { $exception = $e; } if (str_starts_with($_ENV['CONNECTION'] ?? '', 'http')) { self::assertTrue($exception instanceof Neo4jException); } else { - self::assertTrue($exception instanceof ClientException); + self::assertTrue($exception instanceof TransactionException); } self::assertTrue($tsx->isFinished()); @@ -265,14 +265,14 @@ public function testRollbackInvalid(): void $exception = null; try { $tsx->rollback(); - } catch (ClientException|Neo4jException $e) { + } catch (TransactionException|Neo4jException $e) { $exception = $e; } if (str_starts_with($_ENV['CONNECTION'] ?? '', 'http')) { self::assertTrue($exception instanceof Neo4jException); } else { - self::assertTrue($exception instanceof ClientException); + self::assertTrue($exception instanceof TransactionException); } self::assertTrue($tsx->isFinished());