├── get-composer.sh ├── src └── WireMock │ ├── Serde │ ├── SerializationException.php │ ├── ClassDiscriminator.php │ ├── PropNaming │ │ ├── PropertyNamingStrategy.php │ │ ├── ConstantPropertyNamingStrategy.php │ │ └── ReferencingPropertyNamingStrategy.php │ ├── Type │ │ ├── SerdeTypeUntypedArray.php │ │ ├── SerdeType.php │ │ ├── SerdeTypeSingle.php │ │ ├── SerdeTypeNull.php │ │ ├── SerdeTypeArray.php │ │ ├── SerdeTypeTypedArray.php │ │ ├── SerdeTypePrimitive.php │ │ ├── SerdeTypeLookup.php │ │ ├── SerdeTypeAssocArray.php │ │ └── SerdeTypeUnion.php │ ├── CanonicalNameUtils.php │ ├── ArrayMapUtils.php │ ├── SerializerFactory.php │ ├── MethodFactory.php │ ├── StaticFactoryMethodValidator.php │ ├── ClassDiscriminatorMapping.php │ ├── SerdeClassDiscriminationInfo.php │ ├── SerdeProp.php │ ├── SerdeClassDefinition.php │ └── Serializer.php │ ├── Stubbing │ ├── Fault.php │ ├── StubImport.php │ ├── StubImportOptions.php │ ├── ScenarioMapping.php │ ├── Scenario.php │ ├── StubImportBuilder.php │ └── StubMapping.php │ ├── Http │ └── RequestMethod.php │ ├── Client │ ├── MatchResult.php │ ├── CountMatchingRequestsResult.php │ ├── FindNearMissesResult.php │ ├── FindRequestsResult.php │ ├── GetScenariosResult.php │ ├── JsonPathValueMatchingStrategy.php │ ├── EqualToMatchingStrategy.php │ ├── ListStubMappingsResult.php │ ├── RequestJournalDependentResult.php │ ├── GetServeEventsResult.php │ ├── VerificationException.php │ ├── BasicCredentials.php │ ├── PaginatedResult.php │ ├── UnmatchedRequests.php │ ├── DateTimeMatchExpectedOffset.php │ ├── JsonValueMatchingStrategy.php │ ├── LogicalOperatorMatchingStrategy.php │ ├── LoggedResponse.php │ ├── AdvancedPathPattern.php │ ├── XPathValueMatchingStrategy.php │ ├── XmlUnitComparisonType.php │ ├── MultipartValuePattern.php │ ├── NearMiss.php │ ├── ScenarioMappingBuilder.php │ ├── ClientException.php │ ├── MultipartValuePatternBuilder.php │ ├── ServeEvent.php │ ├── HttpWait.php │ ├── EqualToXmlMatchingStrategy.php │ ├── Curl.php │ ├── LoggedRequest.php │ ├── ServeEventQuery.php │ ├── DateTimeMatchingStrategy.php │ ├── ValueMatchingStrategy.php │ └── RequestPatternBuilder.php │ ├── Fault │ ├── GlobalDelaySettings.php │ ├── FixedDelay.php │ ├── UniformDistribution.php │ ├── LogNormal.php │ ├── ChunkedDribbleDelay.php │ └── DelayDistribution.php │ ├── Recording │ ├── RecordingStatusResult.php │ ├── SnapshotRecordResult.php │ ├── ProxiedServeEventFilters.php │ └── RecordSpec.php │ ├── PostServe │ ├── PostServeAction.php │ └── WebhookDefinition.php │ ├── Matching │ ├── CustomMatcherDefinition.php │ ├── UrlMatchingStrategy.php │ └── RequestPattern.php │ ├── SerdeGen │ ├── Tag │ │ ├── SerdeCatchAllTag.php │ │ ├── SerdeUnwrappedTag.php │ │ ├── SerdeNamedByTag.php │ │ ├── SerdeDiscriminateTypeTag.php │ │ ├── SerdeNameTag.php │ │ ├── SerdePossibleNamesTag.php │ │ └── SerdePossibleSubtypeTag.php │ ├── SerdeTypeLookupFactory.php │ ├── PartialSerdeTypeLookup.php │ └── WireMockSerdeGen.php │ └── Verification │ └── CountMatchingStrategy.php ├── test ├── WireMock │ ├── SerdeGen │ │ ├── TestClasses │ │ │ ├── KitchenSinkSubA.php │ │ │ ├── KitchenSinkSubB.php │ │ │ ├── OneSimpleField.php │ │ │ ├── CatchAllPrimitiveProperty.php │ │ │ └── KitchenSink.php │ │ └── WireMockSerdeGenTest.php │ ├── Serde │ │ ├── TestClasses │ │ │ ├── DefaultedField.php │ │ │ ├── NamedProperty.php │ │ │ ├── ClassTypeFields.php │ │ │ ├── UnwrappedArrayProperty.php │ │ │ ├── DiscriminatedTypeSubclassA.php │ │ │ ├── DiscriminatedTypeSubclassB.php │ │ │ ├── UnwrappedPrimitiveProperty.php │ │ │ ├── CatchAllAndNamedProperty.php │ │ │ ├── UnwrappedAndNamedProperty.php │ │ │ ├── CatchAllAndNamedByProperty.php │ │ │ ├── UnwrappedAndNamedByProperty.php │ │ │ ├── UnwrappedClassProperty.php │ │ │ ├── CatchAllUntypedArrayProperty.php │ │ │ ├── CatchAllTypedArrayProperty.php │ │ │ ├── PrimitiveArrayFields.php │ │ │ ├── CatchAllAssocArrayProperty.php │ │ │ ├── FieldOnlyPrimitives.php │ │ │ ├── NamedByProperty.php │ │ │ ├── ConstructorParams.php │ │ │ ├── UnionTypeFields.php │ │ │ └── DiscriminatedTypeParent.php │ │ ├── Type │ │ │ ├── SerdeTypeArrayTest.php │ │ │ ├── SerdeTypeNullTest.php │ │ │ └── SerdeTypePrimitiveTest.php │ │ └── SerializerTest.php │ ├── Integration │ │ ├── ShutdownIntegrationTest.php │ │ ├── MappingsAssertionFunctions.php │ │ ├── WireMockIntegrationTest.php │ │ ├── ProxyingIntegrationTest.php │ │ ├── TestClient.php │ │ ├── NearMissesIntegrationTest.php │ │ ├── WebhookIntegrationTest.php │ │ ├── MultipartIntegrationTest.php │ │ └── MappingsIntegrationTest.php │ ├── HamcrestTestCase.php │ ├── Matching │ │ ├── UrlMatchingStrategyTest.php │ │ └── RequestPatternTest.php │ ├── Client │ │ ├── ValueMatchingStrategyTest.php │ │ ├── MappingBuilderTest.php │ │ └── RequestPatternBuilderTest.php │ └── Stubbing │ │ └── StubMappingTest.php ├── exec_test.sh ├── phpunit.xml └── bootstrap.php ├── .gitignore ├── wiremock ├── stop.sh └── start.sh ├── .circleci └── config.yml ├── LICENSE-MIT.txt ├── composer.json └── Readme.md /get-composer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -s http://getcomposer.org/installer | php -------------------------------------------------------------------------------- /src/WireMock/Serde/SerializationException.php: -------------------------------------------------------------------------------- 1 | sleep.log 2>&1 & -------------------------------------------------------------------------------- /test/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | . 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/NamedProperty.php: -------------------------------------------------------------------------------- 1 | primitiveTypes = $primitiveTypes; 13 | } 14 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/Type/SerdeTypeUntypedArray.php: -------------------------------------------------------------------------------- 1 | unwrappedArray = $unwrappedArray; 16 | } 17 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/Type/SerdeType.php: -------------------------------------------------------------------------------- 1 | subclassAProp = $subclassAProp; 14 | } 15 | } -------------------------------------------------------------------------------- /test/WireMock/Integration/ShutdownIntegrationTest.php: -------------------------------------------------------------------------------- 1 | shutdownServer(); 11 | 12 | // then 13 | assertThat(self::$_wireMock->isShutDown(), is(true)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/DiscriminatedTypeSubclassB.php: -------------------------------------------------------------------------------- 1 | subclassBProp = $subclassBProp; 14 | } 15 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/UnwrappedPrimitiveProperty.php: -------------------------------------------------------------------------------- 1 | unwrappedPrimitive = $unwrappedPrimitive; 16 | } 17 | } -------------------------------------------------------------------------------- /test/WireMock/SerdeGen/TestClasses/CatchAllPrimitiveProperty.php: -------------------------------------------------------------------------------- 1 | catchAllPrimitive = $catchAllPrimitive; 16 | } 17 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/CatchAllAndNamedProperty.php: -------------------------------------------------------------------------------- 1 | 9 | * @serde-catch-all 10 | * @serde-name ignoredSerializedName 11 | */ 12 | private $original; 13 | 14 | public function __construct(array $original) 15 | { 16 | $this->original = $original; 17 | } 18 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/ArrayMapUtils.php: -------------------------------------------------------------------------------- 1 | original = $original; 17 | } 18 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/SerializerFactory.php: -------------------------------------------------------------------------------- 1 | distance = $distance; 16 | } 17 | 18 | /** 19 | * @return float 20 | */ 21 | public function getDistance() 22 | { 23 | return $this->distance; 24 | } 25 | } -------------------------------------------------------------------------------- /wiremock/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is executed from the tests, so the current working directory is the tests folder. 4 | # Change to the wiremock directory 5 | cd ../wiremock 6 | 7 | instance=1 8 | if [ $# -gt 0 ]; then 9 | instance=$1 10 | fi 11 | pidFile=wiremock.$instance.pid 12 | 13 | 14 | if [ -e $pidFile ]; then 15 | kill -9 `cat $pidFile` 16 | rm $pidFile 17 | else 18 | echo WireMock is not started 2>&1 19 | exit 1 20 | fi 21 | 22 | echo WireMock $instance stopped -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/CatchAllAndNamedByProperty.php: -------------------------------------------------------------------------------- 1 | 9 | * @serde-catch-all 10 | * @serde-named-by namer 11 | * @serde-possible-names possibleNames 12 | */ 13 | private $prop; 14 | /** @var string */ 15 | private $namer; 16 | 17 | private static function possibleNames(): array { return ['newName']; } 18 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/UnwrappedAndNamedByProperty.php: -------------------------------------------------------------------------------- 1 | count = $count; 16 | } 17 | 18 | /** 19 | * @return integer 20 | */ 21 | public function getCount() 22 | { 23 | return $this->count; 24 | } 25 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/Type/SerdeTypeSingle.php: -------------------------------------------------------------------------------- 1 | typeString = $typeString; 16 | } 17 | 18 | public function displayName(): string 19 | { 20 | return $this->typeString; 21 | } 22 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/MethodFactory.php: -------------------------------------------------------------------------------- 1 | = 80400 15 | ? ReflectionMethod::createFromMethodName($fqMethodName) 16 | : new ReflectionMethod($fqMethodName); 17 | } 18 | } -------------------------------------------------------------------------------- /test/WireMock/HamcrestTestCase.php: -------------------------------------------------------------------------------- 1 | addToAssertionCount(MatcherAssert::getCount()); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/UnwrappedClassProperty.php: -------------------------------------------------------------------------------- 1 | topLevel = $topLevel; 18 | $this->unwrappedClass = $unwrappedClass; 19 | } 20 | } -------------------------------------------------------------------------------- /src/WireMock/Client/FindNearMissesResult.php: -------------------------------------------------------------------------------- 1 | nearMisses = $nearMisses; 16 | } 17 | 18 | /** 19 | * @return NearMiss[] 20 | */ 21 | public function getNearMisses() 22 | { 23 | return $this->nearMisses; 24 | } 25 | } -------------------------------------------------------------------------------- /src/WireMock/Client/FindRequestsResult.php: -------------------------------------------------------------------------------- 1 | requests = $requests; 16 | } 17 | 18 | /** 19 | * @return LoggedRequest[] 20 | */ 21 | public function getRequests(): array 22 | { 23 | return $this->requests; 24 | } 25 | } -------------------------------------------------------------------------------- /src/WireMock/Client/GetScenariosResult.php: -------------------------------------------------------------------------------- 1 | scenarios = $scenarios; 18 | } 19 | 20 | /** 21 | * @return Scenario[] 22 | */ 23 | public function getScenarios() 24 | { 25 | return $this->scenarios; 26 | } 27 | } -------------------------------------------------------------------------------- /src/WireMock/Fault/GlobalDelaySettings.php: -------------------------------------------------------------------------------- 1 | null, 'fixedDelay' => $millis]; 10 | } 11 | 12 | public static function random($distribution) 13 | { 14 | return ['delayDistribution' => $distribution, 'fixedDelay' => null]; 15 | } 16 | 17 | public static function none() 18 | { 19 | return ['delayDistribution' => null, 'fixedDelay' => null]; 20 | } 21 | } -------------------------------------------------------------------------------- /src/WireMock/Fault/FixedDelay.php: -------------------------------------------------------------------------------- 1 | milliseconds = $milliseconds; 17 | } 18 | 19 | /** 20 | * @return int 21 | */ 22 | public function getMilliseconds(): int 23 | { 24 | return $this->milliseconds; 25 | } 26 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/CatchAllUntypedArrayProperty.php: -------------------------------------------------------------------------------- 1 | topLevel = $topLevel; 22 | $this->catchAll = $catchAll; 23 | } 24 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/CatchAllTypedArrayProperty.php: -------------------------------------------------------------------------------- 1 | topLevel = $topLevel; 22 | $this->catchAll = $catchAll; 23 | } 24 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/PrimitiveArrayFields.php: -------------------------------------------------------------------------------- 1 | */ 12 | private $intByString; 13 | 14 | public function __construct(array $untypedArray, array $intArray, array $intByString) 15 | { 16 | $this->untypedArray = $untypedArray; 17 | $this->intArray = $intArray; 18 | $this->intByString = $intByString; 19 | } 20 | 21 | 22 | } -------------------------------------------------------------------------------- /src/WireMock/Client/JsonPathValueMatchingStrategy.php: -------------------------------------------------------------------------------- 1 | caseInsensitive = $caseInsensitive; 14 | } 15 | 16 | /** 17 | * @return bool 18 | */ 19 | public function isCaseInsensitive() 20 | { 21 | return $this->caseInsensitive; 22 | } 23 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/CatchAllAssocArrayProperty.php: -------------------------------------------------------------------------------- 1 | 11 | * @serde-catch-all 12 | */ 13 | private $catchAll; 14 | 15 | /** 16 | * @param int $topLevel 17 | * @param array $catchAll 18 | */ 19 | public function __construct(int $topLevel, array $catchAll) 20 | { 21 | $this->topLevel = $topLevel; 22 | $this->catchAll = $catchAll; 23 | } 24 | } -------------------------------------------------------------------------------- /src/WireMock/Recording/RecordingStatusResult.php: -------------------------------------------------------------------------------- 1 | status = $status; 20 | } 21 | 22 | /** 23 | * @return string 24 | */ 25 | public function getStatus() 26 | { 27 | return $this->status; 28 | } 29 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/PropNaming/ConstantPropertyNamingStrategy.php: -------------------------------------------------------------------------------- 1 | name = $name; 13 | } 14 | 15 | function getSerializedName(array $data): string 16 | { 17 | return $this->name; 18 | } 19 | 20 | /** 21 | * @return string[] 22 | */ 23 | function getPossibleSerializedNames(): array 24 | { 25 | return [$this->name]; 26 | } 27 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/Type/SerdeTypeNull.php: -------------------------------------------------------------------------------- 1 | canDenormalize($data)) { 22 | throw new SerializationException('Cannot denormalize non-null data to null'); 23 | } 24 | return null; 25 | } 26 | } -------------------------------------------------------------------------------- /test/WireMock/Matching/UrlMatchingStrategyTest.php: -------------------------------------------------------------------------------- 1 | getMatchingType(), is($matchingType)); 18 | assertThat($urlMatchingStrategy->getMatchingValue(), is($matchingValue)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/FieldOnlyPrimitives.php: -------------------------------------------------------------------------------- 1 | int = $int; 20 | $result->float = $float; 21 | $result->bool = $bool; 22 | $result->string = $string; 23 | return $result; 24 | } 25 | } -------------------------------------------------------------------------------- /src/WireMock/Client/ListStubMappingsResult.php: -------------------------------------------------------------------------------- 1 | mappings = $mappings; 20 | } 21 | 22 | 23 | /** 24 | * @return StubMapping[] 25 | */ 26 | public function getMappings() 27 | { 28 | return $this->mappings; 29 | } 30 | } -------------------------------------------------------------------------------- /test/WireMock/SerdeGen/WireMockSerdeGenTest.php: -------------------------------------------------------------------------------- 1 | getMatchingType(), equalTo($matchingType)); 18 | assertThat($valueMatchingStrategy->getMatchingValue(), equalTo($matchingValue)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/WireMock/Client/RequestJournalDependentResult.php: -------------------------------------------------------------------------------- 1 | requestJournalDisabled = $requestJournalDisabled; 18 | } 19 | 20 | /** 21 | * @return bool 22 | */ 23 | public function isRequestJournalDisabled() 24 | { 25 | return $this->requestJournalDisabled; 26 | } 27 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/NamedByProperty.php: -------------------------------------------------------------------------------- 1 | valueName = $valueName; 25 | $this->value = $value; 26 | } 27 | 28 | private static function names(): array 29 | { 30 | return ['one', 'two', 'three']; 31 | } 32 | } -------------------------------------------------------------------------------- /src/WireMock/Client/GetServeEventsResult.php: -------------------------------------------------------------------------------- 1 | requests = $requests; 19 | } 20 | 21 | /** 22 | * @return ServeEvent[] 23 | */ 24 | public function getRequests() 25 | { 26 | return $this->requests; 27 | } 28 | } -------------------------------------------------------------------------------- /src/WireMock/Client/VerificationException.php: -------------------------------------------------------------------------------- 1 | name = $name; 15 | $this->parameters = $parameters; 16 | } 17 | 18 | /** 19 | * @return string 20 | */ 21 | public function getName(): string 22 | { 23 | return $this->name; 24 | } 25 | 26 | /** 27 | * @return WebhookDefinition 28 | */ 29 | public function getParameters(): WebhookDefinition 30 | { 31 | return $this->parameters; 32 | } 33 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/ConstructorParams.php: -------------------------------------------------------------------------------- 1 | fieldWithConstructorParam = $fieldWithConstructorParam; 20 | $this->fieldWithOptionalConstructorParam = $fieldWithOptionalConstructorParam; 21 | } 22 | } -------------------------------------------------------------------------------- /src/WireMock/Client/BasicCredentials.php: -------------------------------------------------------------------------------- 1 | username = $username; 19 | $this->password = $password; 20 | } 21 | 22 | /** 23 | * @return string 24 | */ 25 | public function getUsername() 26 | { 27 | return $this->username; 28 | } 29 | 30 | /** 31 | * @return string 32 | */ 33 | public function getPassword() 34 | { 35 | return $this->password; 36 | } 37 | } -------------------------------------------------------------------------------- /src/WireMock/Matching/CustomMatcherDefinition.php: -------------------------------------------------------------------------------- 1 | name = $name; 19 | $this->parameters = $parameters; 20 | } 21 | 22 | /** 23 | * @return string 24 | */ 25 | public function getName() 26 | { 27 | return $this->name; 28 | } 29 | 30 | /** 31 | * @return array 32 | */ 33 | public function getParameters() 34 | { 35 | return $this->parameters; 36 | } 37 | } -------------------------------------------------------------------------------- /src/WireMock/Fault/UniformDistribution.php: -------------------------------------------------------------------------------- 1 | lower = $lower; 20 | $this->upper = $upper; 21 | } 22 | 23 | /** 24 | * @return int 25 | */ 26 | public function getLower() 27 | { 28 | return $this->lower; 29 | } 30 | 31 | /** 32 | * @return int 33 | */ 34 | public function getUpper() 35 | { 36 | return $this->upper; 37 | } 38 | } -------------------------------------------------------------------------------- /src/WireMock/Fault/LogNormal.php: -------------------------------------------------------------------------------- 1 | median = $median; 20 | $this->sigma = $sigma; 21 | } 22 | 23 | /** 24 | * @return float 25 | */ 26 | public function getMedian() 27 | { 28 | return $this->median; 29 | } 30 | 31 | /** 32 | * @return float 33 | */ 34 | public function getSigma() 35 | { 36 | return $this->sigma; 37 | } 38 | } -------------------------------------------------------------------------------- /src/WireMock/SerdeGen/Tag/SerdeCatchAllTag.php: -------------------------------------------------------------------------------- 1 | format($this); 27 | } 28 | 29 | public function __toString(): string 30 | { 31 | return ''; 32 | } 33 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/StaticFactoryMethodValidator.php: -------------------------------------------------------------------------------- 1 | isStatic()) { 16 | throw new SerializationException("$fqMethodName must be a static method but is not"); 17 | } 18 | $numRequiredParams = $refMethod->getNumberOfRequiredParameters(); 19 | if ($numRequiredParams > 0) { 20 | throw new SerializationException("$fqMethodName must take no required args, but requires $numRequiredParams"); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/WireMock/SerdeGen/Tag/SerdeUnwrappedTag.php: -------------------------------------------------------------------------------- 1 | format($this); 27 | } 28 | 29 | public function __toString(): string 30 | { 31 | return ''; 32 | } 33 | } -------------------------------------------------------------------------------- /src/WireMock/Recording/SnapshotRecordResult.php: -------------------------------------------------------------------------------- 1 | mappings = $mappings; 21 | $this->ids = $ids; 22 | } 23 | 24 | /** 25 | * @return StubMapping[] 26 | */ 27 | public function getMappings() 28 | { 29 | return $this->mappings; 30 | } 31 | 32 | /** 33 | * @return string[] 34 | */ 35 | public function getIds() 36 | { 37 | return $this->ids; 38 | } 39 | } -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | php: circleci/php@1.1.0 5 | 6 | jobs: 7 | run_tests_and_store_logs: 8 | parameters: 9 | version: 10 | description: The PHP version to use, passed to the executor as tag 11 | type: string 12 | executor: 13 | name: php/default 14 | tag: "<< parameters.version >>-browsers" 15 | steps: 16 | - checkout 17 | - php/install-packages 18 | - run: 19 | command: composer test 20 | - store_artifacts: 21 | path: wiremock/wiremock.1.log 22 | - store_artifacts: 23 | path: wiremock/wiremock.2.log 24 | 25 | workflows: 26 | test_all_versions: 27 | jobs: 28 | - run_tests_and_store_logs: 29 | matrix: 30 | parameters: 31 | version: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] 32 | -------------------------------------------------------------------------------- /src/WireMock/Client/PaginatedResult.php: -------------------------------------------------------------------------------- 1 | meta = $meta; 16 | } 17 | 18 | /** 19 | * @return Meta 20 | */ 21 | public function getMeta() 22 | { 23 | return $this->meta; 24 | } 25 | } 26 | 27 | class Meta 28 | { 29 | /** @var int */ 30 | private $total; 31 | 32 | /** 33 | * @param int $total 34 | */ 35 | public function __construct(int $total) 36 | { 37 | $this->total = $total; 38 | } 39 | 40 | /** 41 | * @return int 42 | */ 43 | public function getTotal() 44 | { 45 | return $this->total; 46 | } 47 | } -------------------------------------------------------------------------------- /src/WireMock/Client/UnmatchedRequests.php: -------------------------------------------------------------------------------- 1 | requestJournalDisabled = $requestJournalDisabled; 19 | $this->requests = $requests; 20 | } 21 | 22 | /** 23 | * @return LoggedRequest[] 24 | */ 25 | public function getRequests() 26 | { 27 | return $this->requests; 28 | } 29 | 30 | /** 31 | * @return boolean 32 | */ 33 | public function getRequestJournalDisabled() 34 | { 35 | return $this->requestJournalDisabled; 36 | } 37 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/Type/SerdeTypeArray.php: -------------------------------------------------------------------------------- 1 | canDenormalize($data)) { 25 | throw new SerializationException('Cannot denormalize to ' . $this->displayName() . 26 | ' from data of type ' . gettype($data) . ' at path ' . join('.', $path)); 27 | } 28 | return $this->denormalizeFromArray($data, $path); 29 | } 30 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/UnionTypeFields.php: -------------------------------------------------------------------------------- 1 | primitivesUnion = $primitivesUnion; 25 | $this->arrayUnion = $arrayUnion; 26 | $this->classUnion = $classUnion; 27 | $this->arrayOfUnion = $arrayOfUnion; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/WireMock/Fault/ChunkedDribbleDelay.php: -------------------------------------------------------------------------------- 1 | numberOfChunks = $numberOfChunks; 22 | $this->totalDurationMillis = $totalDurationMillis; 23 | } 24 | 25 | /** 26 | * @return int 27 | */ 28 | public function getNumberOfChunks() 29 | { 30 | return $this->numberOfChunks; 31 | } 32 | 33 | /** 34 | * @return int 35 | */ 36 | public function getTotalDurationMillis() 37 | { 38 | return $this->totalDurationMillis; 39 | } 40 | } -------------------------------------------------------------------------------- /src/WireMock/Stubbing/StubImport.php: -------------------------------------------------------------------------------- 1 | mappings = $mappings; 20 | $this->importOptions = $importOptions; 21 | } 22 | 23 | /** 24 | * @return StubMapping[]|null 25 | */ 26 | public function getMappings(): ?array 27 | { 28 | return $this->mappings; 29 | } 30 | 31 | /** 32 | * @return StubImportOptions 33 | */ 34 | public function getImportOptions(): StubImportOptions 35 | { 36 | return $this->importOptions; 37 | } 38 | } -------------------------------------------------------------------------------- /src/WireMock/Client/DateTimeMatchExpectedOffset.php: -------------------------------------------------------------------------------- 1 | amount = $amount; 25 | $this->unit = $unit; 26 | } 27 | 28 | /** 29 | * @return int 30 | */ 31 | public function getAmount(): int 32 | { 33 | return $this->amount; 34 | } 35 | 36 | /** 37 | * @return string 38 | */ 39 | public function getUnit(): string 40 | { 41 | return $this->unit; 42 | } 43 | } -------------------------------------------------------------------------------- /src/WireMock/Client/JsonValueMatchingStrategy.php: -------------------------------------------------------------------------------- 1 | ignoreArrayOrder = $ignoreArrayOrder; 16 | $this->ignoreExtraElements = $ignoreExtraElements; 17 | } 18 | 19 | /** 20 | * @return bool|null 21 | */ 22 | public function getIgnoreArrayOrder() 23 | { 24 | return $this->ignoreArrayOrder; 25 | } 26 | 27 | /** 28 | * @return bool|null 29 | */ 30 | public function getIgnoreExtraElements() 31 | { 32 | return $this->ignoreExtraElements; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/WireMock/Client/LogicalOperatorMatchingStrategy.php: -------------------------------------------------------------------------------- 1 | expectExceptionMessage('Cannot denormalize'); 14 | $serdeType = new class extends SerdeTypeArray { 15 | function displayName(): string { return 'dummy'; } 16 | function denormalizeFromArray(array &$data, array $path): array { return ['dummy array']; } 17 | }; 18 | $serdeType->denormalize($data, []); 19 | } 20 | 21 | public function providerNonArrayData(): array 22 | { 23 | return [ 24 | 'int' => [123], 25 | 'bool' => [true], 26 | 'float' => [1.23], 27 | 'string' => ['a string'], 28 | 'object' => [$this], 29 | 'null' => [null], 30 | ]; 31 | } 32 | } -------------------------------------------------------------------------------- /src/WireMock/Client/LoggedResponse.php: -------------------------------------------------------------------------------- 1 | status = $status; 22 | $this->headers = $headers; 23 | $this->body = $body; 24 | } 25 | 26 | /** 27 | * @return int 28 | */ 29 | public function getStatus() 30 | { 31 | return $this->status; 32 | } 33 | 34 | /** 35 | * @return array 36 | */ 37 | public function getHeaders() 38 | { 39 | return $this->headers; 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function getBody() 46 | { 47 | return $this->body; 48 | } 49 | } -------------------------------------------------------------------------------- /src/WireMock/Client/AdvancedPathPattern.php: -------------------------------------------------------------------------------- 1 | subMatchingStrategy = $subMatchingStrategy; 22 | $this->expression = $expression; 23 | } 24 | 25 | /** 26 | * @return ValueMatchingStrategy 27 | */ 28 | public function getSubMatchingStrategy(): ValueMatchingStrategy 29 | { 30 | return $this->subMatchingStrategy; 31 | } 32 | 33 | /** 34 | * @return string 35 | */ 36 | public function getExpression(): string 37 | { 38 | return $this->expression; 39 | } 40 | } -------------------------------------------------------------------------------- /src/WireMock/Stubbing/StubImportOptions.php: -------------------------------------------------------------------------------- 1 | duplicatePolicy = $duplicatePolicy; 23 | $this->deleteAllNotInImport = $deleteAllNotInImport; 24 | } 25 | 26 | /** 27 | * @return string 28 | */ 29 | public function getDuplicatePolicy(): string 30 | { 31 | return $this->duplicatePolicy; 32 | } 33 | 34 | /** 35 | * @return bool 36 | */ 37 | public function isDeleteAllNotInImport(): bool 38 | { 39 | return $this->deleteAllNotInImport; 40 | } 41 | } -------------------------------------------------------------------------------- /src/WireMock/SerdeGen/SerdeTypeLookupFactory.php: -------------------------------------------------------------------------------- 1 | addRootTypes(...$canonicalTypes); 22 | $serdeTypeParser = new SerdeTypeParser($partialLookup); 23 | foreach ($canonicalTypes as $type) { 24 | // This creates the SerdeType and adds it to the lookup as a side-effect 25 | $serdeTypeParser->parseTypeString($type); 26 | } 27 | return $partialLookup; 28 | } 29 | } -------------------------------------------------------------------------------- /test/WireMock/Integration/MappingsAssertionFunctions.php: -------------------------------------------------------------------------------- 1 | deserialize($adminJson, ListStubMappingsResult::class); 30 | return $listResult->getMappings(); 31 | } 32 | -------------------------------------------------------------------------------- /test/WireMock/Serde/Type/SerdeTypeNullTest.php: -------------------------------------------------------------------------------- 1 | denormalize($data, []); 15 | 16 | assertThat($denormalized, is(null)); 17 | } 18 | 19 | /** @dataProvider providerNonNullData */ 20 | public function testDenormalizingNonNullThrows($data) 21 | { 22 | $this->expectExceptionMessage('Cannot denormalize'); 23 | $serdeType = new SerdeTypeNull(); 24 | $serdeType->denormalize($data, []); 25 | } 26 | 27 | public function providerNonNullData(): array 28 | { 29 | return [ 30 | 'int' => [123], 31 | 'bool' => [true], 32 | 'float' => [1.23], 33 | 'string' => ['a string'], 34 | 'array' => [[]], 35 | 'object' => [$this] 36 | ]; 37 | } 38 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/TestClasses/DiscriminatedTypeParent.php: -------------------------------------------------------------------------------- 1 | type = $type; 21 | } 22 | 23 | private static function getDiscriminatorMapping() 24 | { 25 | return new ClassDiscriminatorMapping('type', [ 26 | 'parent' => DiscriminatedTypeParent::class, 27 | 'a' => DiscriminatedTypeSubclassA::class, 28 | 'b' => DiscriminatedTypeSubclassB::class, 29 | 30 | // Will result in exception at deserialization time, because this is not a registered possible subtype 31 | 'unknown' => FieldOnlyPrimitives::class, 32 | ]); 33 | } 34 | } -------------------------------------------------------------------------------- /src/WireMock/Client/XPathValueMatchingStrategy.php: -------------------------------------------------------------------------------- 1 | xPathNamespaces[$name] = $namespaceUri; 32 | return $this; 33 | } 34 | 35 | /** 36 | * @return array|null 37 | */ 38 | public function getXPathNamespaces(): ?array 39 | { 40 | return $this->xPathNamespaces; 41 | } 42 | } -------------------------------------------------------------------------------- /src/WireMock/Recording/ProxiedServeEventFilters.php: -------------------------------------------------------------------------------- 1 | requestPattern = $requestPattern; 27 | $this->requestIds = $requestIds; 28 | $this->allowNonProxied = $allowNonProxied; 29 | } 30 | 31 | public function getRequestPattern(): ?RequestPattern 32 | { 33 | return $this->requestPattern; 34 | } 35 | 36 | public function getRequestIds(): ?array 37 | { 38 | return $this->requestIds; 39 | } 40 | 41 | public function getAllowNonProxied(): ?bool 42 | { 43 | return $this->allowNonProxied; 44 | } 45 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/Type/SerdeTypeTypedArray.php: -------------------------------------------------------------------------------- 1 | type = $type; 18 | } 19 | 20 | static function setInnerType(SerdeTypeTypedArray $serdeType, SerdeType $type) 21 | { 22 | $serdeType->type = $type; 23 | } 24 | 25 | public function displayName(): string 26 | { 27 | return $this->type->displayName() . '[]'; 28 | } 29 | 30 | /** 31 | * @throws SerializationException 32 | */ 33 | function denormalizeFromArray(array &$data, array $path): array 34 | { 35 | $result = []; 36 | foreach ($data as $index => $element) { 37 | $newPath = $path; 38 | $newPath[] = "[$index]"; 39 | $result[$index] = $this->type->denormalize($element, $newPath); 40 | } 41 | return $result; 42 | } 43 | } -------------------------------------------------------------------------------- /LICENSE-MIT.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Rowan Hill 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/WireMock/Fault/DelayDistribution.php: -------------------------------------------------------------------------------- 1 | type = $type; 25 | } 26 | 27 | /** 28 | * @return string 29 | */ 30 | public function getType(): string 31 | { 32 | return $this->type; 33 | } 34 | 35 | /** @noinspection PhpUnusedPrivateMethodInspection */ 36 | private static function getDiscriminatorMapping(): ClassDiscriminator 37 | { 38 | return new ClassDiscriminatorMapping('type', [ 39 | 'lognormal' => LogNormal::class, 40 | 'uniform' => UniformDistribution::class, 41 | 'fixed' => FixedDelay::class 42 | ]); 43 | } 44 | } -------------------------------------------------------------------------------- /src/WireMock/Matching/UrlMatchingStrategy.php: -------------------------------------------------------------------------------- 1 | matchingType = $matchingType; 29 | $this->matchingValue = $matchingValue; 30 | } 31 | 32 | /** 33 | * @return string 34 | */ 35 | public function getMatchingType() 36 | { 37 | return $this->matchingType; 38 | } 39 | 40 | /** 41 | * @return string 42 | */ 43 | public function getMatchingValue() 44 | { 45 | return $this->matchingValue; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/WireMock/SerdeGen/TestClasses/KitchenSink.php: -------------------------------------------------------------------------------- 1 | reqArg = $reqArg; 54 | } 55 | } -------------------------------------------------------------------------------- /wiremock/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is executed from the tests, so the current working directory is the tests folder. 4 | # Change to the wiremock directory 5 | cd ../wiremock 6 | 7 | instance=1 8 | port=8080 9 | if [ $# -gt 0 ]; then 10 | instance=$1 11 | port=$2 12 | fi 13 | pidFile=wiremock.$instance.pid 14 | logFile=wiremock.$instance.log 15 | 16 | # Ensure WireMock isn't already running 17 | if [ -e $pidFile ]; then 18 | echo WireMock is already started: see process `cat $pidFile` 1>&2 19 | exit 1 20 | fi 21 | 22 | # Download the wiremock jar if we need it 23 | if ! [ -e wiremock-standalone.jar ]; then 24 | echo WireMock standalone JAR missing. Downloading. 25 | curl https://repo1.maven.org/maven2/com/github/tomakehurst/wiremock-jre8-standalone/2.35.0/wiremock-jre8-standalone-2.35.0.jar -o wiremock-standalone.jar 26 | status=$? 27 | if [ ${status} -ne 0 ]; then 28 | echo curl could not download WireMock JAR 1>&2 29 | exit ${status} 30 | fi 31 | fi 32 | 33 | # Start WireMock in standalone mode (in a background process) and save its output to a log 34 | java -jar wiremock-standalone.jar --port $port --root-dir $instance --verbose &> $logFile 2>&1 & 35 | echo $! > $pidFile 36 | 37 | echo WireMock $instance started on port $port -------------------------------------------------------------------------------- /src/WireMock/Serde/Type/SerdeTypePrimitive.php: -------------------------------------------------------------------------------- 1 | typeString) { 14 | case 'bool': 15 | case 'boolean': 16 | return $dataType === 'boolean'; 17 | 18 | case 'int': 19 | case 'integer': 20 | return $dataType === 'integer'; 21 | 22 | case 'float': 23 | case 'double': 24 | return $dataType === 'double'; 25 | 26 | case 'string': 27 | return $dataType === 'string'; 28 | 29 | default: 30 | return false; 31 | } 32 | } 33 | 34 | function denormalize(&$data, array $path) 35 | { 36 | if (!$this->canDenormalize($data)) { 37 | $dataType = gettype($data); 38 | $targetType = $this->displayName(); 39 | $joinedPath = join('.', $path); 40 | throw new SerializationException("Cannot deserialize data of type $dataType to $targetType ($joinedPath)"); 41 | } 42 | return $data; 43 | } 44 | } -------------------------------------------------------------------------------- /src/WireMock/SerdeGen/Tag/SerdeNamedByTag.php: -------------------------------------------------------------------------------- 1 | namingPropertyName = $namingPropertyName; 19 | } 20 | 21 | public static function create(string $body) 22 | { 23 | $propName = trim($body); 24 | return new static($propName); 25 | } 26 | 27 | public function getName(): string 28 | { 29 | return 'serde-named-by'; 30 | } 31 | 32 | public function getNamingPropertyName(): string 33 | { 34 | return $this->namingPropertyName; 35 | } 36 | 37 | public function render(?Formatter $formatter = null): string 38 | { 39 | if ($formatter === null) { 40 | $formatter = new Formatter\PassthroughFormatter(); 41 | } 42 | 43 | return $formatter->format($this); 44 | } 45 | 46 | public function __toString(): string 47 | { 48 | return $this->getNamingPropertyName(); 49 | } 50 | } -------------------------------------------------------------------------------- /src/WireMock/SerdeGen/Tag/SerdeDiscriminateTypeTag.php: -------------------------------------------------------------------------------- 1 | discriminatorFactory = $discriminatorFactory; 19 | } 20 | 21 | public static function create(string $body) 22 | { 23 | return new static(trim($body)); 24 | } 25 | 26 | public function getName(): string 27 | { 28 | return 'serde-discriminate-type'; 29 | } 30 | 31 | public function getDiscriminatorFactory(): string 32 | { 33 | return $this->discriminatorFactory; 34 | } 35 | 36 | public function render(?Formatter $formatter = null): string 37 | { 38 | if ($formatter === null) { 39 | $formatter = new Formatter\PassthroughFormatter(); 40 | } 41 | 42 | return $formatter->format($this); 43 | } 44 | 45 | public function __toString(): string 46 | { 47 | return $this->getDiscriminatorFactory(); 48 | } 49 | } -------------------------------------------------------------------------------- /src/WireMock/Stubbing/ScenarioMapping.php: -------------------------------------------------------------------------------- 1 | scenarioName = $scenarioName; 26 | $this->requiredScenarioState = $requiredScenarioState; 27 | $this->newScenarioState = $newScenarioState; 28 | } 29 | 30 | /** 31 | * @return string 32 | */ 33 | public function getScenarioName() 34 | { 35 | return $this->scenarioName; 36 | } 37 | 38 | /** 39 | * @return string 40 | */ 41 | public function getRequiredScenarioState() 42 | { 43 | return $this->requiredScenarioState; 44 | } 45 | 46 | /** 47 | * @return string 48 | */ 49 | public function getNewScenarioState() 50 | { 51 | return $this->newScenarioState; 52 | } 53 | } -------------------------------------------------------------------------------- /src/WireMock/SerdeGen/Tag/SerdeNameTag.php: -------------------------------------------------------------------------------- 1 | serializedPropertyName = $serializedPropertyName; 19 | } 20 | 21 | public static function create(string $body) 22 | { 23 | $propName = trim($body); 24 | return new static($propName); 25 | } 26 | 27 | public function getName(): string 28 | { 29 | return 'serde-name'; 30 | } 31 | 32 | public function getSerializedPropertyName(): string 33 | { 34 | return $this->serializedPropertyName; 35 | } 36 | 37 | public function render(?Formatter $formatter = null): string 38 | { 39 | if ($formatter === null) { 40 | $formatter = new Formatter\PassthroughFormatter(); 41 | } 42 | 43 | return $formatter->format($this); 44 | } 45 | 46 | public function __toString(): string 47 | { 48 | return $this->getSerializedPropertyName(); 49 | } 50 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/ClassDiscriminatorMapping.php: -------------------------------------------------------------------------------- 1 | discriminatorPropName = $discriminatorPropName; 19 | $this->discriminatorValueToClassMap = $discriminatorValueToClassMap; 20 | } 21 | 22 | function getDiscriminatedType($data): string 23 | { 24 | $discriminatorName = $this->discriminatorPropName; 25 | if (array_key_exists($discriminatorName, $data)) { 26 | $discriminatorValue = $data[$discriminatorName]; 27 | $map = $this->discriminatorValueToClassMap; 28 | if (array_key_exists($discriminatorValue, $map)) { 29 | return $map[$discriminatorValue]; 30 | } 31 | } 32 | throw new SerializationException("Could not discriminate class type because $discriminatorName not present in data"); 33 | } 34 | } -------------------------------------------------------------------------------- /src/WireMock/SerdeGen/Tag/SerdePossibleNamesTag.php: -------------------------------------------------------------------------------- 1 | possibleNamesGenerator = $possibleNamesGenerator; 19 | } 20 | 21 | public static function create(string $body) 22 | { 23 | $propName = trim($body); 24 | return new static($propName); 25 | } 26 | 27 | public function getName(): string 28 | { 29 | return 'serde-possible-names'; 30 | } 31 | 32 | public function getPossibleNamesGenerator(): string 33 | { 34 | return $this->possibleNamesGenerator; 35 | } 36 | 37 | public function render(?Formatter $formatter = null): string 38 | { 39 | if ($formatter === null) { 40 | $formatter = new Formatter\PassthroughFormatter(); 41 | } 42 | 43 | return $formatter->format($this); 44 | } 45 | 46 | public function __toString(): string 47 | { 48 | return $this->getPossibleNamesGenerator(); 49 | } 50 | } -------------------------------------------------------------------------------- /src/WireMock/Stubbing/Scenario.php: -------------------------------------------------------------------------------- 1 | id = $id; 27 | $this->name = $name; 28 | $this->state = $state; 29 | $this->possibleStates = $possibleStates; 30 | } 31 | 32 | /** 33 | * @return string 34 | */ 35 | public function getId() 36 | { 37 | return $this->id; 38 | } 39 | 40 | /** 41 | * @return string 42 | */ 43 | public function getName() 44 | { 45 | return $this->name; 46 | } 47 | 48 | /** 49 | * @return string 50 | */ 51 | public function getState() 52 | { 53 | return $this->state; 54 | } 55 | 56 | /** 57 | * @return string[] 58 | */ 59 | public function getPossibleStates() 60 | { 61 | return $this->possibleStates; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/WireMock/Client/XmlUnitComparisonType.php: -------------------------------------------------------------------------------- 1 | =7.2" 33 | }, 34 | "require-dev": { 35 | "phpunit/phpunit": "^8.5", 36 | "phake/phake": "^4.5", 37 | "hamcrest/hamcrest-php": "^2.0", 38 | "phpdocumentor/reflection-docblock": "^5.3", 39 | "phpdocumentor/type-resolver": "^1.6" 40 | }, 41 | 42 | "suggest": { 43 | "phpunit/phpunit": "Thrown VerificationExceptions automatically fail tests" 44 | }, 45 | 46 | "scripts": { 47 | "serdegen": "WireMock\\SerdeGen\\WireMockSerdeGen::generateAndSaveWireMockSerdeLookup", 48 | "test": "cd test && phpunit --configuration phpunit.xml --debug --verbose && cd .." 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/WireMock/SerdeGen/Tag/SerdePossibleSubtypeTag.php: -------------------------------------------------------------------------------- 1 | name = 'serde-possible-subtype'; 17 | $this->type = $type; 18 | $this->description = $description; 19 | } 20 | 21 | public static function create(string $body, ?TypeResolver $typeResolver = null, ?TypeContext $context = null) 22 | { 23 | Assert::notNull($typeResolver); 24 | [$typeName, $body] = self::extractTypeFromBody($body); 25 | $type = $typeResolver->resolve($typeName, $context); 26 | $description = new Description($body); 27 | 28 | return new static($type, $description); 29 | } 30 | 31 | public function __toString(): string 32 | { 33 | $type = (string) $this->type; 34 | if ($this->description) { 35 | $description = ' ' . $this->description->render(); 36 | } else { 37 | $description = ''; 38 | } 39 | return "$type$description"; 40 | } 41 | } -------------------------------------------------------------------------------- /test/WireMock/Integration/WireMockIntegrationTest.php: -------------------------------------------------------------------------------- 1 | isAlive(), is(true)); 21 | } 22 | 23 | public static function tearDownAfterClass(): void 24 | { 25 | self::runCmd('./../wiremock/stop.sh'); 26 | } 27 | 28 | protected static function runCmd($cmd) 29 | { 30 | $result = 0; 31 | $output = array(); 32 | $redirect = EXEC_BLOCKS_ON_OUTPUT ? '> /dev/null' : ''; 33 | exec("($cmd) $redirect 2>&1 &", $output, $result); 34 | $output = array_map(function ($line) { 35 | return "\n$line"; 36 | }, $output); 37 | echo implode("\n", $output); 38 | assertThat($result, is(0)); 39 | } 40 | 41 | public function setUp(): void 42 | { 43 | $this->_testClient = new TestClient(); 44 | self::$_wireMock->reset(); 45 | } 46 | 47 | public function clearMappings() 48 | { 49 | exec('rm -f ../wiremock/1/mappings/*'); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/bootstrap.php: -------------------------------------------------------------------------------- 1 | add("WireMock\\", __DIR__, true); 9 | $classLoader->register(); 10 | 11 | \Hamcrest\Util::registerGlobalFunctions(); 12 | \Phake::setClient(Phake::CLIENT_PHPUNIT8); 13 | 14 | // Some systems (e.g. OSX) don't block on commands exec()ed in the below fashion. Other systems (e.g. Travis) do. 15 | // Systems which do block need to have their output redirected from stdout/stderr if the command being exec()ed is 16 | // long running. 17 | // Also, some systems (e.g. OSX) fail the tests if the output of starting WireMock is redirected (possibly because 18 | // they're running tests in parallel?), whereas others (e.g. Travis). 19 | // So far, there's a 1-to-1 mapping between these kinds of systems, but it's unclear if that's just chance or not. 20 | // For now, just check which kind of system we're on, so we know whether to redirect output or not. 21 | $startTime = microtime(true); 22 | exec('(./exec_test.sh) 2>&1 &'); 23 | $endTime = microtime(true); 24 | $sleepTime = (($endTime - $startTime) * 1000); 25 | echo "Took $sleepTime millis to sleep in background exec()\n"; 26 | define('EXEC_BLOCKS_ON_OUTPUT', $sleepTime > 5000); 27 | 28 | // Report all errors - being as strict as possible ensures the library will be compatible with the most environments 29 | error_reporting(E_ALL); 30 | -------------------------------------------------------------------------------- /src/WireMock/SerdeGen/PartialSerdeTypeLookup.php: -------------------------------------------------------------------------------- 1 | rootTypes[$key] = true; 23 | } 24 | } 25 | 26 | /** 27 | * @throws SerializationException 28 | */ 29 | public function addSerdeTypeIfNeeded(string $type, SerdeType $serdeType, bool $isRootType): SerdeType 30 | { 31 | if ($isRootType) { 32 | $this->addRootTypes($type); 33 | } 34 | if (!$this->contains($type)) { 35 | $this->addSerdeType($type, $serdeType); 36 | } 37 | return $this->getSerdeType($type); 38 | } 39 | 40 | private function addSerdeType(string $type, SerdeType $serdeType) 41 | { 42 | $key = CanonicalNameUtils::stripLeadingBackslashIfNeeded($type); 43 | $this->lookup[$key] = $serdeType; 44 | } 45 | 46 | public function contains(string $type): bool 47 | { 48 | $key = CanonicalNameUtils::stripLeadingBackslashIfNeeded($type); 49 | return isset($this->lookup[$key]); 50 | } 51 | } -------------------------------------------------------------------------------- /src/WireMock/Client/MultipartValuePattern.php: -------------------------------------------------------------------------------- 1 | |null */ 13 | private $headers; 14 | /** @var string */ 15 | private $name; 16 | /** @var string */ 17 | private $matchingType; 18 | 19 | /** 20 | * @param ValueMatchingStrategy[]|null $bodyPatterns 21 | * @param array|null $headers 22 | * @param string $name 23 | * @param string $matchingType 24 | */ 25 | public function __construct($bodyPatterns = null, $headers = null, $name = null, $matchingType = null) 26 | { 27 | $this->bodyPatterns = $bodyPatterns; 28 | $this->headers = $headers; 29 | $this->name = $name; 30 | $this->matchingType = $matchingType; 31 | } 32 | 33 | /** 34 | * @return array 35 | */ 36 | public function getBodyPatterns() 37 | { 38 | return $this->bodyPatterns; 39 | } 40 | 41 | /** 42 | * @return array 43 | */ 44 | public function getHeaders() 45 | { 46 | return $this->headers; 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function getName() 53 | { 54 | return $this->name; 55 | } 56 | 57 | /** 58 | * @return string 59 | */ 60 | public function getMatchingType() 61 | { 62 | return $this->matchingType; 63 | } 64 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/Type/SerdeTypeLookup.php: -------------------------------------------------------------------------------- 1 | */ 11 | protected $lookup; 12 | /** @var array */ 13 | protected $rootTypes; 14 | 15 | /** 16 | * @param array $lookup 17 | * @param array $rootTypes 18 | */ 19 | public function __construct(array $lookup, array $rootTypes) 20 | { 21 | $this->lookup = $lookup; 22 | $this->rootTypes = $rootTypes; 23 | } 24 | 25 | /** 26 | * @throws SerializationException 27 | */ 28 | public function getSerdeType(string $type): SerdeType 29 | { 30 | $key = CanonicalNameUtils::stripLeadingBackslashIfNeeded($type); 31 | if (!array_key_exists($key, $this->lookup)) { 32 | throw new SerializationException("Type $key does not exist in the serde type cache"); 33 | } 34 | return $this->lookup[$key]; 35 | } 36 | 37 | public function getSerdeTypeIfExits(string $type): ?SerdeType 38 | { 39 | $key = CanonicalNameUtils::stripLeadingBackslashIfNeeded($type); 40 | return array_key_exists($key, $this->lookup) ? 41 | $this->lookup[$key] : 42 | null; 43 | } 44 | 45 | public function isRootType(string $type): bool 46 | { 47 | $key = CanonicalNameUtils::stripLeadingBackslashIfNeeded($type); 48 | return array_key_exists($key, $this->rootTypes); 49 | } 50 | } -------------------------------------------------------------------------------- /src/WireMock/Client/NearMiss.php: -------------------------------------------------------------------------------- 1 | request = $request; 32 | $this->stubMapping = $stubMapping; 33 | $this->requestPattern = $requestPattern; 34 | $this->matchResult = $matchResult; 35 | } 36 | 37 | /** 38 | * @return LoggedRequest 39 | */ 40 | public function getRequest() 41 | { 42 | return $this->request; 43 | } 44 | 45 | /** 46 | * @return StubMapping 47 | */ 48 | public function getMapping() 49 | { 50 | return $this->stubMapping; 51 | } 52 | 53 | /** 54 | * @return RequestPattern 55 | */ 56 | public function getRequestPattern() 57 | { 58 | return $this->requestPattern; 59 | } 60 | 61 | /** 62 | * @return MatchResult 63 | */ 64 | public function getMatchResult() 65 | { 66 | return $this->matchResult; 67 | } 68 | } -------------------------------------------------------------------------------- /src/WireMock/Client/ScenarioMappingBuilder.php: -------------------------------------------------------------------------------- 1 | scenarioName = $scenarioName; 23 | return $this; 24 | } 25 | 26 | /** 27 | * @param string $requiredScenarioState 28 | * @return ScenarioMappingBuilder 29 | */ 30 | public function withRequiredState($requiredScenarioState) 31 | { 32 | $this->requiredScenarioState = $requiredScenarioState; 33 | return $this; 34 | } 35 | 36 | /** 37 | * @param string $newScenarioState 38 | * @return ScenarioMappingBuilder 39 | */ 40 | public function withNewScenarioState($newScenarioState) 41 | { 42 | $this->newScenarioState = $newScenarioState; 43 | return $this; 44 | } 45 | 46 | /** 47 | * @return null|ScenarioMapping 48 | * @throws \Exception 49 | */ 50 | public function build() 51 | { 52 | if ($this->scenarioName === null) { 53 | if ($this->requiredScenarioState !== null || $this->newScenarioState !== null) { 54 | throw new \Exception('Scenario name must be set'); 55 | } 56 | 57 | return null; 58 | } 59 | 60 | return new ScenarioMapping($this->scenarioName, $this->requiredScenarioState, $this->newScenarioState); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/WireMock/Serde/Type/SerdeTypeAssocArray.php: -------------------------------------------------------------------------------- 1 | keyType = $keyType; 21 | $this->valueType = $valueType; 22 | } 23 | 24 | static function setKeyValueTypes( 25 | SerdeTypeAssocArray $arrayType, 26 | SerdeTypePrimitive $keySerdeType, 27 | SerdeType $valueSerdeType 28 | ) { 29 | $arrayType->keyType = $keySerdeType; 30 | $arrayType->valueType = $valueSerdeType; 31 | } 32 | 33 | function displayName(): string 34 | { 35 | $key = $this->keyType->displayName(); 36 | $value = $this->valueType->displayName(); 37 | return "array<$key, $value>"; 38 | } 39 | 40 | function denormalizeFromArray(array &$data, array $path): array 41 | { 42 | return ArrayMapUtils::array_map_assoc( 43 | function($key, $value) use ($path) { 44 | $newKeyPath = $path; 45 | $newKeyPath[] = "key<$key>"; 46 | $newKey = $this->keyType->denormalize($key, $newKeyPath); 47 | 48 | $newValuePath = $path; 49 | $newValuePath[] = "[$key]"; 50 | $newValue = $this->valueType->denormalize($value, $newValuePath); 51 | 52 | return [$newKey, $newValue]; 53 | }, 54 | $data 55 | ); 56 | } 57 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/SerdeClassDiscriminationInfo.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | private $possibleSerdeTypes; 20 | 21 | /** 22 | * @param string $discriminatorFactoryName 23 | * @param array $possibleSerdeTypes 24 | * @throws SerializationException|ReflectionException 25 | */ 26 | public function __construct(string $discriminatorFactoryName, array $possibleSerdeTypes) 27 | { 28 | $this->discriminatorFactoryName = $discriminatorFactoryName; 29 | StaticFactoryMethodValidator::validate($discriminatorFactoryName); 30 | $this->possibleSerdeTypes = $possibleSerdeTypes; 31 | } 32 | 33 | /** 34 | * @throws ReflectionException 35 | */ 36 | public function getDiscriminator(): ?ClassDiscriminator 37 | { 38 | $refMethod = MethodFactory::createMethod($this->discriminatorFactoryName); 39 | $refMethod->setAccessible(true); 40 | return $refMethod->invoke(null); 41 | } 42 | 43 | /** 44 | * @throws SerializationException 45 | */ 46 | public function getTypeByFullyQualifiedClassName(string $fqn): SerdeTypeClass 47 | { 48 | if (!array_key_exists($fqn, $this->possibleSerdeTypes)) { 49 | throw new SerializationException("Unregistered discriminated subtype $fqn"); 50 | } 51 | return $this->possibleSerdeTypes[$fqn]; 52 | } 53 | } -------------------------------------------------------------------------------- /src/WireMock/Verification/CountMatchingStrategy.php: -------------------------------------------------------------------------------- 1 | closure = $closure; 20 | $this->operator = $operator; 21 | $this->operand = $operand; 22 | } 23 | 24 | /** 25 | * @param int $count 26 | * @return boolean 27 | */ 28 | public function matches($count) 29 | { 30 | $closure = $this->closure; 31 | return $closure($count); 32 | } 33 | 34 | public function describe() 35 | { 36 | return $this->operator . ' ' . $this->operand; 37 | } 38 | 39 | public static function lessThan($expected) { 40 | return new self(function($actual) use ($expected) { return $actual < $expected; }, '<', $expected); 41 | } 42 | 43 | public static function lessThanOrExactly($expected) { 44 | return new self(function($actual) use ($expected) { return $actual <= $expected; }, '<=', $expected); 45 | } 46 | 47 | public static function exactly($expected) { 48 | return new self(function($actual) use ($expected) { return $actual == $expected; }, '==', $expected); 49 | } 50 | 51 | public static function moreThanOrExactly($expected) { 52 | return new self(function($actual) use ($expected) { return $actual >= $expected; }, '>=', $expected); 53 | } 54 | 55 | public static function moreThan($expected) { 56 | return new self(function($actual) use ($expected) { return $actual > $expected; }, '>', $expected); 57 | } 58 | } -------------------------------------------------------------------------------- /src/WireMock/Client/ClientException.php: -------------------------------------------------------------------------------- 1 | ", $nestedMessagePieces); 30 | } 31 | } 32 | if (!empty($nestedMessages)) { 33 | $message .= ". Errors were:\n"; 34 | $message .= join("\n", $nestedMessages); 35 | } 36 | parent::__construct($message); 37 | $this->_responseCode = $responseCode; 38 | $this->_response = $response; 39 | } 40 | 41 | /** 42 | * @return int 43 | */ 44 | public function getResponseCode(): int 45 | { 46 | return $this->_responseCode; 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function getResponse(): string 53 | { 54 | return $this->_response; 55 | } 56 | } -------------------------------------------------------------------------------- /src/WireMock/Client/MultipartValuePatternBuilder.php: -------------------------------------------------------------------------------- 1 | |null */ 10 | private $headers; 11 | /** @var string */ 12 | private $name; 13 | /** @var string */ 14 | private $matchingType = MultipartValuePattern::ANY; 15 | 16 | /** 17 | * @param ValueMatchingStrategy $valueMatchingStrategy 18 | * @return MultipartValuePatternBuilder 19 | */ 20 | public function withMultipartBody($valueMatchingStrategy) 21 | { 22 | $this->bodyPatterns[] = $valueMatchingStrategy; 23 | return $this; 24 | } 25 | 26 | /** 27 | * @param string $name 28 | * @return MultipartValuePatternBuilder 29 | */ 30 | public function withName($name) 31 | { 32 | $this->name = $name; 33 | $this->withHeader('Content-Disposition', WireMock::containing("name=\"$name\"")); 34 | return $this; 35 | } 36 | 37 | /** 38 | * @param string $headerName 39 | * @param ValueMatchingStrategy $valueMatchingStrategy 40 | * @return MultipartValuePatternBuilder 41 | */ 42 | public function withHeader($headerName, $valueMatchingStrategy) 43 | { 44 | $this->headers[$headerName] = $valueMatchingStrategy; 45 | return $this; 46 | } 47 | 48 | /** 49 | * @param string $type 50 | * @return MultipartValuePatternBuilder 51 | */ 52 | public function matchingType($type) 53 | { 54 | $this->matchingType = $type; 55 | return $this; 56 | } 57 | 58 | public function build() 59 | { 60 | return new MultipartValuePattern( 61 | $this->bodyPatterns, 62 | $this->headers, 63 | $this->name, 64 | $this->matchingType 65 | ); 66 | } 67 | } -------------------------------------------------------------------------------- /test/WireMock/Integration/ProxyingIntegrationTest.php: -------------------------------------------------------------------------------- 1 | stubFor(WireMock::get(WireMock::urlEqualTo('/some/url')) 13 | ->willReturn(WireMock::aResponse()->proxiedFrom('http://otherhost.com/approot')) 14 | ); 15 | 16 | // then 17 | assertThat($stubMapping->getResponse()->getProxyBaseUrl(), is('http://otherhost.com/approot')); 18 | assertThatTheOnlyMappingPresentIs($stubMapping); 19 | } 20 | 21 | public function testAdditionProxiedRequestHeadersCanBeSet() 22 | { 23 | // when 24 | $stubMapping = self::$_wireMock->stubFor(WireMock::get(WireMock::urlEqualTo('/some/url')) 25 | ->willReturn( 26 | WireMock::aResponse()->proxiedFrom('http://otherhost.com/approot') 27 | ->withAdditionalRequestHeader('X-Header', 'val') 28 | ) 29 | ); 30 | 31 | // then 32 | assertThat($stubMapping->getResponse()->getAdditionalProxyRequestHeaders(), equalTo(array('X-Header' => 'val'))); 33 | assertThatTheOnlyMappingPresentIs($stubMapping); 34 | } 35 | 36 | public function testProxyUrlPrefixToRemoveCanBeSet() 37 | { 38 | // when 39 | $stubMapping = self::$_wireMock->stubFor(WireMock::get(WireMock::urlEqualTo("/other/service/doc/123")) 40 | ->willReturn(WireMock::aResponse() 41 | ->proxiedFrom("http://otherhost.com/approot") 42 | ->withProxyUrlPrefixToRemove("/other/service"))); 43 | 44 | // then 45 | assertThat($stubMapping->getResponse()->getProxyUrlPrefixToRemove(), equalTo('/other/service')); 46 | assertThatTheOnlyMappingPresentIs($stubMapping); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/WireMock/Client/ServeEvent.php: -------------------------------------------------------------------------------- 1 | id = $id; 36 | $this->request = $request; 37 | $this->stubMapping = $stubMapping; 38 | $this->responseDefinition = $responseDefinition; 39 | $this->response = $response; 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function getId() 46 | { 47 | return $this->id; 48 | } 49 | 50 | /** 51 | * @return LoggedRequest 52 | */ 53 | public function getRequest() 54 | { 55 | return $this->request; 56 | } 57 | 58 | /** 59 | * @return StubMapping 60 | */ 61 | public function getStubMapping() 62 | { 63 | return $this->stubMapping; 64 | } 65 | 66 | /** 67 | * @return ResponseDefinition 68 | */ 69 | public function getResponseDefinition() 70 | { 71 | return $this->responseDefinition; 72 | } 73 | 74 | /** 75 | * @return LoggedResponse 76 | */ 77 | public function getResponse() 78 | { 79 | return $this->response; 80 | } 81 | } -------------------------------------------------------------------------------- /src/WireMock/Stubbing/StubImportBuilder.php: -------------------------------------------------------------------------------- 1 | build(); 24 | $this->mappings[] = $mapping; 25 | return $this; 26 | } 27 | 28 | /** 29 | * @return StubImportBuilder 30 | */ 31 | public function ignoreExisting() 32 | { 33 | $this->duplicatePolicy = StubImportOptions::IGNORE; 34 | return $this; 35 | } 36 | 37 | /** 38 | * @return StubImportBuilder 39 | */ 40 | public function overwriteExisting() 41 | { 42 | $this->duplicatePolicy = StubImportOptions::OVERWRITE; 43 | return $this; 44 | } 45 | 46 | /** 47 | * @return StubImportBuilder 48 | */ 49 | public function deleteAllExistingStubsNotInImport() 50 | { 51 | $this->deleteAllNotInImport = true; 52 | return $this; 53 | } 54 | 55 | /** 56 | * @return StubImportBuilder 57 | */ 58 | public function doNotDeleteExistingStubs() 59 | { 60 | $this->deleteAllNotInImport = false; 61 | return $this; 62 | } 63 | 64 | /** 65 | * @return StubImport 66 | */ 67 | public function build() 68 | { 69 | return new StubImport( 70 | $this->mappings, 71 | new StubImportOptions($this->duplicatePolicy, $this->deleteAllNotInImport) 72 | ); 73 | } 74 | } -------------------------------------------------------------------------------- /src/WireMock/Client/HttpWait.php: -------------------------------------------------------------------------------- 1 | getMessage(); 17 | continue; 18 | } 19 | 20 | if (isset($headers) && isset($headers[0]) && strpos($headers[0], '200 OK') !== false) { 21 | $serverStarted = true; 22 | break; 23 | } else { 24 | if (!isset($headers)) { 25 | $debugTrace[] = "$url not yet up. \$headers not set"; 26 | } else if (!isset($headers[0])) { 27 | $debugTrace[] = "$url not yet up. \$headers[0] not set"; 28 | } else { 29 | $debugTrace[] = "$url not yet up. \$headers[0] was {$headers[0]}"; 30 | } 31 | } 32 | usleep(100000); 33 | } 34 | if (!$serverStarted) { 35 | $time = microtime(true) - $startTime; 36 | if ($debug) { 37 | echo implode("\n", $debugTrace) ."\n"; 38 | } 39 | echo "$url failed to come up after $time seconds"; 40 | 41 | } 42 | return $serverStarted; 43 | } 44 | 45 | public function waitForServerToFailToRespond($url, $timeoutSecs = 5) 46 | { 47 | $startTime = microtime(true); 48 | $serverFailedToRespond = false; 49 | while (microtime(true) - $startTime < $timeoutSecs) { 50 | if (@get_headers($url, 1) === false) { 51 | $serverFailedToRespond = true; 52 | break; 53 | } 54 | } 55 | return $serverFailedToRespond; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/WireMock/Serde/PropNaming/ReferencingPropertyNamingStrategy.php: -------------------------------------------------------------------------------- 1 | namingPropertyName = $namingPropertyName; 28 | $this->possibleNamesGenerator = $possibleNamesGenerator; 29 | StaticFactoryMethodValidator::validate($possibleNamesGenerator); 30 | } 31 | 32 | function getSerializedName(array $data): string 33 | { 34 | if (!array_key_exists($this->namingPropertyName, $data)) { 35 | throw new SerializationException("Cannot name property based on value of $this->namingPropertyName, because it is missing from data"); 36 | } 37 | $value = $data[$this->namingPropertyName]; 38 | if (!is_string($value)) { 39 | throw new SerializationException("Cannot name property based on value of $this->namingPropertyName, because that value is of type " . gettype($value)); 40 | } 41 | return $value; 42 | } 43 | 44 | /** 45 | * @return string[] 46 | * @throws ReflectionException 47 | */ 48 | function getPossibleSerializedNames(): array 49 | { 50 | $refMethod = MethodFactory::createMethod($this->possibleNamesGenerator); 51 | $refMethod->setAccessible(true); 52 | return $refMethod->invoke(null); 53 | } 54 | } -------------------------------------------------------------------------------- /test/WireMock/Integration/TestClient.php: -------------------------------------------------------------------------------- 1 | _hostname = $_hostname; 14 | $this->_port = $_port; 15 | } 16 | 17 | public function get($path, array $headers = array(), $includeResponseHeaders = false) 18 | { 19 | $ch = curl_init($this->_makeUrl($path)); 20 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 21 | if ($includeResponseHeaders) { 22 | curl_setopt($ch, CURLOPT_HEADER, true); 23 | } 24 | if (!empty($headers)) { 25 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 26 | } 27 | 28 | $result = $this->_makeTimedRequest($ch); 29 | 30 | curl_close($ch); 31 | 32 | return $result; 33 | } 34 | 35 | public function post($path, $body) 36 | { 37 | $ch = curl_init($this->_makeUrl($path)); 38 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 39 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); 40 | curl_setopt($ch, CURLOPT_POSTFIELDS, $body); 41 | 42 | $result = $this->_makeTimedRequest($ch); 43 | 44 | curl_close($ch); 45 | 46 | return $result; 47 | } 48 | 49 | /** 50 | * @return float|null 51 | */ 52 | public function getLastRequestTimeMillis() 53 | { 54 | return $this->_lastRequestTimeMillis; 55 | } 56 | 57 | private function _makeUrl($path) 58 | { 59 | if (strpos($path, '/') !== 0) { 60 | $path = '/' . $path; 61 | } 62 | return "http://$this->_hostname:$this->_port$path"; 63 | } 64 | 65 | private function _makeTimedRequest($ch) 66 | { 67 | $startTime = microtime(true); 68 | $result = curl_exec($ch); 69 | $endTime = microtime(true); 70 | $this->_lastRequestTimeMillis = ($endTime - $startTime) * 1000; 71 | 72 | return $result; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/WireMock/Client/EqualToXmlMatchingStrategy.php: -------------------------------------------------------------------------------- 1 | enablePlaceholders = $enablePlaceholders; 29 | $this->placeholderOpeningDelimiterRegex = $placeholderOpeningDelimiterRegex; 30 | $this->placeholderClosingDelimiterRegex = $placeholderClosingDelimiterRegex; 31 | } 32 | 33 | /** 34 | * @param string $comparisonTypes... 35 | * @return $this 36 | */ 37 | public function exemptingComparisons($comparisonTypes) 38 | { 39 | $comparisonTypes = func_get_args(); 40 | $this->exemptedComparisons = $comparisonTypes; 41 | return $this; 42 | } 43 | 44 | /** 45 | * @return bool 46 | */ 47 | public function isEnablePlaceholders(): bool 48 | { 49 | return $this->enablePlaceholders; 50 | } 51 | 52 | /** 53 | * @return string|null 54 | */ 55 | public function getPlaceholderOpeningDelimiterRegex(): ?string 56 | { 57 | return $this->placeholderOpeningDelimiterRegex; 58 | } 59 | 60 | /** 61 | * @return string|null 62 | */ 63 | public function getPlaceholderClosingDelimiterRegex(): ?string 64 | { 65 | return $this->placeholderClosingDelimiterRegex; 66 | } 67 | 68 | /** 69 | * @return string[]|null 70 | */ 71 | public function getExemptedComparisons(): ?array 72 | { 73 | return $this->exemptedComparisons; 74 | } 75 | } -------------------------------------------------------------------------------- /src/WireMock/Client/Curl.php: -------------------------------------------------------------------------------- 1 | makeCurlRequest('GET', $url); 15 | } 16 | 17 | /** 18 | * @param string $url 19 | * @param string|null $body The request body 20 | * @return string The response body 21 | * @throws ClientException 22 | */ 23 | public function post(string $url, ?string $body = null): string 24 | { 25 | return $this->makeCurlRequest('POST', $url, $body); 26 | } 27 | 28 | /** 29 | * @param string $url 30 | * @param array|string|null $body The request body 31 | * @return string The response body 32 | * @throws ClientException 33 | */ 34 | public function put(string $url, ?string $body = null): string 35 | { 36 | return $this->makeCurlRequest('PUT', $url, $body); 37 | } 38 | 39 | /** 40 | * @param string $url 41 | * @return string The response body 42 | * @throws ClientException 43 | */ 44 | public function delete(string $url): string 45 | { 46 | return $this->makeCurlRequest('DELETE', $url); 47 | } 48 | 49 | /** 50 | * @throws ClientException 51 | */ 52 | private function makeCurlRequest(string $method, string $url, ?string $json = null) 53 | { 54 | $ch = curl_init($url); 55 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); 56 | if ($json !== null) { 57 | curl_setopt($ch, CURLOPT_POSTFIELDS, $json); 58 | $contentLength = strlen($json); 59 | } else { 60 | $contentLength = 0; 61 | } 62 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 63 | curl_setopt($ch, CURLOPT_HTTPHEADER, array( 64 | 'Content-Type: application/json', 65 | "Content-Length: $contentLength", 66 | )); 67 | 68 | $result = curl_exec($ch); 69 | 70 | $responseCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); 71 | if ($responseCode < 200 || $responseCode >= 300) { 72 | throw new ClientException($responseCode, $result); 73 | } 74 | 75 | curl_close($ch); 76 | 77 | return $result; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/WireMock/Client/LoggedRequest.php: -------------------------------------------------------------------------------- 1 | */ 16 | private $headers; 17 | /** @var array */ 18 | private $cookies; 19 | /** @var string */ 20 | private $body; 21 | /** @var string */ 22 | private $bodyAsBase64; 23 | /** @var bool */ 24 | private $browserProxyRequest; 25 | /** @var int */ 26 | private $loggedDate; 27 | /** @var string */ 28 | private $loggedDateString; 29 | 30 | /** 31 | * @return string 32 | */ 33 | public function getUrl() 34 | { 35 | return $this->url; 36 | } 37 | 38 | /** 39 | * @return string 40 | */ 41 | public function getAbsoluteUrl() 42 | { 43 | return $this->absoluteUrl; 44 | } 45 | 46 | /** 47 | * @return string 48 | */ 49 | public function getMethod() 50 | { 51 | return $this->method; 52 | } 53 | 54 | /** 55 | * @return string 56 | */ 57 | public function getClientIp() 58 | { 59 | return $this->clientIp; 60 | } 61 | 62 | /** 63 | * @return array 64 | */ 65 | public function getHeaders() 66 | { 67 | return $this->headers; 68 | } 69 | 70 | /** 71 | * @return array 72 | */ 73 | public function getCookies() 74 | { 75 | return $this->cookies; 76 | } 77 | 78 | /** 79 | * @return string 80 | */ 81 | public function getBody() 82 | { 83 | return $this->body; 84 | } 85 | 86 | /** 87 | * @return string 88 | */ 89 | public function getBodyAsBase64() 90 | { 91 | return $this->bodyAsBase64; 92 | } 93 | 94 | /** 95 | * @return boolean 96 | */ 97 | public function isBrowserProxyRequest() 98 | { 99 | return $this->browserProxyRequest; 100 | } 101 | 102 | /** 103 | * @return int 104 | */ 105 | public function getLoggedDate() 106 | { 107 | return $this->loggedDate; 108 | } 109 | 110 | /** 111 | * @return string 112 | */ 113 | public function getLoggedDateString() 114 | { 115 | return $this->loggedDateString; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /test/WireMock/Matching/RequestPatternTest.php: -------------------------------------------------------------------------------- 1 | getMethod(), is($method)); 20 | assertThat($requestPattern->getUrlMatchingStrategy(), is($urlMatchingStrategy)); 21 | } 22 | 23 | public function testRequestHeaderMatchersAreAvailable() 24 | { 25 | // given 26 | $headers = array('aHeader' => WireMock::equalTo('aValue')); 27 | $requestPattern = new RequestPattern( 28 | 'GET', 29 | new UrlMatchingStrategy('url', '/some/url'), 30 | $headers 31 | ); 32 | 33 | // then 34 | assertThat($requestPattern->getHeaders(), is($headers)); 35 | } 36 | 37 | public function testRequestCookieMatchersAreAvailable() 38 | { 39 | // given 40 | $cookies = array('aCookie' => WireMock::equalTo('aValue')); 41 | $requestPattern = new RequestPattern( 42 | 'GET', 43 | new UrlMatchingStrategy('url', '/some/url'), 44 | null, 45 | $cookies 46 | ); 47 | 48 | // then 49 | assertThat($requestPattern->getCookies(), is($cookies)); 50 | } 51 | 52 | public function testRequestBodyMatchersAreAvailable() 53 | { 54 | // given 55 | $bodyPatterns = array(WireMock::equalTo('aValue')); 56 | $requestPattern = new RequestPattern( 57 | 'GET', 58 | new UrlMatchingStrategy('url', '/some/url'), 59 | null, 60 | null, 61 | $bodyPatterns 62 | ); 63 | 64 | // then 65 | assertThat($requestPattern->getBodyPatterns(), is($bodyPatterns)); 66 | } 67 | 68 | public function testQueryParamMatchersAreAvailable() 69 | { 70 | // given 71 | $queryParams = array('aParam' => WireMock::equalTo('aValue')); 72 | $requestPattern = new RequestPattern( 73 | 'GET', 74 | new UrlMatchingStrategy('url', '/some/url'), 75 | null, 76 | null, 77 | null, 78 | null, 79 | $queryParams 80 | ); 81 | 82 | // then 83 | assertThat($requestPattern->getQueryParameters(), is($queryParams)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/WireMock/SerdeGen/WireMockSerdeGen.php: -------------------------------------------------------------------------------- 1 | since = $since; 25 | return $this; 26 | } 27 | 28 | /** 29 | * @param int $limit 30 | * @return $this 31 | */ 32 | public function withLimit($limit) 33 | { 34 | $this->limit = $limit; 35 | return $this; 36 | } 37 | 38 | /** 39 | * @return $this 40 | */ 41 | public function withUnmatched() 42 | { 43 | $this->unmatched = true; 44 | return $this; 45 | } 46 | 47 | /** 48 | * @param string $stubId 49 | * @return $this 50 | */ 51 | public function withStubMapping($stubId) 52 | { 53 | $this->matchingStubId = $stubId; 54 | return $this; 55 | } 56 | 57 | public function toParamsString() 58 | { 59 | $params = []; 60 | if ($this->since) { 61 | $params[] = 'since=' . urlencode($this->since->format(DateTime::ATOM)); 62 | } 63 | if ($this->limit) { 64 | $params[] = 'limit=' . urlencode($this->limit); 65 | } 66 | if ($this->unmatched) { 67 | $params[] = 'unmatched=true'; 68 | } 69 | if ($this->matchingStubId) { 70 | $params[] = 'matchingStub=' . urlencode($this->matchingStubId); 71 | } 72 | return join('&', $params); 73 | } 74 | 75 | /** 76 | * @param DateTime|null $since 77 | * @param int|null $limit 78 | * @return self 79 | */ 80 | public static function paginated($since, $limit = null) 81 | { 82 | $result = new self(); 83 | if ($since) { 84 | $result->withSince($since); 85 | } 86 | if ($limit) { 87 | $result->withLimit($limit); 88 | } 89 | return $result; 90 | } 91 | 92 | /** 93 | * @return self 94 | */ 95 | public static function unmatched() 96 | { 97 | $result = new self(); 98 | return $result->withUnmatched(); 99 | } 100 | 101 | /** 102 | * @param string $stubId 103 | * @return self 104 | */ 105 | public static function forStubMapping($stubId) 106 | { 107 | $result = new self(); 108 | return $result->withStubMapping($stubId); 109 | } 110 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/SerializerTest.php: -------------------------------------------------------------------------------- 1 | normalize($primitiveValue); 19 | 20 | assertThat($normalized, is($primitiveValue)); 21 | } 22 | 23 | public function testNormalizingObjectRegisteredInLookupDelegatesToSerdeTypeClass() 24 | { 25 | $mockSerdeType = Phake::mock(SerdeTypeClass::class); 26 | $serializer = new Serializer(new SerdeTypeLookup([self::class => $mockSerdeType], [])); 27 | Phake::when($mockSerdeType)->normalize($this, $serializer)->thenReturn(['dummy']); 28 | 29 | $normalized = $serializer->normalize($this); 30 | 31 | assertThat($normalized, is(['dummy'])); 32 | } 33 | 34 | public function testSerializingObjectNotInLookupObservesJsonSerializableInterface() 35 | { 36 | $object = new class implements JsonSerializable { 37 | public function jsonSerialize(): array 38 | { 39 | return ['foo' => 'bar']; 40 | } 41 | }; 42 | $mockSerdeType = Phake::mock(SerdeTypeClass::class); 43 | $serializer = new Serializer(new SerdeTypeLookup([self::class => $mockSerdeType], [self::class => true])); 44 | Phake::when($mockSerdeType)->normalize($this, $serializer)->thenReturn(['unregistered' => [$object]]); 45 | 46 | $json = $serializer->serialize($this); 47 | 48 | self::assertEquals('{"unregistered":[{"foo":"bar"}]}', $json); 49 | } 50 | 51 | public function testNormalizingArrayRecursivelyDenormalizesKeepingKeys() 52 | { 53 | $mockSerdeType = Phake::mock(SerdeTypeClass::class); 54 | $serializer = new Serializer(new SerdeTypeLookup([self::class => $mockSerdeType], [])); 55 | Phake::when($mockSerdeType)->normalize($this, $serializer)->thenReturn(['dummy']); 56 | 57 | $normalized = $serializer->normalize(['one' => $this, 'two' => 123]); 58 | 59 | assertThat($normalized, is(['one' => ['dummy'], 'two' => 123])); 60 | } 61 | 62 | public function testDenormalizingDelegatesToSerdeType() 63 | { 64 | $mockSerdeType = Phake::mock(SerdeTypeClass::class); 65 | $serializer = new Serializer(new SerdeTypeLookup([self::class => $mockSerdeType], [])); 66 | $data = ['dummy']; 67 | Phake::when($mockSerdeType)->denormalize($data, [])->thenReturn($this); 68 | 69 | $denormalized = $serializer->denormalize($data, self::class); 70 | 71 | assertThat($denormalized, is($this)); 72 | } 73 | 74 | public function providerPrimitives(): array 75 | { 76 | return [ 77 | 'string' => ['a string'], 78 | 'int' => [123], 79 | 'float' => [1.23], 80 | 'bool' => [false], 81 | 'null' => [null], 82 | ]; 83 | } 84 | } -------------------------------------------------------------------------------- /test/WireMock/Serde/Type/SerdeTypePrimitiveTest.php: -------------------------------------------------------------------------------- 1 | false, 11 | 'boolean' => false, 12 | 'int' => 123, 13 | 'integer' => 123, 14 | 'float' => 1.23, 15 | 'double' => 1.23, 16 | 'string' => 'a string', 17 | ]; 18 | private const EQUIVALENT_TYPES = [ 19 | 'bool' => 'boolean', 20 | 'boolean' => 'bool', 21 | 'int' => 'integer', 22 | 'integer' => 'int', 23 | 'float' => 'double', 24 | 'double' => 'float' 25 | ]; 26 | 27 | /** @dataProvider providerMatchedTypeAndData */ 28 | public function testDenormalizingCorrectTypeDataIsANoOp(string $type, $data) 29 | { 30 | $serdeType = new SerdeTypePrimitive($type); 31 | $denormalized = $serdeType->denormalize($data, []); 32 | assertThat($denormalized, is($data)); 33 | } 34 | 35 | /** @dataProvider providerMismatchedTypeAndData */ 36 | public function testDenormalizingMismatchedTypeDataThrows(string $type, $data) 37 | { 38 | $this->expectExceptionMessage('Cannot deserialize data of type'); 39 | $serdeType = new SerdeTypePrimitive($type); 40 | $serdeType->denormalize($data, []); 41 | } 42 | 43 | /** @dataProvider providerMatchedTypeAndData */ 44 | public function testDenormalizingObjectDataThrows(string $type) 45 | { 46 | $this->expectExceptionMessage('Cannot deserialize data of type'); 47 | $serdeType = new SerdeTypePrimitive($type); 48 | $serdeType->denormalize($this, []); 49 | } 50 | 51 | /** @dataProvider providerMatchedTypeAndData */ 52 | public function testDenormalizingArrayDataThrows(string $type) 53 | { 54 | $this->expectExceptionMessage('Cannot deserialize data of type'); 55 | $serdeType = new SerdeTypePrimitive($type); 56 | $data = []; 57 | $serdeType->denormalize($data, []); 58 | } 59 | 60 | /** @dataProvider providerMatchedTypeAndData */ 61 | public function testDenormalizingNullDataThrows(string $type) 62 | { 63 | $this->expectExceptionMessage('Cannot deserialize data of type'); 64 | $serdeType = new SerdeTypePrimitive($type); 65 | $data = null; 66 | $serdeType->denormalize($data, []); 67 | } 68 | 69 | public function providerMatchedTypeAndData(): array 70 | { 71 | $result = []; 72 | foreach (self::EXAMPLE_DATA as $type => $data) { 73 | $result[$type] = [$type, $data]; 74 | } 75 | return $result; 76 | } 77 | 78 | public function providerMismatchedTypeAndData(): array 79 | { 80 | $result = []; 81 | foreach (self::EXAMPLE_DATA as $type => $ignoredData) { 82 | foreach (self::EXAMPLE_DATA as $otherType => $data) { 83 | if ($type === $otherType || (isset(self::EQUIVALENT_TYPES[$type]) && self::EQUIVALENT_TYPES[$type] === $otherType)) { 84 | continue; 85 | } 86 | $result["$type with $otherType data"] = [$type, $data]; 87 | } 88 | } 89 | return $result; 90 | } 91 | } -------------------------------------------------------------------------------- /test/WireMock/Integration/NearMissesIntegrationTest.php: -------------------------------------------------------------------------------- 1 | stubFor(WireMock::get(WireMock::urlEqualTo('/some-url')) 13 | ->willReturn(WireMock::aResponse())); 14 | $this->_testClient->get('/different-url'); 15 | $loggedRequests = self::$_wireMock->findUnmatchedRequests()->getRequests(); 16 | $loggedRequest = $loggedRequests[0]; 17 | 18 | // when 19 | $nearMissesResult = self::$_wireMock->findNearMissesFor($loggedRequest); 20 | 21 | // then 22 | $nearMisses = $nearMissesResult->getNearMisses(); 23 | $nearMiss = $nearMisses[0]; 24 | assertThat($nearMiss->getRequest()->getUrl(), equalTo('/different-url')); 25 | assertThat($nearMiss->getMapping()->getRequest()->getUrlMatchingStrategy()->getMatchingValue(), equalTo('/some-url')); 26 | assertThat($nearMiss->getRequestPattern(), nullValue()); 27 | assertThat($nearMiss->getMatchResult()->getDistance(), floatValue()); 28 | } 29 | 30 | public function testFindNearMissesForRequestPattern() 31 | { 32 | // given 33 | $requestPattern = WireMock::getRequestedFor(WireMock::urlEqualTo('/some-url')); 34 | $this->_testClient->get('/different-url'); 35 | 36 | // when 37 | $nearMissesResult = self::$_wireMock->findNearMissesFor($requestPattern); 38 | 39 | // then 40 | $nearMisses = $nearMissesResult->getNearMisses(); 41 | $nearMiss = $nearMisses[0]; 42 | assertThat($nearMiss->getRequest()->getUrl(), equalTo('/different-url')); 43 | assertThat($nearMiss->getMapping(), nullValue()); 44 | assertThat($nearMiss->getRequestPattern()->getUrlMatchingStrategy()->getMatchingValue(), equalTo('/some-url')); 45 | assertThat($nearMiss->getMatchResult()->getDistance(), floatValue()); 46 | } 47 | 48 | public function testFindNearMissesForAllUnmatched() 49 | { 50 | // given 51 | self::$_wireMock->stubFor(WireMock::get(WireMock::urlEqualTo('/some-url1')) 52 | ->willReturn(WireMock::aResponse())); 53 | self::$_wireMock->stubFor(WireMock::get(WireMock::urlEqualTo('/some-url2')) 54 | ->willReturn(WireMock::aResponse())); 55 | self::$_wireMock->stubFor(WireMock::get(WireMock::urlEqualTo('/some-url3')) 56 | ->willReturn(WireMock::aResponse())); 57 | self::$_wireMock->stubFor(WireMock::get(WireMock::urlEqualTo('/some-url4')) 58 | ->willReturn(WireMock::aResponse())); 59 | $this->_testClient->get('/different-url'); 60 | 61 | // when 62 | $nearMissesResult = self::$_wireMock->findNearMissesForAllUnmatched(); 63 | 64 | // then 65 | $nearMisses = $nearMissesResult->getNearMisses(); 66 | assertThat($nearMisses, arrayWithSize(3)); 67 | $nearMiss = $nearMisses[0]; 68 | assertThat($nearMiss->getRequest()->getUrl(), equalTo('/different-url')); 69 | assertThat($nearMiss->getMapping()->getRequest()->getUrlMatchingStrategy()->getMatchingValue(), startsWith('/some-url')); 70 | assertThat($nearMiss->getRequestPattern(), nullValue()); 71 | assertThat($nearMiss->getMatchResult()->getDistance(), floatValue()); 72 | } 73 | } -------------------------------------------------------------------------------- /test/WireMock/Integration/WebhookIntegrationTest.php: -------------------------------------------------------------------------------- 1 | stubFor(WireMock::post(WireMock::urlPathEqualTo("/something-async")) 14 | ->willReturn(WireMock::ok()) 15 | ->withPostServeAction("webhook", WireMock::webhook() 16 | ->withMethod(RequestMethod::POST) 17 | ->withUrl("http://my-target-host/callback") 18 | ->withHeader("Content-Type", "application/json") 19 | ->withBody("{ \"result\": \"SUCCESS\" }")) 20 | ); 21 | 22 | // then 23 | assertThatTheOnlyMappingPresentIs($stubMapping); 24 | } 25 | 26 | public function testWebhookCallbackWithBase64Body() 27 | { 28 | // when 29 | $packedResponseBody = pack('c*', 0x23, 0x59, 0x11); 30 | $stubMapping = self::$_wireMock->stubFor(WireMock::post(WireMock::urlPathEqualTo("/something-async")) 31 | ->willReturn(WireMock::ok()) 32 | ->withPostServeAction("webhook", WireMock::webhook() 33 | ->withMethod(RequestMethod::POST) 34 | ->withUrl("http://my-target-host/callback") 35 | ->withHeader("Content-Type", "application/json") 36 | ->withBodyData($packedResponseBody)) 37 | ); 38 | 39 | // then 40 | assertThatTheOnlyMappingPresentIs($stubMapping); 41 | } 42 | 43 | public function testWebhookCallbackWithFixedDelay() 44 | { 45 | // when 46 | $stubMapping = self::$_wireMock->stubFor(WireMock::post(WireMock::urlPathEqualTo("/delayed")) 47 | ->willReturn(WireMock::ok()) 48 | ->withPostServeAction("webhook", WireMock::webhook() 49 | ->withFixedDelay(1000) 50 | ->withMethod(RequestMethod::GET) 51 | ->withUrl("http://my-target-host/callback") 52 | ) 53 | ); 54 | 55 | // then 56 | assertThatTheOnlyMappingPresentIs($stubMapping); 57 | } 58 | 59 | public function testWebhookCallbackWithUniformRandomDelay() 60 | { 61 | // when 62 | $stubMapping = self::$_wireMock->stubFor(WireMock::post(WireMock::urlPathEqualTo("/delayed")) 63 | ->willReturn(WireMock::ok()) 64 | ->withPostServeAction("webhook", WireMock::webhook() 65 | ->withUniformRandomDelay(500, 1000) 66 | ->withMethod(RequestMethod::GET) 67 | ->withUrl("http://my-target-host/callback") 68 | ) 69 | ); 70 | 71 | // then 72 | assertThatTheOnlyMappingPresentIs($stubMapping); 73 | } 74 | 75 | public function testWebhookCallbackWithTemplatingBasedOnExtraParameter() 76 | { 77 | // when 78 | $stubMapping = self::$_wireMock->stubFor(WireMock::post(WireMock::urlPathEqualTo("/templating")) 79 | ->willReturn(WireMock::ok()) 80 | ->withPostServeAction("webhook", WireMock::webhook() 81 | ->withMethod(RequestMethod::GET) 82 | ->withUrl("http://my-target-host/callback") 83 | ->withHeader("X-Multi", "{{parameters.one}}") 84 | ->withExtraParameter("one", "param-one-value") 85 | ) 86 | ); 87 | 88 | // then 89 | assertThatTheOnlyMappingPresentIs($stubMapping); 90 | } 91 | } -------------------------------------------------------------------------------- /test/WireMock/Integration/MultipartIntegrationTest.php: -------------------------------------------------------------------------------- 1 | stubFor(WireMock::post(WireMock::anyUrl()) 14 | ->withMultipartRequestBody(WireMock::aMultipart()->withMultipartBody(WireMock::matching('abc'))) 15 | ->withMultipartRequestBody(WireMock::aMultipart()->withMultipartBody(WireMock::matching('def'))) 16 | ->willReturn(WireMock::aResponse()) 17 | ); 18 | 19 | // then 20 | assertThatTheOnlyMappingPresentIs($stubbing); 21 | assertThat($stubbing->getRequest()->getMultipartPatterns(), arrayWithSize(2)); 22 | $patterns = $stubbing->getRequest()->getMultipartPatterns(); 23 | assertThat($patterns[0]->getBodyPatterns()[0], equalTo(WireMock::matching('abc'))); 24 | assertThat($patterns[1]->getBodyPatterns()[0], equalTo(WireMock::matching('def'))); 25 | } 26 | 27 | public function testMultipartWithNameCanBeRegistered() 28 | { 29 | // when 30 | $stubbing = self::$_wireMock->stubFor(WireMock::post(WireMock::anyUrl()) 31 | ->withMultipartRequestBody(WireMock::aMultipart()->withName('partName')) 32 | ->willReturn(WireMock::aResponse()) 33 | ); 34 | 35 | // then 36 | assertThatTheOnlyMappingPresentIs($stubbing); 37 | $patterns = $stubbing->getRequest()->getMultipartPatterns(); 38 | assertThat($patterns[0]->getHeaders()['Content-Disposition'], equalTo(WireMock::containing('name="partName"'))); 39 | } 40 | 41 | public function testMultipartWithHeaderCanBeRegistered() 42 | { 43 | // when 44 | $stubbing = self::$_wireMock->stubFor(WireMock::post(WireMock::anyUrl()) 45 | ->withMultipartRequestBody(WireMock::aMultipart()->withHeader('X-Header', WireMock::containing('foo'))) 46 | ->willReturn(WireMock::aResponse()) 47 | ); 48 | 49 | // then 50 | assertThatTheOnlyMappingPresentIs($stubbing); 51 | $patterns = $stubbing->getRequest()->getMultipartPatterns(); 52 | assertThat($patterns[0]->getHeaders()['X-Header'], equalTo(WireMock::containing('foo'))); 53 | } 54 | 55 | public function testMultipartMatchingTypeDefaultsToAny() 56 | { 57 | // when 58 | $stubbing = self::$_wireMock->stubFor(WireMock::post(WireMock::anyUrl()) 59 | ->withMultipartRequestBody(WireMock::aMultipart()) 60 | ->willReturn(WireMock::aResponse()) 61 | ); 62 | 63 | // then 64 | assertThatTheOnlyMappingPresentIs($stubbing); 65 | $patterns = $stubbing->getRequest()->getMultipartPatterns(); 66 | assertThat($patterns[0]->getMatchingType(), equalTo(MultipartValuePattern::ANY)); 67 | } 68 | 69 | public function testMultipartMatchingTypeCanBetSetToAll() 70 | { 71 | // when 72 | $stubbing = self::$_wireMock->stubFor(WireMock::post(WireMock::anyUrl()) 73 | ->withMultipartRequestBody(WireMock::aMultipart()->matchingType(MultipartValuePattern::ALL)) 74 | ->willReturn(WireMock::aResponse()) 75 | ); 76 | 77 | // then 78 | assertThatTheOnlyMappingPresentIs($stubbing); 79 | $patterns = $stubbing->getRequest()->getMultipartPatterns(); 80 | assertThat($patterns[0]->getMatchingType(), equalTo(MultipartValuePattern::ALL)); 81 | } 82 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/SerdeProp.php: -------------------------------------------------------------------------------- 1 | name = $name; 47 | $this->owningClassName = $owningClassName; 48 | $this->serdeType = $serdeType; 49 | $this->propertyNamingStrategy = $propertyNamingStrategy; 50 | $this->unwrapped = $unwrapped; 51 | $this->catchAll = $catchAll; 52 | $this->includeInNormalizedForm = true; 53 | } 54 | 55 | /** 56 | * @throws SerializationException 57 | */ 58 | public function instantiateAndConsumeData(array &$data, array $path) 59 | { 60 | $path[] = $this->name; 61 | $name = $this->name; 62 | $propData = array_key_exists($name, $data) ? $data[$name] : null; 63 | unset($data[$name]); 64 | return $this->serdeType->denormalize($propData, $path); 65 | } 66 | 67 | /** 68 | * @throws ReflectionException 69 | */ 70 | public function getData(object $object) 71 | { 72 | $refProp = new ReflectionProperty($this->owningClassName, $this->name); 73 | $refProp->setAccessible(true); 74 | return $refProp->getValue($object); 75 | } 76 | 77 | public function getSerializedName(array $data): string 78 | { 79 | if (!$this->propertyNamingStrategy) { 80 | return $this->name; 81 | } 82 | return $this->propertyNamingStrategy->getSerializedName($data); 83 | } 84 | 85 | public function getPossibleSerializedNames(): array 86 | { 87 | if ($this->propertyNamingStrategy !== null) { 88 | return $this->propertyNamingStrategy->getPossibleSerializedNames(); 89 | } else { 90 | return [$this->name]; 91 | } 92 | } 93 | 94 | /** 95 | * @return SerdeTypeClass 96 | * @throws SerializationException 97 | */ 98 | public function getPotentiallyNullableSerdeTypeClassOrThrow(): SerdeTypeClass { 99 | if ($this->serdeType instanceof SerdeTypeClass) { 100 | return $this->serdeType; 101 | } elseif ($this->serdeType instanceof SerdeTypeUnion) { 102 | if ($this->serdeType->isNullableClass()) { 103 | return $this->serdeType->getClassTypeOrThrow(); 104 | } 105 | } 106 | throw new SerializationException("Cannot get SerdeTypeClass for prop $this->name because it is of type " . $this->serdeType->displayName()); 107 | } 108 | } -------------------------------------------------------------------------------- /src/WireMock/PostServe/WebhookDefinition.php: -------------------------------------------------------------------------------- 1 | method = $method; 33 | return $this; 34 | } 35 | 36 | public function withUrl(string $url): self 37 | { 38 | $this->url = $url; 39 | return $this; 40 | } 41 | 42 | public function withHeader(string $header, string $value): self 43 | { 44 | $this->headers[$header] = $value; 45 | return $this; 46 | } 47 | 48 | public function withBody(string $body): self 49 | { 50 | $this->body = $body; 51 | return $this; 52 | } 53 | 54 | public function withBodyData(string $bytesAsString): self 55 | { 56 | $base64 = base64_encode($bytesAsString); 57 | $this->base64Body = $base64; 58 | return $this; 59 | } 60 | 61 | public function withFixedDelay(int $delayMillis): self 62 | { 63 | $this->delay = new FixedDelay($delayMillis); 64 | return $this; 65 | } 66 | 67 | public function withRandomDelay(DelayDistribution $delayDistribution): self 68 | { 69 | $this->delay = $delayDistribution; 70 | return $this; 71 | } 72 | 73 | public function withLogNormalRandomDelay(float $median, float $sigma): self 74 | { 75 | return $this->withRandomDelay(new LogNormal($median, $sigma)); 76 | } 77 | 78 | public function withUniformRandomDelay(int $lower, int $upper): self 79 | { 80 | return $this->withRandomDelay(new UniformDistribution($lower, $upper)); 81 | } 82 | 83 | public function withExtraParameter(string $name, $value): self 84 | { 85 | $this->extraParameters[$name] = $value; 86 | return $this; 87 | } 88 | 89 | // TODO: Split out getters and fluent builder methods into separate classes 90 | 91 | /** 92 | * @return string|null 93 | */ 94 | public function getMethod(): ?string 95 | { 96 | return $this->method; 97 | } 98 | 99 | /** 100 | * @return string|null 101 | */ 102 | public function getUrl(): ?string 103 | { 104 | return $this->url; 105 | } 106 | 107 | /** 108 | * @return string[]|null 109 | */ 110 | public function getHeaders(): ?array 111 | { 112 | return $this->headers; 113 | } 114 | 115 | /** 116 | * @return string|null 117 | */ 118 | public function getBody(): ?string 119 | { 120 | return $this->body; 121 | } 122 | 123 | /** 124 | * @return string|null 125 | */ 126 | public function getBase64Body(): ?string 127 | { 128 | return $this->base64Body; 129 | } 130 | 131 | /** 132 | * @return DelayDistribution|null 133 | */ 134 | public function getDelay(): ?DelayDistribution 135 | { 136 | return $this->delay; 137 | } 138 | 139 | /** 140 | * @return array|null 141 | */ 142 | public function getExtraParameters(): ?array 143 | { 144 | return $this->extraParameters; 145 | } 146 | } -------------------------------------------------------------------------------- /test/WireMock/Stubbing/StubMappingTest.php: -------------------------------------------------------------------------------- 1 | _serializer = SerializerFactory::default(); 23 | $this->_mockRequestPattern = Phake::mock(RequestPattern::class); 24 | $this->_mockResponseDefinition = Phake::mock(ResponseDefinition::class); 25 | } 26 | 27 | private function toArray($obj) 28 | { 29 | return $this->_serializer->normalize($obj); 30 | } 31 | 32 | public function testRequestPatternAndResponseDefinitionAreAvailableInArray() 33 | { 34 | // given 35 | $stubMapping = new StubMapping( 36 | new RequestPattern('GET', WireMock::anyUrl()), 37 | new ResponseDefinition(200) 38 | ); 39 | 40 | // when 41 | $stubMappingArray = $this->toArray($stubMapping); 42 | 43 | // then 44 | assertThat($stubMappingArray, hasEntry('request', $this->toArray($stubMapping->getRequest()))); 45 | assertThat($stubMappingArray, hasEntry('response', $this->toArray($stubMapping->getResponse()))); 46 | } 47 | 48 | public function testIdIsInArrayIfSpecified() 49 | { 50 | // given 51 | $stubMapping = new StubMapping( 52 | new RequestPattern('GET', WireMock::anyUrl()), 53 | new ResponseDefinition(200), 54 | 'some-long-guid' 55 | ); 56 | 57 | // when 58 | $stubMappingArray = $this->toArray($stubMapping); 59 | 60 | // then 61 | assertThat($stubMappingArray, hasEntry('id', 'some-long-guid')); 62 | } 63 | 64 | public function testNameIsInArrayIfSpecified() 65 | { 66 | // given 67 | $stubMapping = new StubMapping( 68 | new RequestPattern('GET', WireMock::anyUrl()), 69 | new ResponseDefinition(200), 70 | null, 71 | 'stub-name' 72 | ); 73 | 74 | // when 75 | $stubMappingArray = $this->toArray($stubMapping); 76 | 77 | // then 78 | assertThat($stubMappingArray, hasEntry('name', 'stub-name')); 79 | } 80 | 81 | public function testPriorityIsInArrayIfSpecified() 82 | { 83 | // given 84 | $stubMapping = new StubMapping( 85 | new RequestPattern('GET', WireMock::anyUrl()), 86 | new ResponseDefinition(200), 87 | null, 88 | null, 89 | 5 90 | ); 91 | 92 | // when 93 | $stubMappingArray = $this->toArray($stubMapping); 94 | 95 | // then 96 | assertThat($stubMappingArray, hasEntry('priority', 5)); 97 | } 98 | 99 | public function testScenarioArrayIsMergedIntoArrayIfSpecified() 100 | { 101 | // given 102 | $scenarioMapping = new ScenarioMapping('Some Scenario', 'from', 'to'); 103 | $stubMapping = new StubMapping( 104 | new RequestPattern('GET', WireMock::anyUrl()), 105 | new ResponseDefinition(200), 106 | null, 107 | null, 108 | null, 109 | $scenarioMapping 110 | ); 111 | 112 | // when 113 | $stubMappingArray = $this->toArray($stubMapping); 114 | 115 | // then 116 | assertThat($stubMappingArray, hasEntry('scenarioName', 'Some Scenario')); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/WireMock/Serde/SerdeClassDefinition.php: -------------------------------------------------------------------------------- 1 | classDiscriminationInfo = $classDiscriminationInfo; 25 | $this->constructorArgProps = $constructorArgProps; 26 | $this->properties = $properties; 27 | } 28 | 29 | /** 30 | * @return SerdeProp[] 31 | */ 32 | public function getConstructorArgProps(): array 33 | { 34 | return $this->constructorArgProps; 35 | } 36 | 37 | /** 38 | * @throws SerializationException 39 | */ 40 | public function getPropertyByPhpName(string $name): ?SerdeProp 41 | { 42 | $matchingProps = array_filter($this->properties, function($prop) use ($name) { 43 | return $prop->name === $name; 44 | }); 45 | if (count($matchingProps) === 0) { 46 | return null; 47 | } elseif (count($matchingProps) === 1) { 48 | return current($matchingProps); 49 | } else { 50 | throw new SerializationException("Expected 0 or 1 prop to match serialized name $name but found multiple"); 51 | } 52 | } 53 | 54 | /** 55 | * @throws SerializationException 56 | */ 57 | public function getCatchAllProp(): ?SerdeProp 58 | { 59 | $matchingProps = array_filter($this->properties, function($prop) { 60 | return $prop->catchAll; 61 | }); 62 | if (count($matchingProps) === 0) { 63 | return null; 64 | } elseif (count($matchingProps) === 1) { 65 | return current($matchingProps); 66 | } else { 67 | throw new SerializationException("Expected 0 or 1 prop to be marked @serde-catch-all but found multiple"); 68 | } 69 | } 70 | 71 | /** 72 | * @return SerdeProp[] 73 | */ 74 | public function getAllPropertiesAndArgs(): array 75 | { 76 | return $this->properties; 77 | } 78 | 79 | public function isPossibleSerializedName(string $name): bool 80 | { 81 | foreach ($this->properties as $prop) { 82 | foreach ($prop->getPossibleSerializedNames() as $possibleName) { 83 | if ($name === $possibleName) { 84 | return true; 85 | } 86 | } 87 | } 88 | return false; 89 | } 90 | 91 | /** 92 | * @throws SerializationException 93 | */ 94 | public function isPhpName(string $name): bool 95 | { 96 | return $this->getPropertyByPhpName($name) !== null; 97 | } 98 | 99 | /** 100 | * @throws ReflectionException 101 | */ 102 | public function getDiscriminator(): ?ClassDiscriminator 103 | { 104 | if ($this->classDiscriminationInfo === null) { 105 | return null; 106 | } 107 | return $this->classDiscriminationInfo->getDiscriminator(); 108 | } 109 | 110 | /** 111 | * @throws SerializationException 112 | */ 113 | public function getDiscriminatedType(string $fqn): SerdeTypeClass 114 | { 115 | return $this->classDiscriminationInfo->getTypeByFullyQualifiedClassName($fqn); 116 | } 117 | } -------------------------------------------------------------------------------- /test/WireMock/Client/MappingBuilderTest.php: -------------------------------------------------------------------------------- 1 | build()->thenReturn($mockRequestPattern); 20 | $this->_mockRequestPatternBuilder = $mockRequestPatternBuilder; 21 | 22 | $mockResponseDefinition = Phake::mock(ResponseDefinition::class); 23 | 24 | $mockResponseDefinitionBuilder = Phake::mock(ResponseDefinitionBuilder::class); 25 | Phake::when($mockResponseDefinitionBuilder)->build()->thenReturn($mockResponseDefinition); 26 | $this->_mockResponseDefinitionBuilder = $mockResponseDefinitionBuilder; 27 | } 28 | 29 | /** 30 | * @throws \Exception 31 | */ 32 | public function testBuiltStubMappingHasRequestPatternAndResponseDefinition() 33 | { 34 | // given 35 | $mappingBuilder = new MappingBuilder($this->_mockRequestPatternBuilder); 36 | $mappingBuilder->willReturn($this->_mockResponseDefinitionBuilder); 37 | 38 | // when 39 | $stubMapping = $mappingBuilder->build(); 40 | 41 | // then 42 | assertThat($stubMapping->getRequest(), notNullValue()); 43 | assertThat($stubMapping->getResponse(), notNullValue()); 44 | } 45 | 46 | /** 47 | * @throws \Exception 48 | */ 49 | public function testMatchedRequestHeadersAreSetOnRequestPattern() 50 | { 51 | // given 52 | $mappingBuilder = new MappingBuilder($this->_mockRequestPatternBuilder); 53 | $mappingBuilder->willReturn($this->_mockResponseDefinitionBuilder); 54 | $headerName = 'aHeader'; 55 | $valueMatchingStrategy = new ValueMatchingStrategy('equalTo', 'aValue'); 56 | 57 | // when 58 | $mappingBuilder->withHeader($headerName, $valueMatchingStrategy)->build(); 59 | 60 | // then 61 | Phake::verify($this->_mockRequestPatternBuilder)->withHeader($headerName, $valueMatchingStrategy); 62 | } 63 | 64 | /** 65 | * @throws \Exception 66 | */ 67 | public function testMatchedRequestQueryParamsAreSetOnRequestPattern() 68 | { 69 | // given 70 | $mappingBuilder = new MappingBuilder($this->_mockRequestPatternBuilder); 71 | $mappingBuilder->willReturn($this->_mockResponseDefinitionBuilder); 72 | $paramName = 'aParam'; 73 | $valueMatchingStrategy = new ValueMatchingStrategy('equalTo', 'aValue'); 74 | 75 | // when 76 | $mappingBuilder->withQueryParam($paramName, $valueMatchingStrategy)->build(); 77 | 78 | // then 79 | Phake::verify($this->_mockRequestPatternBuilder)->withQueryParam($paramName, $valueMatchingStrategy); 80 | } 81 | 82 | /** 83 | * @throws \Exception 84 | */ 85 | public function testRequestBodyMatcherIsSetOnRequestPattern() 86 | { 87 | // given 88 | $mappingBuilder = new MappingBuilder($this->_mockRequestPatternBuilder); 89 | $mappingBuilder->willReturn($this->_mockResponseDefinitionBuilder); 90 | 91 | // when 92 | $valueMatchingStrategy = new ValueMatchingStrategy('matches', 'aValue'); 93 | $mappingBuilder->withRequestBody($valueMatchingStrategy) 94 | ->build(); 95 | 96 | // then 97 | Phake::verify($this->_mockRequestPatternBuilder)->withRequestBody($valueMatchingStrategy); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/WireMock/Client/DateTimeMatchingStrategy.php: -------------------------------------------------------------------------------- 1 | expectedOffset = new DateTimeMatchExpectedOffset($amount, $unit); 51 | return $this; 52 | } 53 | 54 | /** 55 | * @param string $format 56 | * @return $this 57 | */ 58 | public function actualFormat($format) 59 | { 60 | $this->actualFormat = $format; 61 | return $this; 62 | } 63 | 64 | /** 65 | * @param string $truncationType One of the truncation type constants on DateTimeMatchingStrategy 66 | * @return $this 67 | */ 68 | public function truncateExpected($truncationType) 69 | { 70 | $this->truncateExpected = $truncationType; 71 | return $this; 72 | } 73 | 74 | /** 75 | * @param string $truncationType One of the truncation type constants on DateTimeMatchingStrategy 76 | * @return $this 77 | */ 78 | public function truncateActual($truncationType) 79 | { 80 | $this->truncateActual = $truncationType; 81 | return $this; 82 | } 83 | 84 | /** 85 | * @return DateTimeMatchExpectedOffset|null 86 | */ 87 | public function getExpectedOffset(): ?DateTimeMatchExpectedOffset 88 | { 89 | return $this->expectedOffset; 90 | } 91 | 92 | /** 93 | * @return string|null 94 | */ 95 | public function getActualFormat(): ?string 96 | { 97 | return $this->actualFormat; 98 | } 99 | 100 | /** 101 | * @return string|null 102 | */ 103 | public function getTruncateActual(): ?string 104 | { 105 | return $this->truncateActual; 106 | } 107 | 108 | /** 109 | * @return string|null 110 | */ 111 | public function getTruncateExpected(): ?string 112 | { 113 | return $this->truncateExpected; 114 | } 115 | 116 | public static function before($dateTimeSpec) 117 | { 118 | return new self("before", $dateTimeSpec); 119 | } 120 | 121 | public static function equalToDateTime($dateTimeSpec) 122 | { 123 | return new self("equalToDateTime", $dateTimeSpec); 124 | } 125 | 126 | public static function after($dateTimeSpec) 127 | { 128 | return new self("after", $dateTimeSpec); 129 | } 130 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/Type/SerdeTypeUnion.php: -------------------------------------------------------------------------------- 1 | primitiveSerdeTypes = $primitiveSerdeTypes; 22 | $this->classOrArraySerdeType = $classOrArraySerdeType; 23 | } 24 | 25 | static function setSubtypes(SerdeTypeUnion $serdeType, array $primitiveSerdeTypes, $classOrArraySerdeType) 26 | { 27 | $serdeType->primitiveSerdeTypes = $primitiveSerdeTypes; 28 | $serdeType->classOrArraySerdeType = $classOrArraySerdeType; 29 | } 30 | 31 | function displayName(): string 32 | { 33 | $types = $this->primitiveSerdeTypes; 34 | if ($this->classOrArraySerdeType !== null) { 35 | $types[] = $this->classOrArraySerdeType; 36 | } 37 | $typeDisplayNames = array_map(function($t) { return '('.$t->displayName().')'; }, $types); 38 | return join('|', $typeDisplayNames); 39 | } 40 | 41 | function canDenormalize($data): bool 42 | { 43 | if ($this->classOrArraySerdeType !== null && $this->classOrArraySerdeType->canDenormalize($data)) { 44 | return true; 45 | } 46 | foreach ($this->primitiveSerdeTypes as $serdeType) { 47 | if ($serdeType->canDenormalize($data)) { 48 | return true; 49 | } 50 | } 51 | return false; 52 | } 53 | 54 | /** 55 | * @throws ReflectionException 56 | * @throws SerializationException 57 | */ 58 | function denormalize(&$data, array $path) 59 | { 60 | if ($this->classOrArraySerdeType !== null && $this->classOrArraySerdeType->canDenormalize($data)) { 61 | return $this->classOrArraySerdeType->denormalize($data, $path); 62 | } 63 | 64 | foreach ($this->primitiveSerdeTypes as $serdeType) { 65 | if ($serdeType->canDenormalize($data)) { 66 | return $serdeType->denormalize($data, $path); 67 | } 68 | } 69 | 70 | $dataType = gettype($data); 71 | $targetType = $this->displayName(); 72 | throw new SerializationException("Cannot denormalize data of type $dataType to $targetType"); 73 | } 74 | 75 | /** 76 | * @return bool Whether the union represents either a nullable class type (and nothing more) 77 | */ 78 | function isNullableClass(): bool 79 | { 80 | return $this->classOrArraySerdeType instanceof SerdeTypeClass && 81 | count($this->primitiveSerdeTypes) === 1 && 82 | $this->primitiveSerdeTypes[0] instanceof SerdeTypeNull; 83 | } 84 | 85 | /** 86 | * @return bool Whether the union represents either a nullable class type (and nothing more) 87 | */ 88 | function isNullableArray(): bool 89 | { 90 | return $this->classOrArraySerdeType instanceof SerdeTypeArray && 91 | count($this->primitiveSerdeTypes) === 1 && 92 | $this->primitiveSerdeTypes[0] instanceof SerdeTypeNull; 93 | } 94 | 95 | /** 96 | * @throws SerializationException 97 | */ 98 | function getClassTypeOrThrow(): SerdeTypeClass 99 | { 100 | if (!($this->classOrArraySerdeType instanceof SerdeTypeClass)) { 101 | throw new SerializationException('Expected a SerdeTypeClass but was ' . get_class($this->classOrArraySerdeType)); 102 | } 103 | return $this->classOrArraySerdeType; 104 | } 105 | } -------------------------------------------------------------------------------- /src/WireMock/Recording/RecordSpec.php: -------------------------------------------------------------------------------- 1 | targetBaseUrl = $targetBaseUrl; 56 | $this->filters = $filters; 57 | $this->captureHeaders = $captureHeaders; 58 | $this->extractBodyCriteria = $extractBodyCriteria; 59 | $this->persist = $persist; 60 | $this->repeatsAsScenarios = $repeatsAsScenarios; 61 | $this->transformers = $transformers; 62 | $this->transformerParameters = $transformerParameters; 63 | $this->requestBodyPattern = $requestBodyPattern; 64 | $this->outputFormat = $outputFormat; 65 | } 66 | 67 | /** 68 | * @return string 69 | */ 70 | public function getTargetBaseUrl(): ?string 71 | { 72 | return $this->targetBaseUrl; 73 | } 74 | 75 | /** 76 | * @return ProxiedServeEventFilters|null 77 | */ 78 | public function getFilters(): ?ProxiedServeEventFilters 79 | { 80 | return $this->filters; 81 | } 82 | 83 | /** 84 | * @return array|null 85 | */ 86 | public function getCaptureHeaders(): ?array 87 | { 88 | return $this->captureHeaders; 89 | } 90 | 91 | /** 92 | * @return array|null 93 | */ 94 | public function getExtractBodyCriteria(): ?array 95 | { 96 | return $this->extractBodyCriteria; 97 | } 98 | 99 | /** 100 | * @return bool 101 | */ 102 | public function isPersist(): bool 103 | { 104 | return $this->persist; 105 | } 106 | 107 | /** 108 | * @return bool 109 | */ 110 | public function isRepeatsAsScenarios(): bool 111 | { 112 | return $this->repeatsAsScenarios; 113 | } 114 | 115 | /** 116 | * @return string[]|null 117 | */ 118 | public function getTransformers(): ?array 119 | { 120 | return $this->transformers; 121 | } 122 | 123 | /** 124 | * @return array|null 125 | */ 126 | public function getTransformerParameters(): ?array 127 | { 128 | return $this->transformerParameters; 129 | } 130 | 131 | /** 132 | * @return array 133 | */ 134 | public function getRequestBodyPattern(): ?array 135 | { 136 | return $this->requestBodyPattern; 137 | } 138 | 139 | /** 140 | * @return string 141 | */ 142 | public function getOutputFormat(): ?string 143 | { 144 | return $this->outputFormat; 145 | } 146 | } -------------------------------------------------------------------------------- /src/WireMock/Serde/Serializer.php: -------------------------------------------------------------------------------- 1 | serdeTypeLookup = $serdeTypeLookup; 20 | } 21 | 22 | /** 23 | * Serializes data object into JSON 24 | * 25 | * @param mixed $object object to serialize 26 | * @return string JSON serialization of object 27 | * @throws ReflectionException|SerializationException 28 | */ 29 | public function serialize($object): string 30 | { 31 | $normalizedArray = $this->normalize($object, true); 32 | return json_encode($normalizedArray, JSON_UNESCAPED_SLASHES); 33 | } 34 | 35 | /** 36 | * @param mixed $object object to normalize 37 | * @param bool $isRoot whether this is the root object being normalized 38 | * @param ?SerdeType $serdeType the SerdeType of the class property being normalized (if applicable) 39 | * @return mixed An associative array or a primitive type 40 | * @throws ReflectionException 41 | * @throws SerializationException 42 | */ 43 | public function normalize($object, bool $isRoot = false, $serdeType = null) 44 | { 45 | if (is_object($object)) { 46 | $type = get_class($object); 47 | if ($isRoot === true && $this->serdeTypeLookup->isRootType($type) === false) { 48 | fwrite(STDERR, "Warning: serializing from $type, but this is not a root type\n"); 49 | } 50 | /** @var ?SerdeTypeClass $serdeType */ 51 | $serdeType = $this->serdeTypeLookup->getSerdeTypeIfExits($type); 52 | if ($serdeType) { 53 | $result = $serdeType->normalize($object, $this); 54 | } else { 55 | // There's no SerdeType for this class, so we just return it as is, and let json_decode deal with it 56 | // This allows users to supply objects of unregistered types, perhaps as values within an untyped array, 57 | // and have them serialized (in a way they can control with JsonSerializable) 58 | return $object; 59 | } 60 | } elseif (is_array($object)) { 61 | $result = array_map( 62 | function($value) { return $this->normalize($value); }, 63 | $object 64 | ); 65 | 66 | if ($serdeType instanceof SerdeTypeAssocArray && empty($object)) { 67 | // We want empty associative arrays to be serialize as "{}" rather than "[]", so we use an empty stdClass 68 | // object rather than an empty PHP array as the normalized value 69 | $result = new stdClass(); 70 | } 71 | } else { 72 | $result = $object; 73 | } 74 | 75 | return $result; 76 | } 77 | 78 | /** 79 | * Deserializes JSON into data object of the given type 80 | * 81 | * @param string $json 82 | * @param string $type 83 | * 84 | * @return mixed 85 | * @throws SerializationException 86 | */ 87 | public function deserialize(string $json, string $type) 88 | { 89 | $data = json_decode($json, true); 90 | return $this->denormalize($data, $type, true); 91 | } 92 | 93 | /** 94 | * @throws SerializationException 95 | */ 96 | public function denormalize(&$data, string $type, bool $isRoot = false) 97 | { 98 | $serdeType = $this->serdeTypeLookup->getSerdeType($type); 99 | if ($isRoot === true && $this->serdeTypeLookup->isRootType($type) === false) { 100 | fwrite(STDERR, "Warning: deserializing to $type, but this is not a root type\n"); 101 | } 102 | return $serdeType->denormalize($data, []); 103 | } 104 | } -------------------------------------------------------------------------------- /src/WireMock/Client/ValueMatchingStrategy.php: -------------------------------------------------------------------------------- 1 | ValueMatchingStrategy::class, 23 | 'binaryEqualTo' => ValueMatchingStrategy::class, 24 | 'contains' => ValueMatchingStrategy::class, 25 | 'doesNotContain' => ValueMatchingStrategy::class, 26 | 'matches' => ValueMatchingStrategy::class, 27 | 'doesNotMatch' => ValueMatchingStrategy::class, 28 | 29 | 'before' => DateTimeMatchingStrategy::class, 30 | 'equalToDateTime' => DateTimeMatchingStrategy::class, 31 | 'after' => DateTimeMatchingStrategy::class, 32 | 33 | 'equalTo' => EqualToMatchingStrategy::class, 34 | 35 | 'matchesXPath' => XPathValueMatchingStrategy::class, 36 | 37 | 'equalToXml' => EqualToXmlMatchingStrategy::class, 38 | 39 | 'matchesJsonPath' => JsonPathValueMatchingStrategy::class, 40 | 41 | 'equalToJson' => JsonValueMatchingStrategy::class, 42 | 43 | 'and' => LogicalOperatorMatchingStrategy::class, 44 | 'or' => LogicalOperatorMatchingStrategy::class, 45 | ]; 46 | /** @noinspection PhpUnusedPrivateMethodInspection */ 47 | private static function matchingValueNames(): array { return array_keys(self::$subclassByMatchingType); } 48 | 49 | /** @var string */ 50 | protected $matchingType; 51 | /** 52 | * @var string|boolean|ValueMatchingStrategy[] 53 | * @serde-named-by matchingType 54 | * @serde-possible-names matchingValueNames 55 | */ 56 | protected $matchingValue; 57 | 58 | public function __construct($matchingType, $matchingValue) 59 | { 60 | $this->matchingType = $matchingType; 61 | $this->matchingValue = $matchingValue; 62 | } 63 | 64 | /** 65 | * @return string 66 | */ 67 | public function getMatchingType(): string 68 | { 69 | return $this->matchingType; 70 | } 71 | 72 | /** 73 | * @return bool|string|ValueMatchingStrategy[] 74 | */ 75 | public function getMatchingValue() 76 | { 77 | return $this->matchingValue; 78 | } 79 | 80 | public function and(ValueMatchingStrategy $other) 81 | { 82 | return LogicalOperatorMatchingStrategy::andAll($this, $other); 83 | } 84 | 85 | public function or(ValueMatchingStrategy $other) 86 | { 87 | return LogicalOperatorMatchingStrategy::orAll($this, $other); 88 | } 89 | 90 | /** @noinspection PhpUnusedPrivateMethodInspection */ 91 | private static function getDiscriminatorMapping(): ClassDiscriminator 92 | { 93 | return new class(self::$subclassByMatchingType) implements ClassDiscriminator { 94 | private $subclassByMatchingType; 95 | public function __construct($subclassByMatchingType) 96 | { 97 | $this->subclassByMatchingType = $subclassByMatchingType; 98 | } 99 | 100 | function getDiscriminatedType($data): string 101 | { 102 | foreach ($data as $key => $value) { 103 | if (isset($this->subclassByMatchingType[$key])) { 104 | return $this->subclassByMatchingType[$key]; 105 | } 106 | } 107 | throw new SerializationException("Cannot discriminate subclass of ValueMatchingStrategy"); 108 | } 109 | }; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/WireMock/Stubbing/StubMapping.php: -------------------------------------------------------------------------------- 1 | id = $id; 59 | $this->name = $name; 60 | $this->request = $request; 61 | $this->response = $response; 62 | $this->priority = $priority; 63 | $this->metadata = $metadata; 64 | $this->persistent = $persistent; 65 | $this->postServeActions = $postServeActions; 66 | 67 | if ($scenarioMapping) { 68 | $this->scenarioName = $scenarioMapping->getScenarioName(); 69 | $this->requiredScenarioState = $scenarioMapping->getRequiredScenarioState(); 70 | $this->newScenarioState = $scenarioMapping->getNewScenarioState(); 71 | } 72 | } 73 | 74 | /** 75 | * @return string 76 | */ 77 | public function getId() 78 | { 79 | return $this->id; 80 | } 81 | 82 | /** 83 | * @param string $id 84 | */ 85 | public function setId($id) 86 | { 87 | $this->id = $id; 88 | } 89 | 90 | /** 91 | * @return string|null 92 | */ 93 | public function getName() 94 | { 95 | return $this->name; 96 | } 97 | 98 | /** 99 | * @return RequestPattern 100 | */ 101 | public function getRequest() 102 | { 103 | return $this->request; 104 | } 105 | 106 | /** 107 | * @return ResponseDefinition 108 | */ 109 | public function getResponse() 110 | { 111 | return $this->response; 112 | } 113 | 114 | /** 115 | * @return int 116 | */ 117 | public function getPriority() 118 | { 119 | return $this->priority; 120 | } 121 | 122 | /** 123 | * @return array 124 | */ 125 | public function getMetadata() 126 | { 127 | return $this->metadata; 128 | } 129 | 130 | /** 131 | * @return boolean|null 132 | */ 133 | public function isPersistent() 134 | { 135 | return $this->persistent; 136 | } 137 | 138 | /** 139 | * @return string 140 | */ 141 | public function getScenarioName() 142 | { 143 | return $this->scenarioName; 144 | } 145 | 146 | /** 147 | * @return string 148 | */ 149 | public function getRequiredScenarioState() 150 | { 151 | return $this->requiredScenarioState; 152 | } 153 | 154 | /** 155 | * @return string 156 | */ 157 | public function getNewScenarioState() 158 | { 159 | return $this->newScenarioState; 160 | } 161 | 162 | /** 163 | * @return PostServeAction[]|null 164 | */ 165 | public function getPostServeActions(): ?array 166 | { 167 | return $this->postServeActions; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/WireMock/Matching/RequestPattern.php: -------------------------------------------------------------------------------- 1 | |null */ 19 | private $headers; 20 | /** @var array|null */ 21 | private $cookies; 22 | /** @var array|null */ 23 | private $queryParameters; 24 | /** @var ValueMatchingStrategy[]|null */ 25 | private $bodyPatterns; 26 | /** @var MultipartValuePattern[]|null */ 27 | private $multipartPatterns; 28 | /** @var ?BasicCredentials */ 29 | private $basicAuthCredentials; 30 | /** @var ?CustomMatcherDefinition */ 31 | private $customMatcher; 32 | /** @var ?ValueMatchingStrategy */ 33 | private $host; 34 | 35 | /** 36 | * @param string $method 37 | * @param UrlMatchingStrategy $urlMatchingStrategy 38 | * @param array|null $headers 39 | * @param array|null $cookies 40 | * @param ValueMatchingStrategy[]|null $bodyPatterns 41 | * @param ValueMatchingStrategy[] $multipartPatterns 42 | * @param array|null $queryParameters 43 | * @param BasicCredentials $basicCredentials 44 | * @param CustomMatcherDefinition $customMatcherDefinition 45 | * @param ValueMatchingStrategy $hostPattern 46 | */ 47 | public function __construct( 48 | $method, 49 | $urlMatchingStrategy, 50 | $headers = null, 51 | $cookies = null, 52 | $bodyPatterns = null, 53 | $multipartPatterns = null, 54 | $queryParameters = null, 55 | $basicCredentials = null, 56 | $customMatcherDefinition = null, 57 | $hostPattern = null 58 | ) { 59 | $this->method = $method; 60 | $this->urlMatchingStrategy = $urlMatchingStrategy; 61 | $this->headers = $headers; 62 | $this->cookies = $cookies; 63 | $this->bodyPatterns = $bodyPatterns; 64 | $this->queryParameters = $queryParameters; 65 | $this->basicAuthCredentials = $basicCredentials; 66 | $this->multipartPatterns = $multipartPatterns; 67 | $this->customMatcher = $customMatcherDefinition; 68 | $this->host = $hostPattern; 69 | } 70 | 71 | /** 72 | * @return string 73 | */ 74 | public function getMethod() 75 | { 76 | return $this->method; 77 | } 78 | 79 | /** 80 | * @return UrlMatchingStrategy 81 | */ 82 | public function getUrlMatchingStrategy() 83 | { 84 | return $this->urlMatchingStrategy; 85 | } 86 | 87 | /** 88 | * @return array 89 | */ 90 | public function getHeaders() 91 | { 92 | return $this->headers; 93 | } 94 | 95 | /** 96 | * @return array 97 | */ 98 | public function getCookies() 99 | { 100 | return $this->cookies; 101 | } 102 | 103 | /** 104 | * @return array 105 | */ 106 | public function getQueryParameters() 107 | { 108 | return $this->queryParameters; 109 | } 110 | 111 | /** 112 | * @return ValueMatchingStrategy[] 113 | */ 114 | public function getBodyPatterns() 115 | { 116 | return $this->bodyPatterns; 117 | } 118 | 119 | /** 120 | * @return ValueMatchingStrategy[] 121 | */ 122 | public function getMultipartPatterns() 123 | { 124 | return $this->multipartPatterns; 125 | } 126 | 127 | /** 128 | * @return BasicCredentials 129 | */ 130 | public function getBasicAuthCredentials() 131 | { 132 | return $this->basicAuthCredentials; 133 | } 134 | 135 | /** 136 | * @return CustomMatcherDefinition 137 | */ 138 | public function getCustomMatcher() 139 | { 140 | return $this->customMatcher; 141 | } 142 | 143 | /** 144 | * @return ValueMatchingStrategy|null 145 | */ 146 | public function getHost() 147 | { 148 | return $this->host; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /test/WireMock/Client/RequestPatternBuilderTest.php: -------------------------------------------------------------------------------- 1 | build(); 20 | 21 | // then 22 | assertThat($requestPattern->getMethod(), is($method)); 23 | assertThat($requestPattern->getUrlMatchingStrategy(), is($matchingStrategy)); 24 | } 25 | 26 | public function testHeaderWithValueMatchingStrategyIsInArrayIfSpecified() 27 | { 28 | // given 29 | $requestPatternBuilder = new RequestPatternBuilder('GET', WireMock::urlEqualTo('/some/url')); 30 | $matchingStrategy = WireMock::equalTo('something'); 31 | 32 | // when 33 | $requestPatternBuilder->withHeader('Some-Header', $matchingStrategy); 34 | $requestPattern = $requestPatternBuilder->build(); 35 | 36 | // then 37 | assertThat($requestPattern->getHeaders(), hasEntry('Some-Header', $matchingStrategy)); 38 | } 39 | 40 | public function testHeaderAbsenceIsInArrayIfSpecified() 41 | { 42 | // given 43 | $requestPatternBuilder = new RequestPatternBuilder('GET', WireMock::urlEqualTo('/some/url')); 44 | $matchingStrategy = new ValueMatchingStrategy('absent', true); 45 | 46 | // when 47 | $requestPatternBuilder->withoutHeader('Some-Header'); 48 | $requestPattern = $requestPatternBuilder->build(); 49 | 50 | // then 51 | assertThat($requestPattern->getHeaders(), hasEntry('Some-Header', $matchingStrategy)); 52 | } 53 | 54 | public function testCookieWithValueMatchingStrategyIsInArrayIfSpecified() 55 | { 56 | // given 57 | $requestPatternBuilder = new RequestPatternBuilder('GET', WireMock::urlEqualTo('/some/url')); 58 | $matchingStrategy = WireMock::equalTo('something'); 59 | 60 | // when 61 | $requestPatternBuilder->withCookie('aCookie', $matchingStrategy); 62 | $requestPattern = $requestPatternBuilder->build(); 63 | 64 | // then 65 | assertThat($requestPattern->getCookies(), hasEntry('aCookie', $matchingStrategy)); 66 | } 67 | 68 | public function testRequestBodyPatternsAreInArrayIfSpecified() 69 | { 70 | // given 71 | $requestPatternBuilder = new RequestPatternBuilder('GET', WireMock::urlEqualTo('/some/url')); 72 | $matchingStrategy = new ValueMatchingStrategy('equalTo', 'aValue'); 73 | 74 | // when 75 | $requestPatternBuilder->withRequestBody($matchingStrategy); 76 | $requestPattern = $requestPatternBuilder->build(); 77 | 78 | // then 79 | assertThat($requestPattern->getBodyPatterns(), hasItem($matchingStrategy)); 80 | } 81 | 82 | public function testBasicAuthIsInArrayIfSpecified() 83 | { 84 | // given 85 | $requestPatternBuilder = new RequestPatternBuilder('GET', WireMock::urlEqualTo('/some/url')); 86 | 87 | // when 88 | $requestPatternBuilder->withBasicAuth('uname', 'pword'); 89 | $requestPattern = $requestPatternBuilder->build(); 90 | 91 | // then 92 | assertThat($requestPattern->getBasicAuthCredentials(), equalTo( 93 | new BasicCredentials('uname', 'pword'))); 94 | } 95 | 96 | public function testBuilderCanBeCreatedWithCustomMatcherNameAndParams() 97 | { 98 | // when 99 | $customMatcherName = 'custom-matcher'; 100 | $params = array('param' => 'val'); 101 | $builder = new RequestPatternBuilder($customMatcherName, $params); 102 | $pattern = $builder->build(); 103 | 104 | // then 105 | assertThat($pattern->getMethod(), nullValue()); 106 | assertThat($pattern->getUrlMatchingStrategy(), nullValue()); 107 | assertThat($pattern->getCustomMatcher(), equalTo(new CustomMatcherDefinition($customMatcherName, $params))); 108 | } 109 | 110 | public function testCustomMatcherDefinitionIsInArrayIfSpecified() 111 | { 112 | // given 113 | $builder = new RequestPatternBuilder('GET', WireMock::urlEqualTo('/some/url')); 114 | $customMatcherName = 'custom-matcher'; 115 | $params = array('param' => 'val'); 116 | 117 | // when 118 | $builder->withCustomMatcher($customMatcherName, $params); 119 | $pattern = $builder->build(); 120 | 121 | // then 122 | assertThat($pattern->getCustomMatcher(), equalTo(new CustomMatcherDefinition($customMatcherName, $params))); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /test/WireMock/Integration/MappingsIntegrationTest.php: -------------------------------------------------------------------------------- 1 | listAllStubMappings(); 13 | 14 | // then 15 | assertThat($mappings->getMappings(), emptyArray()); 16 | } 17 | 18 | public function testMappingListContainsStubsPreviouslyCreated() 19 | { 20 | // given 21 | $mapping = self::$_wireMock->stubFor(WireMock::any(WireMock::anyUrl())->willReturn(WireMock::aResponse())); 22 | 23 | // when 24 | $mappings = self::$_wireMock->listAllStubMappings(); 25 | 26 | // then 27 | assertThat($mappings->getMappings(), hasItemInArray($mapping)); 28 | } 29 | 30 | public function testMappingListCanBeLimitedToMostRecent() 31 | { 32 | // given 33 | self::$_wireMock->stubFor(WireMock::any(WireMock::urlEqualTo('/one')) 34 | ->willReturn(WireMock::aResponse())); 35 | $mapping = self::$_wireMock->stubFor(WireMock::any(WireMock::urlEqualTo('/two')) 36 | ->willReturn(WireMock::aResponse())); 37 | 38 | // when 39 | $mappings = self::$_wireMock->listAllStubMappings(1); 40 | 41 | // then 42 | assertThat($mappings->getMappings(), allOf(hasItemInArray($mapping), arrayWithSize(1))); 43 | } 44 | 45 | public function testMappingListCanBeOffsetToRetrieveOlderStubs() 46 | { 47 | // given 48 | $mapping = self::$_wireMock->stubFor(WireMock::any(WireMock::urlEqualTo('/one')) 49 | ->willReturn(WireMock::aResponse())); 50 | self::$_wireMock->stubFor(WireMock::any(WireMock::urlEqualTo('/two')) 51 | ->willReturn(WireMock::aResponse())); 52 | 53 | // when 54 | $mappings = self::$_wireMock->listAllStubMappings(1, 1); 55 | 56 | // then 57 | assertThat($mappings->getMappings(), allOf(hasItemInArray($mapping), arrayWithSize(1))); 58 | } 59 | 60 | public function testGettingSingleMappingRetrievesStubById() 61 | { 62 | // given 63 | $mapping = self::$_wireMock->stubFor(WireMock::any(WireMock::urlEqualTo('/one')) 64 | ->willReturn(WireMock::aResponse())); 65 | 66 | // when 67 | $returnedMapping = self::$_wireMock->getSingleStubMapping($mapping->getId()); 68 | 69 | // then 70 | assertThat($returnedMapping, equalTo($mapping)); 71 | } 72 | 73 | public function testStubsCanBeFoundByMetadata() 74 | { 75 | // given 76 | self::$_wireMock->stubFor(WireMock::any(WireMock::urlEqualTo('/one')) 77 | ->withMetadata(array('customId' => 123)) 78 | ->willReturn(WireMock::aResponse())); 79 | 80 | // when 81 | $mappingsResult = self::$_wireMock->findStubsByMetadata( 82 | WireMock::matchingJsonPath('$.customId', WireMock::equalTo('123')) 83 | ); 84 | 85 | // then 86 | assertThat($mappingsResult->getMappings(), arrayWithSize(1)); 87 | $mappings = $mappingsResult->getMappings(); 88 | assertThat($mappings[0]->getMetadata(), equalTo(array('customId' => 123))); 89 | } 90 | 91 | public function testStubsNotMatchingMetadataAreNotFound() 92 | { 93 | // given 94 | self::$_wireMock->stubFor(WireMock::any(WireMock::urlEqualTo('/one')) 95 | ->withMetadata(array('customId' => 123)) 96 | ->willReturn(WireMock::aResponse())); 97 | 98 | // when 99 | $mappingsResult = self::$_wireMock->findStubsByMetadata( 100 | WireMock::matchingJsonPath('$.customId', WireMock::equalTo('a different value')) 101 | ); 102 | 103 | // then 104 | assertThat($mappingsResult->getMappings(), arrayWithSize(0)); 105 | } 106 | 107 | public function testStubsCanBeRemovedByMetadata() 108 | { 109 | // given 110 | self::$_wireMock->stubFor(WireMock::any(WireMock::urlEqualTo('/one')) 111 | ->withMetadata(array('customId' => 123)) 112 | ->willReturn(WireMock::aResponse())); 113 | 114 | // when 115 | self::$_wireMock->removeStubsByMetadata( 116 | WireMock::matchingJsonPath('$.customId', WireMock::equalTo('123')) 117 | ); 118 | 119 | // then 120 | assertThat(self::$_wireMock->listAllStubMappings()->getMappings(), arrayWithSize(0)); 121 | } 122 | 123 | public function testStubsNotMatchingMetadataAreNotRemoved() 124 | { 125 | // given 126 | self::$_wireMock->stubFor(WireMock::any(WireMock::urlEqualTo('/one')) 127 | ->withMetadata(array('customId' => 123)) 128 | ->willReturn(WireMock::aResponse())); 129 | 130 | // when 131 | self::$_wireMock->removeStubsByMetadata( 132 | WireMock::matchingJsonPath('$.customId', WireMock::equalTo('a different value')) 133 | ); 134 | 135 | // then 136 | assertThat(self::$_wireMock->listAllStubMappings()->getMappings(), arrayWithSize(1)); 137 | } 138 | } -------------------------------------------------------------------------------- /src/WireMock/Client/RequestPatternBuilder.php: -------------------------------------------------------------------------------- 1 | |null */ 14 | private $headers; 15 | /** @var array|null */ 16 | private $cookies; 17 | /** @var array|null */ 18 | private $queryParameters; 19 | /** @var ValueMatchingStrategy[]|null */ 20 | private $bodyPatterns; 21 | /** @var MultipartValuePattern[]|null */ 22 | private $multipartPatterns; 23 | /** @var BasicCredentials */ 24 | private $basicCredentials; 25 | /** @var CustomMatcherDefinition */ 26 | private $customMatcherDefinition; 27 | /** @var ValueMatchingStrategy[] */ 28 | private $hostPattern; 29 | 30 | /** 31 | * @param string $methodOrCustomMatcherName 32 | * @param UrlMatchingStrategy|array $urlMatchingStrategyOrCustomParams 33 | */ 34 | public function __construct($methodOrCustomMatcherName, $urlMatchingStrategyOrCustomParams) 35 | { 36 | if ($urlMatchingStrategyOrCustomParams instanceof UrlMatchingStrategy) { 37 | $this->method = $methodOrCustomMatcherName; 38 | $this->urlMatchingStrategy = $urlMatchingStrategyOrCustomParams; 39 | } else { 40 | $this->customMatcherDefinition = 41 | new CustomMatcherDefinition($methodOrCustomMatcherName, $urlMatchingStrategyOrCustomParams); 42 | } 43 | } 44 | 45 | /** 46 | * @param string $headerName 47 | * @param ValueMatchingStrategy $valueMatchingStrategy 48 | * @return RequestPatternBuilder 49 | */ 50 | public function withHeader($headerName, ValueMatchingStrategy $valueMatchingStrategy) 51 | { 52 | $this->headers[$headerName] = $valueMatchingStrategy; 53 | return $this; 54 | } 55 | 56 | /** 57 | * @param string $cookieName 58 | * @param ValueMatchingStrategy $valueMatchingStrategy 59 | * @return RequestPatternBuilder 60 | */ 61 | public function withCookie($cookieName, ValueMatchingStrategy $valueMatchingStrategy) 62 | { 63 | $this->cookies[$cookieName] = $valueMatchingStrategy; 64 | return $this; 65 | } 66 | 67 | /** 68 | * @param string $headerName 69 | * @return RequestPatternBuilder 70 | */ 71 | public function withoutHeader($headerName) 72 | { 73 | $this->withHeader($headerName, new ValueMatchingStrategy('absent', true)); 74 | return $this; 75 | } 76 | 77 | /** 78 | * @param string $name 79 | * @param ValueMatchingStrategy $valueMatchingStrategy 80 | * @return RequestPatternBuilder 81 | */ 82 | public function withQueryParam($name, ValueMatchingStrategy $valueMatchingStrategy) 83 | { 84 | $this->queryParameters[$name] = $valueMatchingStrategy; 85 | return $this; 86 | } 87 | 88 | /** 89 | * @param ValueMatchingStrategy $valueMatchingStrategy 90 | * @return RequestPatternBuilder 91 | */ 92 | public function withRequestBody(ValueMatchingStrategy $valueMatchingStrategy) 93 | { 94 | $this->bodyPatterns[] = $valueMatchingStrategy; 95 | return $this; 96 | } 97 | 98 | /** 99 | * @param MultipartValuePattern $multipart 100 | * @return $this 101 | */ 102 | public function withMultipartRequestBody($multipart) 103 | { 104 | $this->multipartPatterns[] = $multipart; 105 | return $this; 106 | } 107 | 108 | /** 109 | * @param string $username 110 | * @param string $password 111 | * @return RequestPatternBuilder 112 | */ 113 | public function withBasicAuth($username, $password) 114 | { 115 | $this->basicCredentials = new BasicCredentials($username, $password); 116 | return $this; 117 | } 118 | 119 | /** 120 | * @param string $customMatcherName 121 | * @param array $customParams 122 | * @return RequestPatternBuilder 123 | */ 124 | public function withCustomMatcher($customMatcherName, $customParams) 125 | { 126 | $this->customMatcherDefinition = new CustomMatcherDefinition($customMatcherName, $customParams); 127 | return $this; 128 | } 129 | 130 | /** 131 | * @param ValueMatchingStrategy $hostMatcher 132 | * @return $this 133 | */ 134 | public function withHost($hostMatcher) 135 | { 136 | $this->hostPattern = $hostMatcher; 137 | return $this; 138 | } 139 | 140 | /** 141 | * @return RequestPattern 142 | */ 143 | public function build() 144 | { 145 | return new RequestPattern( 146 | $this->method, 147 | $this->urlMatchingStrategy, 148 | $this->headers, 149 | $this->cookies, 150 | $this->bodyPatterns, 151 | $this->multipartPatterns, 152 | $this->queryParameters, 153 | $this->basicCredentials, 154 | $this->customMatcherDefinition, 155 | $this->hostPattern 156 | ); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | wiremock-php [![CircleCI](https://dl.circleci.com/status-badge/img/gh/rowanhill/wiremock-php/tree/master.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/rowanhill/wiremock-php/tree/master) 2 | ============ 3 | **Stub and mock web services with the power of [WireMock](https://github.com/tomakehurst/wiremock) from PHP.** 4 | 5 | WireMock provides a JSON API for interacting with it; wiremock-php makes it easy to use that JSON API from PHP by 6 | wrapping it up in a fluent API very similar to the Java API provided by WireMock. 7 | 8 | Note that wiremock-php requires a standalone instance of WireMock to be run (which requires Java). 9 | 10 | Version numbers track those of WireMock itself, but may lag behind (i.e. if a WireMock release does not contain changes 11 | to the API, there may be no corresponding version of wiremock-php). 12 | 13 | Alternatives 14 | ------------ 15 | Like the idea of stubbing & verifying HTTP requests, but don't like this library (maybe you don't want to install Java)? 16 | You might want to investigate the following: 17 | * [http-mock](https://github.com/InterNations/http-mock) 18 | * [mock-http-server](https://github.com/cepa/mock-http-server) 19 | 20 | Installation 21 | ------------ 22 | It's easiest to install wiremock-php via Composer: 23 | 24 | ```bash 25 | php composer.phar require --dev wiremock-php/wiremock-php:2.35.3 26 | ``` 27 | 28 | Usage 29 | ----- 30 | ### API 31 | The API is based directly on WireMock's Java API, so see the [WireMock documentation](http://wiremock.org/) for general 32 | help with interacting with WireMock. 33 | 34 | ### Differences to Java API 35 | To provide a fluent interface, the WireMock Java API makes use of statically imported methods (which act upon a default 36 | static instance), but it's also possible to act directly upon a Java WireMock instance (using slightly differently 37 | named methods). 38 | 39 | Prior to version 5.6 (back when wiremock-php was created), PHP didn't support anything like Java's static import of 40 | methods. Instead, in wiremock-php some methods which are static in Java are instance methods. Those methods are: 41 | 42 | - `stubFor`, `editStub`, `importStubs` 43 | - `verify` 44 | - `get`, `post`, `put`, `delete`, `patch`, `head`, `options`, `trace`, `any` 45 | - all the various matcher methods 46 | - `getAllServeEvents`, `findAll`, `findUnmatchedRequests`, `findNearMissesFor`, `findNearMissesForAllUnmatched` 47 | - `listAllStubMappings`, `getSingleStubMapping`, `findStubsByMetadata`, `removeStubsByMetadata` 48 | - `saveAllMappings` 49 | - `reset`, `resetToDefault` 50 | - `resetAllRequests`, `removeServeEvent`, `removeServeEvents`, `removeEventsByStubMetadata` 51 | - `getAllScenarios`, `resetAllScenarios`, `resetScenario`, `setScenarioState` 52 | - `setGlobalFixedDelay`, `setGlobalRandomDelay`, `resetGlobalDelays` 53 | - `startRecording`, `getRecordingStatus`, `stopRecording`, `snapshotRecord` 54 | - `shutdownServer` 55 | 56 | Also, the Java API has methods (`ResponseDefinitionBuilder::withBody` and `WebhookDefinition::withBinaryBody`) that take 57 | a byte array. Byte arrays are less common in PHP, so instead, `withBodyData` methods are provided, which takes a string 58 | to be base64 encoded. To produce an appropriate string from an array of bytes, use [pack](http://php.net/pack). 59 | 60 | The date and time request matcher functions (`before`, `beforeNow`, `equalToDateTime`, `isNow`, `after`, `afterNow`) can 61 | be used with offsets (`expectedOffset($amount, $unit)`). In the Java API, the unit parameter is an enum; in wiremock-php 62 | these values are consts on `DateTimeMatchingStrategy`. Similarly, truncation types (for use with `truncateExpected` and 63 | `truncateActual`) are enums in the Java API, but are consts on `DateTimeMatchingStrategy` in wiremock-php. 64 | 65 | The `stubImport` method is static on `StubBuilder` in Java. In WireMock, to keep all the public static methods in one 66 | predictable place, this method is available as `WireMock::stubImport`. 67 | 68 | The request method constants are available on `WireMock\Http\RequestMethod`. 69 | 70 | In addition, wiremock-php adds the instance method `isAlive`. This polls the standalone WireMock instance until an OK 71 | response is received or a timeout is reached, allowing your PHP code to wait until WireMock is ready. 72 | 73 | ### Example 74 | A typical usage looks something like the following: 75 | ```php 76 | // Create an object to administer a WireMock instance. This is assumed to be at 77 | // localhost:8080 unless these values are overridden. 78 | $wireMock = WireMock::create(/* specify host, port here if needed */); 79 | 80 | // Assert that the standalone service is running (by waiting for it to respond 81 | // to a request within a timeout) 82 | assertThat($wireMock->isAlive(), is(true)); 83 | 84 | // Stub out a request 85 | $wireMock->stubFor(WireMock::get(WireMock::urlEqualTo('/some/url')) 86 | ->willReturn(WireMock::aResponse() 87 | ->withHeader('Content-Type', 'text/plain') 88 | ->withBody('Hello world!'))); 89 | 90 | // ... interact with the server ... 91 | 92 | // Verify a request 93 | $wireMock->verify(WireMock::postRequestedFor(WireMock::urlEqualTo('/verify/this')) 94 | ->withHeader('Content-Type', WireMock::equalTo('text/xml'))); 95 | ``` 96 | 97 | ### Verification PHPUnit integration 98 | If a verification fails a `VerificationException` is thrown. If PHPUnit is present on the include path, this will be a 99 | subclass of `PHPUnit_Framework_AssertionFailedError`, thus causing any containing PHPUnit test to fail; if PHPUnit is 100 | not present, `VerificationException` subclasses `Exception`. 101 | --------------------------------------------------------------------------------