Skip to content

Commit 1aedbaf

Browse files
test: add live cache backend smoke
1 parent b12b567 commit 1aedbaf

5 files changed

Lines changed: 176 additions & 0 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Cache backend smoke
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
native-backends:
15+
name: Redis and Memcached
16+
runs-on: ubuntu-latest
17+
timeout-minutes: 15
18+
services:
19+
redis:
20+
image: redis:7.4-alpine@sha256:6ab0b6e7381779332f97b8ca76193e45b0756f38d4c0dcda72dbb3c32061ab99
21+
ports:
22+
- 6379:6379
23+
options: >-
24+
--health-cmd "redis-cli ping"
25+
--health-interval 5s
26+
--health-timeout 3s
27+
--health-retries 10
28+
memcached:
29+
image: memcached:1.6@sha256:dc561d52bb8ad3c038867123ed2dc8357c4c128f047cdbd526cd65cf39408cbd
30+
ports:
31+
- 11211:11211
32+
steps:
33+
- name: Checkout
34+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
35+
with:
36+
persist-credentials: false
37+
38+
- name: Set up PHP
39+
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
40+
with:
41+
php-version: '8.5'
42+
extensions: redis, memcached
43+
tools: composer:v2
44+
coverage: none
45+
46+
- name: Install dependencies
47+
uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0
48+
env:
49+
COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH_JSON }}
50+
51+
- name: Test native backends
52+
run: composer tests:backends
53+
env:
54+
SYMPRESS_LIVE_CACHE_TESTS: '1'

AGENTS.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# SymPress Framework Bundle agent contract
2+
3+
## Purpose and boundaries
4+
5+
This bundle bridges Symfony FrameworkBundle services into the SymPress kernel and owns the WordPress object-cache drop-in. Cache configuration, backend protocols, signed payloads and drop-in publication are operational and security boundaries.
6+
7+
## Read first
8+
9+
- `Resources/config`: bundle service and package configuration roots.
10+
- `src/DependencyInjection`: Symfony extension and compiler-pass wiring.
11+
- `src/ObjectCache`: configuration, adapters, codec, proxy and drop-in installation.
12+
- `dropin/object-cache.php` and `inc/object-cache-functions.php`: portable WordPress runtime boundary.
13+
- `CONTRIBUTING.md` and the README operational notes before changing cache behavior.
14+
15+
## Verification
16+
17+
- Fast: `composer tests -- --filter <changed subsystem>`.
18+
- Full: `composer qa`.
19+
- Native Redis/Memcached changes: `SYMPRESS_LIVE_CACHE_TESTS=1 composer tests:backends` with both services and PHP extensions available.
20+
- Drop-in changes require tests for ownership/overwrite behavior and a published-file diff.
21+
22+
## Invariants
23+
24+
- Require a strong `APP_SECRET`; never introduce predictable secret fallbacks.
25+
- Never overwrite an unmanaged third-party `object-cache.php`.
26+
- Keep Redis/Memcached counter and group-flush semantics compatible with WordPress.
27+
- Persistent backend failures must degrade safely without exposing secrets or creating web-accessible control endpoints.
28+
- Treat backend stores, cache directories and container dumps as trusted infrastructure.
29+
- Keep service configuration private unless a public entry point is intentional.
30+
31+
## Cross-repository impact
32+
33+
The bundle depends on `sympress/kernel` discovery and container lifecycle. WP Starter may publish the drop-in during setup. Coordinate metadata, service aliases and drop-in path changes with kernel and a runnable WordPress consumer.
34+
35+
## Definition of done
36+
37+
Focused tests and `composer qa` pass; native protocol changes pass the live backend smoke; security/operations docs match behavior; generated or published drop-in content is verified.

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ The QA script runs PHPCS, PHPStan and PHPUnit. The GitHub workflow uses the
5454
shared SymPress workflow set and the organization `.github` repository provides
5555
issue and community templates.
5656

57+
The separate cache-backend smoke runs the native adapters against real Redis
58+
and Memcached services in CI. Run it locally with both PHP extensions and
59+
services available:
60+
61+
```sh
62+
SYMPRESS_LIVE_CACHE_TESTS=1 composer tests:backends
63+
```
64+
5765
## Cache Pool Configuration
5866

5967
The bundle provides defaults that work without project config. Projects can override the `framework.cache` parameter in a SymPress config file:

composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@
9191
"Composer\\Config::disableProcessTimeout",
9292
"qa tests"
9393
],
94+
"tests:backends": [
95+
"Composer\\Config::disableProcessTimeout",
96+
"phpunit --configuration phpunit.xml.dist --group live-cache --no-coverage"
97+
],
9498
"test": [
9599
"@tests"
96100
],
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SymPress\Framework\Tests\Integration;
6+
7+
use PHPUnit\Framework\Attributes\Group;
8+
use PHPUnit\Framework\TestCase;
9+
use SymPress\Framework\ObjectCache\NativeMemcachedObjectCacheAdapter;
10+
use SymPress\Framework\ObjectCache\NativeRedisObjectCacheAdapter;
11+
12+
#[Group('live-cache')]
13+
final class NativeObjectCacheBackendSmokeTest extends TestCase
14+
{
15+
protected function setUp(): void
16+
{
17+
if (getenv('SYMPRESS_LIVE_CACHE_TESTS') !== '1') {
18+
self::markTestSkipped('Set SYMPRESS_LIVE_CACHE_TESTS=1 to test live cache backends.');
19+
}
20+
}
21+
22+
public function testRedisProtocolAndAtomicOperations(): void
23+
{
24+
self::assertTrue(class_exists(\Redis::class), 'The redis PHP extension is required.');
25+
$redis = new \Redis();
26+
$this->connect(fn (): bool => $redis->connect('127.0.0.1', 6379, 1.0));
27+
$cache = new NativeRedisObjectCacheAdapter($redis, 'sympress-smoke-' . bin2hex(random_bytes(6)));
28+
29+
self::assertTrue($cache->set('count', 1));
30+
self::assertSame(1, $cache->get('count'));
31+
self::assertSame(3, $cache->incr('count', 2));
32+
self::assertSame(0, $cache->decr('count', 9));
33+
self::assertTrue($cache->clear());
34+
self::assertFalse($cache->get('count'));
35+
36+
$redis->close();
37+
}
38+
39+
public function testMemcachedProtocolAndAtomicOperations(): void
40+
{
41+
self::assertTrue(class_exists(\Memcached::class), 'The memcached PHP extension is required.');
42+
$memcached = new \Memcached();
43+
self::assertTrue($memcached->addServer('127.0.0.1', 11211));
44+
$this->connect(static fn (): bool => $memcached->getVersion() !== false);
45+
$cache = new NativeMemcachedObjectCacheAdapter(
46+
$memcached,
47+
'sympress-smoke-' . bin2hex(random_bytes(6)),
48+
);
49+
50+
self::assertTrue($cache->set('count', 1));
51+
self::assertSame(1, $cache->get('count'));
52+
self::assertSame(3, $cache->incr('count', 2));
53+
self::assertSame(0, $cache->decr('count', 9));
54+
self::assertTrue($cache->clear());
55+
self::assertFalse($cache->get('count'));
56+
57+
$memcached->quit();
58+
}
59+
60+
/** @param callable(): bool $probe */
61+
private function connect(callable $probe): void
62+
{
63+
for ($attempt = 0; $attempt < 10; $attempt++) {
64+
if ($probe()) {
65+
return;
66+
}
67+
68+
usleep(250_000);
69+
}
70+
71+
self::fail('Cache backend did not become ready.');
72+
}
73+
}

0 commit comments

Comments
 (0)