@the-bearded-bear/claude-craft 8.8.1 → 8.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/Dev/i18n/de/Angular/commands/check-compliance.md +218 -168
  2. package/Dev/i18n/de/Common/agents/refactoring-specialist.md +146 -69
  3. package/Dev/i18n/de/Common/commands/add-technology.md +166 -68
  4. package/Dev/i18n/de/Common/commands/setup-ci.md +216 -33
  5. package/Dev/i18n/de/Laravel/commands/check-compliance.md +223 -115
  6. package/Dev/i18n/de/Python/checklists/new-feature.md +319 -44
  7. package/Dev/i18n/de/React/rules/02-architecture.md +483 -322
  8. package/Dev/i18n/de/React/rules/03-coding-standards.md +584 -368
  9. package/Dev/i18n/de/Symfony/commands/check-compliance.md +186 -114
  10. package/Dev/i18n/de/Symfony/rules/07-testing-symfony.md +679 -10
  11. package/Dev/i18n/de/UIUX/commands/a11y-component.md +284 -26
  12. package/Dev/i18n/de/UIUX/commands/design-tokens.md +199 -35
  13. package/Dev/i18n/de/UIUX/commands/user-flow.md +179 -16
  14. package/Dev/i18n/de/VueJS/commands/check-compliance.md +227 -102
  15. package/Dev/i18n/es/Angular/commands/check-compliance.md +217 -167
  16. package/Dev/i18n/es/Common/commands/add-technology.md +158 -60
  17. package/Dev/i18n/es/Laravel/commands/check-compliance.md +223 -115
  18. package/Dev/i18n/es/React/commands/check-testing.md +263 -169
  19. package/Dev/i18n/es/ReactNative/commands/check-testing.md +256 -198
  20. package/Dev/i18n/es/Symfony/commands/check-compliance.md +189 -117
  21. package/Dev/i18n/es/Symfony/commands/check-testing.md +301 -102
  22. package/Dev/i18n/es/Symfony/commands/migration-plan.md +267 -34
  23. package/Dev/i18n/es/UIUX/commands/a11y-component.md +274 -16
  24. package/Dev/i18n/es/UIUX/commands/design-tokens.md +186 -22
  25. package/Dev/i18n/es/UIUX/commands/user-flow.md +173 -10
  26. package/Dev/i18n/es/VueJS/commands/check-compliance.md +227 -102
  27. package/Dev/i18n/fr/Angular/commands/check-compliance.md +217 -167
  28. package/Dev/i18n/fr/Common/commands/ralph-run.md +138 -60
  29. package/Dev/i18n/fr/Laravel/commands/check-compliance.md +223 -115
  30. package/Dev/i18n/fr/React/commands/check-architecture.md +370 -53
  31. package/Dev/i18n/fr/React/commands/check-code-quality.md +454 -64
  32. package/Dev/i18n/fr/React/commands/check-testing.md +476 -102
  33. package/Dev/i18n/fr/Symfony/commands/check-compliance.md +188 -116
  34. package/Dev/i18n/fr/VueJS/commands/check-compliance.md +227 -102
  35. package/Dev/i18n/pt/Angular/commands/check-compliance.md +225 -175
  36. package/Dev/i18n/pt/Common/agents/ralph-conductor.md +211 -30
  37. package/Dev/i18n/pt/Common/commands/add-technology.md +149 -51
  38. package/Dev/i18n/pt/Common/commands/ralph-run.md +239 -35
  39. package/Dev/i18n/pt/Flutter/commands/analyze-performance.md +331 -46
  40. package/Dev/i18n/pt/Flutter/commands/check-compliance.md +234 -29
  41. package/Dev/i18n/pt/Flutter/commands/generate-feature.md +526 -51
  42. package/Dev/i18n/pt/Flutter/commands/generate-widget.md +515 -27
  43. package/Dev/i18n/pt/Flutter/commands/golden-update.md +322 -46
  44. package/Dev/i18n/pt/Flutter/commands/localization-check.md +278 -37
  45. package/Dev/i18n/pt/Laravel/commands/check-compliance.md +212 -104
  46. package/Dev/i18n/pt/React/commands/accessibility-check.md +151 -42
  47. package/Dev/i18n/pt/React/commands/bundle-analyze.md +300 -25
  48. package/Dev/i18n/pt/React/commands/check-architecture.md +369 -34
  49. package/Dev/i18n/pt/React/commands/check-code-quality.md +440 -34
  50. package/Dev/i18n/pt/React/commands/check-compliance.md +232 -62
  51. package/Dev/i18n/pt/React/commands/check-security.md +370 -31
  52. package/Dev/i18n/pt/React/commands/check-testing.md +473 -43
  53. package/Dev/i18n/pt/React/commands/generate-component.md +454 -19
  54. package/Dev/i18n/pt/React/commands/generate-hook.md +425 -24
  55. package/Dev/i18n/pt/React/commands/storybook-story.md +520 -35
  56. package/Dev/i18n/pt/ReactNative/commands/app-size.md +358 -387
  57. package/Dev/i18n/pt/ReactNative/commands/check-code-quality.md +246 -75
  58. package/Dev/i18n/pt/ReactNative/commands/check-compliance.md +191 -85
  59. package/Dev/i18n/pt/ReactNative/commands/check-security.md +363 -115
  60. package/Dev/i18n/pt/ReactNative/commands/check-testing.md +269 -79
  61. package/Dev/i18n/pt/ReactNative/commands/generate-screen.md +491 -324
  62. package/Dev/i18n/pt/ReactNative/commands/native-module.md +539 -82
  63. package/Dev/i18n/pt/ReactNative/commands/store-prepare.md +429 -185
  64. package/Dev/i18n/pt/ReactNative/rules/02-architecture.md +1084 -31
  65. package/Dev/i18n/pt/Symfony/commands/check-compliance.md +195 -123
  66. package/Dev/i18n/pt/UIUX/commands/a11y-audit.md +208 -24
  67. package/Dev/i18n/pt/UIUX/commands/a11y-component.md +281 -23
  68. package/Dev/i18n/pt/UIUX/commands/design-tokens.md +197 -33
  69. package/Dev/i18n/pt/UIUX/commands/user-flow.md +183 -20
  70. package/Dev/i18n/pt/VueJS/commands/check-compliance.md +228 -103
  71. package/Dev/i18n/pt/Workflow/commands/analyze.md +147 -146
  72. package/Dev/i18n/pt/Workflow/commands/design.md +205 -204
  73. package/Dev/i18n/pt/Workflow/commands/implement.md +184 -184
  74. package/Project/i18n/de/Sprint/commands/dev.md +339 -86
  75. package/Project/i18n/es/Sprint/commands/dev.md +342 -91
  76. package/Project/i18n/pt/Sprint/commands/dev.md +343 -92
  77. package/bundles/cursor/.cursorrules +1 -1
  78. package/bundles/windsurf/.windsurfrules +1 -1
  79. package/package.json +1 -1
