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
7 changes: 7 additions & 0 deletions .env.testing.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
WORDPRESS_ENV=testing
WORDPRESS_URL=http://127.0.0.1:8088
WORDPRESS_USER=sdk_admin
WORDPRESS_APP_PASSWORD=generated-by-composer-test-wordpress
WORDPRESS_TEST_URL=http://127.0.0.1:8088
WORDPRESS_TEST_USERNAME=sdk_admin
WORDPRESS_TEST_APP_PASSWORD=generated-by-composer-test-wordpress
46 changes: 46 additions & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Docker Integration Tests

on:
workflow_dispatch:
pull_request:
branches: [develop]

jobs:
integration-tests:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
extensions: json, curl, mbstring
coverage: none

- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Docker integration tests
run: docker/wordpress/scripts/run-integration-tests.sh

extended-integration-tests:
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
extensions: json, curl, mbstring
coverage: none

- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Extended Docker integration tests
run: docker/wordpress/scripts/run-extended-integration-tests.sh
86 changes: 86 additions & 0 deletions docker/wordpress/docker-compose.integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: jooservices-wordpress-sdk-integration

services:
db:
image: mariadb:11.4
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
MYSQL_ROOT_PASSWORD: wordpress
volumes:
- db-data:/var/lib/mysql
networks:
- wordpress-sdk
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 5s
timeout: 5s
retries: 20

wordpress:
image: wordpress:6.8.1-php8.4-apache
depends_on:
db:
condition: service_healthy
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DEBUG: "1"
WORDPRESS_CONFIG_EXTRA: |
define('WP_ENVIRONMENT_TYPE', 'local');
define('WP_DEBUG_LOG', true);
define('WP_REST_APPLICATION_PASSWORD_STATUS', true);
ports:
- "127.0.0.1:8088:80"
volumes:
- wordpress-data:/var/www/html
- ./plugins/jooservices-sdk-test-endpoints:/var/www/html/wp-content/plugins/jooservices-sdk-test-endpoints:ro
- ../../tests/Fixtures/wordpress-plugins/sdk-test-plugin:/var/www/html/wp-content/plugins/sdk-test-plugin:ro
- ./fixtures/media:/tmp/sdk-fixtures:ro
networks:
- wordpress-sdk
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost/wp-json/ >/dev/null || exit 1"]
interval: 5s
timeout: 5s
retries: 30

wp-cli:
image: wordpress:cli-2.11.0-php8.3
depends_on:
db:
condition: service_healthy
wordpress:
condition: service_started
user: "33:33"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_URL: http://wordpress
WORDPRESS_ADMIN_USER: sdk_admin
WORDPRESS_ADMIN_PASSWORD: sdk_admin_password
WORDPRESS_ADMIN_EMAIL: sdk-admin@example.test
WORDPRESS_EDITOR_USER: sdk_editor
WORDPRESS_EDITOR_PASSWORD: sdk_editor_password
WORDPRESS_EDITOR_EMAIL: sdk-editor@example.test
volumes:
- wordpress-data:/var/www/html
- ./plugins/jooservices-sdk-test-endpoints:/var/www/html/wp-content/plugins/jooservices-sdk-test-endpoints:ro
- ../../tests/Fixtures/wordpress-plugins/sdk-test-plugin:/var/www/html/wp-content/plugins/sdk-test-plugin:ro
- ./fixtures/media:/tmp/sdk-fixtures:ro
networks:
- wordpress-sdk
entrypoint: ["wp"]

volumes:
db-data:
wordpress-data:

networks:
wordpress-sdk:
driver: bridge
Binary file added docker/wordpress/fixtures/media/test-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docker/wordpress/fixtures/media/test-text.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
JOOservices WordPress SDK integration fixture.
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<?php

declare(strict_types=1);

/**
* Plugin Name: JOOservices SDK Test Endpoints
* Description: Deterministic REST endpoints for the JOOservices WordPress SDK integration tests.
* Version: 1.0.0
* Author: JOOservices
*/

