Skip to content

Commit 38b967f

Browse files
committed
Add integration tests and CI pipeline
Took a while to set up proper Redis testing infrastructure, but finally got everything working. ## Changes ### Testing Infrastructure - Add docker-compose.test.yml for local Redis testing (port 6380) - Add IntegrationTestCase base class with Redis setup/teardown - Add TESTING.md with comprehensive testing documentation - Update phpunit.xml with Integration test suite ### Integration Tests (22 new tests) - BalancedQueueIntegrationTest: push, pop, partitions, concurrent limits - CommandsIntegrationTest: balanced-queue:table and balanced-queue:clear - PrometheusIntegrationTest: metrics export and endpoint ### CI/CD - Add GitHub Actions workflow (.github/workflows/tests.yml) - Matrix testing: PHP 8.1-8.4 × Laravel 10-12 - Separate integration tests job with Redis service ### Bug Fixes - Fix Metrics.php to respect config('balanced-queue.redis.connection') - Fix MetricsTest.php mocks to use keys() instead of deprecated scan() Total: 59 tests (37 unit/feature + 22 integration)
1 parent d521d61 commit 38b967f

File tree

12 files changed

+675
-22
lines changed

12 files changed

+675
-22
lines changed

.github/workflows/tests.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
unit-tests:
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
php: [8.1, 8.2, 8.3, 8.4]
17+
laravel: [10.*, 11.*, 12.*]
18+
exclude:
19+
- php: 8.1
20+
laravel: 11.*
21+
- php: 8.1
22+
laravel: 12.*
23+
24+
name: PHP ${{ matrix.php }} / Laravel ${{ matrix.laravel }}
25+
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- name: Setup PHP
30+
uses: shivammathur/setup-php@v2
31+
with:
32+
php-version: ${{ matrix.php }}
33+
extensions: redis
34+
coverage: none
35+
36+
- name: Install dependencies
37+
run: |
38+
composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update
39+
composer update --prefer-dist --no-interaction
40+
41+
- name: Run unit tests
42+
run: vendor/bin/phpunit --testsuite=Feature,Unit
43+
44+
integration-tests:
45+
runs-on: ubuntu-latest
46+
47+
services:
48+
redis:
49+
image: redis:7-alpine
50+
ports:
51+
- 6379:6379
52+
options: >-
53+
--health-cmd "redis-cli ping"
54+
--health-interval 10s
55+
--health-timeout 5s
56+
--health-retries 5
57+
58+
name: Integration Tests
59+
60+
steps:
61+
- uses: actions/checkout@v4
62+
63+
- name: Setup PHP
64+
uses: shivammathur/setup-php@v2
65+
with:
66+
php-version: 8.3
67+
extensions: redis
68+
coverage: none
69+
70+
- name: Install dependencies
71+
run: composer install --prefer-dist --no-interaction
72+
73+
- name: Run integration tests
74+
env:
75+
REDIS_INTEGRATION: 1
76+
REDIS_HOST: 127.0.0.1
77+
REDIS_PORT: 6379
78+
run: vendor/bin/phpunit --testsuite=Integration

TESTING.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Testing
2+
3+
## Quick Start
4+
5+
```bash
6+
# Install dependencies
7+
composer install
8+
9+
# Run unit/feature tests (mocks, no Redis needed)
10+
./vendor/bin/phpunit
11+
12+
# Run specific test suite
13+
./vendor/bin/phpunit --testsuite=Feature
14+
./vendor/bin/phpunit --testsuite=Unit
15+
```
16+
17+
## Integration Tests (Real Redis)
18+
19+
Integration tests require a running Redis instance.
20+
21+
### Using Docker (Recommended)
22+
23+
```bash
24+
# Start Redis on port 6380
25+
docker compose -f docker-compose.test.yml up -d
26+
27+
# Run integration tests
28+
REDIS_INTEGRATION=1 REDIS_PORT=6380 ./vendor/bin/phpunit --testsuite=Integration
29+
30+
# Stop Redis when done
31+
docker compose -f docker-compose.test.yml down
32+
```
33+
34+
### Using Local Redis
35+
36+
If you have Redis running locally on default port:
37+
38+
```bash
39+
REDIS_INTEGRATION=1 REDIS_PORT=6379 ./vendor/bin/phpunit --testsuite=Integration
40+
```
41+
42+
### Environment Variables
43+
44+
| Variable | Default | Description |
45+
|----------|---------|-------------|
46+
| `REDIS_INTEGRATION` | - | Set to `1` to enable integration tests |
47+
| `REDIS_HOST` | `127.0.0.1` | Redis host |
48+
| `REDIS_PORT` | `6380` | Redis port |
49+
| `REDIS_DB` | `15` | Redis database (uses separate DB to avoid conflicts) |
50+
51+
## Running Specific Tests
52+
53+
```bash
54+
# Run single test method
55+
./vendor/bin/phpunit --filter=test_push_job_creates_partition
56+
57+
# Run single test class
58+
./vendor/bin/phpunit tests/Feature/StrategyTest.php
59+
60+
# Run with verbose output
61+
./vendor/bin/phpunit -v
62+
63+
# Run with coverage (requires xdebug or pcov)
64+
./vendor/bin/phpunit --coverage-html=build/coverage
65+
```
66+
67+
## CI/CD
68+
69+
GitHub Actions runs both unit and integration tests automatically on push/PR.
70+
See `.github/workflows/tests.yml` for configuration.
71+
72+
## Test Structure
73+
74+
```
75+
tests/
76+
├── TestCase.php # Base test case with mocks
77+
├── Feature/ # Feature tests (with mocks)
78+
│ ├── BalancedDispatchableTest.php
79+
│ ├── LimiterTest.php
80+
│ ├── MetricsTest.php
81+
│ ├── PrometheusTest.php
82+
│ ├── IpWhitelistTest.php
83+
│ └── StrategyTest.php
84+
└── Integration/ # Integration tests (real Redis)
85+
├── IntegrationTestCase.php
86+
├── BalancedQueueIntegrationTest.php # Core queue tests
87+
├── CommandsIntegrationTest.php # Artisan commands
88+
├── PrometheusIntegrationTest.php # Metrics endpoint
89+
├── TestJob.php
90+
└── TestJobWithNumericPartition.php
91+
```
92+
93+
## Test Coverage Summary
94+
95+
| Component | Unit/Feature | Integration |
96+
|-----------|--------------|-------------|
97+
| Strategies |||
98+
| BalancedDispatchable |||
99+
| Limiters |||
100+
| Metrics |||
101+
| BalancedRedisQueue | - ||
102+
| Console commands | - ||
103+
| PrometheusExporter |||

