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
44 changes: 44 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.0.0] - 2025-11-29

### Changed
- **BREAKING**: Replaced hard Guzzle dependency with PSR-18 (HTTP Client) and PSR-17 (HTTP Factories) interfaces
- Module now depends on PSR standards instead of concrete Guzzle implementation
- HTTP client architecture refactored to support any PSR-18/PSR-17 compliant implementation

### Added
- PSR-18 `ClientInterface` support for true dependency inversion
- PSR-17 `RequestFactoryInterface` and `StreamFactoryInterface` support
- Optional auto-discovery: automatically creates Guzzle instances if available (backward compatible)
- Support for any PSR-18/PSR-17 compliant HTTP client (Guzzle, Symfony HttpClient, custom implementations)
- New configuration options: `httpClient`, `requestFactory`, `streamFactory`
- Private initialization methods: `initHttpClient()`, `initRequestFactory()`, `initStreamFactory()`

### Removed
- Hard dependency on `guzzlehttp/guzzle` (now optional, moved to `require-dev`)

## [1.0.0] - 2025-11-28

### Added
- Initial release of Codeception WireMock integration module
- `haveHttpStubFor()` - Create HTTP stubs for any method with advanced request matching
- `seeHttpRequest()` - Verify HTTP requests were made with pattern matching
- `dontSeeHttpRequest()` - Verify HTTP requests were NOT made
- `seeRequestCount()` - Assert exact number of matching requests
- `grabRequestCount()` - Retrieve count of matching requests
- `grabAllRequests()` - Retrieve all recorded requests for debugging
- `grabUnmatchedRequests()` - Retrieve requests that didn't match any stub
- `sendReset()` - Reset WireMock to default state
- `sendClearRequests()` - Clear request journal without affecting stubs
- Automatic cleanup hooks (`cleanupBefore: test|suite|never`)
- Near-miss analysis for failed request verifications
- Support for advanced request matching (body patterns, headers, query parameters)
- WireMock health check on module initialization
- Comprehensive test coverage (15 unit tests, 11 functional tests)
- Guzzle HTTP client integration
154 changes: 141 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,28 @@ This is a Codeception module for integrating with WireMock, a tool for mocking H
**Key Details:**
- PHP 8.2+ required
- Codeception 5.3+ required
- Guzzle HTTP client 7.8+ required
- PSR-18 (HTTP Client) and PSR-17 (HTTP Factories) required
- Guzzle HTTP client optional (auto-discovered if available)
- MIT licensed
- PSR-4 autoloading structure

**Architecture:**
- Depends on PSR-18/PSR-17 interfaces (true dependency inversion)
- No hard dependency on Guzzle - any PSR-compliant HTTP client works
- Optional auto-discovery creates Guzzle instances if available
- Users can inject their own PSR-18/PSR-17 implementations

## Project Structure

```
src/JasonBenett/CodeceptionModuleWiremock/
├── Module/
│ └── Wiremock.php # Main module class with all public methods
└── Exception/
├── WiremockException.php # Base exception
└── RequestVerificationException.php # Verification failure exception
├── Exception/
│ ├── WiremockException.php # Base exception
│ └── RequestVerificationException.php # Verification failure exception
└── Http/
└── HttpClientException.php # PSR-18 ClientExceptionInterface implementation

tests/
├── unit/
Expand Down Expand Up @@ -58,6 +67,115 @@ docker-compose down # Stop WireMock server
docker-compose logs wiremock # View WireMock logs
```

## Development Workflow & Conventions

### Semantic Commit Messages

