chore: major code cleanup and type safety using phpstan

This commit is contained in:
Björn Fromme committed 2026-04-16 17:11:16 +02:00
1 parent 060cbea9da
commit 3da73d626b
103 files changed
+976 -1192

No files matched your search

-484
View File
@@ -1,484 +0,0 @@
# Symfony 7.4 Upgrade Plan
**Project:** MyEP Next Booking
**Current Version:** Symfony 6.4.x (6.4.33)
**Target Version:** Symfony 7.4 (LTS)
**Date:** 2026-02-23
**Status:** Ready for Upgrade
---
## Executive Summary
**Estimated Effort: LOW**
**Risk Level: LOW**
**Status: READY FOR UPGRADE**
The MyEP Next Booking application is well-positioned for a Symfony 7.4 upgrade. The codebase follows modern Symfony best practices, uses PHP 8 features extensively, and avoids most deprecated patterns. All pre-upgrade fixes have been completed.
**Key Facts:**
- Symfony 7.4 was released on November 27, 2025
- Requires PHP 8.2.0+ (current: PHP 8.3.30)
- LTS version with bug fixes until November 2028, security fixes until November 2029
- Symfony 8.0 = Symfony 7.4 minus deprecations (same features, PHP 8.4 required)
**Pre-Upgrade Status:**
- PHP version: 8.3.30 (compatible; Symfony 7.4 requires 8.2+)
- OAuth2 Server Bundle: 1.1.0 (compatible)
- Stimulus Bundle: 2.32.0 (compatible)
- Doctrine ORM: 3.6.0 (compatible)
- All 433 tests passing
- Email validation mode: Configured (html5)
- 18 test-only deprecation warnings (cosmetic, do not affect runtime)
---
## Previous Plan Status
The previous upgrade plan (dated 2025-12-06) has been updated. All pre-upgrade tasks are now complete.
### Completed Items
| Item | Previous Status | Current Status |
|------|----------------|----------------|
| OAuth2 Server Bundle | 1.0.0 | **1.1.0 installed** |
| Stimulus Bundle | 2.31.0 | **2.32.0 installed** |
| PHP 8.2 deprecations (ParticipantDto::$touched) | Planned | **Completed** |
| Doctrine config (controller_resolver) | Planned | **Completed** |
| SecurityController null safety | Needs verification | **Completed** (uses `?->` and `??`) |
| Email validator "loose" mode deprecation | Needs fix | **Completed** (validator.yaml configured) |
| Failing test in BookingDataProcessorTest | Unrelated to upgrade | **Resolved** (test now passes) |
---
## Current State Analysis
### PHP Version Compatibility
- **Current Runtime:** PHP 8.3.30
- **Composer Platform:** PHP 8.3.30
- **Symfony 7.4 Required:** PHP 8.2.0+
- **Status:** Compatible
### Installed Dependencies
#### Core Symfony Packages (6.4.x)
All 33 Symfony packages currently at 6.4.x need version constraint updates to 7.4.*
#### Third-Party Bundles - Compatible
| Package | Installed | Symfony 7.4 Compatible |
|---------|-----------|------------------------|
| `doctrine/orm` | 3.6.0 | Yes |
| `doctrine/doctrine-bundle` | 2.18.1 | Yes |
| `doctrine/doctrine-migrations-bundle` | 3.7.0 | Yes |
| `league/oauth2-server-bundle` | 1.1.0 | Yes |
| `symfony/stimulus-bundle` | 2.32.0 | Yes |
| `symfony/webpack-encore-bundle` | 2.4.0 | Yes |
| `symfony/monolog-bundle` | 3.11.0 | Yes |
| `zenstruck/schedule-bundle` | 1.9.0 | Yes |
| `league/flysystem-bundle` | 3.6.1 | Yes |
| `league/flysystem-sftp-v3` | 3.30.0 | Yes |
| `nelexa/zip` | 4.0.2 | Yes (verify post-upgrade) |
| `spatie/crypto` | 2.1.1 | Yes |
| `easycorp/easyadmin-bundle` | 4.29.1 (^4.27) | Yes |
### Code Quality Assessment
The codebase implements modern Symfony/PHP patterns:
- All controllers use PHP 8 attributes (`#[Route]`)
- Constructor property promotion used throughout
- Strict types enforced on all files
- Typed properties and return type declarations
- Entities use PHP 8 attributes for ORM mapping
- Modern security system (AbstractLoginFormAuthenticator)
- No deprecated annotation usage
---
## Pre-Upgrade Fixes - COMPLETED
### 1. Email Validator "loose" Mode Deprecation - DONE
**Issue:** The default mode for `#[Assert\Email]` is "loose", which is deprecated in Symfony 6.2+ and removed in Symfony 7.0.
**Resolution:** Framework default configured in `config/packages/validator.yaml`:
```yaml
framework:
validation:
email_validation_mode: html5
```
All Email constraints in the codebase also explicitly use `mode: 'strict'`:
- `src/Form/Model/ParticipantDto.php` - Has `mode: 'strict'`
- `src/Form/Model/RegistrationDto.php` - Has `mode: 'strict'`
- `src/BusProNet/Model/Communication.php` - Has `mode: 'strict'`
- `src/Controller/ResetPasswordController.php` - Has `mode: 'strict'`
### 2. Test Deprecation Warnings - COSMETIC ONLY
**Issue:** 18 deprecation warnings appear during test runs.
**Cause:** `ParticipantEditDtoTest` creates a standalone validator using `Validation::createValidatorBuilder()` which bypasses the framework's `validator.yaml` configuration.
**Impact:** These warnings are cosmetic and do not affect:
- Runtime behavior (all Email constraints use explicit `mode: 'strict'`)
- Symfony 7.4 compatibility (no code changes needed)
- Test results (all 433 tests pass)
**Note:** Warnings can be silenced with `SYMFONY_DEPRECATIONS_HELPER=disabled` if desired.
---
## Required Changes
### 1. Update Composer Version Constraints
**File:** `composer.json`
#### Symfony Core Packages
Update all packages from `6.4.*` to `7.4.*`:
```json
{
"require": {
"symfony/asset": "7.4.*",
"symfony/console": "7.4.*",
"symfony/doctrine-messenger": "7.4.*",
"symfony/dom-crawler": "7.4.*",
"symfony/dotenv": "7.4.*",
"symfony/expression-language": "7.4.*",
"symfony/form": "7.4.*",
"symfony/framework-bundle": "7.4.*",
"symfony/html-sanitizer": "7.4.*",
"symfony/http-client": "7.4.*",
"symfony/intl": "7.4.*",
"symfony/lock": "7.4.*",
"symfony/mailer": "7.4.*",
"symfony/mime": "7.4.*",
"symfony/notifier": "7.4.*",
"symfony/process": "7.4.*",
"symfony/property-access": "7.4.*",
"symfony/property-info": "7.4.*",
"symfony/runtime": "7.4.*",
"symfony/security-bundle": "7.4.*",
"symfony/serializer": "7.4.*",
"symfony/string": "7.4.*",
"symfony/translation": "7.4.*",
"symfony/twig-bundle": "7.4.*",
"symfony/uid": "7.4.*",
"symfony/validator": "7.4.*",
"symfony/web-link": "7.4.*",
"symfony/yaml": "7.4.*"
}
}
```
#### Development Packages
```json
{
"require-dev": {
"symfony/browser-kit": "7.4.*",
"symfony/css-selector": "7.4.*",
"symfony/debug-bundle": "7.4.*",
"symfony/maker-bundle": "^1.0",
"symfony/phpunit-bridge": "7.4.*",
"symfony/stopwatch": "7.4.*",
"symfony/web-profiler-bundle": "7.4.*"
}
}
```
Keep `symfony/maker-bundle` at `^1.0` (compatible with Symfony 7.4).
### 2. Update Symfony Flex Configuration
**File:** `composer.json`
```json
{
"extra": {
"symfony": {
"allow-contrib": true,
"require": "7.4.*",
"docker": true
}
}
}
```
---
## Potential Breaking Changes
### 1. Symfony Core Changes (Symfony 7.0 -> 7.4)
Symfony 7.0 removed all deprecated features from 6.4. Our codebase is fully compliant:
- **Native Return Types:** Already implemented
- **Attribute-based Configuration:** Already implemented
- **Security System:** Modern authenticator pattern in use
- **Form System:** Modern approach in use
- **No Annotation Usage:** All using PHP 8 attributes
### 2. Email Validation Mode
**Breaking Change:** The default `mode` for `Email` constraint changes from `loose` to `html5` in Symfony 7.0.
**Status:** Already handled - all constraints use explicit `mode: 'strict'` and framework default is set to `html5`.
### 3. Deprecation Cleanup
Deprecations accumulated in Symfony 7.1-7.4 will need review:
- Check Symfony Profiler after upgrade for any new deprecation warnings
- Review [UPGRADE-7.0.md](https://github.com/symfony/symfony/blob/7.0/UPGRADE-7.0.md)
- Review [UPGRADE-7.4.md](https://github.com/symfony/symfony/blob/7.4/UPGRADE-7.4.md)
---
## Upgrade Procedure
### Phase 1: Preparation (15 minutes)
1. **Create Feature Branch**
```bash
git checkout -b feature/symfony-7.4-upgrade
```
2. **Backup Current State**
```bash
git tag before-symfony-7.4-upgrade
```
### Phase 2: Dependency Updates (30 minutes - 1 hour)
1. **Update composer.json**
- Update all Symfony package constraints to `7.4.*`
- Update `extra.symfony.require` to `"7.4.*"`
- Consider tightening `php` to `>=8.2` (Symfony 7.4 requires 8.2+; optional if already on 8.3)
2. **Run Composer Update**
```bash
ddev composer update
```
3. **Review Recipe Updates**
```bash
ddev composer recipes
```
Review any recipe updates from Symfony Flex and apply as needed.
4. **Clear All Caches**
```bash
ddev exec bin/console cache:clear
ddev exec bin/console cache:warmup
ddev exec "php -r 'opcache_reset();'"
```
### Phase 3: Testing & Validation (1-2 hours)
1. **Run PHPUnit Test Suite**
```bash
ddev exec ./vendor/bin/phpunit
```
Check for:
- Test failures
- Deprecation warnings
- Fatal errors
2. **Run PHP-CS-Fixer**
```bash
ddev php vendor/bin/php-cs-fixer fix --dry-run --diff
```
3. **Manual Testing - Critical Paths**
- [ ] User authentication (BPN API login)
- [ ] OAuth2 token generation (`/token` endpoint)
- [ ] OAuth2 authorization flow (`/authorize` endpoint)
- [ ] API endpoints (all `/api/*` routes)
- [ ] Booking creation flow (Steps 1-4)
- [ ] Participant form rendering and submission
- [ ] Dynamic field updates (HTMX)
- [ ] Real-time pricing calculations
- [ ] Insurance selection and filtering
- [ ] Transportation service selection
- [ ] Payment method selection
- [ ] Final booking submission to BPN API
- [ ] Booking edit functionality
- [ ] PDF/XML download functionality
- [ ] User registration
- [ ] Password reset
- [ ] Personal data management
- [ ] EasyAdmin CRUD / admin panels
4. **Check Symfony Profiler**
- Review deprecation warnings (should be minimal)
- Check performance metrics
- Verify no errors in logs
5. **Test Frontend Assets**
```bash
npm run build
```
- Verify Stimulus controllers load correctly
- Test HTMX interactions
- Verify Toastify notifications
- Check TailwindCSS compilation
6. **Verify ZIP File Functionality**
- Test any features using `nelexa/zip` (package last updated 2022)
- Verify document generation and downloads work correctly
### Phase 4: Bug Fixes (0-2 hours)
Address any issues discovered during testing:
1. **Review Error Logs**
```bash
ddev logs
```
2. **Fix Any Remaining Deprecations**
- Use Symfony Profiler to identify deprecations
- Update code to use modern alternatives
### Phase 5: Deployment (1 hour)
1. **Deploy to Staging**
```bash
ddev exec vendor/bin/dep deploy staging
```
2. **Staging Validation**
- Full regression testing
- Performance testing
3. **Production Deployment**
- Schedule maintenance window
- Deploy during low-traffic period
- Monitor error logs closely
---
## Rollback Plan
### Immediate Rollback
If critical issues are discovered:
1. **Git Revert**
```bash
git checkout master
git reset --hard before-symfony-7.4-upgrade
```
2. **Restore Dependencies**
```bash
ddev composer install
```
3. **Clear Caches**
```bash
ddev exec bin/console cache:clear
```
### Production Rollback
If issues occur in production:
```bash
ddev exec vendor/bin/dep rollback production
```
---
## Testing Checklist
### Unit Tests
- [ ] All PHPUnit tests pass (433 tests)
- [ ] No new deprecation warnings
- [ ] Code coverage maintained
### Integration Tests
- [ ] BusProNet API integration
- [ ] XML parsing and data processing
- [ ] Booking price calculations
- [ ] Insurance assignment logic
- [ ] Field handler system
### Functional Tests
- [ ] User authentication flow
- [ ] OAuth2 token generation
- [ ] API endpoints respond correctly
- [ ] Booking creation (all steps)
- [ ] Form validation
- [ ] HTMX partial updates
- [ ] Dynamic field state changes
- [ ] Real-time pricing updates
- [ ] Toast notifications
### Browser Testing
- [ ] Chrome (latest)
- [ ] Firefox (latest)
- [ ] Safari (latest)
- [ ] Mobile browsers
---
## Timeline Estimate
| Phase | Estimated Time | Notes |
|-------|---------------|-------|
| **Phase 1:** Preparation | 15 min | Branch creation |
| **Phase 2:** Dependency Updates | 30 min - 1 hour | composer.json updates, composer update |
| **Phase 3:** Testing & Validation | 1-2 hours | Automated + manual testing |
| **Phase 4:** Bug Fixes | 0-2 hours | Address any issues |
| **Phase 5:** Deployment | 1 hour | Staging deployment |
| **Total** | **2.5-6 hours** | Average: **3.5 hours** |
---
## Future Considerations: Symfony 8.0
Symfony 8.0 was released alongside 7.4 (November 2025) with identical features but:
- Requires PHP 8.4
- No deprecation layers (all deprecated code removed)
- Shorter support window (8 months)
**Recommendation:** Stay on Symfony 7.4 LTS for stability. Plan PHP 8.4 upgrade separately, then consider Symfony 8.x path.
---
## Success Criteria
- [ ] All PHPUnit tests pass (433 tests)
- [ ] No deprecation warnings in Symfony Profiler
- [ ] All critical user flows functional
- [ ] OAuth2 authentication working
- [ ] BPN API integration operational
- [ ] Booking creation flow complete
- [ ] Frontend assets compile and load
- [ ] Performance metrics comparable or better
- [ ] No errors in production logs after 24 hours
---
## Additional Resources
### Official Symfony Documentation
- [Upgrade Major Version Guide](https://symfony.com/doc/current/setup/upgrade_major.html)
- [UPGRADE-7.0.md](https://github.com/symfony/symfony/blob/7.0/UPGRADE-7.0.md)
- [UPGRADE-7.4.md](https://github.com/symfony/symfony/blob/7.4/UPGRADE-7.4.md)
- [Symfony 7.4 Release Notes](https://symfony.com/releases/7.4)
- [Preparing for Symfony 7.4 and 8.0](https://symfony.com/blog/preparing-for-symfony-7-4-and-symfony-8-0)
### Bundle-Specific Documentation
- [OAuth2 Server Bundle](https://github.com/thephpleague/oauth2-server-bundle)
- [Doctrine ORM 3.x Documentation](https://www.doctrine-project.org/projects/orm.html)
---
**Document Version:** 7.0
**Previous Version:** 6.0 (dated 2026-01-09)
**Last Updated:** 2026-02-23
**Status:** Ready for Symfony 7.4 Upgrade (updated for current project state)
+2
View File
@@ -0,0 +1,2 @@
parameters:
ignoreErrors: []
+13
View File
@@ -0,0 +1,13 @@
includes:
- vendor/phpstan/phpstan-symfony/extension.neon
- vendor/phpstan/phpstan-doctrine/extension.neon
- vendor/phpstan/phpstan-strict-rules/rules.neon
parameters:
level: 6
paths:
- src
- tests
tmpDir: var/cache/phpstan
symfony:
containerXmlPath: var/cache/dev/App_KernelDevDebugContainer.xml
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Admin\Field; namespace App\Admin\Field;
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet; namespace App\BusProNet;
@@ -128,7 +128,7 @@ class BookingDataProcessor
// Parking service (for self-organized PKW transportation) // Parking service (for self-organized PKW transportation)
$parkingServices = $booking->getAdditionalServicesForParticipantByGroup($index, Constants::TOKEN_PARKING); $parkingServices = $booking->getAdditionalServicesForParticipantByGroup($index, Constants::TOKEN_PARKING);
if (false === empty($parkingServices)) { if ([] !== $parkingServices) {
$participantData->parking = true; $participantData->parking = true;
$participantData->parkingService = reset($parkingServices); $participantData->parkingService = reset($parkingServices);
} else { } else {
@@ -320,7 +320,7 @@ class BookingDataProcessor
// Add purchase vouchers if any exist // Add purchase vouchers if any exist
$purchaseVouchers = $this->mappingCollector->collectPurchaseVouchers($formData); $purchaseVouchers = $this->mappingCollector->collectPurchaseVouchers($formData);
if (false === empty($purchaseVouchers)) { if ([] !== $purchaseVouchers) {
$payload['gutscheine']['gutschein'] = []; $payload['gutscheine']['gutschein'] = [];
foreach ($purchaseVouchers as $code) { foreach ($purchaseVouchers as $code) {
$payload['gutscheine']['gutschein'][] = [ $payload['gutscheine']['gutschein'][] = [
@@ -43,7 +43,7 @@ class ServiceMappingCollector
* Groups board, ski passes, rentals, rental insurance, courses, parking, and additional services * Groups board, ski passes, rentals, rental insurance, courses, parking, and additional services
* by service ID with their participant assignments (1-based). * by service ID with their participant assignments (1-based).
* *
* @return array<string, array<int>> Map of service ID to participant IDs * @return array<int, list<int>> Map of service ID to participant IDs
*/ */
public function collectServiceMappings(BookingDto $bookingDto): array public function collectServiceMappings(BookingDto $bookingDto): array
{ {
@@ -99,7 +99,7 @@ class ServiceMappingCollector
/** /**
* Collects transportation service mappings. * Collects transportation service mappings.
* *
* @return array<string, array<int>> Map of transportation service ID to participant IDs * @return array<int, list<int>> Map of transportation service ID to participant IDs
*/ */
public function collectTransportationMappings(BookingDto $bookingDto): array public function collectTransportationMappings(BookingDto $bookingDto): array
{ {
@@ -126,7 +126,7 @@ class ServiceMappingCollector
* Collects the unified pickup selection which applies to both directions. * Collects the unified pickup selection which applies to both directions.
* The API receives this as outbound pickup data. * The API receives this as outbound pickup data.
* *
* @return array<string, array<int>> Map of pickup ID to participant IDs * @return array<int, list<int>> Map of pickup ID to participant IDs
*/ */
public function collectPickupMappings(BookingDto $bookingDto): array public function collectPickupMappings(BookingDto $bookingDto): array
{ {
@@ -148,7 +148,7 @@ class ServiceMappingCollector
* *
* Collects the drop-off selection for the inbound/return direction. * Collects the drop-off selection for the inbound/return direction.
* *
* @return array<string, array<int>> Map of drop-off ID to participant IDs * @return array<int, list<int>> Map of drop-off ID to participant IDs
*/ */
public function collectDropOffMappings(BookingDto $bookingDto): array public function collectDropOffMappings(BookingDto $bookingDto): array
{ {
@@ -171,7 +171,7 @@ class ServiceMappingCollector
* CRITICAL: Insurance data is only included in CREATE flow, not in UPDATE flow. * CRITICAL: Insurance data is only included in CREATE flow, not in UPDATE flow.
* IMPORTANT: Excludes synthetic "no insurance" option from BPN transmission. * IMPORTANT: Excludes synthetic "no insurance" option from BPN transmission.
* *
* @return array<string, array<int>> Map of insurance ID to participant IDs * @return array<string, list<int>> Map of insurance ID to participant IDs
*/ */
public function collectInsuranceMappings(BookingDto $bookingDto): array public function collectInsuranceMappings(BookingDto $bookingDto): array
{ {
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\Exception; namespace App\BusProNet\Exception;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\Exception; namespace App\BusProNet\Exception;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\Form\ChoiceLoader; namespace App\BusProNet\Form\ChoiceLoader;
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\Form; namespace App\BusProNet\Form;
+9 -2
View File
@@ -182,8 +182,15 @@ class Insurance
$urls = []; $urls = [];
foreach ($this->containedInsurances as $containedInsurance) { foreach ($this->containedInsurances as $containedInsurance) {
if (null !== $containedInsurance->{$urlProperty}) { $url = match ($urlProperty) {
$urls[] = $containedInsurance->{$urlProperty}; 'urlInfo' => $containedInsurance->urlInfo,
'urlProductInfo' => $containedInsurance->urlProductInfo,
'urlTerms' => $containedInsurance->urlTerms,
default => null,
};
if (null !== $url) {
$urls[] = $url;
} }
} }
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\Model; namespace App\BusProNet\Model;
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\Traits; namespace App\BusProNet\Traits;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\Utility; namespace App\BusProNet\Utility;
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\Utility; namespace App\BusProNet\Utility;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\XmlLoader; namespace App\BusProNet\XmlLoader;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser; namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser; namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser; namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser; namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser; namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser; namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser; namespace App\BusProNet\XmlParser;
+4 -2
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser; namespace App\BusProNet\XmlParser;
@@ -15,10 +16,11 @@ class ProductsParser extends AbstractParser
$result $result
->filterXPath('//produkte/produkt') ->filterXPath('//produkte/produkt')
->each(function (Crawler $node) use (&$products) { ->each(function (Crawler $node) use (&$products) {
if (false === empty($node->attr('code'))) { $code = $node->attr('code');
if (null !== $code && '' !== $code) {
$product = new Product(); $product = new Product();
$product->id = (int) $node->attr('id'); $product->id = (int) $node->attr('id');
$product->code = (string) $node->attr('code'); $product->code = $code;
$product->name = (string) $node->attr('bezeichnung'); $product->name = (string) $node->attr('bezeichnung');
$products[$product->id] = $product; $products[$product->id] = $product;
+5 -2
View File
@@ -50,8 +50,11 @@ class BpnFetchTravelCommand extends Command
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$travelId = (int) $input->getArgument('travel-id'); $travelId = (int) $input->getArgument('travel-id');
$filename = $input->getOption('filename') ?? sprintf('Ziel_%d.xml', $travelId); $filenameOption = $input->getOption('filename');
$dryRun = $input->getOption('dry-run'); $filename = is_string($filenameOption) && '' !== $filenameOption
? $filenameOption
: sprintf('Ziel_%d.xml', $travelId);
$dryRun = true === $input->getOption('dry-run');
$io->text(sprintf('Fetching travel data for product ID %d...', $travelId)); $io->text(sprintf('Fetching travel data for product ID %d...', $travelId));
+14 -4
View File
@@ -39,7 +39,14 @@ class BpnReplayCommand extends Command
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$filePath = $input->getArgument('file'); $filePathArgument = $input->getArgument('file');
if (!is_string($filePathArgument) || '' === $filePathArgument) {
$io->error('Invalid file path argument.');
return Command::FAILURE;
}
$filePath = $filePathArgument;
if (false === file_exists($filePath)) { if (false === file_exists($filePath)) {
$io->error(sprintf('File not found: %s', $filePath)); $io->error(sprintf('File not found: %s', $filePath));
@@ -84,8 +91,9 @@ class BpnReplayCommand extends Command
return Command::FAILURE; return Command::FAILURE;
} }
$outputFile = $input->getOption('output'); $outputFileOption = $input->getOption('output');
if (null !== $outputFile) { if (is_string($outputFileOption) && '' !== $outputFileOption) {
$outputFile = $outputFileOption;
if (false === file_put_contents($outputFile, $response)) { if (false === file_put_contents($outputFile, $response)) {
$io->error(sprintf('Failed to write response to: %s', $outputFile)); $io->error(sprintf('Failed to write response to: %s', $outputFile));
@@ -139,6 +147,8 @@ class BpnReplayCommand extends Command
return $xml; return $xml;
} }
return $doc->saveXML() ?: $xml; $formattedXml = $doc->saveXML();
return false === $formattedXml ? $xml : $formattedXml;
} }
} }
+2 -2
View File
@@ -65,8 +65,8 @@ class BpnXmlSyncCommand extends Command
return Command::SUCCESS; return Command::SUCCESS;
} }
$force = $input->getOption('force'); $force = true === $input->getOption('force');
$dryRun = $input->getOption('dry-run'); $dryRun = true === $input->getOption('dry-run');
$remoteInfo = $this->readRemoteInfo(); $remoteInfo = $this->readRemoteInfo();
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Command; namespace App\Command;
@@ -41,7 +41,7 @@ class DraftBackfillBookingNumberCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int protected function execute(InputInterface $input, OutputInterface $output): int
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$dryRun = $input->getOption('dry-run'); $dryRun = true === $input->getOption('dry-run');
$drafts = $this->draftRepository->findBy(['bookingNumber' => null]); $drafts = $this->draftRepository->findBy(['bookingNumber' => null]);
@@ -80,7 +80,7 @@ class DraftBackfillBookingNumberCommand extends Command
continue; continue;
} }
if ($dryRun) { if (true === $dryRun) {
$io->text(sprintf( $io->text(sprintf(
'[DRY RUN] Would set booking number %d for draft %d (bookingId %d)', '[DRY RUN] Would set booking number %d for draft %d (bookingId %d)',
$bookingNumber, $bookingNumber,
@@ -108,7 +108,7 @@ class DraftBackfillBookingNumberCommand extends Command
] ]
); );
if ($dryRun) { if (true === $dryRun) {
$io->note('Dry run - no changes were made.'); $io->note('Dry run - no changes were made.');
} else { } else {
$io->success(sprintf('Updated %d draft(s).', $updated)); $io->success(sprintf('Updated %d draft(s).', $updated));
+2 -2
View File
@@ -38,9 +38,9 @@ class DraftCleanupCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int protected function execute(InputInterface $input, OutputInterface $output): int
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$dryRun = $input->getOption('dry-run'); $dryRun = true === $input->getOption('dry-run');
if ($dryRun) { if (true === $dryRun) {
$expiredDrafts = $this->draftRepository->createQueryBuilder('d') $expiredDrafts = $this->draftRepository->createQueryBuilder('d')
->where('d.travelDate < :today') ->where('d.travelDate < :today')
->setParameter('today', new \DateTimeImmutable('today')) ->setParameter('today', new \DateTimeImmutable('today'))
+9 -6
View File
@@ -38,9 +38,12 @@ class DraftInspectCommand extends Command
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$days = $input->getOption('days'); $daysOption = $input->getOption('days');
$bookingId = $input->getOption('booking-id'); $bookingIdOption = $input->getOption('booking-id');
$shouldDelete = $input->getOption('delete'); $shouldDelete = true === $input->getOption('delete');
$days = is_numeric($daysOption) ? (int) $daysOption : null;
$bookingId = is_numeric($bookingIdOption) ? (int) $bookingIdOption : null;
$qb = $this->draftRepository->createQueryBuilder('d') $qb = $this->draftRepository->createQueryBuilder('d')
->join('d.user', 'u') ->join('d.user', 'u')
@@ -48,14 +51,14 @@ class DraftInspectCommand extends Command
->orderBy('d.updatedAt', 'DESC'); ->orderBy('d.updatedAt', 'DESC');
if (null !== $days) { if (null !== $days) {
$cutoffDate = new \DateTimeImmutable(sprintf('-%d days', (int) $days)); $cutoffDate = new \DateTimeImmutable(sprintf('-%d days', $days));
$qb->andWhere('d.updatedAt < :cutoff') $qb->andWhere('d.updatedAt < :cutoff')
->setParameter('cutoff', $cutoffDate); ->setParameter('cutoff', $cutoffDate);
} }
if (null !== $bookingId) { if (null !== $bookingId) {
$qb->andWhere('d.bookingId = :bookingId') $qb->andWhere('d.bookingId = :bookingId')
->setParameter('bookingId', (int) $bookingId); ->setParameter('bookingId', $bookingId);
} }
$drafts = $qb->getQuery()->getResult(); $drafts = $qb->getQuery()->getResult();
@@ -97,7 +100,7 @@ class DraftInspectCommand extends Command
'Drafts are preserved when: user cancels, API rejects submission, or user simply leaves', 'Drafts are preserved when: user cancels, API rejects submission, or user simply leaves',
]); ]);
if ($shouldDelete) { if (true === $shouldDelete) {
if (false === $io->confirm(sprintf('Are you sure you want to delete %d draft(s)?', \count($drafts)), false)) { if (false === $io->confirm(sprintf('Are you sure you want to delete %d draft(s)?', \count($drafts)), false)) {
$io->warning('Deletion cancelled.'); $io->warning('Deletion cancelled.');
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Command; namespace App\Command;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller\Admin; namespace App\Controller\Admin;
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller\Api; namespace App\Controller\Api;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller\Api; namespace App\Controller\Api;
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller\Api; namespace App\Controller\Api;
+3 -2
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller\Api; namespace App\Controller\Api;
@@ -26,13 +27,13 @@ class LastUpdateController extends AbstractController
$lines = explode("\n", $file); $lines = explode("\n", $file);
$line = trim($lines[0]); $line = trim($lines[0]);
if (true === empty($line)) { if ('' === $line) {
$lastUploadAt = null; $lastUploadAt = null;
} else { } else {
$lastUploadAt = \DateTime::createFromFormat('d.m.Y H:i:s', $line); $lastUploadAt = \DateTime::createFromFormat('d.m.Y H:i:s', $line);
} }
} }
return $this->json(['timestamp' => $lastUploadAt->format('Y-m-d H:i:s')]); return $this->json(['timestamp' => $lastUploadAt?->format('Y-m-d H:i:s')]);
} }
} }
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller\Api; namespace App\Controller\Api;
+4 -1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller\Api; namespace App\Controller\Api;
@@ -64,7 +65,9 @@ class TravelController extends AbstractController
$dateCode = (new DateCodeUtility())->sanitize($dateCode); $dateCode = (new DateCodeUtility())->sanitize($dateCode);
$dateId = $this->travelDataService->mapDateCodeToId($dateCode); $dateId = $this->travelDataService->mapDateCodeToId($dateCode);
$hotelId = $hotelCode ? $this->travelDataService->mapHotelCodeToId($hotelCode) : null; $hotelId = null !== $hotelCode && '' !== $hotelCode
? $this->travelDataService->mapHotelCodeToId($hotelCode)
: null;
if (null === $dateId) { if (null === $dateId) {
return $this->json(['message' => 'Not found'], Response::HTTP_NOT_FOUND); return $this->json(['message' => 'Not found'], Response::HTTP_NOT_FOUND);
+2 -1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller\Api; namespace App\Controller\Api;
@@ -80,7 +81,7 @@ class UserinfoController extends AbstractController
// filter claims by provided scopes // filter claims by provided scopes
foreach ($scopes as $scope) { foreach ($scopes as $scope) {
if (false === in_array($scope, $keys)) { if (false === in_array($scope, $keys, true)) {
continue; continue;
} }
$claims[$scope] = $allClaims[$scope]; $claims[$scope] = $allClaims[$scope];
@@ -53,7 +53,8 @@ class Step1Controller extends AbstractController
$oldRoomSelectionSnapshot = $this->bookingSessionService->getOrCreateBaselineSnapshot($request, $bookingCreateDto); $oldRoomSelectionSnapshot = $this->bookingSessionService->getOrCreateBaselineSnapshot($request, $bookingCreateDto);
// Validate step access - allow step 1 or redirect to current step // Validate step access - allow step 1 or redirect to current step
if ($redirect = $this->validateStepAccess($bookingCreateDto, 1)) { $redirect = $this->validateStepAccess($bookingCreateDto, 1);
if (null !== $redirect) {
return $redirect; return $redirect;
} }
@@ -61,7 +61,8 @@ class Step4Controller extends AbstractController
$bookingCreateDto = $result; $bookingCreateDto = $result;
// Validate step access // Validate step access
if ($redirect = $this->validateStepAccess($bookingCreateDto, 4)) { $redirect = $this->validateStepAccess($bookingCreateDto, 4);
if (null !== $redirect) {
return $redirect; return $redirect;
} }
@@ -218,6 +219,22 @@ class Step4Controller extends AbstractController
return false !== filter_var($normalizedEmail, FILTER_VALIDATE_EMAIL) ? $normalizedEmail : null; return false !== filter_var($normalizedEmail, FILTER_VALIDATE_EMAIL) ? $normalizedEmail : null;
} }
/**
* @param array<string, mixed> $context
*/
private function handleApiError(
string $logMessage,
array $context,
string $flashMessage,
BookingDto $bookingCreateDto,
FormInterface $form,
): Response {
$this->logger->error($logMessage, $context);
$this->addFlash('error', $flashMessage);
return $this->renderStepForm($bookingCreateDto, $form);
}
/** /**
* Clears travel data and availability cache after successful booking. * Clears travel data and availability cache after successful booking.
*/ */
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller\Booking; namespace App\Controller\Booking;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller; namespace App\Controller;
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller; namespace App\Controller;
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Controller; namespace App\Controller;
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace App\DependencyInjection\Env;
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
final class CsvIntEnvVarProcessor implements EnvVarProcessorInterface
{
/**
* @return array<int, int>
*/
public function getEnv(string $prefix, string $name, \Closure $getEnv): array
{
$raw = $getEnv($name);
if (!\is_string($raw)) {
throw new RuntimeException(sprintf('The "csv_int" env var processor expects a string, got "%s" for "%s".', get_debug_type($raw), $name));
}
$values = array_map('trim', explode(',', $raw));
if ([] !== array_filter($values, static fn (string $value): bool => '' === $value)) {
throw new RuntimeException(sprintf('Environment variable "%s" contains empty values and cannot be processed as csv_int.', $name));
}
$integers = array_map(static function (string $value) use ($name): int {
if (1 !== preg_match('/^[+-]?\d+$/', $value)) {
throw new RuntimeException(sprintf('Environment variable "%s" contains non-integer value "%s".', $name, $value));
}
return (int) $value;
}, $values);
return $integers;
}
public static function getProvidedTypes(): array
{
return [
'csv_int' => 'array',
];
}
}
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Form; namespace App\Form;
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Form; namespace App\Form;
@@ -154,7 +154,7 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
} }
} }
if (false === empty($attributes)) { if ([] !== $attributes) {
$stateModifications['attr'] = $attributes; $stateModifications['attr'] = $attributes;
} }
@@ -170,7 +170,7 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
*/ */
public function hasStateConditions(string $fieldName): bool public function hasStateConditions(string $fieldName): bool
{ {
return isset($this->fieldStateConditions[$fieldName]) && !empty($this->fieldStateConditions[$fieldName]); return isset($this->fieldStateConditions[$fieldName]) && [] !== $this->fieldStateConditions[$fieldName];
} }
/** /**
@@ -210,7 +210,7 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
foreach (array_keys($this->fieldStateConditions) as $fieldName) { foreach (array_keys($this->fieldStateConditions) as $fieldName) {
$fieldState = $this->getFieldState($fieldName, $bookingDto, $participantIndex, $formData); $fieldState = $this->getFieldState($fieldName, $bookingDto, $participantIndex, $formData);
if (!empty($fieldState)) { if ([] !== $fieldState) {
$allStates[$fieldName] = $fieldState; $allStates[$fieldName] = $fieldState;
} }
} }
@@ -130,7 +130,7 @@ abstract class AbstractParticipantFieldHandler implements ParticipantFieldHandle
*/ */
protected function normalizeEmptyValue(mixed $value): mixed protected function normalizeEmptyValue(mixed $value): mixed
{ {
return empty($value) ? null : $value; return $this->isEmptyValue($value) ? null : $value;
} }
/** /**
@@ -146,13 +146,24 @@ abstract class AbstractParticipantFieldHandler implements ParticipantFieldHandle
*/ */
protected function normalizeIntValue(mixed $value): ?int protected function normalizeIntValue(mixed $value): ?int
{ {
if (empty($value)) { if ($this->isEmptyValue($value)) {
return null; return null;
} }
return is_numeric($value) ? (int) $value : null; return is_numeric($value) ? (int) $value : null;
} }
private function isEmptyValue(mixed $value): bool
{
return null === $value
|| false === $value
|| 0 === $value
|| 0.0 === $value
|| '' === $value
|| '0' === $value
|| [] === $value;
}
/** /**
* Normalizes a checkbox/form toggle value to a boolean. * Normalizes a checkbox/form toggle value to a boolean.
* *
@@ -37,7 +37,7 @@ class CompositeCondition implements FieldConditionInterface
private const OPERATOR_OR = 'OR'; private const OPERATOR_OR = 'OR';
private const OPERATOR_NOT = 'NOT'; private const OPERATOR_NOT = 'NOT';
/** @var FieldConditionInterface[] */ /** @var non-empty-list<FieldConditionInterface> */
private readonly array $conditions; private readonly array $conditions;
/** /**
@@ -131,7 +131,7 @@ class CompositeCondition implements FieldConditionInterface
*/ */
public static function and(FieldConditionInterface ...$conditions): self public static function and(FieldConditionInterface ...$conditions): self
{ {
if (empty($conditions)) { if ([] === $conditions) {
throw new \InvalidArgumentException('AND condition requires at least one condition'); throw new \InvalidArgumentException('AND condition requires at least one condition');
} }
@@ -145,7 +145,7 @@ class CompositeCondition implements FieldConditionInterface
*/ */
public static function or(FieldConditionInterface ...$conditions): self public static function or(FieldConditionInterface ...$conditions): self
{ {
if (empty($conditions)) { if ([] === $conditions) {
throw new \InvalidArgumentException('OR condition requires at least one condition'); throw new \InvalidArgumentException('OR condition requires at least one condition');
} }
@@ -167,6 +167,8 @@ class CompositeCondition implements FieldConditionInterface
* *
* Returns false as soon as any condition evaluates to false, * Returns false as soon as any condition evaluates to false,
* avoiding unnecessary evaluation of remaining conditions. * avoiding unnecessary evaluation of remaining conditions.
*
* @param array<string, mixed> $formData
*/ */
/** @param array<string, mixed> $formData */ /** @param array<string, mixed> $formData */
private function evaluateAnd(BookingDto $bookingDto, int $participantIndex, array $formData): bool private function evaluateAnd(BookingDto $bookingDto, int $participantIndex, array $formData): bool
@@ -185,6 +187,8 @@ class CompositeCondition implements FieldConditionInterface
* *
* Returns true as soon as any condition evaluates to true, * Returns true as soon as any condition evaluates to true,
* avoiding unnecessary evaluation of remaining conditions. * avoiding unnecessary evaluation of remaining conditions.
*
* @param array<string, mixed> $formData
*/ */
/** @param array<string, mixed> $formData */ /** @param array<string, mixed> $formData */
private function evaluateOr(BookingDto $bookingDto, int $participantIndex, array $formData): bool private function evaluateOr(BookingDto $bookingDto, int $participantIndex, array $formData): bool
@@ -200,6 +204,8 @@ class CompositeCondition implements FieldConditionInterface
/** /**
* Evaluates NOT logic by inverting the result of the single condition. * Evaluates NOT logic by inverting the result of the single condition.
*
* @param array<string, mixed> $formData
*/ */
/** @param array<string, mixed> $formData */ /** @param array<string, mixed> $formData */
private function evaluateNot(BookingDto $bookingDto, int $participantIndex, array $formData): bool private function evaluateNot(BookingDto $bookingDto, int $participantIndex, array $formData): bool
@@ -221,6 +227,8 @@ class CompositeCondition implements FieldConditionInterface
/** /**
* Validates condition count based on operator requirements. * Validates condition count based on operator requirements.
*
* @param list<FieldConditionInterface> $conditions
*/ */
/** @param list<mixed> $conditions */ /** @param list<mixed> $conditions */
private function validateConditionCount(string $operator, array $conditions): void private function validateConditionCount(string $operator, array $conditions): void
@@ -174,6 +174,8 @@ class FieldValueCondition implements FieldConditionInterface
/** /**
* Retrieves field value from form data or participant data. * Retrieves field value from form data or participant data.
*
* @param array<string, mixed> $formData
*/ */
/** @param array<string, mixed> $formData */ /** @param array<string, mixed> $formData */
private function getFieldValue(array $formData, int $participantIndex, BookingDto $bookingDto): mixed private function getFieldValue(array $formData, int $participantIndex, BookingDto $bookingDto): mixed
@@ -186,7 +188,9 @@ class FieldValueCondition implements FieldConditionInterface
// Then check participant DTO data // Then check participant DTO data
$participant = $bookingDto->getParticipant($participantIndex); $participant = $bookingDto->getParticipant($participantIndex);
if (null !== $participant && property_exists($participant, $this->fieldName)) { if (null !== $participant && property_exists($participant, $this->fieldName)) {
return $participant->{$this->fieldName}; $participantValues = get_object_vars($participant);
return $participantValues[$this->fieldName] ?? null;
} }
// Finally check booking-level form data // Finally check booking-level form data
@@ -212,6 +216,8 @@ class FieldValueCondition implements FieldConditionInterface
/** /**
* Checks if field value is in array of expected values. * Checks if field value is in array of expected values.
*
* @param array<int, mixed> $expectedValues
*/ */
/** @param list<mixed> $expectedValues */ /** @param list<mixed> $expectedValues */
private function compareIn(mixed $fieldValue, array $expectedValues): bool private function compareIn(mixed $fieldValue, array $expectedValues): bool
@@ -230,7 +236,7 @@ class FieldValueCondition implements FieldConditionInterface
*/ */
private function isEmpty(mixed $value): bool private function isEmpty(mixed $value): bool
{ {
return null === $value || '' === $value || (is_array($value) && empty($value)); return null === $value || '' === $value || [] === $value;
} }
/** /**
@@ -33,7 +33,7 @@ class RentalInsuranceAvailableCondition implements FieldConditionInterface
true true
); );
return false === empty($services); return [] !== $services;
} }
/** /**
@@ -31,6 +31,10 @@ class RoomSelectionCondition implements FieldConditionInterface
*/ */
public function __construct(array $requiredRoomCodes, string $matchMode = self::MATCH_MODE_PREFIX) public function __construct(array $requiredRoomCodes, string $matchMode = self::MATCH_MODE_PREFIX)
{ {
if (!in_array($matchMode, [self::MATCH_MODE_PREFIX, self::MATCH_MODE_EXACT], true)) {
throw new \InvalidArgumentException(sprintf('Invalid match mode "%s".', $matchMode));
}
$this->requiredRoomCodes = array_map('strtolower', $requiredRoomCodes); $this->requiredRoomCodes = array_map('strtolower', $requiredRoomCodes);
$this->matchMode = $matchMode; $this->matchMode = $matchMode;
} }
@@ -6,6 +6,7 @@ namespace App\Form\Service\Condition;
use App\BusProNet\Model\Service; use App\BusProNet\Model\Service;
use App\Form\Model\BookingDto; use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use App\Form\Service\Contract\FieldConditionInterface; use App\Form\Service\Contract\FieldConditionInterface;
/** /**
@@ -103,7 +104,7 @@ class ServiceSubTypeCondition implements FieldConditionInterface
{ {
$subTypeDescription = is_array($this->expectedSubType) $subTypeDescription = is_array($this->expectedSubType)
? '['.implode(', ', $this->expectedSubType).']' ? '['.implode(', ', $this->expectedSubType).']'
: (string) $this->expectedSubType; : $this->expectedSubType;
return match ($this->operator) { return match ($this->operator) {
self::OPERATOR_EQUALS => sprintf('Service "%s" sub-type equals %s', $this->serviceFieldName, $subTypeDescription), self::OPERATOR_EQUALS => sprintf('Service "%s" sub-type equals %s', $this->serviceFieldName, $subTypeDescription),
@@ -160,6 +161,8 @@ class ServiceSubTypeCondition implements FieldConditionInterface
/** /**
* Retrieves service from form data or participant data. * Retrieves service from form data or participant data.
*
* @param array<string, mixed> $formData
*/ */
/** @param array<string, mixed> $formData */ /** @param array<string, mixed> $formData */
private function getService(array $formData, int $participantIndex, BookingDto $bookingDto): ?Service private function getService(array $formData, int $participantIndex, BookingDto $bookingDto): ?Service
@@ -176,15 +179,30 @@ class ServiceSubTypeCondition implements FieldConditionInterface
// Then check participant DTO data // Then check participant DTO data
$participant = $bookingDto->getParticipant($participantIndex); $participant = $bookingDto->getParticipant($participantIndex);
if (null !== $participant && property_exists($participant, $this->serviceFieldName)) { if (null !== $participant) {
return $participant->{$this->serviceFieldName}; return $this->getServiceFromParticipant($participant);
} }
return null; return null;
} }
private function getServiceFromParticipant(ParticipantDto $participant): ?Service
{
return match ($this->serviceFieldName) {
'skiPass' => $participant->skiPass,
'rentalInsurance' => $participant->rentalInsurance,
'transportationOutbound' => $participant->transportationOutbound,
'transportationInbound' => $participant->transportationInbound,
'veg' => $participant->veg,
'parkingService' => $participant->parkingService,
default => null,
};
}
/** /**
* Checks if sub-type is in the expected array. * Checks if sub-type is in the expected array.
*
* @param string|list<string> $expectedSubTypes
*/ */
/** @param string|list<string> $expectedSubTypes */ /** @param string|list<string> $expectedSubTypes */
private function isSubTypeIn(string $actualSubType, string|array $expectedSubTypes): bool private function isSubTypeIn(string $actualSubType, string|array $expectedSubTypes): bool
@@ -215,6 +233,8 @@ class ServiceSubTypeCondition implements FieldConditionInterface
/** /**
* Validates expected sub-type based on operator requirements. * Validates expected sub-type based on operator requirements.
*
* @param string|list<string> $expectedSubType
*/ */
/** @param string|list<string> $expectedSubType */ /** @param string|list<string> $expectedSubType */
private function validateExpectedSubType(string $operator, string|array $expectedSubType): void private function validateExpectedSubType(string $operator, string|array $expectedSubType): void
@@ -274,7 +274,8 @@ class ParticipantFieldHandlerRegistry
continue; continue;
} }
$dtoValue = $participant->{$fieldName}; $participantValues = get_object_vars($participant);
$dtoValue = $participantValues[$fieldName] ?? null;
$submittedValue = $participantData[$fieldName]; $submittedValue = $participantData[$fieldName];
// Check if the DTO value differs from submitted value // Check if the DTO value differs from submitted value
@@ -427,7 +428,7 @@ class ParticipantFieldHandlerRegistry
} }
// Process handlers in dependency order // Process handlers in dependency order
while (false === empty($queue)) { while ([] !== $queue) {
$current = array_shift($queue); $current = array_shift($queue);
$result[] = $current; $result[] = $current;
@@ -517,7 +518,8 @@ class ParticipantFieldHandlerRegistry
// Only sync fields that were in the original submission // Only sync fields that were in the original submission
// This prevents adding extra fields that would cause validation errors // This prevents adding extra fields that would cause validation errors
if (property_exists($participant, $fieldName) && array_key_exists($fieldName, $participantData)) { if (property_exists($participant, $fieldName) && array_key_exists($fieldName, $participantData)) {
$dtoValue = $participant->{$fieldName}; $participantValues = get_object_vars($participant);
$dtoValue = $participantValues[$fieldName] ?? null;
$participantData[$fieldName] = $this->convertDtoValueToSubmittedFormat($dtoValue); $participantData[$fieldName] = $this->convertDtoValueToSubmittedFormat($dtoValue);
} }
} }
@@ -126,7 +126,7 @@ class ParticipantParkingFieldHandler extends AbstractParticipantFieldHandler
Constants::TOKEN_PARKING Constants::TOKEN_PARKING
); );
if (empty($parkingServices)) { if ([] === $parkingServices) {
return null; return null;
} }
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App\Form; namespace App\Form;
+1
View File
@@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace App; namespace App;
+4 -4
View File
@@ -51,7 +51,7 @@ class BpnXmlAnonymizer
continue; continue;
} }
$personId = $this->resolvePersonId($personNode, (int) $index); $personId = $this->resolvePersonId($personNode, $index);
$identity = $this->getOrCreateIdentity($personId); $identity = $this->getOrCreateIdentity($personId);
$this->replaceNameFields($personNode, $identity); $this->replaceNameFields($personNode, $identity);
@@ -96,7 +96,7 @@ class BpnXmlAnonymizer
} }
if (true === $personNode->hasAttribute('id')) { if (true === $personNode->hasAttribute('id')) {
$attributeValue = trim((string) $personNode->getAttribute('id')); $attributeValue = trim($personNode->getAttribute('id'));
if ('' !== $attributeValue) { if ('' !== $attributeValue) {
return sprintf('attr-id:%s', $attributeValue); return sprintf('attr-id:%s', $attributeValue);
} }
@@ -115,7 +115,7 @@ class BpnXmlAnonymizer
$nameValues[] = trim($nameNode->textContent); $nameValues[] = trim($nameNode->textContent);
} }
if (true === $personNode->hasAttribute('name')) { if (true === $personNode->hasAttribute('name')) {
$nameValues[] = trim((string) $personNode->getAttribute('name')); $nameValues[] = trim($personNode->getAttribute('name'));
} }
$firstNameNode = $this->getDirectChild($personNode, 'vorname'); $firstNameNode = $this->getDirectChild($personNode, 'vorname');
@@ -123,7 +123,7 @@ class BpnXmlAnonymizer
$nameValues[] = trim($firstNameNode->textContent); $nameValues[] = trim($firstNameNode->textContent);
} }
if (true === $personNode->hasAttribute('vorname')) { if (true === $personNode->hasAttribute('vorname')) {
$nameValues[] = trim((string) $personNode->getAttribute('vorname')); $nameValues[] = trim($personNode->getAttribute('vorname'));
} }
$communicationNode = $this->getDirectChild($personNode, 'kommunikation'); $communicationNode = $this->getDirectChild($personNode, 'kommunikation');
+2 -2
View File
@@ -136,7 +136,7 @@ class InsuranceManager
$eligibleInsurances = $this->getEligibleInsurances($sameTypeInsurances, $participant, $booking, $travelPrice); $eligibleInsurances = $this->getEligibleInsurances($sameTypeInsurances, $participant, $booking, $travelPrice);
// Return the first eligible insurance (they should all be equivalent for the same type) // Return the first eligible insurance (they should all be equivalent for the same type)
return !empty($eligibleInsurances) ? array_values($eligibleInsurances)[0] : null; return [] !== $eligibleInsurances ? array_values($eligibleInsurances)[0] : null;
} }
/** /**
@@ -168,7 +168,7 @@ class InsuranceManager
foreach ($booking->getParticipants() as $index => $participant) { foreach ($booking->getParticipants() as $index => $participant) {
$travelPrice = $participantPrices[$index] ?? 0.0; $travelPrice = $participantPrices[$index] ?? 0.0;
$eligibleInsurances = $this->getEligibleInsurances($sameTypeInsurances, $participant, $booking, $travelPrice); $eligibleInsurances = $this->getEligibleInsurances($sameTypeInsurances, $participant, $booking, $travelPrice);
$assignments[$index] = !empty($eligibleInsurances) ? array_values($eligibleInsurances)[0] : null; $assignments[$index] = [] !== $eligibleInsurances ? array_values($eligibleInsurances)[0] : null;
} }
return $assignments; return $assignments;
@@ -70,7 +70,7 @@ class ParticipantEligibilityChecker
fn (Service $service) => $this->isSkiPassAvailableForParticipant($service, $bookingDto, $participantIndex) fn (Service $service) => $this->isSkiPassAvailableForParticipant($service, $bookingDto, $participantIndex)
); );
return !empty($availableSkiPasses); return [] !== $availableSkiPasses;
})(); })();
} }
+8
View File
@@ -282,6 +282,14 @@ class AppRuntime implements RuntimeExtensionInterface
); );
} }
private function renderIcon(Environment $environment, string $icon, ?string $classes): string
{
return $environment->render('_partials/_icon.html.twig', [
'icon' => $icon,
'class' => $classes ?? 'w-4 h-4',
]);
}
/** /**
* Gets the appropriate field state provider based on booking mode. * Gets the appropriate field state provider based on booking mode.
* *
@@ -63,7 +63,7 @@ class MandatoryAdditionalServicesSelectedValidator extends ConstraintValidator
} }
); );
if (true === empty($mandatoryAdditionalServices)) { if ([] === $mandatoryAdditionalServices) {
return; return;
} }
@@ -67,7 +67,7 @@ class ParticipantValidator extends ConstraintValidator
public function assertBodyDimensionsWhenRentalsSelected(ParticipantDto $participant): void public function assertBodyDimensionsWhenRentalsSelected(ParticipantDto $participant): void
{ {
// Check if any rental services are selected // Check if any rental services are selected
if (true === empty($participant->rentals)) { if ([] === $participant->rentals) {
return; return;
} }
+3
View File
@@ -65,6 +65,9 @@ class ApiClientReceiveTest extends TestCase
self::assertSame('0000000005Hello', $result); self::assertSame('0000000005Hello', $result);
} }
/**
* @param resource $socket
*/
private function invokeReceive($socket): string private function invokeReceive($socket): string
{ {
$method = new \ReflectionMethod(ApiClient::class, 'receive'); $method = new \ReflectionMethod(ApiClient::class, 'receive');
@@ -27,7 +27,7 @@ class HotelDataLoaderTest extends TestCase
</hotels>'; </hotels>';
$filesystem = $this->createMock(FilesystemOperator::class); $filesystem = $this->createMock(FilesystemOperator::class);
$filesystem->expects($this->once()) $filesystem->expects(self::once())
->method('read') ->method('read')
->with('hotel.xml') ->with('hotel.xml')
->willReturn($xmlContent); ->willReturn($xmlContent);
@@ -37,17 +37,17 @@ class HotelDataLoaderTest extends TestCase
$hotels = $loader->loadAll(); $hotels = $loader->loadAll();
$this->assertIsArray($hotels); self::assertIsArray($hotels);
$this->assertArrayHasKey(163113, $hotels); self::assertArrayHasKey(163113, $hotels);
$hotel = $hotels[163113]; $hotel = $hotels[163113];
$this->assertInstanceOf(Hotel::class, $hotel); self::assertInstanceOf(Hotel::class, $hotel);
$this->assertEquals(163113, $hotel->id); self::assertEquals(163113, $hotel->id);
$this->assertEquals('L\'Oxalys', $hotel->name); self::assertEquals('L\'Oxalys', $hotel->name);
$this->assertEquals('SBWOXA', $hotel->code); self::assertEquals('SBWOXA', $hotel->code);
$this->assertEquals('Val Thorens', $hotel->city); self::assertEquals('Val Thorens', $hotel->city);
$this->assertEquals('F', $hotel->country); self::assertEquals('F', $hotel->country);
$this->assertEquals('Rue des Lacs', $hotel->street); self::assertEquals('Rue des Lacs', $hotel->street);
$this->assertEquals('', $hotel->phone); self::assertEquals('', $hotel->phone);
} }
} }
@@ -27,7 +27,7 @@ class PickupDataLoaderTest extends TestCase
</zustiege>'; </zustiege>';
$filesystem = $this->createMock(FilesystemOperator::class); $filesystem = $this->createMock(FilesystemOperator::class);
$filesystem->expects($this->once()) $filesystem->expects(self::once())
->method('read') ->method('read')
->with('zustiege.xml') ->with('zustiege.xml')
->willReturn($xmlContent); ->willReturn($xmlContent);
@@ -37,11 +37,11 @@ class PickupDataLoaderTest extends TestCase
$pickup = $loader->loadById(1); $pickup = $loader->loadById(1);
$this->assertInstanceOf(Pickup::class, $pickup); self::assertInstanceOf(Pickup::class, $pickup);
$this->assertEquals(1, $pickup->id); self::assertEquals(1, $pickup->id);
$this->assertEquals('MS-Hbf', $pickup->code); self::assertEquals('MS-Hbf', $pickup->code);
$this->assertEquals('Münster', $pickup->city); self::assertEquals('Münster', $pickup->city);
$this->assertEquals('48143', $pickup->postalCode); self::assertEquals('48143', $pickup->postalCode);
$this->assertEquals('Hafenstr/Ecke Friedrich-Ebert-Str', $pickup->street); self::assertEquals('Hafenstr/Ecke Friedrich-Ebert-Str', $pickup->street);
} }
} }
@@ -29,7 +29,7 @@ class TravelDataLoaderTest extends TestCase
</reisen>'; </reisen>';
$filesystem = $this->createMock(FilesystemOperator::class); $filesystem = $this->createMock(FilesystemOperator::class);
$filesystem->expects($this->once()) $filesystem->expects(self::once())
->method('read') ->method('read')
->with('test_file.xml') ->with('test_file.xml')
->willReturn($xmlContent); ->willReturn($xmlContent);
@@ -45,7 +45,7 @@ class TravelDataLoaderTest extends TestCase
$expectedTravel->label = 'Davos - Sportclub Waldschlössli'; $expectedTravel->label = 'Davos - Sportclub Waldschlössli';
$expectedTravel->priceFrom = 534.2; $expectedTravel->priceFrom = 534.2;
$travelParser->expects($this->once()) $travelParser->expects(self::once())
->method('parse') ->method('parse')
->willReturn($expectedTravel); ->willReturn($expectedTravel);
@@ -54,12 +54,12 @@ class TravelDataLoaderTest extends TestCase
$travel = $loader->loadById(11478, null, 'test_file.xml'); $travel = $loader->loadById(11478, null, 'test_file.xml');
$this->assertInstanceOf(Travel::class, $travel); self::assertInstanceOf(Travel::class, $travel);
$this->assertEquals('DPWMP060125', $travel->code); self::assertEquals('DPWMP060125', $travel->code);
$this->assertEquals('F', $travel->type); self::assertEquals('F', $travel->type);
$this->assertInstanceOf(\DateTimeImmutable::class, $travel->dateFrom); self::assertInstanceOf(\DateTimeImmutable::class, $travel->dateFrom);
$this->assertInstanceOf(\DateTimeImmutable::class, $travel->dateTo); self::assertInstanceOf(\DateTimeImmutable::class, $travel->dateTo);
$this->assertEquals('Davos - Sportclub Waldschlössli', $travel->label); self::assertEquals('Davos - Sportclub Waldschlössli', $travel->label);
$this->assertEquals(534.2, $travel->priceFrom); self::assertEquals(534.2, $travel->priceFrom);
} }
} }
@@ -71,16 +71,16 @@ class BookingDataProcessorTest extends TestCase
$result = $this->processor->createUpdateRequestPayload($formData); $result = $this->processor->createUpdateRequestPayload($formData);
$this->assertIsArray($result); self::assertIsArray($result);
$this->assertArrayHasKey('idbuchung', $result); self::assertArrayHasKey('idbuchung', $result);
$this->assertArrayHasKey('teilnehmerliste', $result); self::assertArrayHasKey('teilnehmerliste', $result);
$this->assertArrayHasKey('zusatzleistungen', $result); self::assertArrayHasKey('zusatzleistungen', $result);
$this->assertArrayHasKey('beförderungen', $result); self::assertArrayHasKey('beförderungen', $result);
$this->assertArrayHasKey('ferienzielunterbringungen', $result); self::assertArrayHasKey('ferienzielunterbringungen', $result);
$this->assertEquals(123, $result['idbuchung']); self::assertEquals(123, $result['idbuchung']);
$this->assertEquals('ACTIVE', $result['status']); self::assertEquals('ACTIVE', $result['status']);
$this->assertCount(2, $result['teilnehmerliste']['teilnehmer']); self::assertCount(2, $result['teilnehmerliste']['teilnehmer']);
} }
public function testCanceledParticipantsAreSkipped(): void public function testCanceledParticipantsAreSkipped(): void
@@ -89,9 +89,9 @@ class BookingDataProcessorTest extends TestCase
$result = $this->processor->createUpdateRequestPayload($formData); $result = $this->processor->createUpdateRequestPayload($formData);
$this->assertCount(2, $result['teilnehmerliste']['teilnehmer']); self::assertCount(2, $result['teilnehmerliste']['teilnehmer']);
$this->assertEquals(1, $result['teilnehmerliste']['teilnehmer'][0]['@id']); self::assertEquals(1, $result['teilnehmerliste']['teilnehmer'][0]['@id']);
$this->assertEquals(2, $result['teilnehmerliste']['teilnehmer'][1]['@id']); self::assertEquals(2, $result['teilnehmerliste']['teilnehmer'][1]['@id']);
} }
public function testAdditionalServicesProcessing(): void public function testAdditionalServicesProcessing(): void
@@ -100,13 +100,13 @@ class BookingDataProcessorTest extends TestCase
$result = $this->processor->createUpdateRequestPayload($formData); $result = $this->processor->createUpdateRequestPayload($formData);
$this->assertNotEmpty($result['zusatzleistungen']['zusatzleistung']); self::assertNotEmpty($result['zusatzleistungen']['zusatzleistung']);
$this->assertCount(2, $result['zusatzleistungen']['zusatzleistung']); self::assertCount(2, $result['zusatzleistungen']['zusatzleistung']);
$service = $result['zusatzleistungen']['zusatzleistung'][0]; $service = $result['zusatzleistungen']['zusatzleistung'][0];
$this->assertEquals(1, $service['@idleistung']); self::assertEquals(1, $service['@idleistung']);
$this->assertEquals(1, $service['@anzahl']); self::assertEquals(1, $service['@anzahl']);
$this->assertEquals('1', $service['@zuordnung']); self::assertEquals('1', $service['@zuordnung']);
} }
public function testTransportationServicesProcessing(): void public function testTransportationServicesProcessing(): void
@@ -115,8 +115,8 @@ class BookingDataProcessorTest extends TestCase
$result = $this->processor->createUpdateRequestPayload($formData); $result = $this->processor->createUpdateRequestPayload($formData);
$this->assertNotEmpty($result['beförderungen']['beförderung']); self::assertNotEmpty($result['beförderungen']['beförderung']);
$this->assertCount(2, $result['beförderungen']['beförderung']); self::assertCount(2, $result['beförderungen']['beförderung']);
} }
public function testBusPickupLocationsProcessing(): void public function testBusPickupLocationsProcessing(): void
@@ -125,9 +125,9 @@ class BookingDataProcessorTest extends TestCase
$result = $this->processor->createUpdateRequestPayload($formData); $result = $this->processor->createUpdateRequestPayload($formData);
$this->assertArrayHasKey('zustiege', $result); self::assertArrayHasKey('zustiege', $result);
$this->assertNotEmpty($result['zustiege']['zustieg']); self::assertNotEmpty($result['zustiege']['zustieg']);
$this->assertEquals(1, $result['zustiege']['zustieg'][0]['@idzustieg']); self::assertEquals(1, $result['zustiege']['zustieg'][0]['@idzustieg']);
} }
public function testNonBusTransportationSkipsPickup(): void public function testNonBusTransportationSkipsPickup(): void
@@ -136,8 +136,8 @@ class BookingDataProcessorTest extends TestCase
$result = $this->processor->createUpdateRequestPayload($formData); $result = $this->processor->createUpdateRequestPayload($formData);
$this->assertArrayHasKey('zustiege', $result); self::assertArrayHasKey('zustiege', $result);
$this->assertSame([], $result['zustiege']); self::assertSame([], $result['zustiege']);
} }
public function testUnusedServicesAreRemoved(): void public function testUnusedServicesAreRemoved(): void
@@ -146,8 +146,8 @@ class BookingDataProcessorTest extends TestCase
$result = $this->processor->createUpdateRequestPayload($formData); $result = $this->processor->createUpdateRequestPayload($formData);
$this->assertEmpty($result['zusatzleistungen']['zusatzleistung']); self::assertEmpty($result['zusatzleistungen']['zusatzleistung']);
$this->assertEmpty($result['beförderungen']['beförderung']); self::assertEmpty($result['beförderungen']['beförderung']);
} }
public function testParticipantPersonalDataUpdate(): void public function testParticipantPersonalDataUpdate(): void
@@ -157,10 +157,10 @@ class BookingDataProcessorTest extends TestCase
$this->processor->createUpdateRequestPayload($formData); $this->processor->createUpdateRequestPayload($formData);
$participant = $formData->booking->participants[0]; $participant = $formData->booking->participants[0];
$this->assertEquals('Updated', $participant->firstName); self::assertEquals('Updated', $participant->firstName);
$this->assertEquals('Participant', $participant->name); self::assertEquals('Participant', $participant->name);
$this->assertEquals('[email protected]', $participant->communication->email); self::assertEquals('[email protected]', $participant->communication->email);
$this->assertEquals('+49123456789', $participant->communication->mobile); self::assertEquals('+49123456789', $participant->communication->mobile);
} }
public function testCanceledParticipantsPersonalDataIsUpdated(): void public function testCanceledParticipantsPersonalDataIsUpdated(): void
@@ -170,8 +170,8 @@ class BookingDataProcessorTest extends TestCase
$this->processor->createUpdateRequestPayload($formData); $this->processor->createUpdateRequestPayload($formData);
$participant = $formData->booking->participants[0]; $participant = $formData->booking->participants[0];
$this->assertEquals('Updated', $participant->firstName); self::assertEquals('Updated', $participant->firstName);
$this->assertEquals('Participant', $participant->name); self::assertEquals('Participant', $participant->name);
} }
public function testApplicantDataSyncWithFirstParticipant(): void public function testApplicantDataSyncWithFirstParticipant(): void
@@ -183,9 +183,9 @@ class BookingDataProcessorTest extends TestCase
$applicant = $formData->booking->applicant; $applicant = $formData->booking->applicant;
$firstParticipant = $formData->booking->participants[0]; $firstParticipant = $formData->booking->participants[0];
$this->assertEquals($firstParticipant->height, $applicant->height); self::assertEquals($firstParticipant->height, $applicant->height);
$this->assertEquals($firstParticipant->weight, $applicant->weight); self::assertEquals($firstParticipant->weight, $applicant->weight);
$this->assertEquals($firstParticipant->shoeSize, $applicant->shoeSize); self::assertEquals($firstParticipant->shoeSize, $applicant->shoeSize);
} }
public function testBankAccountIncludedInPayload(): void public function testBankAccountIncludedInPayload(): void
@@ -194,9 +194,9 @@ class BookingDataProcessorTest extends TestCase
$result = $this->processor->createUpdateRequestPayload($formData); $result = $this->processor->createUpdateRequestPayload($formData);
$this->assertArrayHasKey('bankverbindung', $result['zahlung']); self::assertArrayHasKey('bankverbindung', $result['zahlung']);
$this->assertEquals('Test Bank', $result['zahlung']['bankverbindung']['@kreditinstitut']); self::assertEquals('Test Bank', $result['zahlung']['bankverbindung']['@kreditinstitut']);
$this->assertEquals('DE89370400440532013000', $result['zahlung']['bankverbindung']['@iban']); self::assertEquals('DE89370400440532013000', $result['zahlung']['bankverbindung']['@iban']);
} }
public function testBankAccountNotIncludedWhenNull(): void public function testBankAccountNotIncludedWhenNull(): void
@@ -205,7 +205,7 @@ class BookingDataProcessorTest extends TestCase
$result = $this->processor->createUpdateRequestPayload($formData); $result = $this->processor->createUpdateRequestPayload($formData);
$this->assertArrayNotHasKey('bankverbindung', $result['zahlung']); self::assertArrayNotHasKey('bankverbindung', $result['zahlung']);
} }
public function testAccommodationRoomsProcessing(): void public function testAccommodationRoomsProcessing(): void
@@ -214,13 +214,13 @@ class BookingDataProcessorTest extends TestCase
$result = $this->processor->createUpdateRequestPayload($formData); $result = $this->processor->createUpdateRequestPayload($formData);
$this->assertNotEmpty($result['ferienzielunterbringungen']['ferienzielunterbringung']); self::assertNotEmpty($result['ferienzielunterbringungen']['ferienzielunterbringung']);
$room = $result['ferienzielunterbringungen']['ferienzielunterbringung'][0]; $room = $result['ferienzielunterbringungen']['ferienzielunterbringung'][0];
$this->assertEquals(1, $room['@idzimmer']); self::assertEquals(1, $room['@idzimmer']);
$this->assertEquals('DOUBLE', $room['@kategorie']); self::assertEquals('DOUBLE', $room['@kategorie']);
$this->assertEquals('01.01.2024', $room['@anreise']); self::assertEquals('01.01.2024', $room['@anreise']);
$this->assertEquals('07.01.2024', $room['@abreise']); self::assertEquals('07.01.2024', $room['@abreise']);
} }
public function testCommunicationObjectCreation(): void public function testCommunicationObjectCreation(): void
@@ -230,8 +230,8 @@ class BookingDataProcessorTest extends TestCase
$this->processor->createUpdateRequestPayload($formData); $this->processor->createUpdateRequestPayload($formData);
$participant = $formData->booking->participants[0]; $participant = $formData->booking->participants[0];
$this->assertInstanceOf(Communication::class, $participant->communication); self::assertInstanceOf(Communication::class, $participant->communication);
$this->assertEquals('[email protected]', $participant->communication->email); self::assertEquals('[email protected]', $participant->communication->email);
} }
public function testEmptyPickupsToDoesNotCreateZustiegeSection(): void public function testEmptyPickupsToDoesNotCreateZustiegeSection(): void
@@ -240,8 +240,8 @@ class BookingDataProcessorTest extends TestCase
$result = $this->processor->createUpdateRequestPayload($formData); $result = $this->processor->createUpdateRequestPayload($formData);
$this->assertArrayHasKey('zustiege', $result); self::assertArrayHasKey('zustiege', $result);
$this->assertSame([], $result['zustiege']); self::assertSame([], $result['zustiege']);
} }
private function createCompleteFormData(): BookingDto private function createCompleteFormData(): BookingDto
@@ -543,13 +543,13 @@ class BookingDataProcessorTest extends TestCase
// Canceled participant (index 1) should have default names filled // Canceled participant (index 1) should have default names filled
$canceledParticipant = $formData->booking->participants[1]; $canceledParticipant = $formData->booking->participants[1];
$this->assertEquals('Teilnehmer:in 2', $canceledParticipant->firstName); self::assertEquals('Teilnehmer:in 2', $canceledParticipant->firstName);
$this->assertEquals('Teilnehmer:in 2', $canceledParticipant->name); self::assertEquals('Teilnehmer:in 2', $canceledParticipant->name);
// Active participant (index 0) should retain original names from form // Active participant (index 0) should retain original names from form
$activeParticipant = $formData->booking->participants[0]; $activeParticipant = $formData->booking->participants[0];
$this->assertEquals('Participant0', $activeParticipant->firstName); self::assertEquals('Participant0', $activeParticipant->firstName);
$this->assertEquals('LastName', $activeParticipant->name); self::assertEquals('LastName', $activeParticipant->name);
} }
public function testCanceledParticipantNamesNotFilledForNonAgencyBooking(): void public function testCanceledParticipantNamesNotFilledForNonAgencyBooking(): void
@@ -560,8 +560,8 @@ class BookingDataProcessorTest extends TestCase
// Canceled participant should retain empty names (non-agency booking) // Canceled participant should retain empty names (non-agency booking)
$canceledParticipant = $formData->booking->participants[1]; $canceledParticipant = $formData->booking->participants[1];
$this->assertEquals('', $canceledParticipant->firstName); self::assertEquals('', $canceledParticipant->firstName);
$this->assertEquals('', $canceledParticipant->name); self::assertEquals('', $canceledParticipant->name);
} }
public function testConfirmedParticipantEmptyNamesFilledForAgencyBooking(): void public function testConfirmedParticipantEmptyNamesFilledForAgencyBooking(): void
@@ -572,13 +572,13 @@ class BookingDataProcessorTest extends TestCase
// Confirmed participant (index 1) with empty names should have default names filled // Confirmed participant (index 1) with empty names should have default names filled
$participant = $formData->booking->participants[1]; $participant = $formData->booking->participants[1];
$this->assertEquals('Teilnehmer:in 2', $participant->firstName); self::assertEquals('Teilnehmer:in 2', $participant->firstName);
$this->assertEquals('Teilnehmer:in 2', $participant->name); self::assertEquals('Teilnehmer:in 2', $participant->name);
// Active participant (index 0) should retain original names from form // Active participant (index 0) should retain original names from form
$activeParticipant = $formData->booking->participants[0]; $activeParticipant = $formData->booking->participants[0];
$this->assertEquals('Participant0', $activeParticipant->firstName); self::assertEquals('Participant0', $activeParticipant->firstName);
$this->assertEquals('LastName', $activeParticipant->name); self::assertEquals('LastName', $activeParticipant->name);
} }
public function testConfirmedParticipantPartialEmptyNameFilledForAgencyBooking(): void public function testConfirmedParticipantPartialEmptyNameFilledForAgencyBooking(): void
@@ -589,8 +589,8 @@ class BookingDataProcessorTest extends TestCase
// Participant with lastName but empty firstName should get default firstName only // Participant with lastName but empty firstName should get default firstName only
$participant = $formData->booking->participants[1]; $participant = $formData->booking->participants[1];
$this->assertEquals('Teilnehmer:in 2', $participant->firstName); self::assertEquals('Teilnehmer:in 2', $participant->firstName);
$this->assertEquals('Schnekenburger', $participant->name); self::assertEquals('Schnekenburger', $participant->name);
} }
private function createFormDataWithConfirmedParticipantEmptyNames(): BookingDto private function createFormDataWithConfirmedParticipantEmptyNames(): BookingDto
+14 -14
View File
@@ -34,16 +34,16 @@ class BookingTest extends TestCase
// Test: participant 0 should get the ski pass // Test: participant 0 should get the ski pass
$result = $booking->getSkiPassForParticipant(0); $result = $booking->getSkiPassForParticipant(0);
$this->assertSame($skiPassService, $result); self::assertSame($skiPassService, $result);
$this->assertEquals(123, $result->id); self::assertEquals(123, $result->id);
// Test: participant 1 should get null (no ski pass assigned) // Test: participant 1 should get null (no ski pass assigned)
$result = $booking->getSkiPassForParticipant(1); $result = $booking->getSkiPassForParticipant(1);
$this->assertNull($result); self::assertNull($result);
// Test: participant 2 should get the ski pass // Test: participant 2 should get the ski pass
$result = $booking->getSkiPassForParticipant(2); $result = $booking->getSkiPassForParticipant(2);
$this->assertSame($skiPassService, $result); self::assertSame($skiPassService, $result);
} }
public function testGetSkiPassForParticipantReturnsNullWhenNoSkiPasses(): void public function testGetSkiPassForParticipantReturnsNullWhenNoSkiPasses(): void
@@ -59,7 +59,7 @@ class BookingTest extends TestCase
$booking->additionalServices = [456 => $otherService]; $booking->additionalServices = [456 => $otherService];
$result = $booking->getSkiPassForParticipant(0); $result = $booking->getSkiPassForParticipant(0);
$this->assertNull($result); self::assertNull($result);
} }
public function testGetSkiPassForParticipantReturnsNullWhenNoServices(): void public function testGetSkiPassForParticipantReturnsNullWhenNoServices(): void
@@ -68,7 +68,7 @@ class BookingTest extends TestCase
$booking->additionalServices = []; $booking->additionalServices = [];
$result = $booking->getSkiPassForParticipant(0); $result = $booking->getSkiPassForParticipant(0);
$this->assertNull($result); self::assertNull($result);
} }
public function testGetInsuranceForParticipantReturnsCorrectInsurance(): void public function testGetInsuranceForParticipantReturnsCorrectInsurance(): void
@@ -94,21 +94,21 @@ class BookingTest extends TestCase
// Test: participant 0 should get insurance1 // Test: participant 0 should get insurance1
$result = $booking->getInsuranceForParticipant(0); $result = $booking->getInsuranceForParticipant(0);
$this->assertSame($insurance1, $result); self::assertSame($insurance1, $result);
$this->assertEquals(100, $result->id); self::assertEquals(100, $result->id);
// Test: participant 1 should get insurance2 // Test: participant 1 should get insurance2
$result = $booking->getInsuranceForParticipant(1); $result = $booking->getInsuranceForParticipant(1);
$this->assertSame($insurance2, $result); self::assertSame($insurance2, $result);
$this->assertEquals(200, $result->id); self::assertEquals(200, $result->id);
// Test: participant 2 should get insurance1 // Test: participant 2 should get insurance1
$result = $booking->getInsuranceForParticipant(2); $result = $booking->getInsuranceForParticipant(2);
$this->assertSame($insurance1, $result); self::assertSame($insurance1, $result);
// Test: participant 3 should get null (no insurance assigned) // Test: participant 3 should get null (no insurance assigned)
$result = $booking->getInsuranceForParticipant(3); $result = $booking->getInsuranceForParticipant(3);
$this->assertNull($result); self::assertNull($result);
} }
public function testGetInsuranceForParticipantReturnsNullWhenNoInsurances(): void public function testGetInsuranceForParticipantReturnsNullWhenNoInsurances(): void
@@ -117,7 +117,7 @@ class BookingTest extends TestCase
$booking->insurances = []; $booking->insurances = [];
$result = $booking->getInsuranceForParticipant(0); $result = $booking->getInsuranceForParticipant(0);
$this->assertNull($result); self::assertNull($result);
} }
public function testGetInsuranceForParticipantHandlesEmptyMapping(): void public function testGetInsuranceForParticipantHandlesEmptyMapping(): void
@@ -133,6 +133,6 @@ class BookingTest extends TestCase
$booking->insurances = [100 => $insurance]; $booking->insurances = [100 => $insurance];
$result = $booking->getInsuranceForParticipant(0); $result = $booking->getInsuranceForParticipant(0);
$this->assertNull($result, 'Should return null when mapping is empty'); self::assertNull($result, 'Should return null when mapping is empty');
} }
} }
+8 -8
View File
@@ -41,12 +41,12 @@ class TravelTest extends TestCase
$travel->transportationServices = [$outboundPkw, $inboundPkw]; $travel->transportationServices = [$outboundPkw, $inboundPkw];
$outboundResult = $travel->getTransportationServicesByDirection('HIN'); $outboundResult = $travel->getTransportationServicesByDirection('HIN');
$this->assertCount(1, $outboundResult); self::assertCount(1, $outboundResult);
$this->assertSame($outboundPkw, $outboundResult[0]); self::assertSame($outboundPkw, $outboundResult[0]);
$inboundResult = $travel->getTransportationServicesByDirection('RUECK'); $inboundResult = $travel->getTransportationServicesByDirection('RUECK');
$this->assertCount(1, $inboundResult); self::assertCount(1, $inboundResult);
$this->assertSame($inboundPkw, $inboundResult[0]); self::assertSame($inboundPkw, $inboundResult[0]);
} }
/** /**
@@ -77,7 +77,7 @@ class TravelTest extends TestCase
$result = $travel->getTransportationServicesByDirection('HIN'); $result = $travel->getTransportationServicesByDirection('HIN');
// Both PKW services should be returned (filtering happens at field options provider level) // Both PKW services should be returned (filtering happens at field options provider level)
$this->assertCount(2, $result); self::assertCount(2, $result);
} }
/** /**
@@ -104,9 +104,9 @@ class TravelTest extends TestCase
$result = $travel->getTransportationServicesByDirection('HIN'); $result = $travel->getTransportationServicesByDirection('HIN');
$this->assertCount(2, $result); self::assertCount(2, $result);
// BUS comes before PKW alphabetically // BUS comes before PKW alphabetically
$this->assertSame('BUS', $result[0]->subType); self::assertSame('BUS', $result[0]->subType);
$this->assertSame('PKW', $result[1]->subType); self::assertSame('PKW', $result[1]->subType);
} }
} }
@@ -15,9 +15,12 @@ class DayTimeUtilityTest extends TestCase
$utility = new DayTimeUtility(); $utility = new DayTimeUtility();
$result = $utility->mapTime($time); $result = $utility->mapTime($time);
$this->assertEquals($dayTime, $result); self::assertEquals($dayTime, $result);
} }
/**
* @return array<int, array{0: ?string, 1: ?string}>
*/
public function dayTimeProvider(): array public function dayTimeProvider(): array
{ {
return [ return [
@@ -13,15 +13,15 @@ class TravelCodeUtilityTest extends TestCase
$travelCode = 'ABCDEF/010125'; $travelCode = 'ABCDEF/010125';
$sanitized = $utility->sanitize($travelCode); $sanitized = $utility->sanitize($travelCode);
$this->assertEquals('ABCDEF010125', $sanitized); self::assertEquals('ABCDEF010125', $sanitized);
$travelCode = 'ABCDEF-010125'; $travelCode = 'ABCDEF-010125';
$sanitized = $utility->sanitize($travelCode); $sanitized = $utility->sanitize($travelCode);
$this->assertEquals('ABCDEF010125', $sanitized); self::assertEquals('ABCDEF010125', $sanitized);
$travelCode = 'abcdef/010125'; $travelCode = 'abcdef/010125';
$sanitized = $utility->sanitize($travelCode); $sanitized = $utility->sanitize($travelCode);
$this->assertEquals('ABCDEF010125', $sanitized); self::assertEquals('ABCDEF010125', $sanitized);
} }
public function testGetBaseCode(): void public function testGetBaseCode(): void
@@ -30,11 +30,11 @@ class TravelCodeUtilityTest extends TestCase
$travelCode = 'ABCDEF010125'; $travelCode = 'ABCDEF010125';
$baseCode = $utility->getBaseCode($travelCode); $baseCode = $utility->getBaseCode($travelCode);
$this->assertEquals('ABCDEF', $baseCode); self::assertEquals('ABCDEF', $baseCode);
$travelCode = 'abcdef010125'; $travelCode = 'abcdef010125';
$baseCode = $utility->getBaseCode($travelCode); $baseCode = $utility->getBaseCode($travelCode);
$this->assertEquals('ABCDEF', $baseCode); self::assertEquals('ABCDEF', $baseCode);
} }
public function testGetDate(): void public function testGetDate(): void
@@ -43,6 +43,6 @@ class TravelCodeUtilityTest extends TestCase
$travelCode = 'ABCDEF010125'; $travelCode = 'ABCDEF010125';
$date = $utility->getDate($travelCode); $date = $utility->getDate($travelCode);
$this->assertEquals('2025-01-01', $date->format('Y-m-d')); self::assertEquals('2025-01-01', $date->format('Y-m-d'));
} }
} }
@@ -61,10 +61,10 @@ class PickupLoaderTest extends TestCase
$this->loader->patchPickupsDetails($travel); $this->loader->patchPickupsDetails($travel);
// Pickups should be sorted chronologically: 10, 20, 30 // Pickups should be sorted chronologically: 10, 20, 30
$this->assertSame([10, 20, 30], array_keys($travel->pickups)); self::assertSame([10, 20, 30], array_keys($travel->pickups));
// Drop-offs should be in reverse order: 30, 20, 10 // Drop-offs should be in reverse order: 30, 20, 10
$this->assertSame([30, 20, 10], array_keys($travel->dropOffs)); self::assertSame([30, 20, 10], array_keys($travel->dropOffs));
} }
public function testDropOffsNotInPickupsAreAppendedAtEnd(): void public function testDropOffsNotInPickupsAreAppendedAtEnd(): void
@@ -94,7 +94,7 @@ class PickupLoaderTest extends TestCase
$this->loader->patchPickupsDetails($travel); $this->loader->patchPickupsDetails($travel);
// Drop-offs: reversed pickups (20, 10), then unmatched (99) // Drop-offs: reversed pickups (20, 10), then unmatched (99)
$this->assertSame([20, 10, 99], array_keys($travel->dropOffs)); self::assertSame([20, 10, 99], array_keys($travel->dropOffs));
} }
public function testDropOffsWithTimeDataAreSortedChronologically(): void public function testDropOffsWithTimeDataAreSortedChronologically(): void
@@ -133,6 +133,6 @@ class PickupLoaderTest extends TestCase
$this->loader->patchPickupsDetails($travel); $this->loader->patchPickupsDetails($travel);
// Drop-offs should be sorted by time, not by reversed pickup order // Drop-offs should be sorted by time, not by reversed pickup order
$this->assertSame([30, 20, 10], array_keys($travel->dropOffs)); self::assertSame([30, 20, 10], array_keys($travel->dropOffs));
} }
} }
+24 -24
View File
@@ -45,25 +45,25 @@ class AgencyParserTest extends TestCase
$resultNode = $crawler->filterXPath('//ergebnis'); $resultNode = $crawler->filterXPath('//ergebnis');
$agencies = $this->parser->parse($resultNode); $agencies = $this->parser->parse($resultNode);
$this->assertCount(2, $agencies); self::assertCount(2, $agencies);
// Test first agency (complete data) // Test first agency (complete data)
$this->assertEquals(124487, $agencies[0]->id); self::assertEquals(124487, $agencies[0]->id);
$this->assertEquals('ADAC Hansa e.V.', $agencies[0]->name); self::assertEquals('ADAC Hansa e.V.', $agencies[0]->name);
$this->assertEquals('295', $agencies[0]->code); self::assertEquals('295', $agencies[0]->code);
$this->assertEquals('Musterstr. 123', $agencies[0]->street); self::assertEquals('Musterstr. 123', $agencies[0]->street);
$this->assertEquals('99999', $agencies[0]->postCode); self::assertEquals('99999', $agencies[0]->postCode);
$this->assertEquals('Musterort', $agencies[0]->city); self::assertEquals('Musterort', $agencies[0]->city);
$this->assertEquals('0361-442930', $agencies[0]->phone); self::assertEquals('0361-442930', $agencies[0]->phone);
// Test second agency (missing phone) // Test second agency (missing phone)
$this->assertEquals(75861, $agencies[1]->id); self::assertEquals(75861, $agencies[1]->id);
$this->assertEquals('ADAC im Fördepark', $agencies[1]->name); self::assertEquals('ADAC im Fördepark', $agencies[1]->name);
$this->assertEquals('103', $agencies[1]->code); self::assertEquals('103', $agencies[1]->code);
$this->assertEquals('Sebastianusstr. 24', $agencies[1]->street); self::assertEquals('Sebastianusstr. 24', $agencies[1]->street);
$this->assertEquals('41468', $agencies[1]->postCode); self::assertEquals('41468', $agencies[1]->postCode);
$this->assertEquals('Neuss', $agencies[1]->city); self::assertEquals('Neuss', $agencies[1]->city);
$this->assertNull($agencies[1]->phone); self::assertNull($agencies[1]->phone);
} }
public function testParseMinimalAgency(): void public function testParseMinimalAgency(): void
@@ -83,14 +83,14 @@ class AgencyParserTest extends TestCase
$resultNode = $crawler->filterXPath('//ergebnis'); $resultNode = $crawler->filterXPath('//ergebnis');
$agencies = $this->parser->parse($resultNode); $agencies = $this->parser->parse($resultNode);
$this->assertCount(1, $agencies); self::assertCount(1, $agencies);
$this->assertEquals(12345, $agencies[0]->id); self::assertEquals(12345, $agencies[0]->id);
$this->assertEquals('Test Agency', $agencies[0]->name); self::assertEquals('Test Agency', $agencies[0]->name);
$this->assertEquals('001', $agencies[0]->code); self::assertEquals('001', $agencies[0]->code);
$this->assertNull($agencies[0]->street); self::assertNull($agencies[0]->street);
$this->assertNull($agencies[0]->postCode); self::assertNull($agencies[0]->postCode);
$this->assertNull($agencies[0]->city); self::assertNull($agencies[0]->city);
$this->assertNull($agencies[0]->phone); self::assertNull($agencies[0]->phone);
} }
public function testParseEmptyAgenciesList(): void public function testParseEmptyAgenciesList(): void
@@ -106,6 +106,6 @@ class AgencyParserTest extends TestCase
$resultNode = $crawler->filterXPath('//ergebnis'); $resultNode = $crawler->filterXPath('//ergebnis');
$agencies = $this->parser->parse($resultNode); $agencies = $this->parser->parse($resultNode);
$this->assertCount(0, $agencies); self::assertCount(0, $agencies);
} }
} }
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Tests\BusProNet\XmlParser; namespace App\Tests\BusProNet\XmlParser;
use App\BusProNet\Model\Insurance;
use App\BusProNet\XmlParser\BookingInsurancesParser; use App\BusProNet\XmlParser\BookingInsurancesParser;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Crawler;
@@ -28,15 +29,15 @@ XML;
$parser = new BookingInsurancesParser(); $parser = new BookingInsurancesParser();
$insurances = $parser->parse($insurancesData); $insurances = $parser->parse($insurancesData);
$this->assertCount(1, $insurances); self::assertCount(1, $insurances);
$this->assertArrayHasKey(177240, $insurances); $insuranceValues = array_values($insurances);
$insurance = $insuranceValues[0] ?? null;
$insurance = $insurances[177240]; self::assertInstanceOf(Insurance::class, $insurance);
$this->assertEquals(177240, $insurance->id); self::assertEquals(177240, $insurance->id);
$this->assertEquals('Reiseschutz Platin Auto/Bahn/Bus (Europa)', $insurance->label); self::assertEquals('Reiseschutz Platin Auto/Bahn/Bus (Europa)', $insurance->label);
$this->assertEquals(45.0, $insurance->price); self::assertEquals(45.0, $insurance->price);
$this->assertEquals([0], $insurance->mapping); // zuordnung="1" maps to participant index 0 self::assertEquals([0], $insurance->mapping); // zuordnung="1" maps to participant index 0
$this->assertEquals([0 => 45.0], $insurance->individualPrice); self::assertEquals([0 => 45.0], $insurance->individualPrice);
} }
public function testParseMultipleInsurances(): void public function testParseMultipleInsurances(): void
@@ -59,23 +60,36 @@ XML;
$parser = new BookingInsurancesParser(); $parser = new BookingInsurancesParser();
$insurances = $parser->parse($insurancesData); $insurances = $parser->parse($insurancesData);
$this->assertCount(2, $insurances); self::assertCount(2, $insurances);
/** @var Insurance|null $insurance1 */
$insurance1 = null;
/** @var Insurance|null $insurance2 */
$insurance2 = null;
foreach ($insurances as $insurance) {
if ('100' === (string) $insurance->id) {
$insurance1 = $insurance;
}
if ('200' === (string) $insurance->id) {
$insurance2 = $insurance;
}
}
// First insurance // First insurance
$insurance1 = $insurances[100]; self::assertInstanceOf(Insurance::class, $insurance1);
$this->assertEquals(100, $insurance1->id); self::assertEquals(100, $insurance1->id);
$this->assertEquals('Insurance A', $insurance1->label); self::assertEquals('Insurance A', $insurance1->label);
$this->assertEquals(45.0, $insurance1->price); self::assertEquals(45.0, $insurance1->price);
$this->assertEquals([0], $insurance1->mapping); // zuordnung="1" maps to index 0 self::assertEquals([0], $insurance1->mapping); // zuordnung="1" maps to index 0
$this->assertEquals([0 => 45.0], $insurance1->individualPrice); self::assertEquals([0 => 45.0], $insurance1->individualPrice);
// Second insurance // Second insurance
$insurance2 = $insurances[200]; self::assertInstanceOf(Insurance::class, $insurance2);
$this->assertEquals(200, $insurance2->id); self::assertEquals(200, $insurance2->id);
$this->assertEquals('Insurance B', $insurance2->label); self::assertEquals('Insurance B', $insurance2->label);
$this->assertEquals(30.0, $insurance2->price); self::assertEquals(30.0, $insurance2->price);
$this->assertEquals([1], $insurance2->mapping); // zuordnung="2" maps to index 1 self::assertEquals([1], $insurance2->mapping); // zuordnung="2" maps to index 1
$this->assertEquals([1 => 30.0], $insurance2->individualPrice); self::assertEquals([1 => 30.0], $insurance2->individualPrice);
} }
public function testParseEmptyInsurancesReturnsEmptyArray(): void public function testParseEmptyInsurancesReturnsEmptyArray(): void
@@ -94,7 +108,7 @@ XML;
$parser = new BookingInsurancesParser(); $parser = new BookingInsurancesParser();
$insurances = $parser->parse($insurancesData); $insurances = $parser->parse($insurancesData);
$this->assertCount(0, $insurances); self::assertCount(0, $insurances);
$this->assertIsArray($insurances); self::assertIsArray($insurances);
} }
} }
@@ -37,17 +37,17 @@ class BookingResponseParserTest extends TestCase
$crawler = new Crawler($xmlContent); $crawler = new Crawler($xmlContent);
$response = $this->parser->parse($crawler->filter('ergebnis')); $response = $this->parser->parse($crawler->filter('ergebnis'));
$this->assertTrue($response->isInquiryValid()); self::assertTrue($response->isInquiryValid());
$this->assertEquals(321530, $response->bookingNumber); self::assertEquals(321530, $response->bookingNumber);
$this->assertEquals(755.0, $response->totalPrice); self::assertEquals(755.0, $response->totalPrice);
// Verify purchase voucher is parsed // Verify purchase voucher is parsed
$this->assertNotNull($response->paymentTerms); self::assertNotNull($response->paymentTerms);
$this->assertCount(1, $response->paymentTerms->appliedPurchaseVouchers); self::assertCount(1, $response->paymentTerms->appliedPurchaseVouchers);
$this->assertEquals('G1263', $response->paymentTerms->appliedPurchaseVouchers[0]['nummer']); self::assertEquals('G1263', $response->paymentTerms->appliedPurchaseVouchers[0]['nummer']);
$this->assertEquals(100.0, $response->paymentTerms->appliedPurchaseVouchers[0]['betrag']); self::assertEquals(100.0, $response->paymentTerms->appliedPurchaseVouchers[0]['betrag']);
$this->assertEquals(100.0, $response->paymentTerms->getPurchaseVoucherDiscount()); self::assertEquals(100.0, $response->paymentTerms->getPurchaseVoucherDiscount());
$this->assertEquals(655.0, $response->paymentTerms->finalPaymentAmount); self::assertEquals(655.0, $response->paymentTerms->finalPaymentAmount);
} }
public function testParseBookingResponseWithPromotionalVoucher(): void public function testParseBookingResponseWithPromotionalVoucher(): void
@@ -70,21 +70,21 @@ class BookingResponseParserTest extends TestCase
$crawler = new Crawler($xmlContent); $crawler = new Crawler($xmlContent);
$response = $this->parser->parse($crawler->filter('ergebnis')); $response = $this->parser->parse($crawler->filter('ergebnis'));
$this->assertTrue($response->isInquiryValid()); self::assertTrue($response->isInquiryValid());
$this->assertEquals(615.2, $response->totalPrice); self::assertEquals(615.2, $response->totalPrice);
// Verify price items include promotional voucher // Verify price items include promotional voucher
$this->assertCount(2, $response->priceItems); self::assertCount(2, $response->priceItems);
$voucherItem = $response->priceItems[1]; $voucherItem = $response->priceItems[1];
$this->assertEquals('AKTION', $voucherItem->type); self::assertEquals('AKTION', $voucherItem->type);
$this->assertEquals('TESTERINO1', $voucherItem->label); self::assertEquals('TESTERINO1', $voucherItem->label);
$this->assertEquals(-100.0, $voucherItem->totalPrice); self::assertEquals(-100.0, $voucherItem->totalPrice);
// Verify voucher discount extraction // Verify voucher discount extraction
$voucherDiscounts = $response->getVoucherDiscountsFromPrices(); $voucherDiscounts = $response->getVoucherDiscountsFromPrices();
$this->assertCount(1, $voucherDiscounts); self::assertCount(1, $voucherDiscounts);
$this->assertEquals(100.0, $response->getVoucherDiscountFromPrices()); self::assertEquals(100.0, $response->getVoucherDiscountFromPrices());
$this->assertEquals(100.0, $response->getTotalVoucherDiscount()); self::assertEquals(100.0, $response->getTotalVoucherDiscount());
} }
public function testParseBookingResponseWithGoodwillVoucher(): void public function testParseBookingResponseWithGoodwillVoucher(): void
@@ -105,20 +105,20 @@ class BookingResponseParserTest extends TestCase
$crawler = new Crawler($xmlContent); $crawler = new Crawler($xmlContent);
$response = $this->parser->parse($crawler->filter('ergebnis')); $response = $this->parser->parse($crawler->filter('ergebnis'));
$this->assertTrue($response->isInquiryValid()); self::assertTrue($response->isInquiryValid());
$this->assertEquals(0.0, $response->totalPrice); self::assertEquals(0.0, $response->totalPrice);
// Verify goodwill voucher is parsed // Verify goodwill voucher is parsed
$voucherDiscounts = $response->getVoucherDiscountsFromPrices(); $voucherDiscounts = $response->getVoucherDiscountsFromPrices();
$this->assertCount(1, $voucherDiscounts); self::assertCount(1, $voucherDiscounts);
$goodwillVoucher = array_values($voucherDiscounts)[0]; $goodwillVoucher = array_values($voucherDiscounts)[0];
$this->assertEquals('KULANZGUTSCHEIN', $goodwillVoucher->type); self::assertEquals('KULANZGUTSCHEIN', $goodwillVoucher->type);
$this->assertEquals('Kulanzgutschein K1904', $goodwillVoucher->label); self::assertEquals('Kulanzgutschein K1904', $goodwillVoucher->label);
$this->assertEquals(-679.0, $goodwillVoucher->totalPrice); self::assertEquals(-679.0, $goodwillVoucher->totalPrice);
$this->assertEquals(679.0, $response->getVoucherDiscountFromPrices()); self::assertEquals(679.0, $response->getVoucherDiscountFromPrices());
$this->assertEquals(679.0, $response->getTotalVoucherDiscount()); self::assertEquals(679.0, $response->getTotalVoucherDiscount());
} }
public function testParseBookingResponseWithMultipleVouchers(): void public function testParseBookingResponseWithMultipleVouchers(): void
@@ -144,13 +144,13 @@ class BookingResponseParserTest extends TestCase
$response = $this->parser->parse($crawler->filter('ergebnis')); $response = $this->parser->parse($crawler->filter('ergebnis'));
// Verify all voucher types are parsed // Verify all voucher types are parsed
$this->assertEquals(80.0, $response->getVoucherDiscountFromPrices()); // 50 + 30 self::assertEquals(80.0, $response->getVoucherDiscountFromPrices()); // 50 + 30
$this->assertEquals(100.0, $response->getPurchaseVoucherDiscount()); self::assertEquals(100.0, $response->getPurchaseVoucherDiscount());
$this->assertEquals(180.0, $response->getTotalVoucherDiscount()); // 50 + 30 + 100 self::assertEquals(180.0, $response->getTotalVoucherDiscount()); // 50 + 30 + 100
// Verify price item vouchers // Verify price item vouchers
$voucherDiscounts = $response->getVoucherDiscountsFromPrices(); $voucherDiscounts = $response->getVoucherDiscountsFromPrices();
$this->assertCount(2, $voucherDiscounts); self::assertCount(2, $voucherDiscounts);
} }
public function testParseBookingResponseWithNoVouchers(): void public function testParseBookingResponseWithNoVouchers(): void
@@ -172,14 +172,14 @@ class BookingResponseParserTest extends TestCase
$crawler = new Crawler($xmlContent); $crawler = new Crawler($xmlContent);
$response = $this->parser->parse($crawler->filter('ergebnis')); $response = $this->parser->parse($crawler->filter('ergebnis'));
$this->assertTrue($response->isInquiryValid()); self::assertTrue($response->isInquiryValid());
$this->assertEquals(679.0, $response->totalPrice); self::assertEquals(679.0, $response->totalPrice);
// Verify no voucher discounts // Verify no voucher discounts
$this->assertCount(0, $response->getVoucherDiscountsFromPrices()); self::assertCount(0, $response->getVoucherDiscountsFromPrices());
$this->assertEquals(0.0, $response->getVoucherDiscountFromPrices()); self::assertEquals(0.0, $response->getVoucherDiscountFromPrices());
$this->assertEquals(0.0, $response->getPurchaseVoucherDiscount()); self::assertEquals(0.0, $response->getPurchaseVoucherDiscount());
$this->assertEquals(0.0, $response->getTotalVoucherDiscount()); self::assertEquals(0.0, $response->getTotalVoucherDiscount());
} }
public function testParseBookingResponseWithMultiplePurchaseVouchers(): void public function testParseBookingResponseWithMultiplePurchaseVouchers(): void
@@ -204,10 +204,10 @@ class BookingResponseParserTest extends TestCase
$response = $this->parser->parse($crawler->filter('ergebnis')); $response = $this->parser->parse($crawler->filter('ergebnis'));
// Verify multiple purchase vouchers are parsed // Verify multiple purchase vouchers are parsed
$this->assertNotNull($response->paymentTerms); self::assertNotNull($response->paymentTerms);
$this->assertCount(2, $response->paymentTerms->appliedPurchaseVouchers); self::assertCount(2, $response->paymentTerms->appliedPurchaseVouchers);
$this->assertEquals(125.0, $response->paymentTerms->getPurchaseVoucherDiscount()); // 50 + 75 self::assertEquals(125.0, $response->paymentTerms->getPurchaseVoucherDiscount()); // 50 + 75
$this->assertEquals(125.0, $response->getTotalVoucherDiscount()); self::assertEquals(125.0, $response->getTotalVoucherDiscount());
} }
public function testParseBookingResponseWithGruppenrabatt(): void public function testParseBookingResponseWithGruppenrabatt(): void
@@ -231,24 +231,24 @@ class BookingResponseParserTest extends TestCase
$crawler = new Crawler($xmlContent); $crawler = new Crawler($xmlContent);
$response = $this->parser->parse($crawler->filter('ergebnis')); $response = $this->parser->parse($crawler->filter('ergebnis'));
$this->assertTrue($response->isInquiryValid()); self::assertTrue($response->isInquiryValid());
$this->assertEquals(3972.0, $response->totalPrice); self::assertEquals(3972.0, $response->totalPrice);
// Verify Gruppenrabatt is NOT counted as voucher discount // Verify Gruppenrabatt is NOT counted as voucher discount
$this->assertCount(0, $response->getVoucherDiscountsFromPrices()); self::assertCount(0, $response->getVoucherDiscountsFromPrices());
$this->assertEquals(0.0, $response->getVoucherDiscountFromPrices()); self::assertEquals(0.0, $response->getVoucherDiscountFromPrices());
// Verify Gruppenrabatt IS counted as API-applied discount // Verify Gruppenrabatt IS counted as API-applied discount
$apiAppliedDiscounts = $response->getApiAppliedDiscounts(); $apiAppliedDiscounts = $response->getApiAppliedDiscounts();
$this->assertCount(1, $apiAppliedDiscounts); self::assertCount(1, $apiAppliedDiscounts);
$gruppenrabatt = array_values($apiAppliedDiscounts)[0]; $gruppenrabatt = array_values($apiAppliedDiscounts)[0];
$this->assertEquals('ERM', $gruppenrabatt->type); self::assertEquals('ERM', $gruppenrabatt->type);
$this->assertEquals('GRU', $gruppenrabatt->subType); self::assertEquals('GRU', $gruppenrabatt->subType);
$this->assertEquals('Gruppenrabatt', $gruppenrabatt->label); self::assertEquals('Gruppenrabatt', $gruppenrabatt->label);
$this->assertEquals(-90.0, $gruppenrabatt->totalPrice); self::assertEquals(-90.0, $gruppenrabatt->totalPrice);
$this->assertEquals(90.0, $response->getApiAppliedDiscountTotal()); self::assertEquals(90.0, $response->getApiAppliedDiscountTotal());
} }
public function testParseBookingResponseWithGruppenrabattAndVouchers(): void public function testParseBookingResponseWithGruppenrabattAndVouchers(): void
@@ -273,20 +273,20 @@ class BookingResponseParserTest extends TestCase
$response = $this->parser->parse($crawler->filter('ergebnis')); $response = $this->parser->parse($crawler->filter('ergebnis'));
// Voucher discount should only include AKTION // Voucher discount should only include AKTION
$this->assertEquals(20.0, $response->getVoucherDiscountFromPrices()); self::assertEquals(20.0, $response->getVoucherDiscountFromPrices());
// API-applied discount should only include ERM (Gruppenrabatt) // API-applied discount should only include ERM (Gruppenrabatt)
$this->assertEquals(50.0, $response->getApiAppliedDiscountTotal()); self::assertEquals(50.0, $response->getApiAppliedDiscountTotal());
// Verify they are separate and don't overlap // Verify they are separate and don't overlap
$voucherDiscounts = $response->getVoucherDiscountsFromPrices(); $voucherDiscounts = $response->getVoucherDiscountsFromPrices();
$apiDiscounts = $response->getApiAppliedDiscounts(); $apiDiscounts = $response->getApiAppliedDiscounts();
$this->assertCount(1, $voucherDiscounts); self::assertCount(1, $voucherDiscounts);
$this->assertCount(1, $apiDiscounts); self::assertCount(1, $apiDiscounts);
$this->assertEquals('AKTION', array_values($voucherDiscounts)[0]->type); self::assertEquals('AKTION', array_values($voucherDiscounts)[0]->type);
$this->assertEquals('ERM', array_values($apiDiscounts)[0]->type); self::assertEquals('ERM', array_values($apiDiscounts)[0]->type);
} }
public function testParseBookingResponseWithNoApiAppliedDiscounts(): void public function testParseBookingResponseWithNoApiAppliedDiscounts(): void
@@ -309,8 +309,8 @@ class BookingResponseParserTest extends TestCase
$response = $this->parser->parse($crawler->filter('ergebnis')); $response = $this->parser->parse($crawler->filter('ergebnis'));
// Verify no API-applied discounts // Verify no API-applied discounts
$this->assertCount(0, $response->getApiAppliedDiscounts()); self::assertCount(0, $response->getApiAppliedDiscounts());
$this->assertEquals(0.0, $response->getApiAppliedDiscountTotal()); self::assertEquals(0.0, $response->getApiAppliedDiscountTotal());
} }
public function testParseBookingResponseWithTransportationDiscountNotCountedAsApiApplied(): void public function testParseBookingResponseWithTransportationDiscountNotCountedAsApiApplied(): void
@@ -336,16 +336,16 @@ class BookingResponseParserTest extends TestCase
$crawler = new Crawler($xmlContent); $crawler = new Crawler($xmlContent);
$response = $this->parser->parse($crawler->filter('ergebnis')); $response = $this->parser->parse($crawler->filter('ergebnis'));
$this->assertTrue($response->isInquiryValid()); self::assertTrue($response->isInquiryValid());
$this->assertEquals(1682.0, $response->totalPrice); self::assertEquals(1682.0, $response->totalPrice);
// Transportation discount (BEF with negative price) should NOT be counted as API-applied discount // Transportation discount (BEF with negative price) should NOT be counted as API-applied discount
// because it's already handled by the local price calculator // because it's already handled by the local price calculator
$this->assertCount(0, $response->getApiAppliedDiscounts()); self::assertCount(0, $response->getApiAppliedDiscounts());
$this->assertEquals(0.0, $response->getApiAppliedDiscountTotal()); self::assertEquals(0.0, $response->getApiAppliedDiscountTotal());
// Verify no voucher discounts either // Verify no voucher discounts either
$this->assertEquals(0.0, $response->getVoucherDiscountFromPrices()); self::assertEquals(0.0, $response->getVoucherDiscountFromPrices());
} }
public function testParseBookingResponseWithBothTransportationAndGruppenrabatt(): void public function testParseBookingResponseWithBothTransportationAndGruppenrabatt(): void
@@ -372,13 +372,13 @@ class BookingResponseParserTest extends TestCase
// Only ERM (Gruppenrabatt) should be counted as API-applied discount // Only ERM (Gruppenrabatt) should be counted as API-applied discount
// BEF transportation discount should NOT be counted // BEF transportation discount should NOT be counted
$apiAppliedDiscounts = $response->getApiAppliedDiscounts(); $apiAppliedDiscounts = $response->getApiAppliedDiscounts();
$this->assertCount(1, $apiAppliedDiscounts); self::assertCount(1, $apiAppliedDiscounts);
$gruppenrabatt = array_values($apiAppliedDiscounts)[0]; $gruppenrabatt = array_values($apiAppliedDiscounts)[0];
$this->assertEquals('ERM', $gruppenrabatt->type); self::assertEquals('ERM', $gruppenrabatt->type);
$this->assertEquals(-100.0, $gruppenrabatt->totalPrice); self::assertEquals(-100.0, $gruppenrabatt->totalPrice);
// Total API-applied discount should only include Gruppenrabatt (100), not transportation (300) // Total API-applied discount should only include Gruppenrabatt (100), not transportation (300)
$this->assertEquals(100.0, $response->getApiAppliedDiscountTotal()); self::assertEquals(100.0, $response->getApiAppliedDiscountTotal());
} }
} }
@@ -40,60 +40,60 @@ class InsuranceParserTest extends TestCase
$insurances = $this->parser->parse($crawler); $insurances = $this->parser->parse($crawler);
// Test all insurances are included (2 regular + 1 complementary + 1 package = 4 total) // Test all insurances are included (2 regular + 1 complementary + 1 package = 4 total)
$this->assertCount(4, $insurances); self::assertCount(4, $insurances);
// Test travel cancellation insurance (RRV) // Test travel cancellation insurance (RRV)
$cancellationInsurance = $insurances[178917]; $cancellationInsurance = $insurances['178917'];
$this->assertEquals(178917, $cancellationInsurance->id); self::assertEquals(178917, $cancellationInsurance->id);
$this->assertEquals('903100', $cancellationInsurance->code); self::assertEquals('903100', $cancellationInsurance->code);
$this->assertEquals('Reise-Rücktritt', $cancellationInsurance->label); self::assertEquals('Reise-Rücktritt', $cancellationInsurance->label);
$this->assertEquals('RRV', $cancellationInsurance->subType); self::assertEquals('RRV', $cancellationInsurance->subType);
$this->assertEquals(5.0, $cancellationInsurance->price); self::assertEquals(5.0, $cancellationInsurance->price);
$this->assertFalse($cancellationInsurance->familyInsurance); self::assertFalse($cancellationInsurance->familyInsurance);
$this->assertFalse($cancellationInsurance->package); self::assertFalse($cancellationInsurance->package);
$this->assertFalse($cancellationInsurance->complementary); self::assertFalse($cancellationInsurance->complementary);
// Test travel protection insurance (PAK) with family flag // Test travel protection insurance (PAK) with family flag
$protectionInsurance = $insurances[177236]; $protectionInsurance = $insurances['177236'];
$this->assertEquals(177236, $protectionInsurance->id); self::assertEquals(177236, $protectionInsurance->id);
$this->assertEquals('913320', $protectionInsurance->code); self::assertEquals('913320', $protectionInsurance->code);
$this->assertEquals('Reiseschutz Platin', $protectionInsurance->label); // "Auto/Bahn/Bus (Europa)" is normalized away self::assertEquals('Reiseschutz Platin', $protectionInsurance->label); // "Auto/Bahn/Bus (Europa)" is normalized away
$this->assertEquals('PAK', $protectionInsurance->subType); self::assertEquals('PAK', $protectionInsurance->subType);
$this->assertEquals(9.0, $protectionInsurance->price); self::assertEquals(9.0, $protectionInsurance->price);
$this->assertTrue($protectionInsurance->familyInsurance); self::assertTrue($protectionInsurance->familyInsurance);
$this->assertFalse($protectionInsurance->package); self::assertFalse($protectionInsurance->package);
$this->assertFalse($protectionInsurance->complementary); self::assertFalse($protectionInsurance->complementary);
// Test deductible insurance (OHN) - IS included but marked as complementary // Test deductible insurance (OHN) - IS included but marked as complementary
$deductibleInsurance = $insurances[177270]; $deductibleInsurance = $insurances['177270'];
$this->assertEquals(177270, $deductibleInsurance->id); self::assertEquals(177270, $deductibleInsurance->id);
$this->assertEquals('903551', $deductibleInsurance->code); self::assertEquals('903551', $deductibleInsurance->code);
$this->assertEquals('Selbstbehaltübernahme', $deductibleInsurance->label); self::assertEquals('Selbstbehaltübernahme', $deductibleInsurance->label);
$this->assertEquals('OHN', $deductibleInsurance->subType); self::assertEquals('OHN', $deductibleInsurance->subType);
$this->assertEquals(5.0, $deductibleInsurance->price); self::assertEquals(5.0, $deductibleInsurance->price);
$this->assertFalse($deductibleInsurance->familyInsurance); self::assertFalse($deductibleInsurance->familyInsurance);
$this->assertFalse($deductibleInsurance->package); self::assertFalse($deductibleInsurance->package);
$this->assertTrue($deductibleInsurance->complementary); // Marked as complementary self::assertTrue($deductibleInsurance->complementary); // Marked as complementary
// Test package - should be family insurance because it contains a family insurance (177236) // Test package - should be family insurance because it contains a family insurance (177236)
$package = $insurances['P1000320']; $package = $insurances['P1000320'];
$this->assertEquals('P1000320', $package->id); self::assertEquals('P1000320', $package->id);
$this->assertEquals('Reiseschutz Platin + Selbstbehaltübernahme', $package->label); // "Auto/Bahn/Bus (Europa)" is normalized away self::assertEquals('Reiseschutz Platin + Selbstbehaltübernahme', $package->label); // "Auto/Bahn/Bus (Europa)" is normalized away
$this->assertNull($package->subType); // Packages don't have subtypes self::assertNull($package->subType); // Packages don't have subtypes
$this->assertEquals(14.0, $package->price); self::assertEquals(14.0, $package->price);
$this->assertTrue($package->familyInsurance); // Should be true because it contains family insurance 177236 self::assertTrue($package->familyInsurance); // Should be true because it contains family insurance 177236
$this->assertTrue($package->package); self::assertTrue($package->package);
$this->assertEquals([177236, 177270], $package->containedInsuranceIds); self::assertEquals(['177236', '177270'], $package->containedInsuranceIds);
// Test containedInsurances array is populated with actual Insurance objects // Test containedInsurances array is populated with actual Insurance objects
$this->assertCount(2, $package->containedInsurances); self::assertCount(2, $package->containedInsurances);
$this->assertEquals(177236, $package->containedInsurances[0]->id); self::assertEquals(177236, $package->containedInsurances[0]->id);
$this->assertEquals(177270, $package->containedInsurances[1]->id); self::assertEquals(177270, $package->containedInsurances[1]->id);
// Test URL aggregation from contained insurances // Test URL aggregation from contained insurances
$this->assertEquals(['https://example.com/info2', 'https://example.com/info3'], $package->getAllUrlsInfo()); self::assertEquals(['https://example.com/info2', 'https://example.com/info3'], $package->getAllUrlsInfo());
$this->assertEquals(['https://example.com/product2', 'https://example.com/product3'], $package->getAllUrlsProductInfo()); self::assertEquals(['https://example.com/product2', 'https://example.com/product3'], $package->getAllUrlsProductInfo());
$this->assertEquals(['https://example.com/terms2', 'https://example.com/terms3'], $package->getAllUrlsTerms()); self::assertEquals(['https://example.com/terms2', 'https://example.com/terms3'], $package->getAllUrlsTerms());
} }
public function testParsePackageWithoutSubtype(): void public function testParsePackageWithoutSubtype(): void
@@ -115,14 +115,14 @@ class InsuranceParserTest extends TestCase
$crawler = new Crawler($xmlContent); $crawler = new Crawler($xmlContent);
$insurances = $this->parser->parse($crawler); $insurances = $this->parser->parse($crawler);
$this->assertCount(2, $insurances); self::assertCount(2, $insurances);
// Test package has no subType but has familyInsurance flag // Test package has no subType but has familyInsurance flag
$package = $insurances['P1000320']; $package = $insurances['P1000320'];
$this->assertEquals('P1000320', $package->id); self::assertEquals('P1000320', $package->id);
$this->assertNull($package->subType); self::assertNull($package->subType);
$this->assertTrue($package->familyInsurance); self::assertTrue($package->familyInsurance);
$this->assertTrue($package->package); self::assertTrue($package->package);
} }
public function testParseInsuranceWithNullSubtype(): void public function testParseInsuranceWithNullSubtype(): void
@@ -137,12 +137,12 @@ class InsuranceParserTest extends TestCase
$crawler = new Crawler($xmlContent); $crawler = new Crawler($xmlContent);
$insurances = $this->parser->parse($crawler); $insurances = $this->parser->parse($crawler);
$this->assertCount(1, $insurances); self::assertCount(1, $insurances);
$insurance = $insurances[178917]; $insurance = $insurances['178917'];
$this->assertEquals(178917, $insurance->id); self::assertEquals(178917, $insurance->id);
$this->assertNull($insurance->subType); // Missing unterart attribute should result in null self::assertNull($insurance->subType); // Missing unterart attribute should result in null
$this->assertFalse($insurance->package); self::assertFalse($insurance->package);
} }
public function testParseEmptyXml(): void public function testParseEmptyXml(): void
@@ -158,7 +158,7 @@ class InsuranceParserTest extends TestCase
$crawler = new Crawler($xmlContent); $crawler = new Crawler($xmlContent);
$insurances = $this->parser->parse($crawler); $insurances = $this->parser->parse($crawler);
$this->assertEmpty($insurances); self::assertEmpty($insurances);
} }
public function testParseZusatzversicherungFiltering(): void public function testParseZusatzversicherungFiltering(): void
@@ -183,15 +183,15 @@ class InsuranceParserTest extends TestCase
$insurances = $this->parser->parse($crawler); $insurances = $this->parser->parse($crawler);
// Should include: normal insurance (178917), both complementary insurances (177270, 999999), and package (P1000320) // Should include: normal insurance (178917), both complementary insurances (177270, 999999), and package (P1000320)
$this->assertCount(4, $insurances); self::assertCount(4, $insurances);
$this->assertArrayHasKey(178917, $insurances); self::assertArrayHasKey('178917', $insurances);
$this->assertArrayHasKey(177270, $insurances); self::assertArrayHasKey('177270', $insurances);
$this->assertArrayHasKey(999999, $insurances); self::assertArrayHasKey('999999', $insurances);
$this->assertArrayHasKey('P1000320', $insurances); self::assertArrayHasKey('P1000320', $insurances);
// Verify complementary flags // Verify complementary flags
$this->assertFalse($insurances[178917]->complementary); // Normal insurance self::assertFalse($insurances['178917']->complementary); // Normal insurance
$this->assertTrue($insurances[177270]->complementary); // Complementary insurance self::assertTrue($insurances['177270']->complementary); // Complementary insurance
$this->assertTrue($insurances[999999]->complementary); // Complementary insurance self::assertTrue($insurances['999999']->complementary); // Complementary insurance
} }
} }
+11 -11
View File
@@ -34,7 +34,7 @@ class DomainThemeListenerTest extends TestCase
$listener($event); $listener($event);
$this->assertSame('sbw', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE)); self::assertSame('sbw', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE));
} }
public function testSetsDomainConfigAttribute(): void public function testSetsDomainConfigAttribute(): void
@@ -53,10 +53,10 @@ class DomainThemeListenerTest extends TestCase
$listener($event); $listener($event);
$config = $request->attributes->get(DomainThemeListener::DOMAIN_CONFIG_ATTRIBUTE); $config = $request->attributes->get(DomainThemeListener::DOMAIN_CONFIG_ATTRIBUTE);
$this->assertInstanceOf(DomainConfig::class, $config); self::assertInstanceOf(DomainConfig::class, $config);
$this->assertSame('sbw', $config->theme); self::assertSame('sbw', $config->theme);
$this->assertSame('GTM-123', $config->gtmId); self::assertSame('GTM-123', $config->gtmId);
$this->assertSame('https://cmp.example.com', $config->cmpUrl); self::assertSame('https://cmp.example.com', $config->cmpUrl);
} }
public function testSetsDefaultThemeForUnknownDomain(): void public function testSetsDefaultThemeForUnknownDomain(): void
@@ -70,7 +70,7 @@ class DomainThemeListenerTest extends TestCase
$listener($event); $listener($event);
$this->assertSame('base', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE)); self::assertSame('base', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE));
} }
public function testMatchesPartialDomain(): void public function testMatchesPartialDomain(): void
@@ -84,7 +84,7 @@ class DomainThemeListenerTest extends TestCase
$listener($event); $listener($event);
$this->assertSame('ser', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE)); self::assertSame('ser', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE));
} }
public function testIgnoresSubRequests(): void public function testIgnoresSubRequests(): void
@@ -98,7 +98,7 @@ class DomainThemeListenerTest extends TestCase
$listener($event); $listener($event);
$this->assertFalse($request->attributes->has(DomainThemeListener::THEME_ATTRIBUTE)); self::assertFalse($request->attributes->has(DomainThemeListener::THEME_ATTRIBUTE));
} }
public function testFirstMatchWins(): void public function testFirstMatchWins(): void
@@ -114,7 +114,7 @@ class DomainThemeListenerTest extends TestCase
$listener($event); $listener($event);
$this->assertSame('portal', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE)); self::assertSame('portal', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE));
} }
public function testEmptyMapReturnsDefaultTheme(): void public function testEmptyMapReturnsDefaultTheme(): void
@@ -126,7 +126,7 @@ class DomainThemeListenerTest extends TestCase
$listener($event); $listener($event);
$this->assertSame('base', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE)); self::assertSame('base', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE));
} }
public function testMatchesLocalhostForDevelopment(): void public function testMatchesLocalhostForDevelopment(): void
@@ -140,7 +140,7 @@ class DomainThemeListenerTest extends TestCase
$listener($event); $listener($event);
$this->assertSame('sbw', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE)); self::assertSame('sbw', $request->attributes->get(DomainThemeListener::THEME_ATTRIBUTE));
} }
/** /**
+10 -10
View File
@@ -26,8 +26,8 @@ class AcceptedVouchersTest extends TestCase
amount: 100.00 amount: 100.00
)); ));
$this->assertEquals(150.0, $acceptedVouchers->getTotalDiscount()); self::assertEquals(150.0, $acceptedVouchers->getTotalDiscount());
$this->assertCount(2, $acceptedVouchers->getVouchers()); self::assertCount(2, $acceptedVouchers->getVouchers());
} }
public function testFilterVouchersByType(): void public function testFilterVouchersByType(): void
@@ -52,17 +52,17 @@ class AcceptedVouchersTest extends TestCase
amount: 75.00 amount: 75.00
)); ));
$this->assertCount(1, $acceptedVouchers->getPromotionalVouchers()); self::assertCount(1, $acceptedVouchers->getPromotionalVouchers());
$this->assertCount(1, $acceptedVouchers->getPurchaseVouchers()); self::assertCount(1, $acceptedVouchers->getPurchaseVouchers());
$this->assertCount(1, $acceptedVouchers->getGoodwillVouchers()); self::assertCount(1, $acceptedVouchers->getGoodwillVouchers());
} }
public function testHasVouchersReturnsFalseWhenEmpty(): void public function testHasVouchersReturnsFalseWhenEmpty(): void
{ {
$acceptedVouchers = new AcceptedVouchersDto(); $acceptedVouchers = new AcceptedVouchersDto();
$this->assertFalse($acceptedVouchers->hasVouchers()); self::assertFalse($acceptedVouchers->hasVouchers());
$this->assertEquals(0, $acceptedVouchers->count()); self::assertEquals(0, $acceptedVouchers->count());
} }
public function testHasVouchersReturnsTrueWhenNotEmpty(): void public function testHasVouchersReturnsTrueWhenNotEmpty(): void
@@ -75,8 +75,8 @@ class AcceptedVouchersTest extends TestCase
amount: 50.00 amount: 50.00
)); ));
$this->assertTrue($acceptedVouchers->hasVouchers()); self::assertTrue($acceptedVouchers->hasVouchers());
$this->assertEquals(1, $acceptedVouchers->count()); self::assertEquals(1, $acceptedVouchers->count());
} }
public function testFloatPrecisionHandling(): void public function testFloatPrecisionHandling(): void
@@ -102,6 +102,6 @@ class AcceptedVouchersTest extends TestCase
)); ));
// Should be exactly 100.00 with proper rounding // Should be exactly 100.00 with proper rounding
$this->assertEquals(100.0, $acceptedVouchers->getTotalDiscount()); self::assertEquals(100.0, $acceptedVouchers->getTotalDiscount());
} }
} }
+7 -7
View File
@@ -24,9 +24,9 @@ class BookingDtoTest extends TestCase
$bookingDto = new BookingDto($travel, 1); $bookingDto = new BookingDto($travel, 1);
$this->assertInstanceOf(BookingDto::class, $bookingDto); self::assertInstanceOf(BookingDto::class, $bookingDto);
$this->assertSame($travel, $bookingDto->travel); self::assertSame($travel, $bookingDto->travel);
$this->assertSame(BookingDto::MODE_CREATE, $bookingDto->getMode()); self::assertSame(BookingDto::MODE_CREATE, $bookingDto->getMode());
} }
public function testUnserializeHandlesIntTravelId(): void public function testUnserializeHandlesIntTravelId(): void
@@ -40,8 +40,8 @@ class BookingDtoTest extends TestCase
$serialized = serialize($dto); $serialized = serialize($dto);
$restored = unserialize($serialized); $restored = unserialize($serialized);
$this->assertInstanceOf(BookingDto::class, $restored); self::assertInstanceOf(BookingDto::class, $restored);
$this->assertSame(42, $restored->travel->id); self::assertSame(42, $restored->travel->id);
} }
public function testUnserializeHandlesLegacyTravelObject(): void public function testUnserializeHandlesLegacyTravelObject(): void
@@ -60,7 +60,7 @@ class BookingDtoTest extends TestCase
$dto = new BookingDto(new Travel(), 1); $dto = new BookingDto(new Travel(), 1);
$dto->__unserialize($data); $dto->__unserialize($data);
$this->assertSame($travel, $dto->travel); self::assertSame($travel, $dto->travel);
$this->assertSame(42, $dto->travel->id); self::assertSame(42, $dto->travel->id);
} }
} }
+17 -17
View File
@@ -15,7 +15,7 @@ class ParticipantDtoTest extends TestCase
$participant = new ParticipantDto(); $participant = new ParticipantDto();
$participant->dateOfBirth = null; $participant->dateOfBirth = null;
$this->assertNull($participant->getAge()); self::assertNull($participant->getAge());
} }
public function testGetAgeCalculatesCurrentAgeWhenNoReferenceDateProvided(): void public function testGetAgeCalculatesCurrentAgeWhenNoReferenceDateProvided(): void
@@ -26,7 +26,7 @@ class ParticipantDtoTest extends TestCase
$expectedAge = (new \DateTimeImmutable())->diff($participant->dateOfBirth)->y; $expectedAge = (new \DateTimeImmutable())->diff($participant->dateOfBirth)->y;
$actualAge = $participant->getAge(); $actualAge = $participant->getAge();
$this->assertEquals($expectedAge, $actualAge); self::assertEquals($expectedAge, $actualAge);
} }
public function testGetAgeCalculatesAgeAtSpecificReferenceDate(): void public function testGetAgeCalculatesAgeAtSpecificReferenceDate(): void
@@ -37,7 +37,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getAge($referenceDate); $result = $participant->getAge($referenceDate);
$this->assertEquals(33, $result); // Still 33 years old self::assertEquals(33, $result); // Still 33 years old
} }
public function testGetAgeCalculatesAgeAtSpecificReferenceDateAfterBirthday(): void public function testGetAgeCalculatesAgeAtSpecificReferenceDateAfterBirthday(): void
@@ -48,7 +48,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getAge($referenceDate); $result = $participant->getAge($referenceDate);
$this->assertEquals(34, $result); // Now 34 years old self::assertEquals(34, $result); // Now 34 years old
} }
public function testGetAgeCalculatesAgeForInsuranceEligibilityScenario(): void public function testGetAgeCalculatesAgeForInsuranceEligibilityScenario(): void
@@ -61,7 +61,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getAge($travelStartDate); $result = $participant->getAge($travelStartDate);
// On 2024-08-01, they are still 34 (birthday not yet reached) // On 2024-08-01, they are still 34 (birthday not yet reached)
$this->assertEquals(34, $result); self::assertEquals(34, $result);
} }
public function testGetAgeCalculatesAgeForInsuranceEligibilityScenarioAfterBirthday(): void public function testGetAgeCalculatesAgeForInsuranceEligibilityScenarioAfterBirthday(): void
@@ -74,7 +74,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getAge($travelStartDate); $result = $participant->getAge($travelStartDate);
// On 2024-12-26, they just turned 35 // On 2024-12-26, they just turned 35
$this->assertEquals(35, $result); self::assertEquals(35, $result);
} }
public function testGetAgeHandlesLeapYearScenarios(): void public function testGetAgeHandlesLeapYearScenarios(): void
@@ -86,7 +86,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getAge($referenceDate); $result = $participant->getAge($referenceDate);
$this->assertEquals(22, $result); // Still 22, birthday is technically next day self::assertEquals(22, $result); // Still 22, birthday is technically next day
} }
public function testGetAgeReturnsNullWithReferenceDateWhenDateOfBirthIsNull(): void public function testGetAgeReturnsNullWithReferenceDateWhenDateOfBirthIsNull(): void
@@ -97,7 +97,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getAge($referenceDate); $result = $participant->getAge($referenceDate);
$this->assertNull($result); self::assertNull($result);
} }
public function testGetAgeWithYoungParticipant(): void public function testGetAgeWithYoungParticipant(): void
@@ -109,7 +109,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getAge($referenceDate); $result = $participant->getAge($referenceDate);
$this->assertEquals(4, $result); self::assertEquals(4, $result);
} }
public function testGetAgeWithElderlyParticipant(): void public function testGetAgeWithElderlyParticipant(): void
@@ -121,7 +121,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getAge($referenceDate); $result = $participant->getAge($referenceDate);
$this->assertEquals(84, $result); self::assertEquals(84, $result);
} }
public function testHasInsuranceReturnsFalseWhenNoInsuranceSelected(): void public function testHasInsuranceReturnsFalseWhenNoInsuranceSelected(): void
@@ -131,7 +131,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->hasInsuranceSelected(); $result = $participant->hasInsuranceSelected();
$this->assertFalse($result); self::assertFalse($result);
} }
public function testHasInsuranceReturnsTrueWhenInsuranceSelected(): void public function testHasInsuranceReturnsTrueWhenInsuranceSelected(): void
@@ -141,7 +141,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->hasInsuranceSelected(); $result = $participant->hasInsuranceSelected();
$this->assertTrue($result); self::assertTrue($result);
} }
public function testGetInsuranceLabelReturnsNullWhenNoInsurance(): void public function testGetInsuranceLabelReturnsNullWhenNoInsurance(): void
@@ -151,7 +151,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getInsuranceLabel(); $result = $participant->getInsuranceLabel();
$this->assertNull($result); self::assertNull($result);
} }
public function testGetInsuranceLabelReturnsLabelWhenInsuranceSelected(): void public function testGetInsuranceLabelReturnsLabelWhenInsuranceSelected(): void
@@ -161,7 +161,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getInsuranceLabel(); $result = $participant->getInsuranceLabel();
$this->assertEquals('Reise-Rücktritt', $result); self::assertEquals('Reise-Rücktritt', $result);
} }
public function testGetInsurancePriceReturnsZeroWhenNoInsurance(): void public function testGetInsurancePriceReturnsZeroWhenNoInsurance(): void
@@ -171,7 +171,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getInsurancePrice(); $result = $participant->getInsurancePrice();
$this->assertEquals(0.0, $result); self::assertEquals(0.0, $result);
} }
public function testGetInsurancePriceReturnsPriceWhenInsuranceSelected(): void public function testGetInsurancePriceReturnsPriceWhenInsuranceSelected(): void
@@ -181,7 +181,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getInsurancePrice(); $result = $participant->getInsurancePrice();
$this->assertEquals(25.50, $result); self::assertEquals(25.50, $result);
} }
public function testGetInsurancePriceReturnsZeroWhenInsuranceHasNullPrice(): void public function testGetInsurancePriceReturnsZeroWhenInsuranceHasNullPrice(): void
@@ -193,7 +193,7 @@ class ParticipantDtoTest extends TestCase
$result = $participant->getInsurancePrice(); $result = $participant->getInsurancePrice();
$this->assertEquals(0.0, $result); self::assertEquals(0.0, $result);
} }
private function createInsurance(string $label = 'Test Insurance', float $price = 10.0): Insurance private function createInsurance(string $label = 'Test Insurance', float $price = 10.0): Insurance
@@ -38,7 +38,7 @@ class InsuranceMutabilityConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertFalse($result, 'Insurance should always be editable in create flow'); self::assertFalse($result, 'Insurance should always be editable in create flow');
} }
public function testReadonlyWhen40DaysBeforeTravel(): void public function testReadonlyWhen40DaysBeforeTravel(): void
@@ -51,7 +51,7 @@ class InsuranceMutabilityConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertTrue($result, 'Insurance should be readonly in edit mode (API limitation)'); self::assertTrue($result, 'Insurance should be readonly in edit mode (API limitation)');
} }
public function testReadonlyWhenExactly30DaysBeforeTravel(): void public function testReadonlyWhenExactly30DaysBeforeTravel(): void
@@ -65,7 +65,7 @@ class InsuranceMutabilityConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertTrue($result, 'Insurance should be readonly in edit mode (API limitation)'); self::assertTrue($result, 'Insurance should be readonly in edit mode (API limitation)');
} }
public function testReadonlyWhen20DaysBeforeTravel(): void public function testReadonlyWhen20DaysBeforeTravel(): void
@@ -78,7 +78,7 @@ class InsuranceMutabilityConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertTrue($result, 'Insurance should be readonly when 20 days before travel (standard case)'); self::assertTrue($result, 'Insurance should be readonly when 20 days before travel (standard case)');
} }
public function testReadonlyWhen2DaysAfterLateBooking(): void public function testReadonlyWhen2DaysAfterLateBooking(): void
@@ -92,7 +92,7 @@ class InsuranceMutabilityConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertTrue($result, 'Insurance should be readonly in edit mode (API limitation)'); self::assertTrue($result, 'Insurance should be readonly in edit mode (API limitation)');
} }
public function testReadonlyWhenExactly3DaysAfterLateBooking(): void public function testReadonlyWhenExactly3DaysAfterLateBooking(): void
@@ -106,7 +106,7 @@ class InsuranceMutabilityConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertTrue($result, 'Insurance should be readonly in edit mode (API limitation)'); self::assertTrue($result, 'Insurance should be readonly in edit mode (API limitation)');
} }
public function testReadonlyWhen5DaysAfterLateBooking(): void public function testReadonlyWhen5DaysAfterLateBooking(): void
@@ -120,7 +120,7 @@ class InsuranceMutabilityConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertTrue($result, 'Insurance should be readonly 5 days after late booking'); self::assertTrue($result, 'Insurance should be readonly 5 days after late booking');
} }
public function testReadonlyWhenTravelDatePassed(): void public function testReadonlyWhenTravelDatePassed(): void
@@ -133,24 +133,24 @@ class InsuranceMutabilityConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertTrue($result, 'Insurance should be readonly when travel date has passed'); self::assertTrue($result, 'Insurance should be readonly when travel date has passed');
} }
public function testGetDependentFieldsReturnsEmptyArray(): void public function testGetDependentFieldsReturnsEmptyArray(): void
{ {
$result = $this->condition->getDependentFields(); $result = $this->condition->getDependentFields();
$this->assertIsArray($result); self::assertIsArray($result);
$this->assertEmpty($result, 'Insurance mutability has no field dependencies'); self::assertEmpty($result, 'Insurance mutability has no field dependencies');
} }
public function testGetDescriptionReturnsString(): void public function testGetDescriptionReturnsString(): void
{ {
$result = $this->condition->getDescription(); $result = $this->condition->getDescription();
$this->assertIsString($result); self::assertIsString($result);
$this->assertStringContainsString('not editable', $result); self::assertStringContainsString('not editable', $result);
$this->assertStringContainsString('edit mode', $result); self::assertStringContainsString('edit mode', $result);
} }
private function createEditDto(\DateTimeImmutable $travelDate, \DateTimeImmutable $bookingDate): BookingDto private function createEditDto(\DateTimeImmutable $travelDate, \DateTimeImmutable $bookingDate): BookingDto
@@ -25,7 +25,7 @@ class SingleRoomTypeConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertFalse($result, 'Condition should return false when no rooms are selected'); self::assertFalse($result, 'Condition should return false when no rooms are selected');
} }
public function testReturnsTrueWhenExactlyOneRoomSelected(): void public function testReturnsTrueWhenExactlyOneRoomSelected(): void
@@ -36,7 +36,7 @@ class SingleRoomTypeConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertTrue($result, 'Condition should return true when exactly one room type is selected'); self::assertTrue($result, 'Condition should return true when exactly one room type is selected');
} }
public function testReturnsFalseWhenTwoRoomsSelected(): void public function testReturnsFalseWhenTwoRoomsSelected(): void
@@ -48,7 +48,7 @@ class SingleRoomTypeConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertFalse($result, 'Condition should return false when two room types are selected'); self::assertFalse($result, 'Condition should return false when two room types are selected');
} }
public function testReturnsFalseWhenMultipleRoomsSelected(): void public function testReturnsFalseWhenMultipleRoomsSelected(): void
@@ -61,7 +61,7 @@ class SingleRoomTypeConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertFalse($result, 'Condition should return false when multiple room types are selected'); self::assertFalse($result, 'Condition should return false when multiple room types are selected');
} }
public function testIgnoresRoomsWithZeroQuantity(): void public function testIgnoresRoomsWithZeroQuantity(): void
@@ -74,7 +74,7 @@ class SingleRoomTypeConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertTrue($result, 'Condition should ignore rooms with zero quantity'); self::assertTrue($result, 'Condition should ignore rooms with zero quantity');
} }
public function testIgnoresRoomsWithNullQuantity(): void public function testIgnoresRoomsWithNullQuantity(): void
@@ -86,7 +86,7 @@ class SingleRoomTypeConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertTrue($result, 'Condition should ignore rooms with null quantity'); self::assertTrue($result, 'Condition should ignore rooms with null quantity');
} }
public function testReturnsFalseWhenAllRoomsHaveZeroQuantity(): void public function testReturnsFalseWhenAllRoomsHaveZeroQuantity(): void
@@ -98,24 +98,24 @@ class SingleRoomTypeConditionTest extends TestCase
$result = $this->condition->evaluate($bookingDto, 0, []); $result = $this->condition->evaluate($bookingDto, 0, []);
$this->assertFalse($result, 'Condition should return false when all rooms have zero quantity'); self::assertFalse($result, 'Condition should return false when all rooms have zero quantity');
} }
public function testGetDependentFieldsReturnsEmptyArray(): void public function testGetDependentFieldsReturnsEmptyArray(): void
{ {
$result = $this->condition->getDependentFields(); $result = $this->condition->getDependentFields();
$this->assertIsArray($result); self::assertIsArray($result);
$this->assertEmpty($result, 'Single room type condition has no participant-level field dependencies'); self::assertEmpty($result, 'Single room type condition has no participant-level field dependencies');
} }
public function testGetDescriptionReturnsString(): void public function testGetDescriptionReturnsString(): void
{ {
$result = $this->condition->getDescription(); $result = $this->condition->getDescription();
$this->assertIsString($result); self::assertIsString($result);
$this->assertStringContainsString('readonly', $result); self::assertStringContainsString('readonly', $result);
$this->assertStringContainsString('one room type', $result); self::assertStringContainsString('one room type', $result);
} }
/** /**
@@ -22,24 +22,24 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
public function testGetFieldName(): void public function testGetFieldName(): void
{ {
$this->assertSame('assignedRoomId', $this->handler->getFieldName()); self::assertSame('assignedRoomId', $this->handler->getFieldName());
} }
public function testGetDependencies(): void public function testGetDependencies(): void
{ {
$this->assertSame([], $this->handler->getDependencies()); self::assertSame([], $this->handler->getDependencies());
} }
public function testShouldProcessReturnsTrueWhenFieldPresent(): void public function testShouldProcessReturnsTrueWhenFieldPresent(): void
{ {
$this->assertTrue($this->handler->shouldProcess(['assignedRoomId' => '1'], BookingDto::MODE_CREATE, 0)); self::assertTrue($this->handler->shouldProcess(['assignedRoomId' => '1'], BookingDto::MODE_CREATE, 0));
$this->assertTrue($this->handler->shouldProcess(['assignedRoomId' => ''], BookingDto::MODE_EDIT, 5)); self::assertTrue($this->handler->shouldProcess(['assignedRoomId' => ''], BookingDto::MODE_EDIT, 5));
} }
public function testShouldProcessReturnsFalseWhenFieldNotPresent(): void public function testShouldProcessReturnsFalseWhenFieldNotPresent(): void
{ {
$this->assertFalse($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0)); self::assertFalse($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0));
$this->assertFalse($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5)); self::assertFalse($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5));
} }
public function testProcessFieldWithoutParticipant(): void public function testProcessFieldWithoutParticipant(): void
@@ -67,7 +67,7 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertSame(1, $participant->assignedRoomId); self::assertSame(1, $participant->assignedRoomId);
} }
public function testProcessFieldUnassignsParticipantWhenRoomIdIsEmpty(): void public function testProcessFieldUnassignsParticipantWhenRoomIdIsEmpty(): void
@@ -83,7 +83,7 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->assignedRoomId); self::assertNull($participant->assignedRoomId);
} }
public function testProcessFieldUnassignsParticipantWhenRoomIdIsNull(): void public function testProcessFieldUnassignsParticipantWhenRoomIdIsNull(): void
@@ -99,7 +99,7 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->assignedRoomId); self::assertNull($participant->assignedRoomId);
} }
public function testProcessFieldUnassignsHighestIndexParticipantWhenRoomAtCapacity(): void public function testProcessFieldUnassignsHighestIndexParticipantWhenRoomAtCapacity(): void
@@ -125,9 +125,9 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 2); $this->handler->processField($submittedData, $bookingDto, 2);
// Participant 1 (highest index with room 1) should be unassigned // Participant 1 (highest index with room 1) should be unassigned
$this->assertSame(1, $participant0->assignedRoomId); // Still assigned (applicant protection) self::assertSame(1, $participant0->assignedRoomId); // Still assigned (applicant protection)
$this->assertNull($participant1->assignedRoomId); // Unassigned self::assertNull($participant1->assignedRoomId); // Unassigned
$this->assertSame(1, $participant2->assignedRoomId); // Newly assigned self::assertSame(1, $participant2->assignedRoomId); // Newly assigned
} }
public function testProcessFieldGeneratesWarningNotificationForUnassignedParticipant(): void public function testProcessFieldGeneratesWarningNotificationForUnassignedParticipant(): void
@@ -151,13 +151,13 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
// Participant 1 should have a warning notification // Participant 1 should have a warning notification
$notifications = $participant1->notifications; $notifications = $participant1->notifications;
$this->assertCount(1, $notifications); self::assertCount(1, $notifications);
// Get first notification (notifications are keyed by hash, not numeric index) // Get first notification (notifications are keyed by hash, not numeric index)
$firstNotification = reset($notifications); $firstNotification = reset($notifications);
$this->assertSame('warning', $firstNotification['type']); self::assertSame('warning', $firstNotification['type']);
$this->assertStringContainsString('Doppelzimmer', $firstNotification['message']); self::assertStringContainsString('Doppelzimmer', $firstNotification['message']);
$this->assertStringContainsString('wurde von', $firstNotification['message']); self::assertStringContainsString('wurde von', $firstNotification['message']);
$this->assertStringContainsString('entfernt', $firstNotification['message']); self::assertStringContainsString('entfernt', $firstNotification['message']);
} }
public function testProcessFieldDoesNotGenerateNotificationForAssignedParticipant(): void public function testProcessFieldDoesNotGenerateNotificationForAssignedParticipant(): void
@@ -180,7 +180,7 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 2); $this->handler->processField($submittedData, $bookingDto, 2);
// Participant 2 (the one being assigned) should NOT have a notification (user-initiated action) // Participant 2 (the one being assigned) should NOT have a notification (user-initiated action)
$this->assertEmpty($participant2->notifications); self::assertEmpty($participant2->notifications);
} }
public function testProcessFieldHandlesRoomChangeWithoutConflict(): void public function testProcessFieldHandlesRoomChangeWithoutConflict(): void
@@ -203,9 +203,9 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Both should remain assigned (participant 0's own slot was freed, so no conflict) // Both should remain assigned (participant 0's own slot was freed, so no conflict)
$this->assertSame(1, $participant0->assignedRoomId); self::assertSame(1, $participant0->assignedRoomId);
$this->assertSame(1, $participant1->assignedRoomId); self::assertSame(1, $participant1->assignedRoomId);
$this->assertEmpty($participant1->notifications); self::assertEmpty($participant1->notifications);
} }
public function testProcessFieldUnassignsMultipleParticipantsWhenNeeded(): void public function testProcessFieldUnassignsMultipleParticipantsWhenNeeded(): void
@@ -234,10 +234,10 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
// All three participants with room 1 should be unassigned (highest indices first: 2, 1, 0) // All three participants with room 1 should be unassigned (highest indices first: 2, 1, 0)
// But we only need to unassign enough to make space (3 participants, only 1 space needed) // But we only need to unassign enough to make space (3 participants, only 1 space needed)
$this->assertNull($participant2->assignedRoomId); // Highest index, unassigned first self::assertNull($participant2->assignedRoomId); // Highest index, unassigned first
$this->assertNull($participant1->assignedRoomId); // Second highest, unassigned second self::assertNull($participant1->assignedRoomId); // Second highest, unassigned second
$this->assertNull($participant0->assignedRoomId); // Third highest, unassigned third self::assertNull($participant0->assignedRoomId); // Third highest, unassigned third
$this->assertSame(1, $participant3->assignedRoomId); // Newly assigned self::assertSame(1, $participant3->assignedRoomId); // Newly assigned
} }
public function testProcessFieldHandlesNoConflictWithMultipleRoomQuantities(): void public function testProcessFieldHandlesNoConflictWithMultipleRoomQuantities(): void
@@ -265,13 +265,13 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 3); $this->handler->processField($submittedData, $bookingDto, 3);
// All should remain assigned (4 beds available, 4 participants) // All should remain assigned (4 beds available, 4 participants)
$this->assertSame(1, $participant0->assignedRoomId); self::assertSame(1, $participant0->assignedRoomId);
$this->assertSame(1, $participant1->assignedRoomId); self::assertSame(1, $participant1->assignedRoomId);
$this->assertSame(1, $participant2->assignedRoomId); self::assertSame(1, $participant2->assignedRoomId);
$this->assertSame(1, $participant3->assignedRoomId); self::assertSame(1, $participant3->assignedRoomId);
$this->assertEmpty($participant0->notifications); self::assertEmpty($participant0->notifications);
$this->assertEmpty($participant1->notifications); self::assertEmpty($participant1->notifications);
$this->assertEmpty($participant2->notifications); self::assertEmpty($participant2->notifications);
} }
public function testProcessFieldHandlesComplexScenarioWithMultipleRoomTypes(): void public function testProcessFieldHandlesComplexScenarioWithMultipleRoomTypes(): void
@@ -301,10 +301,10 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 3); $this->handler->processField($submittedData, $bookingDto, 3);
// Participant 1 (highest index with room 1) should be unassigned // Participant 1 (highest index with room 1) should be unassigned
$this->assertSame(1, $participant0->assignedRoomId); self::assertSame(1, $participant0->assignedRoomId);
$this->assertNull($participant1->assignedRoomId); // Unassigned self::assertNull($participant1->assignedRoomId); // Unassigned
$this->assertSame(2, $participant2->assignedRoomId); // Still in Einzelzimmer self::assertSame(2, $participant2->assignedRoomId); // Still in Einzelzimmer
$this->assertSame(1, $participant3->assignedRoomId); // Newly assigned to Doppelzimmer self::assertSame(1, $participant3->assignedRoomId); // Newly assigned to Doppelzimmer
} }
public function testProcessFieldHandlesEdgeCaseWhenRoomNotFoundInSelections(): void public function testProcessFieldHandlesEdgeCaseWhenRoomNotFoundInSelections(): void
@@ -322,7 +322,7 @@ class ParticipantAssignedRoomFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Should still assign (no conflict detection possible, but assignment proceeds) // Should still assign (no conflict detection possible, but assignment proceeds)
$this->assertSame(999, $participant->assignedRoomId); self::assertSame(999, $participant->assignedRoomId);
} }
/** /**
@@ -24,12 +24,12 @@ class ParticipantDropOffFieldHandlerTest extends TestCase
public function testGetFieldName(): void public function testGetFieldName(): void
{ {
$this->assertSame('dropOff', $this->handler->getFieldName()); self::assertSame('dropOff', $this->handler->getFieldName());
} }
public function testGetDependencies(): void public function testGetDependencies(): void
{ {
$this->assertSame( self::assertSame(
['transportationOutbound', 'transportationInbound', 'pickup'], ['transportationOutbound', 'transportationInbound', 'pickup'],
$this->handler->getDependencies() $this->handler->getDependencies()
); );
@@ -37,8 +37,8 @@ class ParticipantDropOffFieldHandlerTest extends TestCase
public function testShouldProcessAlwaysReturnsTrue(): void public function testShouldProcessAlwaysReturnsTrue(): void
{ {
$this->assertTrue($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0)); self::assertTrue($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0));
$this->assertTrue($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5)); self::assertTrue($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5));
} }
public function testProcessFieldWithoutParticipant(): void public function testProcessFieldWithoutParticipant(): void
@@ -69,9 +69,9 @@ class ParticipantDropOffFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$participant = $bookingDto->getParticipant(0); $participant = $bookingDto->getParticipant(0);
$this->assertNotNull($participant->dropOff); self::assertNotNull($participant->dropOff);
$this->assertSame(10, $participant->dropOff->id); self::assertSame(10, $participant->dropOff->id);
$this->assertTrue($participant->differentDropOff); self::assertTrue($participant->differentDropOff);
} }
public function testBusBusCheckboxUncheckedAssignsMatchingDropOff(): void public function testBusBusCheckboxUncheckedAssignsMatchingDropOff(): void
@@ -98,9 +98,9 @@ class ParticipantDropOffFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNotNull($participant->dropOff); self::assertNotNull($participant->dropOff);
$this->assertSame(10, $participant->dropOff->id); self::assertSame(10, $participant->dropOff->id);
$this->assertFalse($participant->differentDropOff); self::assertFalse($participant->differentDropOff);
} }
public function testBusBusCheckboxUncheckedNoPickupKeepsDropOffNull(): void public function testBusBusCheckboxUncheckedNoPickupKeepsDropOffNull(): void
@@ -121,8 +121,8 @@ class ParticipantDropOffFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->dropOff); self::assertNull($participant->dropOff);
$this->assertFalse($participant->differentDropOff); self::assertFalse($participant->differentDropOff);
} }
public function testBusBusCheckboxUncheckedPickupWithNoMatchingDropOff(): void public function testBusBusCheckboxUncheckedPickupWithNoMatchingDropOff(): void
@@ -147,8 +147,8 @@ class ParticipantDropOffFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->dropOff); self::assertNull($participant->dropOff);
$this->assertFalse($participant->differentDropOff); self::assertFalse($participant->differentDropOff);
} }
public function testPkwBusValidSelection(): void public function testPkwBusValidSelection(): void
@@ -167,9 +167,9 @@ class ParticipantDropOffFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$participant = $bookingDto->getParticipant(0); $participant = $bookingDto->getParticipant(0);
$this->assertNotNull($participant->dropOff); self::assertNotNull($participant->dropOff);
$this->assertSame(20, $participant->dropOff->id); self::assertSame(20, $participant->dropOff->id);
$this->assertFalse($participant->differentDropOff); self::assertFalse($participant->differentDropOff);
} }
public function testBusPkwClearsDropOff(): void public function testBusPkwClearsDropOff(): void
@@ -189,8 +189,8 @@ class ParticipantDropOffFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->dropOff); self::assertNull($participant->dropOff);
$this->assertFalse($participant->differentDropOff); self::assertFalse($participant->differentDropOff);
} }
public function testPkwPkwClearsDropOff(): void public function testPkwPkwClearsDropOff(): void
@@ -209,8 +209,8 @@ class ParticipantDropOffFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->dropOff); self::assertNull($participant->dropOff);
$this->assertFalse($participant->differentDropOff); self::assertFalse($participant->differentDropOff);
} }
public function testInvalidDropOffIdSetsNull(): void public function testInvalidDropOffIdSetsNull(): void
@@ -229,7 +229,7 @@ class ParticipantDropOffFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$participant = $bookingDto->getParticipant(0); $participant = $bookingDto->getParticipant(0);
$this->assertNull($participant->dropOff); self::assertNull($participant->dropOff);
} }
public function testBusBusCheckboxCheckedNoSelection(): void public function testBusBusCheckboxCheckedNoSelection(): void
@@ -248,8 +248,8 @@ class ParticipantDropOffFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$participant = $bookingDto->getParticipant(0); $participant = $bookingDto->getParticipant(0);
$this->assertNull($participant->dropOff); self::assertNull($participant->dropOff);
$this->assertTrue($participant->differentDropOff); self::assertTrue($participant->differentDropOff);
} }
private function createDropOff(int $id, string $label): Pickup private function createDropOff(int $id, string $label): Pickup
@@ -15,13 +15,15 @@ use App\Service\BookingPriceCalculator;
use App\Service\InsuranceManager; use App\Service\InsuranceManager;
use App\Service\ServiceLabelFormatter; use App\Service\ServiceLabelFormatter;
use App\Service\ServiceAvailabilityCalculator; use App\Service\ServiceAvailabilityCalculator;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
class ParticipantFieldOptionsProviderBabyTest extends TestCase class ParticipantFieldOptionsProviderBabyTest extends TestCase
{ {
private ParticipantFieldOptionsProvider $provider; private ParticipantFieldOptionsProvider $provider;
private ServiceAvailabilityCalculator $serviceAvailabilityCalculator; /** @var ServiceAvailabilityCalculator&MockObject */
private $serviceAvailabilityCalculator;
protected function setUp(): void protected function setUp(): void
{ {
@@ -57,9 +59,9 @@ class ParticipantFieldOptionsProviderBabyTest extends TestCase
$choiceIds = array_map(fn (Service $service) => $service->id, $choices); $choiceIds = array_map(fn (Service $service) => $service->id, $choices);
// Baby should see only service with age range 0-14 that includes them // Baby should see only service with age range 0-14 that includes them
$this->assertContains(1, $choiceIds, 'Service with age range 0-14 should be shown for baby'); self::assertContains(1, $choiceIds, 'Service with age range 0-14 should be shown for baby');
$this->assertNotContains(2, $choiceIds, 'Service with age range 6-14 should be hidden for baby (age < 6)'); self::assertNotContains(2, $choiceIds, 'Service with age range 6-14 should be hidden for baby (age < 6)');
$this->assertNotContains(3, $choiceIds, 'Service without age restrictions should be hidden for baby'); self::assertNotContains(3, $choiceIds, 'Service without age restrictions should be hidden for baby');
} }
public function testTwoYearOldOnlySeesServicesWithExplicitAgeRange(): void public function testTwoYearOldOnlySeesServicesWithExplicitAgeRange(): void
@@ -79,9 +81,9 @@ class ParticipantFieldOptionsProviderBabyTest extends TestCase
$choiceIds = array_map(fn (Service $service) => $service->id, $choices); $choiceIds = array_map(fn (Service $service) => $service->id, $choices);
// 2-year-old should see only service with age range that includes them // 2-year-old should see only service with age range that includes them
$this->assertContains(1, $choiceIds, 'Service with age range 0-14 should be shown for 2-year-old'); self::assertContains(1, $choiceIds, 'Service with age range 0-14 should be shown for 2-year-old');
$this->assertNotContains(2, $choiceIds, 'Service with age range 6-14 should be hidden for 2-year-old'); self::assertNotContains(2, $choiceIds, 'Service with age range 6-14 should be hidden for 2-year-old');
$this->assertNotContains(3, $choiceIds, 'Service without age restrictions should be hidden for 2-year-old'); self::assertNotContains(3, $choiceIds, 'Service without age restrictions should be hidden for 2-year-old');
} }
public function testThreeYearOldSeesAllApplicableServices(): void public function testThreeYearOldSeesAllApplicableServices(): void
@@ -101,9 +103,9 @@ class ParticipantFieldOptionsProviderBabyTest extends TestCase
$choiceIds = array_map(fn (Service $service) => $service->id, $choices); $choiceIds = array_map(fn (Service $service) => $service->id, $choices);
// 3-year-old is not a baby, should see service with age range that includes them AND no-restriction services // 3-year-old is not a baby, should see service with age range that includes them AND no-restriction services
$this->assertContains(1, $choiceIds, 'Service with age range 0-14 should be shown for 3-year-old'); self::assertContains(1, $choiceIds, 'Service with age range 0-14 should be shown for 3-year-old');
$this->assertNotContains(2, $choiceIds, 'Service with age range 6-14 should be hidden for 3-year-old (age < 6)'); self::assertNotContains(2, $choiceIds, 'Service with age range 6-14 should be hidden for 3-year-old (age < 6)');
$this->assertContains(3, $choiceIds, 'Service without age restrictions should be shown for non-baby'); self::assertContains(3, $choiceIds, 'Service without age restrictions should be shown for non-baby');
} }
public function testAdultSeesAllApplicableServices(): void public function testAdultSeesAllApplicableServices(): void
@@ -123,9 +125,9 @@ class ParticipantFieldOptionsProviderBabyTest extends TestCase
$choiceIds = array_map(fn (Service $service) => $service->id, $choices); $choiceIds = array_map(fn (Service $service) => $service->id, $choices);
// Adult should see service without age restrictions but not children services // Adult should see service without age restrictions but not children services
$this->assertNotContains(1, $choiceIds, 'Service with age range 0-14 should be hidden for adult (age > 14)'); self::assertNotContains(1, $choiceIds, 'Service with age range 0-14 should be hidden for adult (age > 14)');
$this->assertNotContains(2, $choiceIds, 'Service with age range 6-14 should be hidden for adult (age > 14)'); self::assertNotContains(2, $choiceIds, 'Service with age range 6-14 should be hidden for adult (age > 14)');
$this->assertContains(3, $choiceIds, 'Service without age restrictions should be shown for adult'); self::assertContains(3, $choiceIds, 'Service without age restrictions should be shown for adult');
} }
public function testBabyGetRegularPkwInTransportation(): void public function testBabyGetRegularPkwInTransportation(): void
@@ -145,9 +147,9 @@ class ParticipantFieldOptionsProviderBabyTest extends TestCase
$choiceIds = array_map(fn (Service $service) => $service->id, $choices); $choiceIds = array_map(fn (Service $service) => $service->id, $choices);
// Baby should see BUS and regular PKW, but NOT discounted PKW // Baby should see BUS and regular PKW, but NOT discounted PKW
$this->assertContains(1, $choiceIds, 'BUS should be shown for baby'); self::assertContains(1, $choiceIds, 'BUS should be shown for baby');
$this->assertContains(3, $choiceIds, 'Regular PKW should be shown for baby'); self::assertContains(3, $choiceIds, 'Regular PKW should be shown for baby');
$this->assertNotContains(2, $choiceIds, 'Discounted PKW should be hidden for baby'); self::assertNotContains(2, $choiceIds, 'Discounted PKW should be hidden for baby');
} }
public function testTwoYearOldGetRegularPkwInTransportation(): void public function testTwoYearOldGetRegularPkwInTransportation(): void
@@ -167,9 +169,9 @@ class ParticipantFieldOptionsProviderBabyTest extends TestCase
$choiceIds = array_map(fn (Service $service) => $service->id, $choices); $choiceIds = array_map(fn (Service $service) => $service->id, $choices);
// 2-year-old (at BABY_MAX_AGE) should see BUS and regular PKW, but NOT discounted PKW // 2-year-old (at BABY_MAX_AGE) should see BUS and regular PKW, but NOT discounted PKW
$this->assertContains(1, $choiceIds, 'BUS should be shown for 2-year-old'); self::assertContains(1, $choiceIds, 'BUS should be shown for 2-year-old');
$this->assertContains(3, $choiceIds, 'Regular PKW should be shown for 2-year-old'); self::assertContains(3, $choiceIds, 'Regular PKW should be shown for 2-year-old');
$this->assertNotContains(2, $choiceIds, 'Discounted PKW should be hidden for 2-year-old'); self::assertNotContains(2, $choiceIds, 'Discounted PKW should be hidden for 2-year-old');
} }
public function testThreeYearOldGetSmartPkwFiltering(): void public function testThreeYearOldGetSmartPkwFiltering(): void
@@ -194,9 +196,9 @@ class ParticipantFieldOptionsProviderBabyTest extends TestCase
$choiceIds = array_map(fn (Service $service) => $service->id, $choices); $choiceIds = array_map(fn (Service $service) => $service->id, $choices);
// 3-year-old is not a baby, should get smart filtering (discounted when available) // 3-year-old is not a baby, should get smart filtering (discounted when available)
$this->assertContains(1, $choiceIds, 'BUS should be shown for 3-year-old'); self::assertContains(1, $choiceIds, 'BUS should be shown for 3-year-old');
$this->assertContains(2, $choiceIds, 'Discounted PKW should be shown for 3-year-old when available'); self::assertContains(2, $choiceIds, 'Discounted PKW should be shown for 3-year-old when available');
$this->assertNotContains(3, $choiceIds, 'Regular PKW should be hidden when discounted is available'); self::assertNotContains(3, $choiceIds, 'Regular PKW should be hidden when discounted is available');
} }
public function testBabyFilteringAppliesToMultipleServiceTypes(): void public function testBabyFilteringAppliesToMultipleServiceTypes(): void
@@ -214,15 +216,15 @@ class ParticipantFieldOptionsProviderBabyTest extends TestCase
$boardOptions = $this->provider->getFieldOptions('board', $bookingDto, 0); $boardOptions = $this->provider->getFieldOptions('board', $bookingDto, 0);
$boardChoiceIds = array_map(fn (Service $service) => $service->id, $boardOptions['choices']); $boardChoiceIds = array_map(fn (Service $service) => $service->id, $boardOptions['choices']);
$this->assertContains(10, $boardChoiceIds, 'Board with age range 0-5 should be shown for baby'); self::assertContains(10, $boardChoiceIds, 'Board with age range 0-5 should be shown for baby');
$this->assertNotContains(11, $boardChoiceIds, 'Board without age range should be hidden for baby'); self::assertNotContains(11, $boardChoiceIds, 'Board without age range should be hidden for baby');
// Test courses services // Test courses services
$coursesOptions = $this->provider->getFieldOptions('courses', $bookingDto, 0); $coursesOptions = $this->provider->getFieldOptions('courses', $bookingDto, 0);
$coursesChoiceIds = array_map(fn (Service $service) => $service->id, $coursesOptions['choices']); $coursesChoiceIds = array_map(fn (Service $service) => $service->id, $coursesOptions['choices']);
$this->assertContains(20, $coursesChoiceIds, 'Course with age range 0-3 should be shown for baby'); self::assertContains(20, $coursesChoiceIds, 'Course with age range 0-3 should be shown for baby');
$this->assertNotContains(21, $coursesChoiceIds, 'Course without age range should be hidden for baby'); self::assertNotContains(21, $coursesChoiceIds, 'Course without age range should be hidden for baby');
} }
private function createTravelWithMixedServices(): Travel private function createTravelWithMixedServices(): Travel
@@ -59,9 +59,9 @@ class ParticipantFieldOptionsProviderMandatoryServiceTest extends TestCase
$choiceAttr = $options['choice_attr']; $choiceAttr = $options['choice_attr'];
$attributes = $choiceAttr($mandatoryService); $attributes = $choiceAttr($mandatoryService);
$this->assertTrue($attributes['readonly']); self::assertTrue($attributes['readonly']);
$this->assertSame('Diese Leistung ist nicht abwählbar', $attributes['data-tooltip']); self::assertSame('Diese Leistung ist nicht abwählbar', $attributes['data-tooltip']);
$this->assertArrayNotHasKey('checked', $attributes); self::assertArrayNotHasKey('checked', $attributes);
} }
public function testMandatoryServiceNotSelectedHasNoReadonlyOrChecked(): void public function testMandatoryServiceNotSelectedHasNoReadonlyOrChecked(): void
@@ -80,9 +80,9 @@ class ParticipantFieldOptionsProviderMandatoryServiceTest extends TestCase
$choiceAttr = $options['choice_attr']; $choiceAttr = $options['choice_attr'];
$attributes = $choiceAttr($mandatoryService); $attributes = $choiceAttr($mandatoryService);
$this->assertArrayNotHasKey('readonly', $attributes); self::assertArrayNotHasKey('readonly', $attributes);
$this->assertArrayNotHasKey('checked', $attributes); self::assertArrayNotHasKey('checked', $attributes);
$this->assertArrayNotHasKey('data-tooltip', $attributes); self::assertArrayNotHasKey('data-tooltip', $attributes);
} }
public function testNonMandatoryServiceUnaffected(): void public function testNonMandatoryServiceUnaffected(): void
@@ -109,8 +109,8 @@ class ParticipantFieldOptionsProviderMandatoryServiceTest extends TestCase
$choiceAttr = $options['choice_attr']; $choiceAttr = $options['choice_attr'];
$attributes = $choiceAttr($nonMandatoryService); $attributes = $choiceAttr($nonMandatoryService);
$this->assertArrayNotHasKey('readonly', $attributes); self::assertArrayNotHasKey('readonly', $attributes);
$this->assertArrayNotHasKey('checked', $attributes); self::assertArrayNotHasKey('checked', $attributes);
} }
public function testParkingLabelIncludesPrice(): void public function testParkingLabelIncludesPrice(): void
@@ -144,7 +144,7 @@ class ParticipantFieldOptionsProviderMandatoryServiceTest extends TestCase
// Do not set participant — age filtering cannot determine choices, returns empty // Do not set participant — age filtering cannot determine choices, returns empty
$options = $this->provider->getFieldOptions('additionalServices', $bookingDto, 0); $options = $this->provider->getFieldOptions('additionalServices', $bookingDto, 0);
$this->assertEmpty($options); self::assertEmpty($options);
} }
private function createMandatoryService(int $id, string $label): Service private function createMandatoryService(int $id, string $label): Service
@@ -160,6 +160,9 @@ class ParticipantFieldOptionsProviderMandatoryServiceTest extends TestCase
return $service; return $service;
} }
/**
* @param list<Service> $services
*/
private function createTravelWithAdditionalServices(array $services): Travel private function createTravelWithAdditionalServices(array $services): Travel
{ {
$travel = new Travel(); $travel = new Travel();
@@ -21,18 +21,18 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
public function testGetFieldName(): void public function testGetFieldName(): void
{ {
$this->assertSame('licensePlate', $this->handler->getFieldName()); self::assertSame('licensePlate', $this->handler->getFieldName());
} }
public function testGetDependencies(): void public function testGetDependencies(): void
{ {
$this->assertSame(['parking'], $this->handler->getDependencies()); self::assertSame(['parking'], $this->handler->getDependencies());
} }
public function testShouldProcessAlwaysReturnsTrue(): void public function testShouldProcessAlwaysReturnsTrue(): void
{ {
$this->assertTrue($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0)); self::assertTrue($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0));
$this->assertTrue($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5)); self::assertTrue($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5));
} }
public function testProcessFieldWithoutParticipant(): void public function testProcessFieldWithoutParticipant(): void
@@ -61,7 +61,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate); self::assertNull($participant->licensePlate);
} }
public function testProcessFieldSetsLicensePlateWhenParkingSelected(): void public function testProcessFieldSetsLicensePlateWhenParkingSelected(): void
@@ -77,7 +77,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertSame('AB-CD 123', $participant->licensePlate); self::assertSame('AB-CD 123', $participant->licensePlate);
} }
public function testProcessFieldHandlesEmptyLicensePlate(): void public function testProcessFieldHandlesEmptyLicensePlate(): void
@@ -93,7 +93,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate); self::assertNull($participant->licensePlate);
} }
public function testProcessFieldHandlesNullLicensePlate(): void public function testProcessFieldHandlesNullLicensePlate(): void
@@ -109,7 +109,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate); self::assertNull($participant->licensePlate);
} }
public function testProcessFieldTrimsWhitespace(): void public function testProcessFieldTrimsWhitespace(): void
@@ -125,7 +125,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertSame('AB-CD 123', $participant->licensePlate); self::assertSame('AB-CD 123', $participant->licensePlate);
} }
public function testProcessFieldHandlesWhitespaceOnlyAsEmpty(): void public function testProcessFieldHandlesWhitespaceOnlyAsEmpty(): void
@@ -141,7 +141,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate); self::assertNull($participant->licensePlate);
} }
public function testProcessFieldHandlesNonStringValues(): void public function testProcessFieldHandlesNonStringValues(): void
@@ -156,12 +156,12 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
// Test with integer value // Test with integer value
$submittedData = ['licensePlate' => 123]; $submittedData = ['licensePlate' => 123];
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate); self::assertNull($participant->licensePlate);
// Test with array value // Test with array value
$submittedData = ['licensePlate' => ['invalid']]; $submittedData = ['licensePlate' => ['invalid']];
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate); self::assertNull($participant->licensePlate);
} }
public function testProcessFieldHandlesMissingLicensePlateField(): void public function testProcessFieldHandlesMissingLicensePlateField(): void
@@ -177,7 +177,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate); self::assertNull($participant->licensePlate);
} }
public function testProcessFieldWithDifferentParticipantIndex(): void public function testProcessFieldWithDifferentParticipantIndex(): void
@@ -197,7 +197,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
// Process for participant at index 1 // Process for participant at index 1
$this->handler->processField($submittedData, $bookingDto, 1); $this->handler->processField($submittedData, $bookingDto, 1);
$this->assertNull($participant1->licensePlate); // Should not be affected self::assertNull($participant1->licensePlate); // Should not be affected
$this->assertSame('AB-CD 123', $participant2->licensePlate); // Should be set self::assertSame('AB-CD 123', $participant2->licensePlate); // Should be set
} }
} }
@@ -23,18 +23,18 @@ class ParticipantParkingFieldHandlerTest extends TestCase
public function testGetFieldName(): void public function testGetFieldName(): void
{ {
$this->assertSame('parking', $this->handler->getFieldName()); self::assertSame('parking', $this->handler->getFieldName());
} }
public function testGetDependencies(): void public function testGetDependencies(): void
{ {
$this->assertSame(['transportationOutbound', 'transportationInbound', 'dateOfBirth'], $this->handler->getDependencies()); self::assertSame(['transportationOutbound', 'transportationInbound', 'dateOfBirth'], $this->handler->getDependencies());
} }
public function testShouldProcessAlwaysReturnsTrue(): void public function testShouldProcessAlwaysReturnsTrue(): void
{ {
$this->assertTrue($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0)); self::assertTrue($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0));
$this->assertTrue($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5)); self::assertTrue($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5));
} }
/** /**
@@ -42,7 +42,7 @@ class ParticipantParkingFieldHandlerTest extends TestCase
*/ */
public function testNormalizeCheckboxValueReturnsTrueWhenChecked(mixed $value): void public function testNormalizeCheckboxValueReturnsTrueWhenChecked(mixed $value): void
{ {
$this->assertTrue($this->invokeNormalizeCheckboxValue($value)); self::assertTrue($this->invokeNormalizeCheckboxValue($value));
} }
/** /**
@@ -50,7 +50,7 @@ class ParticipantParkingFieldHandlerTest extends TestCase
*/ */
public function testNormalizeCheckboxValueReturnsFalseWhenUnchecked(mixed $value): void public function testNormalizeCheckboxValueReturnsFalseWhenUnchecked(mixed $value): void
{ {
$this->assertFalse($this->invokeNormalizeCheckboxValue($value)); self::assertFalse($this->invokeNormalizeCheckboxValue($value));
} }
public function testProcessFieldWithoutParticipant(): void public function testProcessFieldWithoutParticipant(): void
@@ -81,8 +81,8 @@ class ParticipantParkingFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking); self::assertFalse($participant->parking);
$this->assertNull($participant->parkingService); self::assertNull($participant->parkingService);
} }
public function testProcessFieldClearsParkingWhenParticipantIsUnder18(): void public function testProcessFieldClearsParkingWhenParticipantIsUnder18(): void
@@ -103,8 +103,8 @@ class ParticipantParkingFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking); self::assertFalse($participant->parking);
$this->assertNull($participant->parkingService); self::assertNull($participant->parkingService);
} }
public function testProcessFieldClearsParkingWhenParticipantIsExactly17(): void public function testProcessFieldClearsParkingWhenParticipantIsExactly17(): void
@@ -130,7 +130,7 @@ class ParticipantParkingFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking); self::assertFalse($participant->parking);
} }
public function testProcessFieldAllowsParkingForAdultWithPkw(): void public function testProcessFieldAllowsParkingForAdultWithPkw(): void
@@ -150,7 +150,7 @@ class ParticipantParkingFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertTrue($participant->parking); self::assertTrue($participant->parking);
} }
public function testProcessFieldAllowsParkingForExactly18YearOld(): void public function testProcessFieldAllowsParkingForExactly18YearOld(): void
@@ -170,7 +170,7 @@ class ParticipantParkingFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertTrue($participant->parking); self::assertTrue($participant->parking);
} }
public function testProcessFieldClearsParkingWhenNoDateOfBirth(): void public function testProcessFieldClearsParkingWhenNoDateOfBirth(): void
@@ -190,8 +190,8 @@ class ParticipantParkingFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking); self::assertFalse($participant->parking);
$this->assertNull($participant->parkingService); self::assertNull($participant->parkingService);
} }
public function testProcessFieldClearsParkingWhenBabyParticipant(): void public function testProcessFieldClearsParkingWhenBabyParticipant(): void
@@ -212,8 +212,8 @@ class ParticipantParkingFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking); self::assertFalse($participant->parking);
$this->assertNull($participant->parkingService); self::assertNull($participant->parkingService);
} }
public function testProcessFieldClearsParkingForChildParticipant(): void public function testProcessFieldClearsParkingForChildParticipant(): void
@@ -234,7 +234,7 @@ class ParticipantParkingFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking); self::assertFalse($participant->parking);
} }
public function testProcessFieldSetsParkingToFalseWhenNotSelected(): void public function testProcessFieldSetsParkingToFalseWhenNotSelected(): void
@@ -253,8 +253,8 @@ class ParticipantParkingFieldHandlerTest extends TestCase
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking); self::assertFalse($participant->parking);
$this->assertNull($participant->parkingService); self::assertNull($participant->parkingService);
} }
/** /**
@@ -11,11 +11,14 @@ use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto; use App\Form\Model\ParticipantDto;
use App\Form\Service\ParticipantTransportationDiscountReplacementFieldHandler; use App\Form\Service\ParticipantTransportationDiscountReplacementFieldHandler;
use App\Service\ServiceAvailabilityCalculator; use App\Service\ServiceAvailabilityCalculator;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestCase class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestCase
{ {
private ParticipantTransportationDiscountReplacementFieldHandler $handler; private ParticipantTransportationDiscountReplacementFieldHandler $handler;
/** @var ServiceAvailabilityCalculator&MockObject */
private ServiceAvailabilityCalculator $serviceAvailabilityCalculator; private ServiceAvailabilityCalculator $serviceAvailabilityCalculator;
protected function setUp(): void protected function setUp(): void
@@ -28,18 +31,18 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
public function testGetFieldName(): void public function testGetFieldName(): void
{ {
$this->assertSame('transportationDiscountReplacement', $this->handler->getFieldName()); self::assertSame('transportationDiscountReplacement', $this->handler->getFieldName());
} }
public function testGetDependencies(): void public function testGetDependencies(): void
{ {
$this->assertSame(['transportationOutbound', 'transportationInbound'], $this->handler->getDependencies()); self::assertSame(['transportationOutbound', 'transportationInbound'], $this->handler->getDependencies());
} }
public function testShouldProcessAlwaysReturnsTrue(): void public function testShouldProcessAlwaysReturnsTrue(): void
{ {
$this->assertTrue($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0)); self::assertTrue($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0));
$this->assertTrue($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5)); self::assertTrue($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5));
} }
public function testProcessFieldReplacesDiscountedWithRegularWhenInboundIsBus(): void public function testProcessFieldReplacesDiscountedWithRegularWhenInboundIsBus(): void
@@ -65,16 +68,16 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Assert outbound was replaced with regular PKW // Assert outbound was replaced with regular PKW
$this->assertSame($regularPkw->id, $participant->transportationOutbound->id); self::assertSame($regularPkw->id, $participant->transportationOutbound->id);
$this->assertSame($regularPkw->label, $participant->transportationOutbound->label); self::assertSame($regularPkw->label, $participant->transportationOutbound->label);
// Assert notification was generated // Assert notification was generated
$this->assertCount(1, $participant->notifications); self::assertCount(1, $participant->notifications);
$notification = reset($participant->notifications); // Get first notification (keyed by MD5 hash) $notification = reset($participant->notifications); // Get first notification (keyed by MD5 hash)
$this->assertSame('warning', $notification['type']); self::assertSame('warning', $notification['type']);
$this->assertStringContainsString('Hinfahrt automatisch angepasst', $notification['message']); self::assertStringContainsString('Hinfahrt automatisch angepasst', $notification['message']);
$this->assertStringContainsString('Selbstorganisiert (-15€ Rabatt)', $notification['message']); self::assertStringContainsString('Selbstorganisiert (-15€ Rabatt)', $notification['message']);
$this->assertStringContainsString('Selbstorganisiert', $notification['message']); self::assertStringContainsString('Selbstorganisiert', $notification['message']);
} }
public function testProcessFieldRestoresDiscountedPkwWhenInboundChangesToPkw(): void public function testProcessFieldRestoresDiscountedPkwWhenInboundChangesToPkw(): void
@@ -96,7 +99,7 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
// Mock availability: discounted PKW is available // Mock availability: discounted PKW is available
$this->serviceAvailabilityCalculator $this->serviceAvailabilityCalculator
->expects($this->once()) ->expects(self::once())
->method('isServiceUnavailable') ->method('isServiceUnavailable')
->with($discountedPkw->id, $bookingDto, 0) ->with($discountedPkw->id, $bookingDto, 0)
->willReturn(false); ->willReturn(false);
@@ -107,15 +110,15 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Assert outbound was replaced with discounted PKW // Assert outbound was replaced with discounted PKW
$this->assertSame($discountedPkw->id, $participant->transportationOutbound->id); self::assertSame($discountedPkw->id, $participant->transportationOutbound->id);
$this->assertSame($discountedPkw->label, $participant->transportationOutbound->label); self::assertSame($discountedPkw->label, $participant->transportationOutbound->label);
// Assert notification was generated // Assert notification was generated
$this->assertCount(1, $participant->notifications); self::assertCount(1, $participant->notifications);
$notification = reset($participant->notifications); // Get first notification (keyed by MD5 hash) $notification = reset($participant->notifications); // Get first notification (keyed by MD5 hash)
$this->assertSame('success', $notification['type']); self::assertSame('success', $notification['type']);
$this->assertStringContainsString('Hinfahrt automatisch angepasst', $notification['message']); self::assertStringContainsString('Hinfahrt automatisch angepasst', $notification['message']);
$this->assertStringContainsString('wieder verfügbar', $notification['message']); self::assertStringContainsString('wieder verfügbar', $notification['message']);
} }
public function testProcessFieldDoesNotRestoreDiscountWhenUnavailable(): void public function testProcessFieldDoesNotRestoreDiscountWhenUnavailable(): void
@@ -137,7 +140,7 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
// Mock availability: discounted PKW is sold out // Mock availability: discounted PKW is sold out
$this->serviceAvailabilityCalculator $this->serviceAvailabilityCalculator
->expects($this->once()) ->expects(self::once())
->method('isServiceUnavailable') ->method('isServiceUnavailable')
->with($discountedPkw->id, $bookingDto, 0) ->with($discountedPkw->id, $bookingDto, 0)
->willReturn(true); ->willReturn(true);
@@ -148,10 +151,10 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Assert outbound remains regular PKW // Assert outbound remains regular PKW
$this->assertSame($regularPkw, $participant->transportationOutbound); self::assertSame($regularPkw, $participant->transportationOutbound);
// Assert no notification was generated // Assert no notification was generated
$this->assertEmpty($participant->notifications); self::assertEmpty($participant->notifications);
} }
public function testProcessFieldNoReplacementWhenOutboundIsBus(): void public function testProcessFieldNoReplacementWhenOutboundIsBus(): void
@@ -177,10 +180,10 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Assert outbound remains BUS (no replacement) // Assert outbound remains BUS (no replacement)
$this->assertSame($busOutbound, $participant->transportationOutbound); self::assertSame($busOutbound, $participant->transportationOutbound);
// Assert no notification was generated // Assert no notification was generated
$this->assertEmpty($participant->notifications); self::assertEmpty($participant->notifications);
} }
public function testProcessFieldNoReplacementWhenOutboundIsRegularPkwAndInboundIsBus(): void public function testProcessFieldNoReplacementWhenOutboundIsRegularPkwAndInboundIsBus(): void
@@ -206,10 +209,10 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Assert outbound remains regular PKW (already correct) // Assert outbound remains regular PKW (already correct)
$this->assertSame($regularPkw, $participant->transportationOutbound); self::assertSame($regularPkw, $participant->transportationOutbound);
// Assert no notification was generated // Assert no notification was generated
$this->assertEmpty($participant->notifications); self::assertEmpty($participant->notifications);
} }
public function testProcessFieldNoReplacementWhenOutboundIsDiscountedAndInboundIsPkw(): void public function testProcessFieldNoReplacementWhenOutboundIsDiscountedAndInboundIsPkw(): void
@@ -235,10 +238,10 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Assert outbound remains discounted PKW (already optimal) // Assert outbound remains discounted PKW (already optimal)
$this->assertSame($discountedPkw, $participant->transportationOutbound); self::assertSame($discountedPkw, $participant->transportationOutbound);
// Assert no notification was generated // Assert no notification was generated
$this->assertEmpty($participant->notifications); self::assertEmpty($participant->notifications);
} }
public function testProcessFieldHandlesNullOutbound(): void public function testProcessFieldHandlesNullOutbound(): void
@@ -259,8 +262,8 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Assert no error and no notification // Assert no error and no notification
$this->assertNull($participant->transportationOutbound); self::assertNull($participant->transportationOutbound);
$this->assertEmpty($participant->notifications); self::assertEmpty($participant->notifications);
} }
public function testProcessFieldHandlesNullInbound(): void public function testProcessFieldHandlesNullInbound(): void
@@ -281,7 +284,7 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
// Mock availability: discounted PKW is available // Mock availability: discounted PKW is available
$this->serviceAvailabilityCalculator $this->serviceAvailabilityCalculator
->expects($this->once()) ->expects(self::once())
->method('isServiceUnavailable') ->method('isServiceUnavailable')
->with($discountedPkw->id, $bookingDto, 0) ->with($discountedPkw->id, $bookingDto, 0)
->willReturn(false); ->willReturn(false);
@@ -292,8 +295,8 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Assert outbound was replaced with discounted PKW // Assert outbound was replaced with discounted PKW
$this->assertSame($discountedPkw->id, $participant->transportationOutbound->id); self::assertSame($discountedPkw->id, $participant->transportationOutbound->id);
$this->assertSame($discountedPkw->label, $participant->transportationOutbound->label); self::assertSame($discountedPkw->label, $participant->transportationOutbound->label);
} }
public function testProcessFieldHandlesNullParticipant(): void public function testProcessFieldHandlesNullParticipant(): void
@@ -333,7 +336,7 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
// Availability calculator should NOT be called for babies // Availability calculator should NOT be called for babies
$this->serviceAvailabilityCalculator $this->serviceAvailabilityCalculator
->expects($this->never()) ->expects(self::never())
->method('isServiceUnavailable'); ->method('isServiceUnavailable');
$submittedData = []; $submittedData = [];
@@ -342,10 +345,10 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Assert outbound remains regular PKW (no discount restoration for babies) // Assert outbound remains regular PKW (no discount restoration for babies)
$this->assertSame($regularPkw->id, $participant->transportationOutbound->id); self::assertSame($regularPkw->id, $participant->transportationOutbound->id);
// Assert no notification was generated // Assert no notification was generated
$this->assertEmpty($participant->notifications); self::assertEmpty($participant->notifications);
} }
public function testProcessFieldSkipsDiscountRestorationForTwoYearOldParticipant(): void public function testProcessFieldSkipsDiscountRestorationForTwoYearOldParticipant(): void
@@ -369,7 +372,7 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
// Availability calculator should NOT be called for babies // Availability calculator should NOT be called for babies
$this->serviceAvailabilityCalculator $this->serviceAvailabilityCalculator
->expects($this->never()) ->expects(self::never())
->method('isServiceUnavailable'); ->method('isServiceUnavailable');
$submittedData = []; $submittedData = [];
@@ -378,10 +381,10 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Assert outbound remains regular PKW // Assert outbound remains regular PKW
$this->assertSame($regularPkw->id, $participant->transportationOutbound->id); self::assertSame($regularPkw->id, $participant->transportationOutbound->id);
// Assert no notification was generated // Assert no notification was generated
$this->assertEmpty($participant->notifications); self::assertEmpty($participant->notifications);
} }
public function testProcessFieldRestoresDiscountForThreeYearOldParticipant(): void public function testProcessFieldRestoresDiscountForThreeYearOldParticipant(): void
@@ -406,7 +409,7 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
// Mock availability: discounted PKW is available // Mock availability: discounted PKW is available
$this->serviceAvailabilityCalculator $this->serviceAvailabilityCalculator
->expects($this->once()) ->expects(self::once())
->method('isServiceUnavailable') ->method('isServiceUnavailable')
->with($discountedPkw->id, $bookingDto, 0) ->with($discountedPkw->id, $bookingDto, 0)
->willReturn(false); ->willReturn(false);
@@ -417,10 +420,10 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
$this->handler->processField($submittedData, $bookingDto, 0); $this->handler->processField($submittedData, $bookingDto, 0);
// Assert outbound was replaced with discounted PKW (3-year-old is eligible) // Assert outbound was replaced with discounted PKW (3-year-old is eligible)
$this->assertSame($discountedPkw->id, $participant->transportationOutbound->id); self::assertSame($discountedPkw->id, $participant->transportationOutbound->id);
// Assert notification was generated // Assert notification was generated
$this->assertCount(1, $participant->notifications); self::assertCount(1, $participant->notifications);
} }
private function createPkwService(int $id, string $label, float $price, string $direction = DirectionMapper::OUTBOUND_TRAVEL): Service private function createPkwService(int $id, string $label, float $price, string $direction = DirectionMapper::OUTBOUND_TRAVEL): Service
@@ -447,6 +450,9 @@ class ParticipantTransportationDiscountReplacementFieldHandlerTest extends TestC
return $service; return $service;
} }
/**
* @param array<int, Service> $services
*/
private function createTravelWithServices(array $services): Travel private function createTravelWithServices(array $services): Travel
{ {
$travel = new Travel(); $travel = new Travel();
@@ -10,6 +10,7 @@ use App\BusProNet\Model\Travel;
use App\Form\Model\BookingDto; use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto; use App\Form\Model\ParticipantDto;
use App\Form\Service\ParticipantVegFieldHandler; use App\Form\Service\ParticipantVegFieldHandler;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class ParticipantVegFieldHandlerTest extends TestCase class ParticipantVegFieldHandlerTest extends TestCase
@@ -23,22 +24,22 @@ class ParticipantVegFieldHandlerTest extends TestCase
public function testGetFieldName(): void public function testGetFieldName(): void
{ {
$this->assertEquals('veg', $this->handler->getFieldName()); self::assertEquals('veg', $this->handler->getFieldName());
} }
public function testGetDependencies(): void public function testGetDependencies(): void
{ {
$dependencies = $this->handler->getDependencies(); $dependencies = $this->handler->getDependencies();
$this->assertEquals(['dateOfBirth'], $dependencies); self::assertEquals(['dateOfBirth'], $dependencies);
} }
public function testShouldProcessAlwaysReturnsTrue(): void public function testShouldProcessAlwaysReturnsTrue(): void
{ {
// Should always process to handle deselection cases // Should always process to handle deselection cases
$this->assertTrue($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0)); self::assertTrue($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0));
$this->assertTrue($this->handler->shouldProcess([], BookingDto::MODE_EDIT, 0)); self::assertTrue($this->handler->shouldProcess([], BookingDto::MODE_EDIT, 0));
$this->assertTrue($this->handler->shouldProcess(['veg' => '123'], BookingDto::MODE_CREATE, 0)); self::assertTrue($this->handler->shouldProcess(['veg' => '123'], BookingDto::MODE_CREATE, 0));
} }
public function testProcessFieldDoesNothingWhenNoParticipant(): void public function testProcessFieldDoesNothingWhenNoParticipant(): void
@@ -63,7 +64,7 @@ class ParticipantVegFieldHandlerTest extends TestCase
$this->handler->processField(['veg' => null], $bookingDto, 0); $this->handler->processField(['veg' => null], $bookingDto, 0);
$this->assertNull($participant->veg); self::assertNull($participant->veg);
} }
public function testProcessFieldClearsVegWhenEmptyStringSelection(): void public function testProcessFieldClearsVegWhenEmptyStringSelection(): void
@@ -77,7 +78,7 @@ class ParticipantVegFieldHandlerTest extends TestCase
$this->handler->processField(['veg' => ''], $bookingDto, 0); $this->handler->processField(['veg' => ''], $bookingDto, 0);
$this->assertNull($participant->veg); self::assertNull($participant->veg);
} }
public function testProcessFieldClearsVegWhenMissingFromData(): void public function testProcessFieldClearsVegWhenMissingFromData(): void
@@ -91,7 +92,7 @@ class ParticipantVegFieldHandlerTest extends TestCase
$this->handler->processField([], $bookingDto, 0); $this->handler->processField([], $bookingDto, 0);
$this->assertNull($participant->veg); self::assertNull($participant->veg);
} }
public function testProcessFieldClearsVegWhenNotFoundInAvailableServices(): void public function testProcessFieldClearsVegWhenNotFoundInAvailableServices(): void
@@ -105,7 +106,7 @@ class ParticipantVegFieldHandlerTest extends TestCase
$this->handler->processField(['veg' => '123'], $bookingDto, 0); $this->handler->processField(['veg' => '123'], $bookingDto, 0);
$this->assertNull($participant->veg); self::assertNull($participant->veg);
} }
public function testProcessFieldSetsVegWhenAvailable(): void public function testProcessFieldSetsVegWhenAvailable(): void
@@ -119,7 +120,7 @@ class ParticipantVegFieldHandlerTest extends TestCase
$this->handler->processField(['veg' => '123'], $bookingDto, 0); $this->handler->processField(['veg' => '123'], $bookingDto, 0);
$this->assertSame($vegService, $participant->veg); self::assertSame($vegService, $participant->veg);
} }
public function testProcessFieldWorksWithStringAndIntegerIds(): void public function testProcessFieldWorksWithStringAndIntegerIds(): void
@@ -133,7 +134,7 @@ class ParticipantVegFieldHandlerTest extends TestCase
$this->handler->processField(['veg' => '123'], $bookingDto, 0); // String selection $this->handler->processField(['veg' => '123'], $bookingDto, 0); // String selection
$this->assertSame($vegService, $participant->veg); self::assertSame($vegService, $participant->veg);
} }
public function testProcessFieldHandlesEmptyServicesArray(): void public function testProcessFieldHandlesEmptyServicesArray(): void
@@ -146,7 +147,7 @@ class ParticipantVegFieldHandlerTest extends TestCase
$this->handler->processField(['veg' => '123'], $bookingDto, 0); $this->handler->processField(['veg' => '123'], $bookingDto, 0);
$this->assertNull($participant->veg); self::assertNull($participant->veg);
} }
public function testProcessFieldSelectsFromMultipleVegOptions(): void public function testProcessFieldSelectsFromMultipleVegOptions(): void
@@ -161,7 +162,7 @@ class ParticipantVegFieldHandlerTest extends TestCase
$this->handler->processField(['veg' => '2'], $bookingDto, 0); $this->handler->processField(['veg' => '2'], $bookingDto, 0);
$this->assertSame($vegan, $participant->veg); self::assertSame($vegan, $participant->veg);
} }
public function testGetFieldStateModificationsReturnsEmptyArray(): void public function testGetFieldStateModificationsReturnsEmptyArray(): void
@@ -170,16 +171,16 @@ class ParticipantVegFieldHandlerTest extends TestCase
$result = $this->handler->getFieldStateModifications([], $bookingDto, 0); $result = $this->handler->getFieldStateModifications([], $bookingDto, 0);
$this->assertIsArray($result); self::assertIsArray($result);
$this->assertEmpty($result); self::assertEmpty($result);
} }
public function testGetAffectedFieldNamesReturnsEmptyArray(): void public function testGetAffectedFieldNamesReturnsEmptyArray(): void
{ {
$result = $this->handler->getAffectedFieldNames(); $result = $this->handler->getAffectedFieldNames();
$this->assertIsArray($result); self::assertIsArray($result);
$this->assertEmpty($result); self::assertEmpty($result);
} }
private function createService(int $id, string $label = 'Test Service'): Service private function createService(int $id, string $label = 'Test Service'): Service
@@ -192,6 +193,11 @@ class ParticipantVegFieldHandlerTest extends TestCase
return $service; return $service;
} }
/**
* @param array<int, Service> $vegServices
*
* @return Travel&MockObject
*/
private function createMockTravel(array $vegServices): Travel private function createMockTravel(array $vegServices): Travel
{ {
$travel = $this->createMock(Travel::class); $travel = $this->createMock(Travel::class);
@@ -202,6 +208,9 @@ class ParticipantVegFieldHandlerTest extends TestCase
return $travel; return $travel;
} }
/**
* @return BookingDto&MockObject
*/
private function createMockBookingDto(?Travel $travel = null): BookingDto private function createMockBookingDto(?Travel $travel = null): BookingDto
{ {
$bookingDto = $this->createMock(BookingDto::class); $bookingDto = $this->createMock(BookingDto::class);
+18 -18
View File
@@ -59,7 +59,7 @@ XML;
$result = $anonymizer->anonymize($xml); $result = $anonymizer->anonymize($xml);
$document = new \DOMDocument(); $document = new \DOMDocument();
$this->assertTrue($document->loadXML($result)); self::assertTrue($document->loadXML($result));
$xpath = new \DOMXPath($document); $xpath = new \DOMXPath($document);
@@ -70,13 +70,13 @@ XML;
$participantOneMobilePhone = trim((string) $xpath->evaluate('string(//teilnehmer[@id="1"]/kommunikation/telefonmobil)')); $participantOneMobilePhone = trim((string) $xpath->evaluate('string(//teilnehmer[@id="1"]/kommunikation/telefonmobil)'));
$participantTwoName = trim((string) $xpath->evaluate('string(//teilnehmer[@id="2"]/name)')); $participantTwoName = trim((string) $xpath->evaluate('string(//teilnehmer[@id="2"]/name)'));
$this->assertNotSame('Schlegelmilch', $applicantName); self::assertNotSame('Schlegelmilch', $applicantName);
$this->assertSame($applicantName, $participantOneName); self::assertSame($applicantName, $participantOneName);
$this->assertStringEndsWith('@example.com', $participantOneEmail); self::assertStringEndsWith('@example.com', $participantOneEmail);
$this->assertNotSame('02212722760', $participantOneMobilePhone); self::assertNotSame('02212722760', $participantOneMobilePhone);
$this->assertSame($applicantMobilePhone, $participantOneMobilePhone); self::assertSame($applicantMobilePhone, $participantOneMobilePhone);
$this->assertNotSame($participantOneName, $participantTwoName); self::assertNotSame($participantOneName, $participantTwoName);
$this->assertSame(2, $anonymizer->getLastIdentityCount()); self::assertSame(2, $anonymizer->getLastIdentityCount());
} }
public function testAnonymizeReplacesKundeAttributes(): void public function testAnonymizeReplacesKundeAttributes(): void
@@ -99,15 +99,15 @@ XML;
$result = $anonymizer->anonymize($xml); $result = $anonymizer->anonymize($xml);
$document = new \DOMDocument(); $document = new \DOMDocument();
$this->assertTrue($document->loadXML($result)); self::assertTrue($document->loadXML($result));
$xpath = new \DOMXPath($document); $xpath = new \DOMXPath($document);
$firstCustomerLastName = trim((string) $xpath->evaluate('string(//kunde[@id="1"]/@name)')); $firstCustomerLastName = trim((string) $xpath->evaluate('string(//kunde[@id="1"]/@name)'));
$firstCustomerFirstName = trim((string) $xpath->evaluate('string(//kunde[@id="1"]/@vorname)')); $firstCustomerFirstName = trim((string) $xpath->evaluate('string(//kunde[@id="1"]/@vorname)'));
$this->assertNotSame('Fromme', $firstCustomerLastName); self::assertNotSame('Fromme', $firstCustomerLastName);
$this->assertNotSame('Björn', $firstCustomerFirstName); self::assertNotSame('Björn', $firstCustomerFirstName);
} }
public function testAnonymizeThrowsOnInvalidXml(): void public function testAnonymizeThrowsOnInvalidXml(): void
@@ -157,15 +157,15 @@ XML;
$result = $anonymizer->anonymize($xml); $result = $anonymizer->anonymize($xml);
$document = new \DOMDocument(); $document = new \DOMDocument();
$this->assertTrue($document->loadXML($result)); self::assertTrue($document->loadXML($result));
$xpath = new \DOMXPath($document); $xpath = new \DOMXPath($document);
$applicantName = trim((string) $xpath->evaluate('string(//anmelder/name)')); $applicantName = trim((string) $xpath->evaluate('string(//anmelder/name)'));
$participantName = trim((string) $xpath->evaluate('string(//teilnehmer[@id="1"]/name)')); $participantName = trim((string) $xpath->evaluate('string(//teilnehmer[@id="1"]/name)'));
$this->assertSame($applicantName, $participantName); self::assertSame($applicantName, $participantName);
$this->assertSame(1, $anonymizer->getLastIdentityCount()); self::assertSame(1, $anonymizer->getLastIdentityCount());
} }
public function testAnonymizeCanReuseIdentityAcrossMultipleXmlDocuments(): void public function testAnonymizeCanReuseIdentityAcrossMultipleXmlDocuments(): void
@@ -205,16 +205,16 @@ XML;
$anonymizedResponse = $anonymizer->anonymize($responseXml, false); $anonymizedResponse = $anonymizer->anonymize($responseXml, false);
$requestDocument = new \DOMDocument(); $requestDocument = new \DOMDocument();
$this->assertTrue($requestDocument->loadXML($anonymizedRequest)); self::assertTrue($requestDocument->loadXML($anonymizedRequest));
$requestXpath = new \DOMXPath($requestDocument); $requestXpath = new \DOMXPath($requestDocument);
$requestName = trim((string) $requestXpath->evaluate('string(//anmelder/name)')); $requestName = trim((string) $requestXpath->evaluate('string(//anmelder/name)'));
$responseDocument = new \DOMDocument(); $responseDocument = new \DOMDocument();
$this->assertTrue($responseDocument->loadXML($anonymizedResponse)); self::assertTrue($responseDocument->loadXML($anonymizedResponse));
$responseXpath = new \DOMXPath($responseDocument); $responseXpath = new \DOMXPath($responseDocument);
$responseName = trim((string) $responseXpath->evaluate('string(//teilnehmer[@id="1"]/name)')); $responseName = trim((string) $responseXpath->evaluate('string(//teilnehmer[@id="1"]/name)'));
$this->assertSame($requestName, $responseName); self::assertSame($requestName, $responseName);
$this->assertSame(1, $anonymizer->getLastIdentityCount()); self::assertSame(1, $anonymizer->getLastIdentityCount());
} }
} }
+15 -15
View File
@@ -24,9 +24,9 @@ class DomainConfigProviderTest extends TestCase
$config = $provider->getConfig(); $config = $provider->getConfig();
$this->assertSame('custom', $config->theme); self::assertSame('custom', $config->theme);
$this->assertSame('GTM-123', $config->gtmId); self::assertSame('GTM-123', $config->gtmId);
$this->assertSame('https://cmp.example.com', $config->cmpUrl); self::assertSame('https://cmp.example.com', $config->cmpUrl);
} }
public function testReturnsConfigForSubdomainMatch(): void public function testReturnsConfigForSubdomainMatch(): void
@@ -41,8 +41,8 @@ class DomainConfigProviderTest extends TestCase
$config = $provider->getConfig(); $config = $provider->getConfig();
$this->assertSame('custom', $config->theme); self::assertSame('custom', $config->theme);
$this->assertSame('GTM-456', $config->gtmId); self::assertSame('GTM-456', $config->gtmId);
} }
public function testReturnsDefaultConfigForUnknownDomain(): void public function testReturnsDefaultConfigForUnknownDomain(): void
@@ -57,9 +57,9 @@ class DomainConfigProviderTest extends TestCase
$config = $provider->getConfig(); $config = $provider->getConfig();
$this->assertSame(DomainConfig::DEFAULT_THEME, $config->theme); self::assertSame(DomainConfig::DEFAULT_THEME, $config->theme);
$this->assertSame('', $config->gtmId); self::assertSame('', $config->gtmId);
$this->assertSame('', $config->cmpUrl); self::assertSame('', $config->cmpUrl);
} }
public function testReturnsDefaultConfigWhenNoRequest(): void public function testReturnsDefaultConfigWhenNoRequest(): void
@@ -69,7 +69,7 @@ class DomainConfigProviderTest extends TestCase
$config = $provider->getConfig(); $config = $provider->getConfig();
$this->assertSame(DomainConfig::DEFAULT_THEME, $config->theme); self::assertSame(DomainConfig::DEFAULT_THEME, $config->theme);
} }
public function testHandlesMissingConfigValues(): void public function testHandlesMissingConfigValues(): void
@@ -82,9 +82,9 @@ class DomainConfigProviderTest extends TestCase
$config = $provider->getConfig(); $config = $provider->getConfig();
$this->assertSame('custom', $config->theme); self::assertSame('custom', $config->theme);
$this->assertSame('', $config->gtmId); self::assertSame('', $config->gtmId);
$this->assertSame('', $config->cmpUrl); self::assertSame('', $config->cmpUrl);
} }
public function testCachesConfigForSameHost(): void public function testCachesConfigForSameHost(): void
@@ -100,7 +100,7 @@ class DomainConfigProviderTest extends TestCase
$config1 = $provider->getConfigForHost('example.com'); $config1 = $provider->getConfigForHost('example.com');
$config2 = $provider->getConfigForHost('example.com'); $config2 = $provider->getConfigForHost('example.com');
$this->assertSame($config1, $config2); self::assertSame($config1, $config2);
} }
public function testFirstMatchWins(): void public function testFirstMatchWins(): void
@@ -115,7 +115,7 @@ class DomainConfigProviderTest extends TestCase
$config = $provider->getConfig(); $config = $provider->getConfig();
$this->assertSame('portal', $config->theme); self::assertSame('portal', $config->theme);
} }
public function testCaseInsensitiveMatching(): void public function testCaseInsensitiveMatching(): void
@@ -128,7 +128,7 @@ class DomainConfigProviderTest extends TestCase
$config = $provider->getConfig(); $config = $provider->getConfig();
$this->assertSame('custom', $config->theme); self::assertSame('custom', $config->theme);
} }
/** /**
+11 -11
View File
@@ -24,7 +24,7 @@ class ErrorCodeGeneratorTest extends TestCase
{ {
$result = $this->service->getErrorCode(); $result = $this->service->getErrorCode();
$this->assertNull($result); self::assertNull($result);
} }
public function testReturnsNullWithoutRequestId(): void public function testReturnsNullWithoutRequestId(): void
@@ -34,7 +34,7 @@ class ErrorCodeGeneratorTest extends TestCase
$result = $this->service->getErrorCode(); $result = $this->service->getErrorCode();
$this->assertNull($result); self::assertNull($result);
} }
public function testGeneratesCodeWithRequestId(): void public function testGeneratesCodeWithRequestId(): void
@@ -45,8 +45,8 @@ class ErrorCodeGeneratorTest extends TestCase
$result = $this->service->getErrorCode(); $result = $this->service->getErrorCode();
$this->assertNotNull($result); self::assertNotNull($result);
$this->assertMatchesRegularExpression('/^E-[A-Z0-9]{8}$/', $result); self::assertMatchesRegularExpression('/^E-[A-Z0-9]{8}$/', $result);
} }
public function testGeneratesConsistentCodeForSameRequestId(): void public function testGeneratesConsistentCodeForSameRequestId(): void
@@ -58,7 +58,7 @@ class ErrorCodeGeneratorTest extends TestCase
$result1 = $this->service->getErrorCode(); $result1 = $this->service->getErrorCode();
$result2 = $this->service->getErrorCode(); $result2 = $this->service->getErrorCode();
$this->assertSame($result1, $result2); self::assertSame($result1, $result2);
} }
public function testGeneratesDifferentCodesForDifferentRequestIds(): void public function testGeneratesDifferentCodesForDifferentRequestIds(): void
@@ -79,7 +79,7 @@ class ErrorCodeGeneratorTest extends TestCase
$service2 = new ErrorCodeGenerator($this->requestStack); $service2 = new ErrorCodeGenerator($this->requestStack);
$result2 = $service2->getErrorCode(); $result2 = $service2->getErrorCode();
$this->assertNotSame($result1, $result2); self::assertNotSame($result1, $result2);
} }
public function testSequenceSuffixesProduceSameCode(): void public function testSequenceSuffixesProduceSameCode(): void
@@ -100,7 +100,7 @@ class ErrorCodeGeneratorTest extends TestCase
$service2 = new ErrorCodeGenerator($this->requestStack); $service2 = new ErrorCodeGenerator($this->requestStack);
$result2 = $service2->getErrorCode(); $result2 = $service2->getErrorCode();
$this->assertSame($result1, $result2); self::assertSame($result1, $result2);
} }
public function testSequenceSuffixMatchesBaseId(): void public function testSequenceSuffixMatchesBaseId(): void
@@ -121,19 +121,19 @@ class ErrorCodeGeneratorTest extends TestCase
$service2 = new ErrorCodeGenerator($this->requestStack); $service2 = new ErrorCodeGenerator($this->requestStack);
$result2 = $service2->getErrorCode(); $result2 = $service2->getErrorCode();
$this->assertSame($result1, $result2); self::assertSame($result1, $result2);
} }
public function testHasErrorReturnsFalseByDefault(): void public function testHasErrorReturnsFalseByDefault(): void
{ {
$this->assertFalse($this->service->hasError()); self::assertFalse($this->service->hasError());
} }
public function testMarkErrorOccurredSetsFlag(): void public function testMarkErrorOccurredSetsFlag(): void
{ {
$this->service->markErrorOccurred(); $this->service->markErrorOccurred();
$this->assertTrue($this->service->hasError()); self::assertTrue($this->service->hasError());
} }
public function testHasErrorRemainsTrueAfterMultipleCalls(): void public function testHasErrorRemainsTrueAfterMultipleCalls(): void
@@ -141,6 +141,6 @@ class ErrorCodeGeneratorTest extends TestCase
$this->service->markErrorOccurred(); $this->service->markErrorOccurred();
$this->service->markErrorOccurred(); $this->service->markErrorOccurred();
$this->assertTrue($this->service->hasError()); self::assertTrue($this->service->hasError());
} }
} }
+60 -39
View File
@@ -42,8 +42,8 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getSelectableInsurances($travel); $result = $this->service->getSelectableInsurances($travel);
$this->assertCount(1, $result); self::assertCount(1, $result);
$this->assertSame($regularInsurance, $result[0]); self::assertSame($regularInsurance, $result[0]);
} }
public function testGetSelectableInsurancesCachesResults(): void public function testGetSelectableInsurancesCachesResults(): void
@@ -58,7 +58,7 @@ class InsuranceManagerTest extends TestCase
$result1 = $this->service->getSelectableInsurances($travel); $result1 = $this->service->getSelectableInsurances($travel);
$result2 = $this->service->getSelectableInsurances($travel); $result2 = $this->service->getSelectableInsurances($travel);
$this->assertSame($result1, $result2); self::assertSame($result1, $result2);
} }
public function testGetEligibleInsurancesWithMatchingCriteria(): void public function testGetEligibleInsurancesWithMatchingCriteria(): void
@@ -81,8 +81,8 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertCount(1, $result); self::assertCount(1, $result);
$this->assertContains($insurance, $result); self::assertContains($insurance, $result);
} }
public function testGetEligibleInsurancesExcludesInsuranceWithAgeTooYoung(): void public function testGetEligibleInsurancesExcludesInsuranceWithAgeTooYoung(): void
@@ -94,7 +94,7 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertEmpty($result); self::assertEmpty($result);
} }
public function testGetEligibleInsurancesExcludesInsuranceWithAgeTooOld(): void public function testGetEligibleInsurancesExcludesInsuranceWithAgeTooOld(): void
@@ -106,7 +106,7 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertEmpty($result); self::assertEmpty($result);
} }
public function testGetEligibleInsurancesExcludesInsuranceWithTravelDateTooEarly(): void public function testGetEligibleInsurancesExcludesInsuranceWithTravelDateTooEarly(): void
@@ -121,7 +121,7 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertEmpty($result); self::assertEmpty($result);
} }
public function testGetEligibleInsurancesExcludesInsuranceWithTravelDateTooLate(): void public function testGetEligibleInsurancesExcludesInsuranceWithTravelDateTooLate(): void
@@ -136,7 +136,7 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertEmpty($result); self::assertEmpty($result);
} }
public function testGetEligibleInsurancesExcludesInsuranceWithBookingDateTooEarly(): void public function testGetEligibleInsurancesExcludesInsuranceWithBookingDateTooEarly(): void
@@ -151,7 +151,7 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertEmpty($result); self::assertEmpty($result);
} }
public function testGetEligibleInsurancesExcludesInsuranceWithBookingDateTooLate(): void public function testGetEligibleInsurancesExcludesInsuranceWithBookingDateTooLate(): void
@@ -166,7 +166,7 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertEmpty($result); self::assertEmpty($result);
} }
public function testGetEligibleInsurancesExcludesInsuranceWithTravelPriceTooLow(): void public function testGetEligibleInsurancesExcludesInsuranceWithTravelPriceTooLow(): void
@@ -179,7 +179,7 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertEmpty($result); self::assertEmpty($result);
} }
public function testGetEligibleInsurancesExcludesInsuranceWithTravelPriceTooHigh(): void public function testGetEligibleInsurancesExcludesInsuranceWithTravelPriceTooHigh(): void
@@ -192,7 +192,7 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertEmpty($result); self::assertEmpty($result);
} }
public function testGetEligibleInsurancesExcludesInsuranceWithTravelDurationTooShort(): void public function testGetEligibleInsurancesExcludesInsuranceWithTravelDurationTooShort(): void
@@ -204,7 +204,7 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertEmpty($result); self::assertEmpty($result);
} }
public function testGetEligibleInsurancesExcludesInsuranceWithTravelDurationTooLong(): void public function testGetEligibleInsurancesExcludesInsuranceWithTravelDurationTooLong(): void
@@ -216,7 +216,7 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertEmpty($result); self::assertEmpty($result);
} }
public function testGetEligibleInsurancesHandlesParticipantWithoutBirthDate(): void public function testGetEligibleInsurancesHandlesParticipantWithoutBirthDate(): void
@@ -232,9 +232,9 @@ class InsuranceManagerTest extends TestCase
// Participants without birth date should have insurances available // Participants without birth date should have insurances available
// (field visibility is controlled by field state conditions, not service logic) // (field visibility is controlled by field state conditions, not service logic)
$this->assertNotEmpty($result); self::assertNotEmpty($result);
$this->assertCount(1, $result); self::assertCount(1, $result);
$this->assertSame($insurance, $result[0]); self::assertSame($insurance, $result[0]);
} }
public function testGetEligibleInsurancesWithNullConstraints(): void public function testGetEligibleInsurancesWithNullConstraints(): void
@@ -247,8 +247,8 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertCount(1, $result); self::assertCount(1, $result);
$this->assertContains($insurance, $result); self::assertContains($insurance, $result);
} }
public function testGetEligibleInsurancesWithMultipleInsurances(): void public function testGetEligibleInsurancesWithMultipleInsurances(): void
@@ -263,10 +263,10 @@ class InsuranceManagerTest extends TestCase
$insurances = [$eligibleInsurance1, $eligibleInsurance2, $ineligibleInsurance]; $insurances = [$eligibleInsurance1, $eligibleInsurance2, $ineligibleInsurance];
$result = $this->service->getEligibleInsurances($insurances, $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances($insurances, $participant, $booking, 500.0);
$this->assertCount(2, $result); self::assertCount(2, $result);
$this->assertContains($eligibleInsurance1, $result); self::assertContains($eligibleInsurance1, $result);
$this->assertContains($eligibleInsurance2, $result); self::assertContains($eligibleInsurance2, $result);
$this->assertNotContains($ineligibleInsurance, $result); self::assertNotContains($ineligibleInsurance, $result);
} }
public function testGetEligibleInsurancesWithAgeExactlyAtBoundary(): void public function testGetEligibleInsurancesWithAgeExactlyAtBoundary(): void
@@ -278,8 +278,8 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertCount(1, $result); self::assertCount(1, $result);
$this->assertContains($insurance, $result); self::assertContains($insurance, $result);
} }
public function testGetEligibleInsurancesWithTravelDurationExactlyAtBoundary(): void public function testGetEligibleInsurancesWithTravelDurationExactlyAtBoundary(): void
@@ -291,8 +291,8 @@ class InsuranceManagerTest extends TestCase
$result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0); $result = $this->service->getEligibleInsurances([$insurance], $participant, $booking, 500.0);
$this->assertCount(1, $result); self::assertCount(1, $result);
$this->assertContains($insurance, $result); self::assertContains($insurance, $result);
} }
public function testFilterByTypeForPackages(): void public function testFilterByTypeForPackages(): void
@@ -319,9 +319,9 @@ class InsuranceManagerTest extends TestCase
$insurances = [$packageInsurance1, $packageInsurance2, $differentPackage]; $insurances = [$packageInsurance1, $packageInsurance2, $differentPackage];
$result = $this->service->filterByType($insurances, $packageInsurance1); $result = $this->service->filterByType($insurances, $packageInsurance1);
$this->assertCount(2, $result); self::assertCount(2, $result);
$this->assertContains($packageInsurance1, $result); self::assertContains($packageInsurance1, $result);
$this->assertContains($packageInsurance2, $result); self::assertContains($packageInsurance2, $result);
} }
public function testFilterByTypeForIndividualInsurances(): void public function testFilterByTypeForIndividualInsurances(): void
@@ -348,9 +348,9 @@ class InsuranceManagerTest extends TestCase
$insurances = [$insurance1, $insurance2, $differentType]; $insurances = [$insurance1, $insurance2, $differentType];
$result = $this->service->filterByType($insurances, $insurance1); $result = $this->service->filterByType($insurances, $insurance1);
$this->assertCount(2, $result); self::assertCount(2, $result);
$this->assertContains($insurance1, $result); self::assertContains($insurance1, $result);
$this->assertContains($insurance2, $result); self::assertContains($insurance2, $result);
} }
public function testReassignInsuranceForPriceChange(): void public function testReassignInsuranceForPriceChange(): void
@@ -384,7 +384,7 @@ class InsuranceManagerTest extends TestCase
500.0 500.0
); );
$this->assertSame($newTierInsurance, $result); self::assertSame($newTierInsurance, $result);
} }
public function testBatchAssignInsuranceToParticipants(): void public function testBatchAssignInsuranceToParticipants(): void
@@ -420,9 +420,9 @@ class InsuranceManagerTest extends TestCase
$participantPrices $participantPrices
); );
$this->assertCount(2, $result); self::assertCount(2, $result);
$this->assertNotNull($result[0]); self::assertNotNull($result[0]);
$this->assertNotNull($result[1]); self::assertNotNull($result[1]);
} }
private function createParticipant(string $dateOfBirth): ParticipantDto private function createParticipant(string $dateOfBirth): ParticipantDto
@@ -452,6 +452,9 @@ class InsuranceManagerTest extends TestCase
return $travel; return $travel;
} }
/**
* @param array<string, mixed> $properties
*/
private function createInsurance(array $properties = []): Insurance private function createInsurance(array $properties = []): Insurance
{ {
$insurance = new Insurance(); $insurance = new Insurance();
@@ -462,7 +465,25 @@ class InsuranceManagerTest extends TestCase
$insurance->familyInsurance = false; $insurance->familyInsurance = false;
foreach ($properties as $property => $value) { foreach ($properties as $property => $value) {
$insurance->$property = $value; match ($property) {
'id' => $insurance->id = (string) $value,
'complementary' => $insurance->complementary = (bool) $value,
'package' => $insurance->package = (bool) $value,
'familyInsurance' => $insurance->familyInsurance = (bool) $value,
'label' => $insurance->label = (string) $value,
'subType' => $insurance->subType = null === $value ? null : (string) $value,
'travelDateFrom' => $insurance->travelDateFrom = $value,
'travelDateTo' => $insurance->travelDateTo = $value,
'bookingDateFrom' => $insurance->bookingDateFrom = $value,
'bookingDateTo' => $insurance->bookingDateTo = $value,
'travelPriceFrom' => $insurance->travelPriceFrom = null === $value ? null : (float) $value,
'travelPriceTo' => $insurance->travelPriceTo = null === $value ? null : (float) $value,
'travelDurationFrom' => $insurance->travelDurationFrom = null === $value ? null : (int) $value,
'travelDurationTo' => $insurance->travelDurationTo = null === $value ? null : (int) $value,
'ageFrom' => $insurance->ageFrom = null === $value ? null : (int) $value,
'ageTo' => $insurance->ageTo = null === $value ? null : (int) $value,
default => null,
};
} }
return $insurance; return $insurance;
@@ -34,7 +34,7 @@ class ParticipantEligibilityCheckerTest extends TestCase
$result = $this->service->isParticipantEligible($bookingDto, 0); $result = $this->service->isParticipantEligible($bookingDto, 0);
$this->assertTrue($result, 'Baby participant (1 year old) should be eligible regardless of ski pass availability'); self::assertTrue($result, 'Baby participant (1 year old) should be eligible regardless of ski pass availability');
} }
public function testTwoYearOldParticipantIsEligibleAsBaby(): void public function testTwoYearOldParticipantIsEligibleAsBaby(): void
@@ -50,7 +50,7 @@ class ParticipantEligibilityCheckerTest extends TestCase
$result = $this->service->isParticipantEligible($bookingDto, 0); $result = $this->service->isParticipantEligible($bookingDto, 0);
$this->assertTrue($result, 'Participant at BABY_MAX_AGE (2 years) should be eligible'); self::assertTrue($result, 'Participant at BABY_MAX_AGE (2 years) should be eligible');
} }
public function testThreeYearOldParticipantIsNotBabyEligible(): void public function testThreeYearOldParticipantIsNotBabyEligible(): void
@@ -66,7 +66,7 @@ class ParticipantEligibilityCheckerTest extends TestCase
$result = $this->service->isParticipantEligible($bookingDto, 0); $result = $this->service->isParticipantEligible($bookingDto, 0);
$this->assertFalse($result, 'Participant over BABY_MAX_AGE (3 years) should not be eligible without ski passes'); self::assertFalse($result, 'Participant over BABY_MAX_AGE (3 years) should not be eligible without ski passes');
} }
public function testAdultParticipantWithSkiPassIsEligible(): void public function testAdultParticipantWithSkiPassIsEligible(): void
@@ -82,7 +82,7 @@ class ParticipantEligibilityCheckerTest extends TestCase
$result = $this->service->isParticipantEligible($bookingDto, 0); $result = $this->service->isParticipantEligible($bookingDto, 0);
$this->assertTrue($result, 'Adult participant with available ski passes should be eligible'); self::assertTrue($result, 'Adult participant with available ski passes should be eligible');
} }
public function testParticipantWithoutDateOfBirthIsNotEligible(): void public function testParticipantWithoutDateOfBirthIsNotEligible(): void
@@ -98,7 +98,7 @@ class ParticipantEligibilityCheckerTest extends TestCase
$result = $this->service->isParticipantEligible($bookingDto, 0); $result = $this->service->isParticipantEligible($bookingDto, 0);
$this->assertFalse($result, 'Participant without date of birth should not be eligible'); self::assertFalse($result, 'Participant without date of birth should not be eligible');
} }
public function testNewbornBabyIsEligible(): void public function testNewbornBabyIsEligible(): void
@@ -114,7 +114,7 @@ class ParticipantEligibilityCheckerTest extends TestCase
$result = $this->service->isParticipantEligible($bookingDto, 0); $result = $this->service->isParticipantEligible($bookingDto, 0);
$this->assertTrue($result, 'Newborn baby (0 years old) should be eligible'); self::assertTrue($result, 'Newborn baby (0 years old) should be eligible');
} }
public function testBabyAgeIsCalculatedAtTravelDate(): void public function testBabyAgeIsCalculatedAtTravelDate(): void
@@ -135,7 +135,7 @@ class ParticipantEligibilityCheckerTest extends TestCase
$result = $this->service->isParticipantEligible($bookingDto, 0); $result = $this->service->isParticipantEligible($bookingDto, 0);
$this->assertTrue($result, 'Age should be calculated at travel date, not current date'); self::assertTrue($result, 'Age should be calculated at travel date, not current date');
} }
private function createTravelWithNoSkiPasses(): Travel private function createTravelWithNoSkiPasses(): Travel
Loaded 100 of 103 files, more files were not shown because too many files have changed in this diff. Show more