if (!defined('ABSPATH')) {
exit;
}

const JOOSERVICES_SDK_TEST_OPTION = 'jooservices_sdk_test_items';

add_action('rest_api_init', static function (): void {
register_rest_route('jooservices-sdk-test/v1', '/marker', [
'methods' => WP_REST_Server::READABLE,
'callback' => static fn (): WP_REST_Response => new WP_REST_Response([
'jooservices_sdk_test_site' => (int) get_option('jooservices_sdk_test_site', 0),
'wp_version' => get_bloginfo('version'),
'active_theme' => wp_get_theme()->get_stylesheet(),
], 200),
'permission_callback' => 'jooservices_sdk_test_can_manage',
]);

register_rest_route('jooservices-sdk-test/v1', '/items', [
[
'methods' => WP_REST_Server::READABLE,
'callback' => 'jooservices_sdk_test_items_list',
'permission_callback' => 'jooservices_sdk_test_can_manage',
],
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => 'jooservices_sdk_test_items_create',
'permission_callback' => 'jooservices_sdk_test_can_manage',
],
]);

register_rest_route('jooservices-sdk-test/v1', '/items/(?P<id>\d+)', [
[
'methods' => WP_REST_Server::READABLE,
'callback' => 'jooservices_sdk_test_items_get',
'permission_callback' => 'jooservices_sdk_test_can_manage',
],
[
'methods' => WP_REST_Server::EDITABLE,
'callback' => 'jooservices_sdk_test_items_update',
'permission_callback' => 'jooservices_sdk_test_can_manage',
],
[
'methods' => 'PATCH',
'callback' => 'jooservices_sdk_test_items_update',
'permission_callback' => 'jooservices_sdk_test_can_manage',
],
[
'methods' => WP_REST_Server::DELETABLE,
'callback' => 'jooservices_sdk_test_items_delete',
'permission_callback' => 'jooservices_sdk_test_can_manage',
],
]);
});

function jooservices_sdk_test_can_manage(): bool
{
return current_user_can('edit_posts');
}

/**
* @return array<int, array<string, mixed>>
*/
function jooservices_sdk_test_items_store(): array
{
$items = get_option(JOOSERVICES_SDK_TEST_OPTION, []);

return is_array($items) ? $items : [];
}

/**
* @param array<int, array<string, mixed>> $items
*/
function jooservices_sdk_test_items_save(array $items): void
{
update_option(JOOSERVICES_SDK_TEST_OPTION, $items, false);
}

function jooservices_sdk_test_items_list(WP_REST_Request $request): WP_REST_Response
{
return new WP_REST_Response(array_values(jooservices_sdk_test_items_store()), 200);
}

function jooservices_sdk_test_items_create(WP_REST_Request $request): WP_REST_Response
{
$items = jooservices_sdk_test_items_store();
$ids = array_map('intval', array_keys($items));
$id = $ids === [] ? 1 : max($ids) + 1;
$payload = $request->get_json_params();
$payload = is_array($payload) ? $payload : [];

$item = [
'id' => $id,
'name' => sanitize_text_field((string) ($payload['name'] ?? 'SDK Test Item')),
'meta' => isset($payload['meta']) && is_array($payload['meta']) ? $payload['meta'] : [],
];

$items[$id] = $item;
jooservices_sdk_test_items_save($items);

return new WP_REST_Response($item, 201);
}

function jooservices_sdk_test_items_get(WP_REST_Request $request): WP_REST_Response|WP_Error
{
$id = (int) $request['id'];
$items = jooservices_sdk_test_items_store();

if (!isset($items[$id])) {
return new WP_Error('jooservices_sdk_test_not_found', 'Test item not found.', ['status' => 404]);
}

return new WP_REST_Response($items[$id], 200);
}