**IMPORTANT:** This project uses [Conventional Commits](https://www.conventionalcommits.org/) specification for all commit messages.

**Format:**
```
<type>(<optional scope>): <description>

[optional body]

[optional footer(s)]
```

**Types:**
- `feat:` - New feature for users
- `fix:` - Bug fix for users
- `docs:` - Documentation changes
- `style:` - Code style changes (formatting, missing semi colons, etc)
- `refactor:` - Code refactoring (neither fixes a bug nor adds a feature)
- `perf:` - Performance improvements
- `test:` - Adding or updating tests
- `chore:` - Changes to build process, CI, dependencies, etc

**Examples:**
```bash
# Feature addition
git commit -m "feat: add support for delayed stub responses"

# Bug fix
git commit -m "fix: handle empty response body in makeAdminRequest"

# Refactoring
git commit -m "refactor: extract HTTP client abstraction to use PSR-18"

# Documentation
git commit -m "docs: update README with PSR configuration examples"

# Breaking change
git commit -m "feat!: replace Guzzle with PSR-18 interfaces

BREAKING CHANGE: httpClient, requestFactory, and streamFactory are now required configuration options"
```

**All commits MUST:**
1. Follow the conventional commits format
2. Include the footer: `🤖 Generated with [Claude Code](https://claude.com/claude-code)`
3. Include: `Co-Authored-By: Claude <noreply@anthropic.com>`

### CHANGELOG Maintenance

**IMPORTANT:** The CHANGELOG.md file MUST be updated for every user-facing change.

**Format:** We follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.

**Workflow:**
1. **During Development:** Add changes to the `[Unreleased]` section under appropriate category:
- `### Added` - New features
- `### Changed` - Changes in existing functionality
- `### Deprecated` - Soon-to-be removed features
- `### Removed` - Removed features
- `### Fixed` - Bug fixes
- `### Security` - Security fixes

2. **Before Release:** Move `[Unreleased]` changes to a new version section:
```markdown
## [1.1.0] - 2025-02-15

### Added
- New feature description
```

3. **Update Guidelines:**
- Write for users, not developers (focus on behavior, not implementation)
- Be specific about what changed and why users care
- Link to issues/PRs when relevant: `(#123)`
- For breaking changes, explain migration path

**Example Entry:**
```markdown
## [Unreleased]

### Added
- Support for custom HTTP headers in all stub methods

### Changed
- `haveHttpStubFor()` now validates request matchers before sending to WireMock

### Fixed
- Near-miss analysis now handles special characters in URLs correctly
```

### Git Workflow

1. **Before Committing:**
- Run all quality checks: `composer test && composer phpstan && composer cs-check`
- Update CHANGELOG.md if user-facing changes
- Verify all tests pass

2. **Committing:**
- Use semantic commit message format
- Include Claude Code footer

3. **Pull Requests:**
- Ensure CI passes (all PHP versions, all checks)
- Update README.md if API changes
- Update CHANGELOG.md in Unreleased section

## Module Architecture

### Configuration Options
Expand All @@ -68,16 +186,19 @@ The module accepts the following configuration in `codeception.yml`:
modules:
enabled:
- \JasonBenett\CodeceptionModuleWiremock\Module\Wiremock:
host: localhost # WireMock host (default: 127.0.0.1)
port: 8080 # WireMock port (default: 8080)
protocol: http # Protocol (default: http)
timeout: 10.0 # Request timeout in seconds (default: 10.0)
cleanupBefore: test # When to cleanup: 'never', 'test', or 'suite' (default: test)
preserveFileMappings: true # Keep file-based stubs on reset (default: true)
verifySSL: true # Verify SSL certificates (default: true)
adminPath: /__admin # Admin API path (default: /__admin)
host: localhost # Required: WireMock host (default: 127.0.0.1)
port: 8080 # Required: WireMock port (default: 8080)
protocol: http # Optional: Protocol (default: http)
cleanupBefore: test # Optional: When to cleanup: 'never', 'test', or 'suite' (default: test)
preserveFileMappings: true # Optional: Keep file-based stubs on reset (default: true)
adminPath: /__admin # Optional: Admin API path (default: /__admin)
httpClient: null # Optional: PSR-18 ClientInterface instance (auto-creates Guzzle if null)
requestFactory: null # Optional: PSR-17 RequestFactoryInterface instance (auto-creates if null)
streamFactory: null # Optional: PSR-17 StreamFactoryInterface instance (auto-creates if null)
```

**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.

### Public Methods (MVP)

#### Setup Methods (have*)
Expand All @@ -99,10 +220,17 @@ modules:

### Lifecycle Hooks

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

### Internal Methods

- `initHttpClient(): void` - Get PSR-18 client from config or auto-create Guzzle instance
- `initRequestFactory(): void` - Get PSR-17 request factory from config or auto-create
- `initStreamFactory(RequestFactoryInterface): void` - Get PSR-17 stream factory from config or auto-create
- `makeAdminRequest(string, string, array): array` - Make HTTP request to WireMock Admin API using PSR-18/PSR-17

### WireMock Admin API Endpoints Used

- `POST /__admin/mappings` - Create stub mapping
Expand Down
101 changes: 90 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ A Codeception module for WireMock integration, allowing you to mock HTTP service

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

## Installation
Expand All @@ -33,6 +35,35 @@ Install via Composer:
composer require jasonbenett/codeception-module-wiremock
```

This module depends on PSR-18 (HTTP Client) and PSR-17 (HTTP Factories) interfaces. You'll need to install a compatible implementation:

**Using Guzzle (recommended):**
```bash
composer require guzzlehttp/guzzle
```

**Using Symfony HttpClient:**
```bash
composer require symfony/http-client nyholm/psr7
```

**Other PSR-18/PSR-17 implementations work as well.**

## Architecture

This module follows **PSR-18** (HTTP Client) and **PSR-17** (HTTP Factories) standards, providing true dependency inversion:

- **No hard dependency on Guzzle** - Use any PSR-compliant HTTP client
- **Framework agnostic** - Works with Symfony HttpClient, Guzzle, or custom clients
- **Optional auto-discovery** - Automatically creates Guzzle instances if available
- **Full control** - Inject your own configured PSR clients for advanced scenarios

This approach allows you to:
- Choose your preferred HTTP client library
- Control HTTP client configuration (timeouts, SSL, proxies, etc.)
- Test with mock PSR-18 clients
- Upgrade HTTP client versions independently

## Quick Start

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

Or use the included `docker-compose.yml`:

```bash
docker-compose up -d
```

### 2. Configure Codeception

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

## Configuration Options

All configuration options and their defaults:
### Basic Configuration (Auto-Discovery)

When using Guzzle, the module can auto-create PSR client instances:

```yaml
modules:
Expand All @@ -97,13 +124,65 @@ modules:
host: 127.0.0.1 # WireMock server host
port: 8080 # WireMock server port
protocol: http # Protocol (http or https)
timeout: 10.0 # Request timeout in seconds
cleanupBefore: test # When to cleanup: 'never', 'test', or 'suite'
preserveFileMappings: true # Keep file-based stubs on reset
verifySSL: true # Verify SSL certificates
adminPath: /__admin # Admin API path
```

### Advanced Configuration (Custom PSR Clients)

For full control and dependency inversion, provide your own PSR-18/PSR-17 implementations:

```php
// tests/_bootstrap.php or tests/_support/Helper/HttpClientProvider.php

use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;

// Create PSR-18 HTTP Client
$httpClient = new Client([
'timeout' => 10.0,
'verify' => false, // Disable SSL verification if needed
'http_errors' => false,
]);

// Create PSR-17 factories (Guzzle's HttpFactory implements both interfaces)
$httpFactory = new HttpFactory();

// Store in global for Codeception access
$GLOBALS['wiremock_http_client'] = $httpClient;
$GLOBALS['wiremock_request_factory'] = $httpFactory;
$GLOBALS['wiremock_stream_factory'] = $httpFactory;
```

```yaml
# codeception.yml or suite config
modules:
enabled:
- \JasonBenett\CodeceptionModuleWiremock\Module\Wiremock:
host: 127.0.0.1
port: 8080
httpClient: !php/const GLOBALS['wiremock_http_client']
requestFactory: !php/const GLOBALS['wiremock_request_factory']
streamFactory: !php/const GLOBALS['wiremock_stream_factory']
```

### All Configuration Options

```yaml
host: 127.0.0.1 # Required: WireMock server host
port: 8080 # Required: WireMock server port
protocol: http # Optional: Protocol (http or https)
cleanupBefore: test # Optional: When to cleanup: 'never', 'test', or 'suite'
preserveFileMappings: true # Optional: Keep file-based stubs on reset
adminPath: /__admin # Optional: Admin API path
httpClient: null # Optional: PSR-18 ClientInterface instance
requestFactory: null # Optional: PSR-17 RequestFactoryInterface instance
streamFactory: null # Optional: PSR-17 StreamFactoryInterface instance
```

**Note:** If `httpClient`, `requestFactory`, or `streamFactory` are not provided, the module will attempt to auto-create Guzzle instances if available.

## Available Methods

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

### Local Development
Expand All @@ -501,7 +580,7 @@ composer test:functional
### Code Quality Metrics

- **PHPStan**: Max level, zero errors
- **Code Coverage**: 100% with Codecov reporting
- **Code Coverage**: with Codecov reporting
- **Code Style**: PER Coding Style 3.0 (successor to PSR-12)
- **Type Safety**: Full PHPDoc annotations with array shapes
- **Documentation**: Comprehensive inline documentation
Expand Down
Loading