Skip to content

Commit 4c2ec0e

Browse files
JasonBenettclaude
andauthored
refactor!: replace Guzzle with PSR-18/PSR-17 interfaces (#1)
Replace hard Guzzle dependency with PSR-18 (HTTP Client) and PSR-17 (HTTP Factories) interfaces for true dependency inversion. The module now supports any PSR-compliant HTTP client implementation. BREAKING CHANGE: Module now requires PSR-18/PSR-17 implementations. Guzzle is auto-discovered if available but is no longer a hard dependency. Users can inject their own PSR clients via configuration. Changes: - Replace GuzzleHttp\Client with PSR-18 ClientInterface - Add PSR-17 RequestFactoryInterface and StreamFactoryInterface support - Implement auto-discovery for Guzzle when PSR clients not provided - Add HttpClientException implementing PSR-18 ClientExceptionInterface - Add HTTP_BAD_REQUEST constant for status code threshold - Replace string concatenation/interpolation with sprintf in exceptions - Move guzzlehttp/guzzle to require-dev (optional dependency) - Update all documentation (CHANGELOG, README, CLAUDE.md) - Remove docker-compose.yml (users should manage WireMock separately) Migration: - Install a PSR-18/PSR-17 implementation (e.g., guzzlehttp/guzzle) - Or provide custom PSR clients via httpClient, requestFactory, streamFactory config - Remove deprecated config options: timeout, verifySSL (configure via PSR client) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0a6f49a commit 4c2ec0e

File tree

8 files changed

+490
-98
lines changed

8 files changed

+490
-98
lines changed

CHANGELOG.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [2.0.0] - 2025-11-29
9+
10+
### Changed
11+
- **BREAKING**: Replaced hard Guzzle dependency with PSR-18 (HTTP Client) and PSR-17 (HTTP Factories) interfaces
12+
- Module now depends on PSR standards instead of concrete Guzzle implementation
13+
- HTTP client architecture refactored to support any PSR-18/PSR-17 compliant implementation
14+
15+
### Added
16+
- PSR-18 `ClientInterface` support for true dependency inversion
17+
- PSR-17 `RequestFactoryInterface` and `StreamFactoryInterface` support
18+
- Optional auto-discovery: automatically creates Guzzle instances if available (backward compatible)
19+
- Support for any PSR-18/PSR-17 compliant HTTP client (Guzzle, Symfony HttpClient, custom implementations)
20+
- New configuration options: `httpClient`, `requestFactory`, `streamFactory`
21+
- Private initialization methods: `initHttpClient()`, `initRequestFactory()`, `initStreamFactory()`
22+
23+
### Removed
24+
- Hard dependency on `guzzlehttp/guzzle` (now optional, moved to `require-dev`)
25+
26+
## [1.0.0] - 2025-11-28
27+
28+
### Added
29+
- Initial release of Codeception WireMock integration module
30+
- `haveHttpStubFor()` - Create HTTP stubs for any method with advanced request matching
31+
- `seeHttpRequest()` - Verify HTTP requests were made with pattern matching
32+
- `dontSeeHttpRequest()` - Verify HTTP requests were NOT made
33+
- `seeRequestCount()` - Assert exact number of matching requests
34+
- `grabRequestCount()` - Retrieve count of matching requests
35+
- `grabAllRequests()` - Retrieve all recorded requests for debugging
36+
- `grabUnmatchedRequests()` - Retrieve requests that didn't match any stub
37+
- `sendReset()` - Reset WireMock to default state
38+
- `sendClearRequests()` - Clear request journal without affecting stubs
39+
- Automatic cleanup hooks (`cleanupBefore: test|suite|never`)
40+
- Near-miss analysis for failed request verifications
41+
- Support for advanced request matching (body patterns, headers, query parameters)
42+
- WireMock health check on module initialization
43+
- Comprehensive test coverage (15 unit tests, 11 functional tests)
44+
- Guzzle HTTP client integration

CLAUDE.md

Lines changed: 141 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,28 @@ This is a Codeception module for integrating with WireMock, a tool for mocking H
99
**Key Details:**
1010
- PHP 8.2+ required
1111
- Codeception 5.3+ required
12-
- Guzzle HTTP client 7.8+ required
12+
- PSR-18 (HTTP Client) and PSR-17 (HTTP Factories) required
13+
- Guzzle HTTP client optional (auto-discovered if available)
1314
- MIT licensed
1415
- PSR-4 autoloading structure
1516

17+
**Architecture:**
18+
- Depends on PSR-18/PSR-17 interfaces (true dependency inversion)
19+
- No hard dependency on Guzzle - any PSR-compliant HTTP client works
20+
- Optional auto-discovery creates Guzzle instances if available
21+
- Users can inject their own PSR-18/PSR-17 implementations
22+
1623
## Project Structure
1724

1825
```
1926
src/JasonBenett/CodeceptionModuleWiremock/
2027
├── Module/
2128
│ └── Wiremock.php # Main module class with all public methods
22-
└── Exception/
23-
├── WiremockException.php # Base exception
24-
└── RequestVerificationException.php # Verification failure exception
29+
├── Exception/
30+
│ ├── WiremockException.php # Base exception
31+
│ └── RequestVerificationException.php # Verification failure exception
32+
└── Http/
33+
└── HttpClientException.php # PSR-18 ClientExceptionInterface implementation
2534
2635
tests/
2736
├── unit/
@@ -58,6 +67,115 @@ docker-compose down # Stop WireMock server
5867
docker-compose logs wiremock # View WireMock logs
5968
```
6069

70+
## Development Workflow & Conventions
71+
72+
### Semantic Commit Messages
73+
74+
**IMPORTANT:** This project uses [Conventional Commits](https://www.conventionalcommits.org/) specification for all commit messages.
75+
76+
**Format:**
77+
```
78+
<type>(<optional scope>): <description>
79+
80+
[optional body]
81+
82+
[optional footer(s)]
83+
```
84+
85+
**Types:**
86+
- `feat:` - New feature for users
87+
- `fix:` - Bug fix for users
88+
- `docs:` - Documentation changes
89+
- `style:` - Code style changes (formatting, missing semi colons, etc)
90+
- `refactor:` - Code refactoring (neither fixes a bug nor adds a feature)
91+
- `perf:` - Performance improvements
92+
- `test:` - Adding or updating tests
93+
- `chore:` - Changes to build process, CI, dependencies, etc
94+
95+
**Examples:**
96+
```bash
97+
# Feature addition
98+
git commit -m "feat: add support for delayed stub responses"
99+
100+
# Bug fix
101+
git commit -m "fix: handle empty response body in makeAdminRequest"
102+
103+
# Refactoring
104+
git commit -m "refactor: extract HTTP client abstraction to use PSR-18"
105+
106+
# Documentation
107+
git commit -m "docs: update README with PSR configuration examples"
108+
109+
# Breaking change
110+
git commit -m "feat!: replace Guzzle with PSR-18 interfaces
111+
112+
BREAKING CHANGE: httpClient, requestFactory, and streamFactory are now required configuration options"
113+
```
114+
115+
**All commits MUST:**
116+
1. Follow the conventional commits format
117+
2. Include the footer: `🤖 Generated with [Claude Code](https://claude.com/claude-code)`
118+
3. Include: `Co-Authored-By: Claude <noreply@anthropic.com>`
119+
120+
### CHANGELOG Maintenance
121+
122+
**IMPORTANT:** The CHANGELOG.md file MUST be updated for every user-facing change.
123+
124+
**Format:** We follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.
125+
126+
**Workflow:**
127+
1. **During Development:** Add changes to the `[Unreleased]` section under appropriate category:
128+
- `### Added` - New features
129+
- `### Changed` - Changes in existing functionality
130+
- `### Deprecated` - Soon-to-be removed features
131+
- `### Removed` - Removed features
132+
- `### Fixed` - Bug fixes
133+
- `### Security` - Security fixes
134+
135+
2. **Before Release:** Move `[Unreleased]` changes to a new version section:
136+
```markdown
137+
## [1.1.0] - 2025-02-15
138+
139+
### Added
140+
- New feature description
141+
```
142+
143+
3. **Update Guidelines:**
144+
- Write for users, not developers (focus on behavior, not implementation)
145+
- Be specific about what changed and why users care
146+
- Link to issues/PRs when relevant: `(#123)`
147+
- For breaking changes, explain migration path
148+
149+
**Example Entry:**
150+
```markdown
151+
## [Unreleased]
152+
153+
### Added
154+
- Support for custom HTTP headers in all stub methods
155+
156+
### Changed
157+
- `haveHttpStubFor()` now validates request matchers before sending to WireMock
158+
159+
### Fixed
160+
- Near-miss analysis now handles special characters in URLs correctly
161+
```
162+
163+
### Git Workflow
164+
165+
1. **Before Committing:**
166+
- Run all quality checks: `composer test && composer phpstan && composer cs-check`
167+
- Update CHANGELOG.md if user-facing changes
168+
- Verify all tests pass
169+
170+
2. **Committing:**
171+
- Use semantic commit message format
172+
- Include Claude Code footer
173+
174+
3. **Pull Requests:**
175+
- Ensure CI passes (all PHP versions, all checks)
176+
- Update README.md if API changes
177+
- Update CHANGELOG.md in Unreleased section
178+
61179
## Module Architecture
62180

63181
### Configuration Options
@@ -68,16 +186,19 @@ The module accepts the following configuration in `codeception.yml`:
68186
modules:
69187
enabled:
70188
- \JasonBenett\CodeceptionModuleWiremock\Module\Wiremock:
71-
host: localhost # WireMock host (default: 127.0.0.1)
72-
port: 8080 # WireMock port (default: 8080)
73-
protocol: http # Protocol (default: http)
74-
timeout: 10.0 # Request timeout in seconds (default: 10.0)
75-
cleanupBefore: test # When to cleanup: 'never', 'test', or 'suite' (default: test)
76-
preserveFileMappings: true # Keep file-based stubs on reset (default: true)
77-
verifySSL: true # Verify SSL certificates (default: true)
78-
adminPath: /__admin # Admin API path (default: /__admin)
189+
host: localhost # Required: WireMock host (default: 127.0.0.1)
190+
port: 8080 # Required: WireMock port (default: 8080)
191+
protocol: http # Optional: Protocol (default: http)
192+
cleanupBefore: test # Optional: When to cleanup: 'never', 'test', or 'suite' (default: test)
193+
preserveFileMappings: true # Optional: Keep file-based stubs on reset (default: true)
194+
adminPath: /__admin # Optional: Admin API path (default: /__admin)
195+
httpClient: null # Optional: PSR-18 ClientInterface instance (auto-creates Guzzle if null)
196+
requestFactory: null # Optional: PSR-17 RequestFactoryInterface instance (auto-creates if null)
197+
streamFactory: null # Optional: PSR-17 StreamFactoryInterface instance (auto-creates if null)
79198
```
80199
200+
**Note:** If PSR client instances are not provided, the module will automatically create Guzzle instances if guzzlehttp/guzzle is installed. For custom HTTP clients, provide PSR-18/PSR-17 implementations.
201+
81202
### Public Methods (MVP)
82203
83204
#### Setup Methods (have*)
@@ -99,10 +220,17 @@ modules:
99220

100221
### Lifecycle Hooks
101222

102-
- `_initialize()` - Creates Guzzle HTTP client and verifies WireMock connectivity
223+
- `_initialize()` - Validates/creates PSR-18/PSR-17 clients and verifies WireMock connectivity
103224
- `_beforeSuite()` - Cleanup if `cleanupBefore: suite`
104225
- `_before()` - Cleanup if `cleanupBefore: test` (default behavior)
105226

227+
### Internal Methods
228+
229+
- `initHttpClient(): void` - Get PSR-18 client from config or auto-create Guzzle instance
230+
- `initRequestFactory(): void` - Get PSR-17 request factory from config or auto-create
231+
- `initStreamFactory(RequestFactoryInterface): void` - Get PSR-17 stream factory from config or auto-create
232+
- `makeAdminRequest(string, string, array): array` - Make HTTP request to WireMock Admin API using PSR-18/PSR-17
233+
106234
### WireMock Admin API Endpoints Used
107235

108236
- `POST /__admin/mappings` - Create stub mapping

README.md

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ A Codeception module for WireMock integration, allowing you to mock HTTP service
2323

2424
- PHP 8.2 or higher
2525
- Codeception 5.3 or higher
26+
- A PSR-18 HTTP Client implementation (e.g., Guzzle, Symfony HttpClient)
27+
- A PSR-17 HTTP Factory implementation (e.g., guzzlehttp/psr7)
2628
- A running WireMock server
2729

2830
## Installation
@@ -33,6 +35,35 @@ Install via Composer:
3335
composer require jasonbenett/codeception-module-wiremock
3436
```
3537

38+
This module depends on PSR-18 (HTTP Client) and PSR-17 (HTTP Factories) interfaces. You'll need to install a compatible implementation:
39+
40+
**Using Guzzle (recommended):**
41+
```bash
42+
composer require guzzlehttp/guzzle
43+
```
44+
45+
**Using Symfony HttpClient:**
46+
```bash
47+
composer require symfony/http-client nyholm/psr7
48+
```
49+
50+
**Other PSR-18/PSR-17 implementations work as well.**
51+
52+
## Architecture
53+
54+
This module follows **PSR-18** (HTTP Client) and **PSR-17** (HTTP Factories) standards, providing true dependency inversion:
55+
56+
- **No hard dependency on Guzzle** - Use any PSR-compliant HTTP client
57+
- **Framework agnostic** - Works with Symfony HttpClient, Guzzle, or custom clients
58+
- **Optional auto-discovery** - Automatically creates Guzzle instances if available
59+
- **Full control** - Inject your own configured PSR clients for advanced scenarios
60+
61+
This approach allows you to:
62+
- Choose your preferred HTTP client library
63+
- Control HTTP client configuration (timeouts, SSL, proxies, etc.)
64+
- Test with mock PSR-18 clients
65+
- Upgrade HTTP client versions independently
66+
3667
## Quick Start
3768

3869
### 1. Start WireMock Server
@@ -43,12 +74,6 @@ Using Docker (recommended):
4374
docker run -d -p 8080:8080 wiremock/wiremock:latest
4475
```
4576

46-
Or use the included `docker-compose.yml`:
47-
48-
```bash
49-
docker-compose up -d
50-
```
51-
5277
### 2. Configure Codeception
5378

5479
Add the WireMock module to your `codeception.yml` or suite configuration:
@@ -88,7 +113,9 @@ class ApiTestCest
88113

89114
## Configuration Options
90115

91-
All configuration options and their defaults:
116+
### Basic Configuration (Auto-Discovery)
117+
118+
When using Guzzle, the module can auto-create PSR client instances:
92119

93120
```yaml
94121
modules:
@@ -97,13 +124,65 @@ modules:
97124
host: 127.0.0.1 # WireMock server host
98125
port: 8080 # WireMock server port
99126
protocol: http # Protocol (http or https)
100-
timeout: 10.0 # Request timeout in seconds
101127
cleanupBefore: test # When to cleanup: 'never', 'test', or 'suite'
102128
preserveFileMappings: true # Keep file-based stubs on reset
103-
verifySSL: true # Verify SSL certificates
104129
adminPath: /__admin # Admin API path
105130
```
106131
132+
### Advanced Configuration (Custom PSR Clients)
133+
134+
For full control and dependency inversion, provide your own PSR-18/PSR-17 implementations:
135+
136+
```php
137+
// tests/_bootstrap.php or tests/_support/Helper/HttpClientProvider.php
138+
139+
use GuzzleHttp\Client;
140+
use GuzzleHttp\Psr7\HttpFactory;
141+
142+
// Create PSR-18 HTTP Client
143+
$httpClient = new Client([
144+
'timeout' => 10.0,
145+
'verify' => false, // Disable SSL verification if needed
146+
'http_errors' => false,
147+
]);
148+
149+
// Create PSR-17 factories (Guzzle's HttpFactory implements both interfaces)
150+
$httpFactory = new HttpFactory();
151+
152+
// Store in global for Codeception access
153+
$GLOBALS['wiremock_http_client'] = $httpClient;
154+
$GLOBALS['wiremock_request_factory'] = $httpFactory;
155+
$GLOBALS['wiremock_stream_factory'] = $httpFactory;
156+
```
157+
158+
```yaml
159+
# codeception.yml or suite config
160+
modules:
161+
enabled:
162+
- \JasonBenett\CodeceptionModuleWiremock\Module\Wiremock:
163+
host: 127.0.0.1
164+
port: 8080
165+
httpClient: !php/const GLOBALS['wiremock_http_client']
166+
requestFactory: !php/const GLOBALS['wiremock_request_factory']
167+
streamFactory: !php/const GLOBALS['wiremock_stream_factory']
168+
```
169+
170+
### All Configuration Options
171+
172+
```yaml
173+
host: 127.0.0.1 # Required: WireMock server host
174+
port: 8080 # Required: WireMock server port
175+
protocol: http # Optional: Protocol (http or https)
176+
cleanupBefore: test # Optional: When to cleanup: 'never', 'test', or 'suite'
177+
preserveFileMappings: true # Optional: Keep file-based stubs on reset
178+
adminPath: /__admin # Optional: Admin API path
179+
httpClient: null # Optional: PSR-18 ClientInterface instance
180+
requestFactory: null # Optional: PSR-17 RequestFactoryInterface instance
181+
streamFactory: null # Optional: PSR-17 StreamFactoryInterface instance
182+
```
183+
184+
**Note:** If `httpClient`, `requestFactory`, or `streamFactory` are not provided, the module will attempt to auto-create Guzzle instances if available.
185+
107186
## Available Methods
108187

109188
### Setup Methods (have*)
@@ -477,7 +556,7 @@ Every push and pull request is automatically tested via GitHub Actions across mu
477556
- ✅ **PHP 8.2, 8.3, 8.4** - Full compatibility testing
478557
- ✅ **PHPStan Level Max** - Zero errors in static analysis
479558
- ✅ **PER Coding Style 3.0** - Strict code style compliance
480-
-**100% Test Coverage** - 26 passing tests (15 unit + 11 functional)
559+
- ✅ **Testing** - Unit and Functional
481560
- ✅ **WireMock Integration Tests** - Tests against real WireMock server
482561

483562
### Local Development
@@ -501,7 +580,7 @@ composer test:functional
501580
### Code Quality Metrics
502581

503582
- **PHPStan**: Max level, zero errors
504-
- **Code Coverage**: 100% with Codecov reporting
583+
- **Code Coverage**: with Codecov reporting
505584
- **Code Style**: PER Coding Style 3.0 (successor to PSR-12)
506585
- **Type Safety**: Full PHPDoc annotations with array shapes
507586
- **Documentation**: Comprehensive inline documentation

0 commit comments

Comments
 (0)