function jooservices_sdk_test_items_update(WP_REST_Request $request): WP_REST_Response|WP_Error
{
$id = (int) $request['id'];
$items = jooservices_sdk_test_items_store();

if (!isset($items[$id])) {
return new WP_Error('jooservices_sdk_test_not_found', 'Test item not found.', ['status' => 404]);
}

$payload = $request->get_json_params();
$payload = is_array($payload) ? $payload : [];
$items[$id] = array_merge($items[$id], [
'name' => sanitize_text_field((string) ($payload['name'] ?? $items[$id]['name'])),
'meta' => isset($payload['meta']) && is_array($payload['meta']) ? $payload['meta'] : $items[$id]['meta'],
]);
jooservices_sdk_test_items_save($items);

return new WP_REST_Response($items[$id], 200);
}

function jooservices_sdk_test_items_delete(WP_REST_Request $request): WP_REST_Response|WP_Error
{
$id = (int) $request['id'];
$items = jooservices_sdk_test_items_store();

if (!isset($items[$id])) {
return new WP_Error('jooservices_sdk_test_not_found', 'Test item not found.', ['status' => 404]);
}

$previous = $items[$id];
unset($items[$id]);
jooservices_sdk_test_items_save($items);

return new WP_REST_Response(['deleted' => true, 'previous' => $previous], 200);
}
60 changes: 60 additions & 0 deletions docker/wordpress/scripts/provision-extended.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -euo pipefail

compose_file="${COMPOSE_FILE:-docker/wordpress/docker-compose.integration.yml}"
env_file="${INTEGRATION_ENV_FILE:-.env.integration}"

run_wp() {
docker compose -f "$compose_file" run --rm wp-cli "$@"
}

echo "Provisioning optional WordPress capabilities for extended integration tests..."

if [[ ! -f "$env_file" ]]; then
echo "Missing $env_file; run docker/wordpress/scripts/setup.sh first." >&2
exit 1
fi

run_wp plugin activate sdk-test-plugin >/dev/null

if run_wp theme is-installed twentytwentysix >/dev/null 2>&1; then
run_wp theme activate twentytwentysix >/dev/null
elif run_wp theme is-installed twentytwentyfive >/dev/null 2>&1; then
run_wp theme activate twentytwentyfive >/dev/null
elif run_wp theme is-installed twentytwentyfour >/dev/null 2>&1; then
run_wp theme activate twentytwentyfour >/dev/null
else
echo "No bundled block theme was available; continuing with the current active theme." >&2
fi

run_wp rewrite flush --hard >/dev/null

active_theme="$(run_wp theme list --status=active --field=name | tail -n 1)"
active_plugins="$(run_wp plugin list --status=active --field=name | tr '\n' ',' | sed 's/,$//')"

if ! grep -q '^WORDPRESS_EXTENDED_INTEGRATION=' "$env_file"; then
printf '\nWORDPRESS_EXTENDED_INTEGRATION=1\n' >> "$env_file"
fi

if ! grep -q '^WORDPRESS_EXTENDED_ACTIVE_THEME=' "$env_file"; then
printf 'WORDPRESS_EXTENDED_ACTIVE_THEME=%s\n' "$active_theme" >> "$env_file"
fi

echo "Extended active theme: $active_theme"
echo "Extended active plugins: $active_plugins"
echo "Extended route check:"
run_wp eval '
$routes = rest_get_server()->get_routes();
foreach ([
"/wp/v2/plugins",
"/wp/v2/global-styles",
"/wp/v2/navigation",
"/wp/v2/templates",
"/wp/v2/template-parts",
"/wp-site-health/v1/tests/background-updates",
] as $route) {
WP_CLI::line($route . ": " . (array_key_exists($route, $routes) ? "available" : "missing"));
}
$block_type = WP_Block_Type_Registry::get_instance()->get_registered("jooservices/test-dynamic-block");
WP_CLI::line("jooservices/test-dynamic-block: " . ($block_type === null ? "missing" : "registered"));
'
Loading
Loading