diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c8035d55..a281c69b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,9 +17,9 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: '8.2' - extensions: mysqli, curl, openssl, json, redis, pcov - coverage: pcov - ini-values: pcov.enabled=1 + extensions: mysqli, curl, openssl, json, redis, xdebug + coverage: xdebug + ini-values: variables_order=EGPCS,xdebug.mode=coverage - name: Resolve dependencies working-directory: services/nginx/app @@ -72,6 +72,7 @@ jobs: with: php-version: '8.2' extensions: mysqli, curl, openssl, json, redis + ini-values: variables_order=EGPCS - name: Resolve dependencies working-directory: services/nginx/app @@ -139,6 +140,7 @@ jobs: with: php-version: '8.2' extensions: mysqli, curl, openssl, json, redis + ini-values: variables_order=EGPCS - name: Resolve dependencies working-directory: services/nginx/app @@ -148,6 +150,7 @@ jobs: working-directory: services/nginx/app env: RUN_API_TESTS: '1' + API_TEST_BOOTSTRAP_SCHEMA: '1' USE_ENV: 'true' DEBUG: '0' ENCRYPTION_KEY: test-key diff --git a/services/nginx/app/config.php b/services/nginx/app/config.php index 03ff1ce5..521d8f4a 100644 --- a/services/nginx/app/config.php +++ b/services/nginx/app/config.php @@ -1,9 +1,14 @@ 'user', 'REDIS_CONFIG_DEBUG_PASSWORD' => 'debug_password' ]; - $dbTarget = strtolower(trim((string)($_ENV['CONFIG_DB_TARGET'] ?? 'live'))); + $dbTarget = strtolower(trim($readEnv('CONFIG_DB_TARGET', 'live'))); if ($dbTarget !== 'live' && $dbTarget !== 'debug') { $dbTarget = 'live'; } - $resolveDbValue = function (string $key) use ($dbTarget): string { + $resolveDbValue = function (string $key) use ($dbTarget, $readEnv): string { $liveKey = 'CONFIG_DB_' . $key; $debugKey = 'CONFIG_DB_DEBUG_' . $key; - $liveValue = (string)($_ENV[$liveKey] ?? ''); - $debugValue = (string)($_ENV[$debugKey] ?? ''); + $liveValue = $readEnv($liveKey); + $debugValue = $readEnv($debugKey); if ($dbTarget === 'debug' && $debugValue !== '') { return $debugValue; @@ -71,15 +76,15 @@ if (isset($_ENV['USE_ENV']) && $_ENV['USE_ENV'] === 'true') { /** * Set the debug configuration */ - $DEBUG = $_ENV['DEBUG']; + $DEBUG = $readEnv('DEBUG'); /** * Set the encryption key */ - $ENCRYPTION_KEY = $_ENV['ENCRYPTION_KEY']; + $ENCRYPTION_KEY = $readEnv('ENCRYPTION_KEY'); /** * Set the CORS configuration */ - $CORS = $_ENV['CORS']; + $CORS = $readEnv('CORS'); /** * Set the economic API configuration */ @@ -102,32 +107,32 @@ if (isset($_ENV['USE_ENV']) && $_ENV['USE_ENV'] === 'true') { /** * Set the WordPress static token */ - $WORDPRESS_STATIC_TOKEN = $_ENV['WORDPRESS_STATIC_TOKEN']; + $WORDPRESS_STATIC_TOKEN = $readEnv('WORDPRESS_STATIC_TOKEN'); /** * Set the email wash certificate token */ - $EMAIL_WASH_CERTIFICATE_TOKEN = $_ENV['EMAIL_WASH_CERTIFICATE_TOKEN']; + $EMAIL_WASH_CERTIFICATE_TOKEN = $readEnv('EMAIL_WASH_CERTIFICATE_TOKEN'); /** * Set the WordPress API URL */ - $WORDPRESS_API_URL = $_ENV['WORDPRESS_API_URL']; + $WORDPRESS_API_URL = $readEnv('WORDPRESS_API_URL'); /** * Set the Minio configuration */ $MINIO = [ - 'endpoint' => $_ENV['MINIO_ENDPOINT'], - 'access_key' => $_ENV['MINIO_ACCESS_KEY'], - 'secret_key' => $_ENV['MINIO_SECRET_KEY'] + 'endpoint' => $readEnv('MINIO_ENDPOINT'), + 'access_key' => $readEnv('MINIO_ACCESS_KEY'), + 'secret_key' => $readEnv('MINIO_SECRET_KEY') ]; /** * Set the Slack default webhook */ - $SLACK_DEFAULT_WEBHOOK = $_ENV['SLACK_DEFAULT_WEBHOOK']; - $resolveRedisValue = function (string $key) use ($dbTarget): string { + $SLACK_DEFAULT_WEBHOOK = $readEnv('SLACK_DEFAULT_WEBHOOK'); + $resolveRedisValue = function (string $key) use ($dbTarget, $readEnv): string { $liveKey = 'REDIS_CONFIG_' . $key; $debugKey = 'REDIS_CONFIG_DEBUG_' . $key; - $liveValue = (string)($_ENV[$liveKey] ?? ''); - $debugValue = (string)($_ENV[$debugKey] ?? ''); + $liveValue = $readEnv($liveKey); + $debugValue = $readEnv($debugKey); if ($dbTarget === 'debug' && $debugValue !== '') { return $debugValue; @@ -148,7 +153,7 @@ if (isset($_ENV['USE_ENV']) && $_ENV['USE_ENV'] === 'true') { ]; // Set the timezone - $timezone = $_ENV['CONFIG_TIMEZONE'] ?? 'Europe/Copenhagen'; + $timezone = $readEnv('CONFIG_TIMEZONE', 'Europe/Copenhagen'); date_default_timezone_set($timezone); // Set the all config diff --git a/services/nginx/app/tests/Support/Api/ApiSchemaBootstrap.php b/services/nginx/app/tests/Support/Api/ApiSchemaBootstrap.php new file mode 100644 index 00000000..9b44832d --- /dev/null +++ b/services/nginx/app/tests/Support/Api/ApiSchemaBootstrap.php @@ -0,0 +1,443 @@ +tableStatements() as $name => $sql) { + $this->execute($name, $sql); + } + + foreach ($this->viewStatements() as $name => $sql) { + $this->execute($name, $sql); + } + } + + /** + * @return array + */ + private function tableStatements(): array + { + return [ + 'groups' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `groups` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `description` TEXT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'users' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `users` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `customer_number` INT NOT NULL, + `display_name` VARCHAR(255) NULL, + `email` VARCHAR(255) NULL, + `phone_country_code` INT NULL, + `phone` BIGINT NULL, + `password` VARCHAR(255) NULL, + `group_id` INT NOT NULL DEFAULT 0, + `xlvask_customer_id` VARCHAR(255) NULL, + `sms_notifications_enabled` TINYINT(1) NOT NULL DEFAULT 0, + `email_notifications_enabled` TINYINT(1) NOT NULL DEFAULT 0, + `wash_certificate_email` VARCHAR(255) NULL, + `two_factor_enabled` TINYINT(1) NOT NULL DEFAULT 0, + `two_factor_secret` VARCHAR(255) NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted_at` DATETIME NULL, + PRIMARY KEY (`id`), + KEY `idx_users_customer_number` (`customer_number`), + KEY `idx_users_group_id` (`group_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'groups_permissions' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `groups_permissions` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `group_id` INT NOT NULL, + `permission` VARCHAR(191) NOT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_groups_permissions_group_id` (`group_id`), + KEY `idx_groups_permissions_permission` (`permission`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'departments' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `departments` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `description` TEXT NULL, + `economic_department_id` INT NOT NULL DEFAULT 0, + `slack_webhook` TEXT NULL, + `dimension` INT NOT NULL DEFAULT 0, + `branding` INT NOT NULL DEFAULT 0, + `visible` TINYINT(1) NOT NULL DEFAULT 1, + `latitude` DECIMAL(10,7) NOT NULL DEFAULT 0, + `longitude` DECIMAL(10,7) NOT NULL DEFAULT 0, + `order_priority` INT NOT NULL DEFAULT 0, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_departments_visible` (`visible`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'department_variables' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `department_variables` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `department_id` INT NOT NULL, + `variable` VARCHAR(191) NOT NULL, + `value` TEXT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_department_variables_department_id` (`department_id`), + KEY `idx_department_variables_variable` (`variable`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'department_gates' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `department_gates` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `department` INT NOT NULL, + `is_entrance` TINYINT(1) NOT NULL DEFAULT 0, + `is_exit` TINYINT(1) NOT NULL DEFAULT 0, + `name` VARCHAR(255) NOT NULL, + `config` LONGTEXT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted_at` DATETIME NULL, + PRIMARY KEY (`id`), + KEY `idx_department_gates_department` (`department`), + KEY `idx_department_gates_deleted_at` (`deleted_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'categories' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `categories` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `description` TEXT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'department_categories' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `department_categories` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `department_id` INT NOT NULL, + `category_id` INT NOT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted_at` DATETIME NULL, + PRIMARY KEY (`id`), + KEY `idx_department_categories_department_id` (`department_id`), + KEY `idx_department_categories_category_id` (`category_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'collected_order_invoices' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `collected_order_invoices` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `customer_number` INT NOT NULL, + `name` VARCHAR(255) NOT NULL, + `notes` TEXT NULL, + `processor` INT NOT NULL DEFAULT 0, + `external_id` VARCHAR(255) NULL, + `booked_invoice_id` INT NULL, + `po_number` VARCHAR(255) NULL, + `error_message` TEXT NULL, + `closed_at` DATETIME NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted_at` DATETIME NULL, + PRIMARY KEY (`id`), + KEY `idx_collected_order_invoices_customer_number` (`customer_number`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'orders' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `orders` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `customer_id` INT NOT NULL, + `cashier_id` INT NOT NULL DEFAULT 0, + `department_id` INT NOT NULL, + `reference` VARCHAR(255) NOT NULL, + `notes` TEXT NULL, + `reg_1` VARCHAR(32) NULL, + `reg_2` VARCHAR(32) NULL, + `reg_3` VARCHAR(32) NULL, + `invoice_collection_id` INT NULL, + `booking_id` INT NULL, + `wash_id` VARCHAR(255) NULL, + `lane` INT NULL, + `po` VARCHAR(255) NULL, + `safety_seal` VARCHAR(255) NULL, + `using_hand_held` TINYINT(1) NOT NULL DEFAULT 0, + `include_in_invoice` TINYINT(1) NOT NULL DEFAULT 1, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `completed_at` DATETIME NULL, + `deleted_at` DATETIME NULL, + PRIMARY KEY (`id`), + KEY `idx_orders_customer_id` (`customer_id`), + KEY `idx_orders_department_id` (`department_id`), + KEY `idx_orders_invoice_collection_id` (`invoice_collection_id`), + KEY `idx_orders_reg_1` (`reg_1`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'order_items' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `order_items` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `order_id` INT NOT NULL, + `product_id` INT NOT NULL, + `reference` VARCHAR(255) NULL, + `notes` TEXT NULL, + `cashier_id` INT NOT NULL DEFAULT 0, + `price` INT NOT NULL DEFAULT 0, + `quantity` INT NOT NULL DEFAULT 1, + `related_item_id` INT NULL, + `include_in_invoice` TINYINT(1) NOT NULL DEFAULT 1, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted_at` DATETIME NULL, + PRIMARY KEY (`id`), + KEY `idx_order_items_order_id` (`order_id`), + KEY `idx_order_items_deleted_at` (`deleted_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'customer_vehicles' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `customer_vehicles` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `customer_id` INT NOT NULL, + `type` INT NOT NULL DEFAULT 0, + `reg` VARCHAR(32) NOT NULL, + `wash_subscription` TINYINT(1) NOT NULL DEFAULT 0, + `notes` TEXT NULL, + `reference` VARCHAR(255) NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted_at` DATETIME NULL, + PRIMARY KEY (`id`), + KEY `idx_customer_vehicles_customer_id` (`customer_id`), + KEY `idx_customer_vehicles_reg` (`reg`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'customer_vehicles_addons' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `customer_vehicles_addons` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `vehicle_id` INT NOT NULL, + `addon_id` INT NOT NULL, + `amount` INT NOT NULL DEFAULT 1, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_customer_vehicles_addons_vehicle_id` (`vehicle_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'subusers' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `subusers` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `username` VARCHAR(255) NULL, + `password` VARCHAR(255) NULL, + `name` VARCHAR(255) NULL, + `email` VARCHAR(255) NULL, + `phone_country_code` INT NULL, + `phone` BIGINT NULL, + `two_factor_enabled` TINYINT(1) NOT NULL DEFAULT 0, + `two_factor_secret` VARCHAR(255) NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `suspended_at` DATETIME NULL, + PRIMARY KEY (`id`), + KEY `idx_subusers_username` (`username`), + KEY `idx_subusers_phone` (`phone`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'subuser_grants' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `subuser_grants` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `billing_customer_number` INT NOT NULL, + `subuser` INT NOT NULL, + `enabled` TINYINT(1) NOT NULL DEFAULT 1, + `note` TEXT NULL, + `permissions` LONGTEXT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted_at` DATETIME NULL, + PRIMARY KEY (`id`), + KEY `idx_subuser_grants_subuser` (`subuser`), + KEY `idx_subuser_grants_billing_customer_number` (`billing_customer_number`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'tokens' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `tokens` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `type` VARCHAR(64) NOT NULL, + `description` VARCHAR(255) NULL, + `token` VARCHAR(255) NOT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_tokens_token` (`token`), + KEY `idx_tokens_user_id` (`user_id`), + KEY `idx_tokens_type` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'customer_attributes' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `customer_attributes` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `attribute` VARCHAR(191) NOT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_customer_attributes_user_id` (`user_id`), + KEY `idx_customer_attributes_attribute` (`attribute`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'module_config' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `module_config` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `module` VARCHAR(191) NOT NULL, + `variable` VARCHAR(191) NOT NULL, + `value` LONGTEXT NULL, + `type` VARCHAR(64) NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_module_config_module_variable` (`module`, `variable`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'user_key_value_pairs' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `user_key_value_pairs` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `var` VARCHAR(191) NOT NULL, + `val` LONGTEXT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_key_value_pairs_user_id` (`user_id`), + KEY `idx_user_key_value_pairs_var` (`var`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'price_overrides' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `price_overrides` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `is_category` TINYINT(1) NOT NULL DEFAULT 0, + `product_or_category_id` VARCHAR(191) NOT NULL, + `percentage` INT NOT NULL DEFAULT 0, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_price_overrides_user_id` (`user_id`), + KEY `idx_price_overrides_lookup` (`user_id`, `is_category`, `product_or_category_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'products_options' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `products_options` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `product_id` INT NOT NULL, + `option_id` INT NOT NULL, + `name` VARCHAR(255) NULL, + `min` INT NULL, + `max` INT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_products_options_product_id` (`product_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'economic_module_orders' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `economic_module_orders` ( + `id` INT NOT NULL, + `invoice_draft_id` INT NULL, + `invoice_id` INT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'stripe_module_orders' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `stripe_module_orders` ( + `id` INT NOT NULL, + `invoice_id` VARCHAR(255) NULL, + `customer_id` VARCHAR(255) NULL, + `email_sent` DATETIME NULL, + `url` TEXT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'stripe_payment_intents' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `stripe_payment_intents` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `order_id` INT NOT NULL, + `payment_intent_id` VARCHAR(255) NOT NULL, + `client_secret` VARCHAR(255) NULL, + `data` LONGTEXT NULL, + `reader_id` VARCHAR(255) NULL, + `tax_percentage` INT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_stripe_payment_intents_order_id` (`order_id`), + KEY `idx_stripe_payment_intents_payment_intent_id` (`payment_intent_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + 'object_attachments' => <<<'SQL' +CREATE TABLE IF NOT EXISTS `object_attachments` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `object_type` VARCHAR(191) NOT NULL, + `object_id` INT NOT NULL, + `content` LONGTEXT NULL, + `created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted_at` DATETIME NULL, + PRIMARY KEY (`id`), + KEY `idx_object_attachments_lookup` (`object_type`, `object_id`), + KEY `idx_object_attachments_deleted_at` (`deleted_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SQL, + ]; + } + + /** + * @return array + */ + private function viewStatements(): array + { + return [ + 'orders_with_invoice_collections' => <<<'SQL' +CREATE OR REPLACE VIEW `orders_with_invoice_collections` AS +SELECT + o.*, + coi.closed_at, + coi.booked_invoice_id, + coi.processor +FROM `orders` o +LEFT JOIN `collected_order_invoices` coi ON coi.id = o.invoice_collection_id +SQL, + ]; + } + + private function execute(string $name, string $sql): void + { + if ($this->db->query($sql) === false) { + throw new RuntimeException( + sprintf('Failed to bootstrap API test schema for "%s": %s', $name, (string)$this->db->error) + ); + } + } +} diff --git a/services/nginx/app/tests/Support/Api/ApiTestRuntime.php b/services/nginx/app/tests/Support/Api/ApiTestRuntime.php index 3f61be18..d7c4c09e 100644 --- a/services/nginx/app/tests/Support/Api/ApiTestRuntime.php +++ b/services/nginx/app/tests/Support/Api/ApiTestRuntime.php @@ -21,6 +21,7 @@ final class ApiTestRuntime private ?ApiFixtures $fixtures = null; private ?ApiCleanup $cleanup = null; private bool $bootstrapped = false; + private bool $schemaBootstrapped = false; private bool $shutdownRegistered = false; private string $baseUrl = self::DEFAULT_BASE_URL; private bool $usesExternalBaseUrl = false; @@ -38,6 +39,7 @@ final class ApiTestRuntime try { $this->bootstrapEnvironment(); + $this->bootstrapSchemaIfRequested(); } catch (Throwable $throwable) { return $throwable->getMessage(); } @@ -53,6 +55,7 @@ final class ApiTestRuntime public function beginTest(): void { $this->bootstrapEnvironment(); + $this->bootstrapSchemaIfRequested(); $this->cleanup = new ApiCleanup(); $this->fixtures = new ApiFixtures($this->db(), $this->redis(), $this->cleanup); $this->ensureServerIsRunning(); @@ -133,6 +136,7 @@ final class ApiTestRuntime } $this->bootstrapped = false; + $this->schemaBootstrapped = false; } private function bootstrapEnvironment(): void @@ -182,6 +186,20 @@ final class ApiTestRuntime $this->bootstrapped = true; } + private function bootstrapSchemaIfRequested(): void + { + if ($this->schemaBootstrapped) { + return; + } + + if (getenv('API_TEST_BOOTSTRAP_SCHEMA') !== '1') { + return; + } + + (new ApiSchemaBootstrap($this->db()))->ensureSchema(); + $this->schemaBootstrapped = true; + } + private function ensureServerIsRunning(): void { if ($this->usesExternalBaseUrl) { diff --git a/services/nginx/app/tests/Support/ApiTestSupport.php b/services/nginx/app/tests/Support/ApiTestSupport.php index 97b75798..516c4cf8 100644 --- a/services/nginx/app/tests/Support/ApiTestSupport.php +++ b/services/nginx/app/tests/Support/ApiTestSupport.php @@ -6,6 +6,7 @@ require_once __DIR__ . '/Api/ApiCleanup.php'; require_once __DIR__ . '/Api/ApiResponse.php'; require_once __DIR__ . '/Api/ApiClient.php'; require_once __DIR__ . '/Api/ApiServer.php'; +require_once __DIR__ . '/Api/ApiSchemaBootstrap.php'; require_once __DIR__ . '/Api/ApiFixtures.php'; require_once __DIR__ . '/Api/ApiTestRuntime.php';