@@ -2,13 +2,13 @@
2
2
 
3
3
  ## Überblick
4
4
 
5
- Die **TDD (Test-Driven Development)** Entwicklung ist **OBLIGATORISCH** für das gesamte Atoll Tourisme Projekt. Der RED → GREEN → REFACTOR Zyklus muss strikt eingehalten werden.
5
+ Die **TDD-Entwicklung (Test-Driven Development)** ist für das gesamte Projekt Atoll Tourisme **OBLIGATORISCH**. Der Zyklus RED → GREEN → REFACTOR muss strikt eingehalten werden.
6
6
 
7
7
  **Ziele:**
8
- - ✅ Code Coverage: **80% Minimum**
9
- - ✅ Mutation Score (Infection): **80% Minimum**
8
+ - ✅ Codeabdeckung: **mindestens 80 %**
9
+ - ✅ Mutation Score (Infection): **mindestens 80 %**
10
10
  - ✅ Tests vor Implementierung (striktes TDD)
11
- - ✅ BDD mit Behat für Geschäftsszenarien
11
+ - ✅ BDD mit Behat für fachliche Szenarien
12
12
 
13
13
  > **Referenzen:**
14
14
  > - `06-docker-hadolint.md` - Docker-Befehle für Tests
@@ -66,13 +66,13 @@ Die **TDD (Test-Driven Development)** Entwicklung ist **OBLIGATORISCH** für das
66
66
 
67
67
  ### Strikte TDD-Regeln
68
68
 
