From 90ebec84bf91fc30a35ed3cca63cc77104f7ddcf Mon Sep 17 00:00:00 2001 From: Jeppe Bundgaard Date: Thu, 28 May 2026 17:58:06 +0200 Subject: [PATCH] Add PHP CI test script and optimize Redis config in tests - Introduced a PHP CI test script for managing test suites. - Consolidated Redis configuration retrieval. - Optimized test fixture queries with dynamic object type assignments. --- scripts/.php-ci-test.lf.52582.sh | 102 ++++++++++++++++++ .../app/tests/Api/SelfserveFixtureApiTest.php | 37 +++---- 2 files changed, 114 insertions(+), 25 deletions(-) create mode 100644 scripts/.php-ci-test.lf.52582.sh diff --git a/scripts/.php-ci-test.lf.52582.sh b/scripts/.php-ci-test.lf.52582.sh new file mode 100644 index 00000000..63158f36 --- /dev/null +++ b/scripts/.php-ci-test.lf.52582.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env sh +set -eu + +suite="${1:-}" +case "$suite" in + unit|integration|api|legacy|all) + ;; + *) + echo "Usage: $0 " >&2 + exit 2 + ;; +esac + +script_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +repo_root="$(CDPATH= cd -- "$script_dir/.." && pwd)" +cd "$repo_root" + +compose_files="-f docker-compose.yml -f .github/docker-compose.ci.yml" +project_suffix="$(date +%s)-$$" +export COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-php-local-${suite}-${project_suffix}}" + +log_dir=".tmp/ci-logs/$suite" +mkdir -p "$log_dir" + +env_backup_dir=".tmp/php-ci-env-backup-$project_suffix" +mkdir -p "$env_backup_dir" +had_env=0 +had_env_staging=0 +if [ -f .env ]; then + cp .env "$env_backup_dir/env" + had_env=1 +fi +if [ -f .env.staging ]; then + cp .env.staging "$env_backup_dir/env.staging" + had_env_staging=1 +fi + +cp .github/ci.env .env +cp .github/ci.env.staging .env.staging + +collect_logs() { + status="$1" + if [ "$status" -eq 0 ]; then + return + fi + + mkdir -p "$log_dir" + docker compose $compose_files ps > "$log_dir/docker-compose-ps.txt" 2>&1 || true + docker compose $compose_files logs --no-color > "$log_dir/docker-compose.log" 2>&1 || true + docker compose $compose_files cp php1:/var/www/html/build/logs "$log_dir/app-build-logs" >/dev/null 2>&1 || true + docker compose $compose_files cp php1:/var/log/php "$log_dir/php-logs" >/dev/null 2>&1 || true +} + +cleanup() { + status="$?" + collect_logs "$status" + docker compose $compose_files down -v >/dev/null 2>&1 || true + if [ "$had_env" -eq 1 ]; then + cp "$env_backup_dir/env" .env + else + rm -f .env + fi + if [ "$had_env_staging" -eq 1 ]; then + cp "$env_backup_dir/env.staging" .env.staging + else + rm -f .env.staging + fi + rm -rf "$env_backup_dir" + exit "$status" +} +trap cleanup EXIT INT TERM + +docker compose $compose_files up -d redis mysql-debug php1 + +docker compose $compose_files exec -T php1 sh -lc ' + set -eu + for i in $(seq 1 90); do + if MYSQL_PWD="${CONFIG_DB_PASSWORD:-debug_root_password}" mysqladmin \ + -h "${CONFIG_DB_HOST:-mysql-debug}" \ + -P "${CONFIG_DB_PORT:-3306}" \ + -u "${CONFIG_DB_USER:-root}" \ + ping --silent >/dev/null 2>&1; then + exit 0 + fi + sleep 1 + done + echo "Timed out waiting for mysql-debug" >&2 + exit 1 +' + +tar \ + --exclude='./vendor' \ + --exclude='./.phpunit.cache' \ + --exclude='./build/logs' \ + -C services/nginx/app -cf - . \ + | docker compose $compose_files exec -T php1 tar -C /var/www/html -xf - + +docker compose $compose_files exec -T php1 sh -lc \ + 'cd /var/www/html && composer install --no-interaction --prefer-dist --no-progress' + +docker compose $compose_files exec -T php1 sh -lc \ + "cd /var/www/html && composer test:ci:$suite" diff --git a/services/nginx/app/tests/Api/SelfserveFixtureApiTest.php b/services/nginx/app/tests/Api/SelfserveFixtureApiTest.php index 2000c0f7..127d7f4e 100644 --- a/services/nginx/app/tests/Api/SelfserveFixtureApiTest.php +++ b/services/nginx/app/tests/Api/SelfserveFixtureApiTest.php @@ -8,31 +8,17 @@ function selfserve_fixture_ensure_legacy_redis_constant(): void return; } - $client = api_test_runtime()->redis(); - if ($client === null) { - throw new RuntimeException('Self-serve lane tests require Redis.'); - } + global $REDIS_CONFIG; - define('redis', new class($client) { - public function __construct(private readonly \Predis\Client $client) - { - } + $REDIS_CONFIG = [ + 'host' => getenv('REDIS_CONFIG_HOST') ?: getenv('REDIS_CONFIG_DEBUG_HOST') ?: 'redis', + 'user' => getenv('REDIS_CONFIG_USER') ?: getenv('REDIS_CONFIG_DEBUG_USER') ?: 'default', + 'database' => getenv('REDIS_CONFIG_DATABASE') ?: getenv('REDIS_CONFIG_DEBUG_DATABASE') ?: '0', + 'password' => getenv('REDIS_CONFIG_PASSWORD') ?: getenv('REDIS_CONFIG_DEBUG_PASSWORD') ?: '', + 'port' => getenv('REDIS_CONFIG_PORT') ?: getenv('REDIS_CONFIG_DEBUG_PORT') ?: '6379', + ]; - public function get(string $key): mixed - { - return $this->client->get($key); - } - - public function set(string $key, mixed $value): mixed - { - return $this->client->set($key, $value); - } - - public function delete(string $key): mixed - { - return $this->client->del([$key]); - } - }); + define('redis', (new \classes\redis())->connect()); } it('creates a comprehensive self-serve API scenario with demo relays', function (): void { @@ -86,13 +72,14 @@ it('creates self-serve invoice orders on the draft customer with original custom expect($orderId)->toBeInt()->toBeGreaterThan(0); $order = api_fixtures()->fetchRowById('orders', $orderId); + $attachmentObjectType = '`orders`'; $invoiceCollectionId = (int)($order['invoice_collection_id'] ?? 0); if ($invoiceCollectionId > 0) { api_fixtures()->cleanupDeleteById('collected_order_invoices', $invoiceCollectionId); } api_fixtures()->cleanupDeleteById('orders', $orderId); api_fixtures()->cleanupDeleteWhere('order_items', ['order_id' => $orderId]); - api_fixtures()->cleanupDeleteWhere('object_attachments', ['object_type' => 'orders', 'object_id' => $orderId]); + api_fixtures()->cleanupDeleteWhere('object_attachments', ['object_type' => $attachmentObjectType, 'object_id' => $orderId]); expect($order)->not->toBeNull() ->and((int)$order['customer_id'])->toBe((int)$draftCustomer['customer_number']) @@ -103,7 +90,7 @@ it('creates self-serve invoice orders on the draft customer with original custom $db = api_test_runtime()->db(); $result = $db->query( - 'SELECT content FROM object_attachments WHERE object_type = "orders" AND object_id = ' . (int)$orderId . ' AND deleted_at IS NULL ORDER BY id DESC LIMIT 1' + "SELECT content FROM object_attachments WHERE object_type = '{$attachmentObjectType}' AND object_id = " . (int)$orderId . ' AND deleted_at IS NULL ORDER BY id DESC LIMIT 1' ); $attachment = $result ? $result->fetch_assoc() : null; $content = json_decode((string)($attachment['content'] ?? ''), true);