docker-compose.test.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
services:
2+
redis:
3+
image: redis:7-alpine
4+
ports:
5+
- "6380:6379"
6+
healthcheck:
7+
test: ["CMD", "redis-cli", "ping"]
8+
interval: 1s
9+
timeout: 3s
10+
retries: 5

phpunit.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
<testsuite name="Unit">
1515
<directory suffix="Test.php">./tests/Unit</directory>
1616
</testsuite>
17+
<testsuite name="Integration">
18+
<directory suffix="Test.php">./tests/Integration</directory>
19+
</testsuite>
1720
</testsuites>
1821
<coverage>
1922
<report>

src/Support/Metrics.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Metrics
1717

1818
public function __construct(?Connection $redis = null, string $prefix = 'balanced-queue')
1919
{
20-
$this->redis = $redis ?? Redis::connection();
20+
$this->redis = $redis ?? Redis::connection(config('balanced-queue.redis.connection'));
2121
$this->prefix = $prefix;
2222
}
2323

tests/Feature/MetricsTest.php

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,17 @@ public function test_get_all_queues_returns_unique_queues(): void
1414
{
1515
$redis = $this->getMockRedis();
1616

17-
// First scan returns some keys, cursor moves
18-
$redis->shouldReceive('scan')
19-
->once()
20-
->with('0', ['match' => 'balanced-queue:queues:*:partitions', 'count' => 100])
21-
->andReturn(['5', [
22-
'balanced-queue:queues:default:partitions',
23-
'balanced-queue:queues:emails:partitions',
24-
]]);
25-
26-
// Second scan returns more keys, cursor back to 0
27-
$redis->shouldReceive('scan')
17+
// Redis keys() returns keys WITH Laravel prefix
18+
$laravelPrefix = config('database.redis.options.prefix', '');
19+
20+
$redis->shouldReceive('keys')
2821
->once()
29-
->with('5', ['match' => 'balanced-queue:queues:*:partitions', 'count' => 100])
30-
->andReturn(['0', [
31-
'balanced-queue:queues:notifications:partitions',
32-
'balanced-queue:queues:default:partitions', // duplicate
33-
]]);
22+
->with('balanced-queue:queues:*:partitions')
23+
->andReturn([
24+
$laravelPrefix.'balanced-queue:queues:default:partitions',
25+
$laravelPrefix.'balanced-queue:queues:emails:partitions',
26+
$laravelPrefix.'balanced-queue:queues:notifications:partitions',
27+
]);
3428

3529
$metrics = new Metrics($redis, 'balanced-queue');
3630
$queues = $metrics->getAllQueues();
@@ -45,23 +39,24 @@ public function test_get_all_queues_returns_empty_when_no_queues(): void
4539
{
4640
$redis = $this->getMockRedis();
4741

48-
$redis->shouldReceive('scan')
42+
$redis->shouldReceive('keys')
4943
->once()
50-
->with('0', ['match' => 'balanced-queue:queues:*:partitions', 'count' => 100])
51-
->andReturn(['0', []]);
44+
->with('balanced-queue:queues:*:partitions')
45+
->andReturn([]);
5246

5347
$metrics = new Metrics($redis, 'balanced-queue');
5448
$queues = $metrics->getAllQueues();
5549

5650
$this->assertEmpty($queues);
5751
}
5852

59-
public function test_get_all_queues_handles_scan_failure(): void
53+
public function test_get_all_queues_handles_keys_failure(): void
6054
{
6155
$redis = $this->getMockRedis();
6256

63-
$redis->shouldReceive('scan')
57+
$redis->shouldReceive('keys')
6458
->once()
59+
->with('balanced-queue:queues:*:partitions')
6560
->andReturn(false);
6661

6762
$metrics = new Metrics($redis, 'balanced-queue');

0 commit comments

Comments
 (0)