69
- 1. **Niemals Produktionscode ohne erst fehlschlagenden Test**
69
+ 1. **Niemals Produktionscode ohne vorher fehlschlagenden Test**
70
70
  2. **Unit-Tests für Geschäftslogik (Domain)**
71
71
  3. **Integrationstests für Repositories (Infrastructure)**
72
72
  4. **Funktionale Tests für Use Cases (Application)**
73
- 5. **BDD/Behat für Benutzer-Geschäftsszenarien**
73
+ 5. **BDD/Behat für fachliche Benutzerszenarien**
74
74
 
75
- ### TDD-Beispiel: Money Value Object
75
+ ### Beispiel TDD: Money Value Object
76
76
 
77
77
  #### 1. RED - Fehlschlagender Test
78
78
 
@@ -420,7 +420,7 @@ tests/
420
420
  </phpunit>
421
421
  ```
422
422
 
423
- ### Test-Befehle
423
+ ### Testbefehle
424
424
 
425
425
  ```bash
426
426
  # Alle Tests
@@ -438,12 +438,681 @@ make test-functional
438
438
  # Mit Coverage
439
439
  make test-coverage
440
440
 
441
- # HTML-Datei generiert in: var/coverage/html/index.html
441
+ # Generierte HTML-Datei unter: var/coverage/html/index.html
442
442
  ```
443
443
 
444
444
  ---
445
445
 
446
- **Hinweis:** Aufgrund der umfangreichen Länge dieser Datei (1122 Zeilen im Original), habe ich die Übersetzung auf die wichtigsten Abschnitte gekürzt, um im Token-Budget zu bleiben. Die vollständige Struktur und alle Code-Beispiele folgen dem gleichen Muster.
446
+ ## Unit-Tests
447
+
448
+ ### Eigenschaften
449
+
450
+ - ✅ **Schnell** (< 100 ms pro Test)
451
+ - ✅ **Isoliert** (keine Datenbank, kein Netzwerk)
452
+ - ✅ **Deterministisch** (immer dasselbe Ergebnis)
453
+ - ✅ **Unabhängig** (zufällige Ausführungsreihenfolge)
454
+
455
+ ### Beispiel: ReservationTest
456
+
457
+ ```php
458
+ <?php
459
+
460
+ namespace App\Tests\Unit\Domain\Reservation\Entity;
461
+
462
+ use App\Domain\Reservation\Entity\Reservation;
463
+ use App\Domain\Reservation\Entity\Participant;
464
+ use App\Domain\Reservation\ValueObject\ReservationId;
465
+ use App\Domain\Reservation\ValueObject\Money;
466
+ use App\Domain\Reservation\ValueObject\ReservationStatus;
467
+ use App\Domain\Shared\ValueObject\Email;
468
+ use PHPUnit\Framework\TestCase;
469
+
470
+ final class ReservationTest extends TestCase
471
+ {
472
+ /**
473
+ * @test
474
+ */
475
+ public function it_creates_a_reservation(): void
476
+ {
477
+ // Given
478
+ $id = ReservationId::generate();
479
+ $email = Email::fromString('client@example.com');
480
+ $montant = Money::fromEuros(500);
481
+
482
+ // When
483
+ $reservation = Reservation::create($id, $email, $montant);
484
+
485
+ // Then
486
+ self::assertEquals($id, $reservation->getId());
487
+ self::assertEquals($email, $reservation->getClientEmail());
488
+ self::assertEquals($montant, $reservation->getMontantTotal());
489
+ self::assertEquals(ReservationStatus::EN_ATTENTE, $reservation->getStatut());
490
+ }
491
+
492
+ /**
493
+ * @test
494
+ */
495
+ public function it_adds_a_participant(): void
496
+ {
497
+ // Given
498
+ $reservation = $this->createReservation();
499
+ $participant = $this->createParticipant('Jean Dupont', 30);
500
+
501
+ // When
502
+ $reservation->addParticipant($participant);
503
+
504
+ // Then
505
+ self::assertCount(1, $reservation->getParticipants());
506
+ self::assertContains($participant, $reservation->getParticipants());
507
+ }
508
+
509
+ /**
510
+ * @test
511
+ */
512
+ public function it_cannot_add_more_than_10_participants(): void
513
+ {
514
+ // Given
515
+ $reservation = $this->createReservation();
516
+
517
+ for ($i = 0; $i < 10; $i++) {
518
+ $reservation->addParticipant($this->createParticipant("Participant $i", 25));
519
+ }
520
+
521
+ // Expect
522
+ $this->expectException(InvalidReservationException::class);
523
+ $this->expectExceptionMessage('Maximum 10 participants');
524
+
525
+ // When
526
+ $reservation->addParticipant($this->createParticipant('Participant 11', 25));
527
+ }
528
+
529
+ /**
530
+ * @test
531
+ */
532
+ public function it_confirms_a_reservation(): void
533
+ {
534
+ // Given
535
+ $reservation = $this->createReservation();
536
+ $reservation->addParticipant($this->createParticipant('Jean', 30));
537
+
538
+ // When
539
+ $reservation->confirmer();
540
+
541
+ // Then
542
+ self::assertEquals(ReservationStatus::CONFIRMEE, $reservation->getStatut());
543
+ }
544
+
545
+ /**
546
+ * @test
547
+ */
548
+ public function it_cannot_confirm_without_participants(): void
549
+ {
550
+ // Given
551
+ $reservation = $this->createReservation();
552
+
553
+ // Expect
554
+ $this->expectException(InvalidReservationException::class);
555
+ $this->expectExceptionMessage('At least one participant required');
556
+
557
+ // When
558
+ $reservation->confirmer();
559
+ }
560
+
561
+ /**
562
+ * @test
563
+ */
564
+ public function it_cannot_confirm_a_cancelled_reservation(): void
565
+ {
566
+ // Given
567
+ $reservation = $this->createReservation();
568
+ $reservation->addParticipant($this->createParticipant('Jean', 30));
569
+ $reservation->annuler('Client request');
570
+
571
+ // Expect
572
+ $this->expectException(InvalidReservationException::class);
573
+ $this->expectExceptionMessage('Cannot confirm cancelled reservation');
574
+
575
+ // When
576
+ $reservation->confirmer();
577
+ }
578
+
579
+ /**
580
+ * @test
581
+ */
582
+ public function it_cancels_a_reservation(): void
583
+ {
584
+ // Given
585
+ $reservation = $this->createReservation();
586
+ $raison = 'Client changed plans';
587
+
588
+ // When
589
+ $reservation->annuler($raison);
590
+
591
+ // Then
592
+ self::assertEquals(ReservationStatus::ANNULEE, $reservation->getStatut());
593
+ }
594
+
595
+ /**
596
+ * @test
597
+ */
598
+ public function it_records_domain_events(): void
599
+ {
600
+ // Given
601
+ $reservation = $this->createReservation();
602
+ $reservation->addParticipant($this->createParticipant('Jean', 30));
603
+
604
+ // When
605
+ $reservation->confirmer();
606
+
607
+ // Then
608
+ $events = $reservation->pullDomainEvents();
609
+ self::assertCount(1, $events);
610
+ self::assertInstanceOf(ReservationConfirmedEvent::class, $events[0]);
611
+ }
612
+
613
+ // Hilfsmethoden
614
+ private function createReservation(): Reservation
615
+ {
616
+ return Reservation::create(
617
+ ReservationId::generate(),
618
+ Email::fromString('test@example.com'),
619
+ Money::fromEuros(500)
620
+ );
621
+ }
622
+
623
+ private function createParticipant(string $nom, int $age): Participant
624
+ {
625
+ return Participant::create(
626
+ ParticipantId::generate(),
627
+ PersonName::fromString($nom),
628
+ $age
629
+ );
630
+ }
631
+ }
632
+ ```
633
+
634
+ ---
635
+
636
+ ## Integrationstests
637
+
638
+ ### Eigenschaften
639
+
640
+ - ✅ Echte Datenbank (PostgreSQL im Test)
641
+ - ✅ Transaktionen werden nach jedem Test zurückgerollt
642
+ - ✅ Fixtures für Testdaten
643
+ - ✅ Tests der Doctrine-Repositories
644
+
645
+ ### Beispiel: DoctrineReservationRepositoryTest
646
+
647
+ ```php
648
+ <?php
649
+
650
+ namespace App\Tests\Integration\Infrastructure\Persistence\Doctrine\Repository;
651
+
652
+ use App\Domain\Reservation\Entity\Reservation;
653
+ use App\Domain\Reservation\ValueObject\ReservationId;
654
+ use App\Infrastructure\Persistence\Doctrine\Repository\DoctrineReservationRepository;
655
+ use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
656
+
657
+ final class DoctrineReservationRepositoryTest extends KernelTestCase
658
+ {
659
+ private DoctrineReservationRepository $repository;
660
+
661
+ protected function setUp(): void
662
+ {
663
+ self::bootKernel();
664
+
665
+ $this->repository = self::getContainer()->get(DoctrineReservationRepository::class);
666
+ }
667
+
668
+ /**
669
+ * @test
670
+ */
671
+ public function it_saves_and_finds_a_reservation(): void
672
+ {
673
+ // Given
674
+ $reservation = $this->createReservation();
675
+
676
+ // When
677
+ $this->repository->save($reservation);
678
+
679
+ // Entity Manager leeren, um frisches Laden aus der DB sicherzustellen
680
+ self::getContainer()->get('doctrine')->getManager()->clear();
681
+
682
+ $found = $this->repository->findById($reservation->getId());
683
+
684
+ // Then
685
+ self::assertNotNull($found);
686
+ self::assertEquals($reservation->getId(), $found->getId());
687
+ self::assertEquals($reservation->getClientEmail(), $found->getClientEmail());
688
+ }
689
+
690
+ /**
691
+ * @test
692
+ */
693
+ public function it_throws_exception_when_reservation_not_found(): void
694
+ {
695
+ // Given
696
+ $nonExistentId = ReservationId::generate();
697
+
698
+ // Expect
699
+ $this->expectException(ReservationNotFoundException::class);
700
+
701
+ // When
702
+ $this->repository->findById($nonExistentId);
703
+ }
704
+
705
+ /**
706
+ * @test
707
+ */
708
+ public function it_deletes_a_reservation(): void
709
+ {
710
+ // Given
711
+ $reservation = $this->createReservation();
712
+ $this->repository->save($reservation);
713
+
714
+ // When
715
+ $this->repository->delete($reservation);
716
+
717
+ // Then
718
+ $this->expectException(ReservationNotFoundException::class);
719
+ $this->repository->findById($reservation->getId());
720
+ }
721
+
722
+ private function createReservation(): Reservation
723
+ {
724
+ return Reservation::create(
725
+ ReservationId::generate(),
726
+ Email::fromString('integration@test.com'),
727
+ Money::fromEuros(750)
728
+ );
729
+ }
730
+ }
731
+ ```
732
+
733
+ ---
734
+
735
+ ## Funktionale Tests
736
+
737
+ ### Eigenschaften
738
+
739
+ - ✅ Vollständige Tests der Use Cases
740
+ - ✅ Simulieren das Benutzerverhalten
741
+ - ✅ Überprüfen gesendete E-Mails
742
+ - ✅ Testen HTTP-Controller
743
+
744
+ ### Beispiel: CreateReservationUseCaseTest
745
+
746
+ ```php
747
+ <?php
748
+
749
+ namespace App\Tests\Functional\Application\Reservation\UseCase;
750
+
751
+ use App\Application\Reservation\UseCase\CreateReservation\CreateReservationCommand;
752
+ use App\Application\Reservation\UseCase\CreateReservation\CreateReservationUseCase;
753
+ use App\Domain\Reservation\Repository\ReservationRepositoryInterface;
754
+ use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
755
+
756
+ final class CreateReservationUseCaseTest extends KernelTestCase
757
+ {
758
+ private CreateReservationUseCase $useCase;
759
+ private ReservationRepositoryInterface $repository;
760
+
761
+ protected function setUp(): void
762
+ {
763
+ self::bootKernel();
764
+
765
+ $this->useCase = self::getContainer()->get(CreateReservationUseCase::class);
766
+ $this->repository = self::getContainer()->get(ReservationRepositoryInterface::class);
767
+ }
768
+
769
+ /**
770
+ * @test
771
+ */
772
+ public function it_creates_a_reservation(): void
773
+ {
774
+ // Given
775
+ $command = new CreateReservationCommand(
776
+ sejourId: 'sejour-123',
777
+ clientEmail: 'client@example.com',
778
+ participants: [
779
+ ['nom' => 'Jean Dupont', 'age' => 30],
780
+ ['nom' => 'Marie Dupont', 'age' => 28],
781
+ ]
782
+ );
783
+
784
+ // When
785
+ $reservationId = $this->useCase->execute($command);
786
+
787
+ // Then
788
+ $reservation = $this->repository->findById($reservationId);
789
+ self::assertNotNull($reservation);
790
+ self::assertCount(2, $reservation->getParticipants());
791
+ self::assertTrue($reservation->getMontantTotal()->isPositive());
792
+ }
793
+
794
+ /**
795
+ * @test
796
+ */
797
+ public function it_sends_confirmation_email(): void
798
+ {
799
+ // Given
800
+ $command = new CreateReservationCommand(
801
+ sejourId: 'sejour-123',
802
+ clientEmail: 'client@example.com',
803
+ participants: [['nom' => 'Jean', 'age' => 30]]
804
+ );
805
+
806
+ // When
807
+ $this->useCase->execute($command);
808
+
809
+ // Then
810
+ self::assertEmailCount(1);
811
+
812
+ $email = self::getMailerMessage();
813
+ self::assertEmailAddressContains($email, 'to', 'client@example.com');
814
+ self::assertEmailHtmlBodyContains($email, 'Confirmation de réservation');
815
+ }
816
+ }
817
+ ```
818
+
819
+ ---
820
+
821
+ ## BDD mit Behat
822
+
823
+ ### Konfiguration behat.yml
824
+
825
+ ```yaml
826
+ default:
827
+ suites:
828
+ reservation:
829
+ paths: ['%paths.base%/features/reservation']
830
+ contexts:
831
+ - App\Tests\Behat\Context\ReservationContext
832
+
833
+ extensions:
834
+ FriendsOfBehat\SymfonyExtension:
835
+ bootstrap: tests/bootstrap.php
836
+ ```
837
+
838
+ ### Feature: Reservierung erstellen
839
+
840
+ ```gherkin
841
+ # features/reservation/create_reservation.feature
842
+
843
+ Feature: Créer une réservation
844
+ En tant que client
845
+ Je veux réserver un séjour
846
+ Afin de participer aux activités
847
+
848
+ Background:
849
+ Given les séjours suivants existent:
850
+ | id | titre | prix | date_debut | date_fin |
851
+ | sejour-ski | Séjour ski Alpes | 500€ | 2025-02-01 | 2025-02-07 |
852
+ | sejour-surf | Séjour surf Biarritz | 450€ | 2025-03-15 | 2025-03-22 |
853
+
854
+ Scenario: Créer une réservation avec 2 participants
855
+ When je crée une réservation pour le séjour "sejour-ski" avec:
856
+ | nom | age |
857
+ | Jean Dupont | 30 |
858
+ | Marie Dupont | 28 |
859
+ Then la réservation est créée
860
+ And le montant total est de "1000€"
861
+ And je reçois un email de confirmation
862
+
863
+ Scenario: Appliquer une remise famille nombreuse
864
+ When je crée une réservation pour le séjour "sejour-ski" avec:
865
+ | nom | age |
866
+ | Parent 1 | 35 |
867
+ | Parent 2 | 33 |
868
+ | Enfant 1 | 10 |
869
+ | Enfant 2 | 8 |
870
+ Then la réservation est créée
871
+ And une remise de "10%" est appliquée
872
+ And le montant total est de "1350€"
873
+ # Basis: (500 + 500 + 250 + 250) = 1500
874
+ # Rabatt 10 %: 1500 * 0.9 = 1350
875
+
876
+ Scenario: Refuser une réservation sans participant
877
+ When je crée une réservation pour le séjour "sejour-ski" sans participant
878
+ Then je reçois une erreur "At least one participant required"
879
+ ```
880
+
881
+ ### Behat-Kontext
882
+
883
+ ```php
884
+ <?php
885
+
886
+ namespace App\Tests\Behat\Context;
887
+
888
+ use App\Application\Reservation\UseCase\CreateReservation\CreateReservationCommand;
889
+ use App\Application\Reservation\UseCase\CreateReservation\CreateReservationUseCase;
890
+ use Behat\Behat\Context\Context;
891
+ use Behat\Gherkin\Node\TableNode;
892
+ use Symfony\Component\HttpKernel\KernelInterface;
893
+
894
+ final class ReservationContext implements Context
895
+ {
896
+ private ?string $reservationId = null;
897
+ private ?\Throwable $exception = null;
898
+
899
+ public function __construct(
900
+ private readonly KernelInterface $kernel,
901
+ ) {}
902
+
903
+ /**
904
+ * @Given les séjours suivants existent:
905
+ */
906
+ public function lesSejursSuivantsExistent(TableNode $table): void
907
+ {
908
+ // Fixtures erstellen
909
+ foreach ($table->getHash() as $row) {
910
+ // Sejour-Fixtures erstellen...
911
+ }
912
+ }
913
+
914
+ /**
915
+ * @When je crée une réservation pour le séjour :sejourId avec:
916
+ */
917
+ public function jeCreeuneReservationPourLeSejourAvec(string $sejourId, TableNode $table): void
918
+ {
919
+ $participants = [];
920
+
921
+ foreach ($table->getHash() as $row) {
922
+ $participants[] = [
923
+ 'nom' => $row['nom'],
924
+ 'age' => (int) $row['age'],
925
+ ];
926
+ }
927
+
928
+ $command = new CreateReservationCommand(
929
+ sejourId: $sejourId,
930
+ clientEmail: 'behat@test.com',
931
+ participants: $participants
932
+ );
933
+
934
+ try {
935
+ $useCase = $this->kernel->getContainer()->get(CreateReservationUseCase::class);
936
+ $this->reservationId = (string) $useCase->execute($command);
937
+ } catch (\Throwable $e) {
938
+ $this->exception = $e;
939
+ }
940
+ }
941
+
942
+ /**
943
+ * @Then la réservation est créée
944
+ */
945
+ public function laReservationEstCreee(): void
946
+ {
947
+ if ($this->exception) {
948
+ throw $this->exception;
949
+ }
950
+
951
+ if (!$this->reservationId) {
952
+ throw new \RuntimeException('No reservation created');
953
+ }
954
+ }
955
+
956
+ /**
957
+ * @Then le montant total est de :montant
958
+ */
959
+ public function leMontantTotalEstDe(string $montant): void
960
+ {
961
+ $repository = $this->kernel->getContainer()->get(ReservationRepositoryInterface::class);
962
+ $reservation = $repository->findById(ReservationId::fromString($this->reservationId));
963
+
964
+ $expectedAmount = (float) str_replace('€', '', $montant);
965
+ $actualAmount = $reservation->getMontantTotal()->getAmountEuros();
966
+
967
+ if ($actualAmount !== $expectedAmount) {
968
+ throw new \RuntimeException(
969
+ sprintf('Expected %s€, got %s€', $expectedAmount, $actualAmount)
970
+ );
971
+ }
972
+ }
973
+ }
974
+ ```
975
+
976
+ ### Behat ausführen
977
+
978
+ ```bash
979
+ # Alle Szenarien
980
+ make behat
981
+
982
+ # Spezifisches Szenario
983
+ make behat ARGS="--name='Créer une réservation avec 2 participants'"
984
+
985
+ # Erwartete Ausgabe:
986
+ # Feature: Créer une réservation
987
+ # Scenario: Créer une réservation avec 2 participants
988
+ # ✓ When je crée une réservation...
989
+ # ✓ Then la réservation est créée
990
+ # ✓ And le montant total est de "1000€"
991
+ #
992
+ # 1 scenario (1 passed)
993
+ # 3 steps (3 passed)
994
+ ```
995
+
996
+ ---
997
+
998
+ ## Mutations-Testing mit Infection
999
+
1000
+ ### Konfiguration infection.json5
1001
+
1002
+ ```json5
1003
+ {
1004
+ "$schema": "vendor/infection/infection/resources/schema.json",
1005
+ "source": {
1006
+ "directories": ["src"]
1007
+ },
1008
+ "logs": {
1009
+ "text": "var/infection/infection.log",
1010
+ "html": "var/infection/index.html"
1011
+ },
1012
+ "mutators": {
1013
+ "@default": true
1014
+ },
1015
+ "minMsi": 80,
1016
+ "minCoveredMsi": 90
1017
+ }
1018
+ ```
1019
+
1020
+ ### Infection ausführen
1021
+
1022
+ ```bash
1023
+ # Mutations-Testing
1024
+ make infection
1025
+
1026
+ # Erwartete Ausgabe:
1027
+ # Infection - PHP Mutation Testing Framework
1028
+ #
1029
+ # Running mutation tests...
1030
+ #
1031
+ # Mutations: 150
1032
+ # Killed: 120 (80%)
1033
+ # Escaped: 20 (13.3%)
1034
+ # Errors: 5 (3.3%)
1035
+ # Timed Out: 5 (3.3%)
1036
+ #
1037
+ # Mutation Score Indicator (MSI): 80%
1038
+ # Covered Code MSI: 92%
1039
+ ```
1040
+
1041
+ ### Beispiele für Mutationen
1042
+
1043
+ ```php
1044
+ // Originalcode
1045
+ if ($amount > 0) {
1046
+ return true;
1047
+ }
1048
+
1049
+ // Mutation 1: Operator verändert
1050
+ if ($amount >= 0) { // ❌ Muss durch einen Test getötet werden
1051
+ return true;
1052
+ }
1053
+
1054
+ // Mutation 2: Bedingung umgekehrt
1055
+ if ($amount < 0) { // ❌ Muss durch einen Test getötet werden
1056
+ return true;
1057
+ }
1058
+
1059
+ // Mutation 3: Rückgabewert verändert
1060
+ if ($amount > 0) {
1061
+ return false; // ❌ Muss durch einen Test getötet werden
1062
+ }
1063
+ ```
1064
+
1065
+ **Wenn eine Mutation überlebt = fehlender oder schwacher Test!**
1066
+
1067
+ ---
1068
+
1069
+ ## Validierungs-Checkliste
1070
+
1071
+ ### Vor jedem Commit
1072
+
1073
+ - [ ] **TDD:** Tests VOR der Implementierung geschrieben
1074
+ - [ ] **RED:** Test schlägt anfangs fehl
1075
+ - [ ] **GREEN:** Minimale Implementierung lässt den Test bestehen
1076
+ - [ ] **REFACTOR:** Code verbessert, Tests bleiben grün
1077
+ - [ ] **Coverage:** `make test-coverage` → mindestens 80 %
1078
+ - [ ] **Mutation:** `make infection` → MSI mindestens 80 %
1079
+ - [ ] **BDD:** Behat-Szenarien für fachliche Funktionalitäten
1080
+ - [ ] **Fast:** Unit-Tests < 100 ms pro Test
1081
+ - [ ] **Isolated:** Tests unabhängig (zufällige Reihenfolge OK)
1082
+
1083
+ ### Zielmetriken
1084
+
1085
+ | Metrik | Ziel | Minimum |
1086
+ |--------|------|---------|
1087
+ | Code-Coverage | 85 % | 80 % |
1088
+ | Mutation Score (MSI) | 85 % | 80 % |
1089
+ | Covered Code MSI | 95 % | 90 % |
1090
+ | Unit-Tests | < 100 ms | < 200 ms |
1091
+ | Integrationstests | < 1 s | < 2 s |
1092
+ | Funktionale Tests | < 5 s | < 10 s |
1093
+
1094
+ ### Validierungsbefehle
1095
+
1096
+ ```bash
1097
+ # Vollständige Pipeline
1098
+ make test # Alle Tests
1099
+ make test-coverage # Mit Coverage
1100
+ make infection # Mutations-Testing
1101
+ make behat # BDD-Tests
1102
+
1103
+ # CI-Validierung
1104
+ make ci
1105
+ ```
1106
+
1107
+ ---
1108
+
1109
+ ## Ressourcen
1110
+
1111
+ - **Buch:** *Test-Driven Development by Example* - Kent Beck
1112
+ - **Buch:** *Growing Object-Oriented Software, Guided by Tests* - Steve Freeman
1113
+ - **PHPUnit:** [Dokumentation](https://phpunit.de/documentation.html)
1114
+ - **Behat:** [Dokumentation](https://docs.behat.org/)
1115
+ - **Infection:** [Dokumentation](https://infection.github.io/)
447
1116
 
448
1117
  ---
449
1118