├── .editorconfig ├── .gitignore ├── .java-version ├── LICENSE ├── README.md ├── assets ├── cucumber-menu.png ├── cukes-architecture.png └── cukes-rest-logo.png ├── cukes-core ├── pom.xml └── src │ ├── main │ ├── java │ │ └── lv │ │ │ └── ctco │ │ │ └── cukes │ │ │ └── core │ │ │ ├── CukesCoreHooks.java │ │ │ ├── CukesDocs.java │ │ │ ├── CukesOptions.java │ │ │ ├── CukesRuntimeException.java │ │ │ ├── api │ │ │ └── GivenSteps.java │ │ │ ├── extension │ │ │ ├── AbstractCukesModule.java │ │ │ ├── CukesInjectableModule.java │ │ │ └── CukesPlugin.java │ │ │ ├── facade │ │ │ ├── RandomGeneratorFacade.java │ │ │ ├── RandomGeneratorFacadeImpl.java │ │ │ ├── VariableFacade.java │ │ │ └── VariableFacadeImpl.java │ │ │ └── internal │ │ │ ├── CucumberFacade.java │ │ │ ├── context │ │ │ ├── BaseContextHandler.java │ │ │ ├── CaptureContext.java │ │ │ ├── CaptureContextInterceptor.java │ │ │ ├── ContextCapturer.java │ │ │ ├── ContextInflater.java │ │ │ ├── CukesMissingPropertyException.java │ │ │ ├── GlobalWorld.java │ │ │ ├── GlobalWorldFacade.java │ │ │ ├── InflateContext.java │ │ │ └── InflateContextInterceptor.java │ │ │ ├── di │ │ │ ├── CukesGuiceModule.java │ │ │ └── SingletonObjectFactory.java │ │ │ ├── helpers │ │ │ ├── Files.java │ │ │ ├── IO.java │ │ │ ├── Strings.java │ │ │ ├── Time.java │ │ │ └── TimeUnitDictionary.java │ │ │ ├── matchers │ │ │ ├── ArrayWithSizeMatcher.java │ │ │ ├── ContainsPattern.java │ │ │ ├── EndsWithRegexp.java │ │ │ ├── EqualToIgnoringTypeMatcher.java │ │ │ ├── JsonMatchers.java │ │ │ ├── MiscMatchers.java │ │ │ └── OfTypeMatcher.java │ │ │ ├── resources │ │ │ ├── FilePathService.java │ │ │ └── ResourceFileReader.java │ │ │ ├── switches │ │ │ ├── SwitchedBy.java │ │ │ └── SwitchedByInterceptor.java │ │ │ └── templating │ │ │ └── TemplatingEngine.java │ └── resources │ │ ├── META-INF │ │ └── services │ │ │ └── io.cucumber.core.backend.ObjectFactory │ │ └── cucumber.properties │ └── test │ ├── java │ └── lv │ │ └── ctco │ │ └── cukes │ │ └── core │ │ ├── RunCukesTest.java │ │ ├── TestCukesGuiceModule.java │ │ ├── api │ │ └── TestSteps.java │ │ ├── di │ │ └── SingletonObjectFactoryTests.java │ │ ├── facade │ │ └── RandomGeneratorFacadeImplTest.java │ │ └── internal │ │ └── context │ │ ├── BaseContextHandlerTest.java │ │ ├── ContextCapturerTest.java │ │ └── ContextInflaterTest.java │ └── resources │ └── features │ ├── issue92.feature │ └── passwordGeneration.feature ├── cukes-documentation-generator ├── pom.xml ├── readme.md └── src │ └── main │ ├── java │ └── lv │ │ └── ctco │ │ └── cukes │ │ └── docgen │ │ ├── CukesComponent.java │ │ ├── DocumentationGenerator.java │ │ ├── StepDefinition.java │ │ └── StepType.java │ └── resources │ └── header.md ├── cukes-environment-variables-plugin ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── lv │ └── ctco │ └── cukes │ └── core │ └── extension │ └── EnvironmentVariablesPlugin.java ├── cukes-graphql ├── pom.xml └── src │ └── main │ └── java │ └── lv │ └── ctco │ └── cukes │ └── graphql │ ├── CukesGraphQLGuiceModule.java │ ├── CukesGraphQLHooks.java │ ├── api │ ├── GivenSteps.java │ └── WhenSteps.java │ ├── facade │ └── GQLRequestFacade.java │ └── internal │ ├── GraphQLRequest.java │ └── PreprocessGraphQLRequestBody.java ├── cukes-http-mock ├── pom.xml └── src │ ├── main │ └── java │ │ └── lv │ │ └── ctco │ │ └── cukes │ │ └── http │ │ └── mock │ │ ├── api │ │ └── MockGivenSteps.java │ │ ├── extension │ │ └── HttpMockPlugin.java │ │ ├── facade │ │ └── HttpMockFacade.java │ │ └── internal │ │ ├── MockClientServerFacade.java │ │ └── MockResponse.java │ └── test │ └── resources │ └── cukes.properties ├── cukes-http ├── pom.xml └── src │ ├── main │ └── java │ │ └── lv │ │ └── ctco │ │ └── cukes │ │ └── http │ │ ├── AwaitCondition.java │ │ ├── CukesHttpGuiceModule.java │ │ ├── CukesHttpHooks.java │ │ ├── HttpMethod.java │ │ ├── RestAssuredConfiguration.java │ │ ├── api │ │ ├── AwaitSteps.java │ │ ├── GivenSteps.java │ │ └── ThenSteps.java │ │ ├── extension │ │ ├── AbstractCukesHttpModule.java │ │ └── CukesHttpPlugin.java │ │ ├── facade │ │ ├── HttpAssertionFacade.java │ │ ├── HttpAssertionFacadeImpl.java │ │ ├── HttpRequestFacade.java │ │ ├── HttpResponseFacade.java │ │ └── ResponseContentProvider.java │ │ ├── https │ │ └── TrustAllTrustManager.java │ │ ├── json │ │ ├── JsonParser.java │ │ └── SafeJsonReader.java │ │ ├── logging │ │ ├── HttpLoggingPlugin.java │ │ └── LoggerPrintStream.java │ │ └── matchers │ │ ├── AwaitConditionMatcher.java │ │ ├── ResponseMatcher.java │ │ └── StatusCodeMatcher.java │ └── test │ └── java │ └── lv │ └── ctco │ └── cukes │ └── http │ ├── CustomMatchers.java │ ├── RequestBody.java │ ├── facade │ └── HttpAssertionFacadeImplTest.java │ ├── json │ └── JsonParserTest.groovy │ ├── logging │ └── HttpLoggingPluginTest.java │ ├── matchers │ ├── ArrayWithSizeMatcherTest.groovy │ ├── EndsWithRegexpTest.java │ ├── JsonMatchersTest.groovy │ └── OfTypeMatcherTest.groovy │ └── templating │ └── TemplatingEngineTest.java ├── cukes-ldap ├── pom.xml ├── readme.md └── src │ ├── main │ └── java │ │ └── lv │ │ └── ctco │ │ └── cukes │ │ └── ldap │ │ ├── CukesLDAPGuiceModule.java │ │ ├── CukesLDAPHooks.java │ │ ├── api │ │ ├── GivenSteps.java │ │ ├── ThenSteps.java │ │ └── WhenSteps.java │ │ ├── facade │ │ ├── EntityFacade.java │ │ ├── ModificationFacade.java │ │ └── SetupFacade.java │ │ └── internal │ │ ├── ConnectionService.java │ │ ├── DnComparator.java │ │ ├── EntityService.java │ │ └── ldif │ │ └── LDIFUtils.java │ └── test │ ├── java │ └── lv │ │ └── ctco │ │ └── cukes │ │ └── ldap │ │ ├── facade │ │ └── EntityFacadeTest.java │ │ └── internal │ │ ├── DnComparatorTest.java │ │ └── ldif │ │ └── LDIFUtilsTest.java │ └── resources │ └── example.ldif ├── cukes-oauth ├── pom.xml ├── readme.md └── src │ └── main │ └── java │ └── lv │ └── ctco │ └── cukes │ └── oauth │ ├── GrantType.java │ ├── OAuthCukesConstants.java │ ├── OAuthCukesGuiceModule.java │ ├── OAuthCukesHttpPlugin.java │ ├── api │ └── GivenSteps.java │ ├── facade │ └── OAuthFacade.java │ └── internal │ └── OAuthTokenRetriever.java ├── cukes-rabbitmq ├── pom.xml ├── readme.md └── src │ └── main │ └── java │ └── lv │ └── ctco │ └── cukes │ └── rabbitmq │ ├── ConfigurationParameters.java │ ├── CukesRabbitMQGuiceModule.java │ ├── CukesRabbitMQHooks.java │ ├── api │ ├── NamePatterns.java │ ├── given │ │ ├── BindQueueSteps.java │ │ ├── ExchangeConfigurationSteps.java │ │ ├── PrepareMessageSteps.java │ │ └── SetupConnectionSteps.java │ ├── then │ │ ├── MessageAssertionSteps.java │ │ └── ReceiveMessageSteps.java │ └── when │ │ └── SendMessageSteps.java │ ├── facade │ ├── RequestFacade.java │ ├── ResponseFacade.java │ └── SetupFacade.java │ └── internal │ ├── ConnectionService.java │ ├── ExchangeService.java │ ├── MessageService.java │ ├── MessageWrapper.java │ ├── MessageWrapperContentProvider.java │ └── QueueService.java ├── cukes-rest ├── pom.xml └── src │ └── main │ └── java │ └── lv │ └── ctco │ └── cukes │ └── rest │ ├── CukesRestGuiceModule.java │ ├── CukesRestHooks.java │ ├── api │ ├── GivenSteps.java │ └── WhenSteps.java │ ├── facade │ └── RestRequestFacade.java │ └── internal │ └── PreprocessRestRequestBody.java ├── cukes-samples ├── cukes-graphql-sample │ ├── graphql.config.json │ ├── graphql.schema.json │ ├── pom.xml │ └── src │ │ └── test │ │ ├── java │ │ └── lv │ │ │ └── ctco │ │ │ └── cukes │ │ │ └── graphql │ │ │ └── RunCukesGraphQLTest.java │ │ └── resources │ │ ├── cukes.properties │ │ └── features │ │ └── conference │ │ ├── Conference.feature │ │ └── queries │ │ └── Berlin2017Conference.graphql ├── cukes-http-mock-sample │ ├── pom.xml │ └── src │ │ └── test │ │ ├── java │ │ └── lv │ │ │ └── ctco │ │ │ └── cukes │ │ │ └── rest │ │ │ └── run │ │ │ └── RunCustomCukesTest.java │ │ └── resources │ │ ├── cukes.properties │ │ └── features │ │ └── custom │ │ ├── custom.feature │ │ ├── mockingSameResource.feature │ │ └── mockingSameResourceComplex.feature ├── cukes-ldap-sample │ ├── pom.xml │ ├── readme.md │ └── src │ │ ├── main │ │ ├── java │ │ │ └── lv │ │ │ │ └── ctco │ │ │ │ └── cukes │ │ │ │ └── ldap │ │ │ │ └── sample │ │ │ │ ├── Application.java │ │ │ │ └── EmbeddedLDAPServer.java │ │ └── resources │ │ │ └── logback.xml │ │ └── test │ │ ├── java │ │ └── lv │ │ │ └── ctco │ │ │ └── cukes │ │ │ └── ldap │ │ │ └── sample │ │ │ ├── CukesLDAPBootstrap.java │ │ │ └── RunCukesLDAPTest.java │ │ └── resources │ │ ├── cukes.properties │ │ ├── features │ │ ├── CreateEntity.feature │ │ ├── DeleteEntity.feature │ │ ├── LDIF.feature │ │ ├── ModifyEntity.feature │ │ ├── ReadEntity.feature │ │ └── SearchEntities.feature │ │ ├── init.ldif │ │ └── ldif │ │ └── test.ldif ├── cukes-oauth-sample │ ├── pom.xml │ ├── readme.md │ └── src │ │ ├── main │ │ ├── java │ │ │ └── lv │ │ │ │ └── ctco │ │ │ │ └── cukes │ │ │ │ └── oauth │ │ │ │ └── sample │ │ │ │ └── OAuthSampleApplication.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ ├── java │ │ └── lv │ │ │ └── ctco │ │ │ └── cukes │ │ │ └── oauth │ │ │ └── sample │ │ │ ├── CukesOAuthBootstrap.java │ │ │ └── RunCukesOAuthTest.java │ │ └── resources │ │ ├── cukes.properties │ │ └── features │ │ └── OAuthShowcase.feature ├── cukes-plugin-sample │ ├── pom.xml │ ├── readme.md │ └── src │ │ └── test │ │ ├── java │ │ └── lv │ │ │ └── ctco │ │ │ └── cukes │ │ │ └── plugins │ │ │ ├── RunCukesTest.java │ │ │ ├── SamplePlugin.java │ │ │ ├── TestCukesGuiceModule.java │ │ │ └── api │ │ │ └── TestSteps.java │ │ └── resources │ │ ├── cukes.properties │ │ └── features │ │ └── showcase.feature ├── cukes-rabbitmq-sample │ ├── pom.xml │ ├── readme.md │ └── src │ │ ├── main │ │ ├── java │ │ │ └── lv │ │ │ │ └── ctco │ │ │ │ └── cukes │ │ │ │ └── rabbitmq │ │ │ │ └── sample │ │ │ │ ├── Application.java │ │ │ │ ├── configuration │ │ │ │ ├── InMemoryAMQPBroker.java │ │ │ │ └── RabbitMQConfiguration.java │ │ │ │ ├── listeners │ │ │ │ ├── PrependHello.java │ │ │ │ └── ToUpperCase.java │ │ │ │ └── message │ │ │ │ └── StringMessage.java │ │ └── resources │ │ │ ├── initial-config.json │ │ │ └── logback.xml │ │ └── test │ │ ├── java │ │ └── lv │ │ │ └── ctco │ │ │ └── cukes │ │ │ └── rabbitmq │ │ │ ├── E2ETest.java │ │ │ └── RunCukesRabbmitMQTest.java │ │ └── resources │ │ ├── cukes.properties │ │ └── features │ │ ├── example.feature │ │ ├── minimalistic.feature │ │ └── variables.feature ├── cukes-rest-sample │ ├── pom.xml │ ├── server.yml │ └── src │ │ ├── main │ │ └── java │ │ │ └── lv │ │ │ └── ctco │ │ │ └── cukes │ │ │ └── rest │ │ │ ├── SampleApplication.java │ │ │ ├── SampleConfiguration.java │ │ │ ├── SampleHealthCheck.java │ │ │ ├── common │ │ │ ├── InMemoryStorage.java │ │ │ └── RestUtils.java │ │ │ ├── gadgets │ │ │ ├── DummyGadgetService.java │ │ │ ├── GadgetResource.java │ │ │ └── dto │ │ │ │ ├── GadgetData.java │ │ │ │ ├── GadgetDto.java │ │ │ │ ├── GadgetType.java │ │ │ │ └── Owner.java │ │ │ ├── healthcheck │ │ │ ├── CustomHeadersResource.java │ │ │ └── StaticTypesResource.java │ │ │ └── multipart │ │ │ ├── BinaryStreamResource.java │ │ │ └── MultipartResource.java │ │ └── test │ │ ├── java │ │ └── lv │ │ │ └── ctco │ │ │ └── cukes │ │ │ └── rest │ │ │ └── run │ │ │ ├── RunCukesTest.java │ │ │ ├── RunCustomCukesTest.java │ │ │ └── custom │ │ │ └── steps │ │ │ └── CustomSteps.java │ │ └── resources │ │ ├── cukes.properties │ │ ├── features │ │ ├── custom │ │ │ └── custom.feature │ │ ├── gadgets │ │ │ ├── Create Gadgets.feature │ │ │ ├── Delete Gadgets.feature │ │ │ ├── Read Gadgets.feature │ │ │ ├── Update Gadgets.feature │ │ │ └── requests │ │ │ │ ├── newGadget.json │ │ │ │ └── updatedGadget.json │ │ ├── healthcheck │ │ │ └── healthcheck.feature │ │ ├── multipart │ │ │ ├── BinaryStream.feature │ │ │ ├── Mutlipart.feature │ │ │ ├── test.txt │ │ │ └── testExcel.xlsx │ │ └── variables │ │ │ ├── Dynamic File Name.feature │ │ │ └── requests │ │ │ └── newGadget.json │ │ └── logback.xml ├── cukes-soap-sample │ ├── pom.xml │ ├── readme.md │ └── src │ │ ├── main │ │ └── java │ │ │ └── lv │ │ │ └── ctco │ │ │ └── cukes │ │ │ └── soap │ │ │ └── sample │ │ │ ├── Application.java │ │ │ ├── CalculatorEndpoint.java │ │ │ ├── WebServiceConfiguration.java │ │ │ └── dto │ │ │ ├── CalculatorRequest.java │ │ │ ├── CalculatorResponse.java │ │ │ └── package-info.java │ │ └── test │ │ ├── java │ │ └── lv │ │ │ └── ctco │ │ │ └── cukes │ │ │ └── soap │ │ │ └── sample │ │ │ ├── CukesBootstrap.java │ │ │ ├── RunCukesSOAPTest.java │ │ │ ├── TestGuiceModule.java │ │ │ └── TestPlugin.java │ │ └── resources │ │ ├── cukes.properties │ │ └── features │ │ └── calculate.feature ├── pom.xml └── readme.md ├── cukes-sql ├── Readme.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── lv │ │ └── ctco │ │ └── cukes │ │ └── sql │ │ ├── ConnectionFactory.java │ │ ├── GenericTableRepository.java │ │ ├── facade │ │ └── DataBaseRequestFacade.java │ │ ├── steps │ │ └── DataBaseSteps.java │ │ └── utils │ │ └── TableAssertUtils.java │ └── test │ ├── java │ └── lv │ │ └── ctco │ │ └── cukes │ │ └── sql │ │ ├── CucumberBootstrap.java │ │ ├── CucumberTests.java │ │ ├── H2MemoryDatabase.java │ │ └── TestModule.java │ └── resources │ ├── cukes.properties │ ├── features │ └── testDataBaseSteps.feature │ └── logback.xml ├── docs ├── 0.0.14 │ └── readme.md ├── 0.0.15 │ └── readme.md ├── 0.0.16 │ └── readme.md ├── 0.0.17 │ └── readme.md ├── 0.0.18 │ └── readme.md ├── 0.0.19 │ └── readme.md ├── 0.0.20 │ └── readme.md ├── 0.0.21 │ └── readme.md ├── 0.0.22 │ └── readme.md ├── 0.0.23 │ └── readme.md ├── 0.0.24 │ └── readme.md ├── 0.0.25 │ └── readme.md ├── 0.0.26 │ └── readme.md ├── 0.0.27 │ └── readme.md ├── 0.0.28 │ └── readme.md ├── 0.0.29 │ └── readme.md ├── 0.0.30 │ └── readme.md ├── 0.0.31 │ └── readme.md ├── 0.0.32 │ └── readme.md ├── 0.0.33 │ └── readme.md ├── 0.0.34 │ └── readme.md ├── 0.0.35 │ └── readme.md ├── 1.0.0 │ └── readme.md ├── 1.0.1 │ └── readme.md ├── 1.0.10 │ └── readme.md ├── 1.0.2 │ └── readme.md ├── 1.0.3 │ └── readme.md ├── 1.0.4 │ └── readme.md ├── 1.0.5 │ └── readme.md ├── 1.0.6 │ └── readme.md ├── 1.0.7 │ └── readme.md ├── 1.0.8 │ └── readme.md ├── 1.0.9 │ └── readme.md ├── current │ └── readme.md └── readme.md ├── pom.xml └── wercker.yml /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*.java] 5 | indent_style = space 6 | indent_size = 4 7 | continuation_indent_size = 4 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*] 14 | end_of_line = lf 15 | insert_final_newline = true 16 | charset = utf-8 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | **/target/ 4 | **/out/ 5 | .gradle 6 | **/build/ 7 | derby.log 8 | work/ 9 | 10 | .settings/ 11 | .classpath 12 | .project 13 | 14 | # Misc 15 | .DS_Store 16 | *.swp 17 | -------------------------------------------------------------------------------- /.java-version: -------------------------------------------------------------------------------- 1 | 1.8 2 | -------------------------------------------------------------------------------- /assets/cucumber-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctco/cukes/ddede60dccfc8c1d7666cb02c50461738b5990a5/assets/cucumber-menu.png -------------------------------------------------------------------------------- /assets/cukes-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctco/cukes/ddede60dccfc8c1d7666cb02c50461738b5990a5/assets/cukes-architecture.png -------------------------------------------------------------------------------- /assets/cukes-rest-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctco/cukes/ddede60dccfc8c1d7666cb02c50461738b5990a5/assets/cukes-rest-logo.png -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/CukesCoreHooks.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core; 2 | 3 | import com.google.inject.Inject; 4 | import io.cucumber.java.After; 5 | import io.cucumber.java.Before; 6 | import lv.ctco.cukes.core.internal.CucumberFacade; 7 | 8 | public class CukesCoreHooks { 9 | 10 | @Inject 11 | CucumberFacade cucumberFacade; 12 | 13 | @Before(order = 500) 14 | public void beforeScenario() { 15 | if (cucumberFacade.firstScenario()) { 16 | cucumberFacade.beforeAllTests(); 17 | } 18 | cucumberFacade.beforeScenario(); 19 | } 20 | 21 | @After 22 | public void afterScenario() { 23 | cucumberFacade.afterScenario(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/CukesDocs.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.METHOD) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface CukesDocs { 11 | String value(); 12 | } 13 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/CukesOptions.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core; 2 | 3 | /** 4 | * List of variables and options used in Cukes. 5 | */ 6 | public interface CukesOptions { 7 | 8 | String PROPERTIES_PREFIX = "cukes."; 9 | String HEADER_PREFIX = "header."; 10 | String DELIMITER = ","; 11 | 12 | String RESOURCES_ROOT = "resources_root"; 13 | String BASE_URI = "base_uri"; 14 | String PLUGINS = "plugins"; 15 | String PROXY = "proxy"; 16 | String AUTH_TYPE = "auth_type"; 17 | String USERNAME = "username"; 18 | String PASSWORD = "password"; 19 | 20 | String ASSERTS_STATUS_CODE_DISPLAY_BODY = "asserts.status_code.display_body"; 21 | String ASSERTS_STATUS_CODE_MAX_SIZE = "asserts.status_code.max_size"; 22 | 23 | String LOGGING_LOGGER_NAME = "logging.http.logger.name"; 24 | String LOGGING_REQUEST_INCLUDES = "logging.http.requests.include"; 25 | String LOGGING_RESPONSE_INCLUDES = "logging.http.responses.include"; 26 | 27 | String URL_ENCODING_ENABLED = "url_encoding_enabled"; 28 | String RELAXED_HTTPS = "relaxed_https"; 29 | 30 | String GZIP_SUPPORT = "gzip_support"; 31 | String REQUEST_BODY_TEMPLATES_ENABLED = "request_body_templates_enabled"; 32 | 33 | String CONTEXT_INFLATING_ENABLED = "context_inflating_enabled"; 34 | String ASSERTIONS_DISABLED = "assertions_disabled"; 35 | 36 | String FOLLOW_REDIRECTS = "follow_redirects"; 37 | } 38 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/CukesRuntimeException.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core; 2 | 3 | public class CukesRuntimeException extends RuntimeException { 4 | public CukesRuntimeException(Throwable cause) { 5 | super(cause); 6 | } 7 | 8 | public CukesRuntimeException(String message, Throwable cause) { 9 | super(message, cause); 10 | } 11 | 12 | public CukesRuntimeException(String message) { 13 | super(message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/extension/AbstractCukesModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.extension; 2 | 3 | import com.google.inject.AbstractModule; 4 | import com.google.inject.multibindings.Multibinder; 5 | import lv.ctco.cukes.core.CukesOptions; 6 | import lv.ctco.cukes.core.CukesRuntimeException; 7 | import org.apache.commons.lang3.ArrayUtils; 8 | 9 | import java.net.URL; 10 | import java.util.Properties; 11 | 12 | import static lv.ctco.cukes.core.internal.helpers.Files.createCukesPropertyFileUrl; 13 | 14 | public abstract class AbstractCukesModule extends AbstractModule { 15 | 16 | @SuppressWarnings("unchecked") 17 | protected void bindPlugins(Class clazz) { 18 | try { 19 | Multibinder multibinder = Multibinder.newSetBinder(binder(), clazz); 20 | 21 | // add user configured plugins 22 | ClassLoader classLoader = AbstractCukesModule.class.getClassLoader(); 23 | 24 | Properties properties = new Properties(); 25 | URL url = createCukesPropertyFileUrl(classLoader); 26 | if (url == null) return; 27 | properties.load(url.openStream()); 28 | 29 | String plugins = properties.getProperty(CukesOptions.PROPERTIES_PREFIX + CukesOptions.PLUGINS); 30 | if (plugins == null) return; 31 | 32 | String[] pluginClasses = plugins.split(CukesOptions.DELIMITER); 33 | for (String pluginClass : pluginClasses) { 34 | Class aClass = classLoader.loadClass(pluginClass); 35 | if (ArrayUtils.contains(aClass.getInterfaces(), clazz)) { 36 | multibinder.addBinding().to(aClass); 37 | } 38 | } 39 | } catch (Exception e) { 40 | throw new CukesRuntimeException("Binding of Cukes plugins failed"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/extension/CukesInjectableModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.extension; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.TYPE) 10 | public @interface CukesInjectableModule { 11 | } 12 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/extension/CukesPlugin.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.extension; 2 | 3 | 4 | public interface CukesPlugin { 5 | 6 | void beforeAllTests(); 7 | 8 | void afterAllTests(); 9 | 10 | void beforeScenario(); 11 | 12 | void afterScenario(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/facade/RandomGeneratorFacade.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.facade; 2 | 3 | public interface RandomGeneratorFacade { 4 | String byPattern(String pattern); 5 | 6 | String withLength(int length); 7 | } 8 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/facade/VariableFacade.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.facade; 2 | 3 | public interface VariableFacade { 4 | 5 | void setVariable(String name, String value); 6 | 7 | void setUUIDToVariable(String name); 8 | 9 | void setCurrentTimestampToVariable(String name); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/facade/VariableFacadeImpl.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.facade; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lv.ctco.cukes.core.internal.context.GlobalWorldFacade; 6 | import lv.ctco.cukes.core.internal.context.InflateContext; 7 | 8 | import java.util.UUID; 9 | 10 | @Singleton 11 | @InflateContext 12 | public class VariableFacadeImpl implements VariableFacade { 13 | 14 | @Inject 15 | private GlobalWorldFacade world; 16 | 17 | @Override 18 | public void setVariable(String name, String value) { 19 | if (!value.equals("null")) { 20 | world.put(name, value); 21 | } else { 22 | world.remove(name); 23 | } 24 | } 25 | 26 | @Override 27 | public void setUUIDToVariable(String name) { 28 | world.put(name, UUID.randomUUID().toString()); 29 | } 30 | 31 | @Override 32 | public void setCurrentTimestampToVariable(String name) { 33 | world.put(name, String.valueOf(System.currentTimeMillis())); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/CucumberFacade.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lv.ctco.cukes.core.extension.CukesPlugin; 6 | import lv.ctco.cukes.core.internal.context.GlobalWorldFacade; 7 | 8 | import java.util.Set; 9 | 10 | @Singleton 11 | public class CucumberFacade { 12 | 13 | /* Ugly Hack proposed by Cucumber developers: https://github.com/cucumber/cucumber-jvm/pull/295 */ 14 | private static boolean firstRun = true; 15 | 16 | @Inject 17 | GlobalWorldFacade world; 18 | 19 | @Inject 20 | Set pluginSet; 21 | 22 | 23 | public boolean firstScenario() { 24 | return firstRun; 25 | } 26 | 27 | public void beforeAllTests() { 28 | firstRun = false; 29 | for (CukesPlugin cukesPlugin : pluginSet) { 30 | cukesPlugin.beforeAllTests(); 31 | } 32 | Runtime.getRuntime().addShutdownHook(new Thread(this::afterAllTests)); 33 | } 34 | 35 | public void beforeScenario() { 36 | world.reconstruct(); 37 | for (CukesPlugin cukesPlugin : pluginSet) { 38 | cukesPlugin.beforeScenario(); 39 | } 40 | } 41 | 42 | public void afterScenario() { 43 | for (CukesPlugin cukesPlugin : pluginSet) { 44 | cukesPlugin.afterScenario(); 45 | } 46 | } 47 | 48 | public void afterAllTests() { 49 | for (CukesPlugin cukesPlugin : pluginSet) { 50 | cukesPlugin.afterAllTests(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/context/BaseContextHandler.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.context; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | public abstract class BaseContextHandler { 9 | 10 | public static final String GROUP_PATTERN = "\\{\\(([\\w.-]+)\\)}"; 11 | 12 | protected List extractGroups(String pattern) { 13 | List allMatches = new ArrayList<>(); 14 | if (pattern == null) { 15 | return allMatches; 16 | } 17 | Matcher m = Pattern.compile(GROUP_PATTERN).matcher(pattern); 18 | while (m.find()) { 19 | allMatches.add(m.group(1)); 20 | } 21 | 22 | return allMatches; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/context/CaptureContext.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.context; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.METHOD, ElementType.TYPE}) 10 | public @interface CaptureContext { 11 | 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target(ElementType.PARAMETER) 14 | @interface Pattern {} 15 | 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target(ElementType.PARAMETER) 18 | @interface Value {} 19 | } 20 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/context/CaptureContextInterceptor.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.context; 2 | 3 | import com.google.inject.Inject; 4 | import org.aopalliance.intercept.MethodInterceptor; 5 | import org.aopalliance.intercept.MethodInvocation; 6 | 7 | import java.lang.annotation.Annotation; 8 | 9 | public class CaptureContextInterceptor implements MethodInterceptor { 10 | 11 | @Inject 12 | ContextCapturer capturer; 13 | 14 | @Override 15 | public Object invoke(MethodInvocation invocation) throws Throwable { 16 | Object[] arguments = invocation.getArguments(); 17 | 18 | Object pattern = null; 19 | Object value = null; 20 | 21 | Annotation[][] annotations = invocation.getMethod().getParameterAnnotations(); 22 | for (int i = 0; i < annotations.length; i++) { 23 | Annotation[] parameterAnnotations = annotations[i]; 24 | for (Annotation annotation : parameterAnnotations) { 25 | if (annotation.annotationType().equals(CaptureContext.Pattern.class)) { 26 | pattern = arguments[i]; 27 | } else if (annotation.annotationType().equals(CaptureContext.Value.class)) { 28 | value = arguments[i]; 29 | } 30 | } 31 | } 32 | 33 | if (pattern != null && value != null) { 34 | capturer.capture((String) pattern, (String) value); 35 | } 36 | return invocation.proceed(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/context/ContextCapturer.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.context; 2 | 3 | import com.google.inject.Inject; 4 | 5 | import java.util.List; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | public class ContextCapturer extends BaseContextHandler { 10 | 11 | public static final String GROUP_PATTERN_REGEX = "(.*)"; 12 | 13 | @Inject 14 | GlobalWorldFacade world; 15 | 16 | public void capture(String pattern, String value) { 17 | List groups = extractGroups(pattern); 18 | if (groups.size() < 1) return; 19 | String regexPattern = transformToRegex(pattern); 20 | if (doesNotMatchPattern(value, regexPattern)) return; 21 | captureValuesFromPattern(regexPattern, groups, value); 22 | } 23 | 24 | String transformToRegex(String pattern) { 25 | return pattern.replaceAll(GROUP_PATTERN, GROUP_PATTERN_REGEX); 26 | } 27 | 28 | boolean doesNotMatchPattern(String value, String regexPattern) { 29 | return !value.matches(regexPattern); 30 | } 31 | 32 | void captureValuesFromPattern(String regexPattern, List groups, String value) { 33 | Matcher matcher = Pattern.compile(regexPattern).matcher(value); 34 | for (int i = 1; matcher.find(); i++) { 35 | if (matcher.group().isEmpty()) return; 36 | String groupValue = matcher.group(i); 37 | String key = groups.get(i - 1); 38 | world.put(key, groupValue); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/context/ContextInflater.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.context; 2 | 3 | import com.google.inject.Inject; 4 | import lv.ctco.cukes.core.CukesOptions; 5 | 6 | import java.util.HashSet; 7 | import java.util.Optional; 8 | import java.util.Set; 9 | 10 | public class ContextInflater extends BaseContextHandler { 11 | 12 | @Inject 13 | GlobalWorldFacade world; 14 | 15 | public String inflate(String input) { 16 | Set groups = new HashSet<>(extractGroups(input)); 17 | boolean inflatingEnabled = world.getBoolean(CukesOptions.CONTEXT_INFLATING_ENABLED, true); 18 | if (inflatingEnabled) { 19 | return inflateGroups(input, groups); 20 | } 21 | return inflateGroupsWithPlaceholders(input, groups); 22 | } 23 | 24 | String inflateGroups(String input, Set groups) { 25 | String result = input; 26 | for (String key : groups) { 27 | Optional $value = world.get(key); 28 | if ($value.isPresent()) { 29 | result = result.replaceAll("\\{\\(" + key + "\\)}", $value.get()); 30 | } 31 | } 32 | return result; 33 | } 34 | 35 | String inflateGroupsWithPlaceholders(String input, Set groups) { 36 | String result = input; 37 | for (String key : groups) { 38 | result = result.replaceAll("\\{\\(" + key + "\\)}", "{" + key + "}"); 39 | } 40 | return result; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/context/CukesMissingPropertyException.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.context; 2 | 3 | import lv.ctco.cukes.core.CukesRuntimeException; 4 | 5 | public class CukesMissingPropertyException extends CukesRuntimeException { 6 | 7 | public CukesMissingPropertyException(String key) { 8 | super("Property cukes." + key + " is missing"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/context/GlobalWorldFacade.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.context; 2 | 3 | import com.google.inject.Inject; 4 | 5 | import java.util.Objects; 6 | import java.util.Optional; 7 | import java.util.Set; 8 | import java.util.stream.Collectors; 9 | 10 | public class GlobalWorldFacade { 11 | 12 | @Inject 13 | GlobalWorld world; 14 | 15 | @CaptureContext 16 | public void put(@CaptureContext.Pattern String key, @CaptureContext.Value String value) { 17 | world.put(key, value); 18 | } 19 | 20 | public String getOrThrow(String key) { 21 | return world.get(key).orElseGet(() -> { 22 | throw new CukesMissingPropertyException(key); 23 | }); 24 | } 25 | 26 | public Optional get(String key) { 27 | return world.get(key); 28 | } 29 | 30 | public boolean getBoolean(String key) { 31 | return getBoolean(key, false); 32 | } 33 | 34 | public boolean getBoolean(String key, boolean defaultValue) { 35 | return Boolean.parseBoolean(get(key, Boolean.toString(defaultValue))); 36 | } 37 | 38 | public String get(String key, String defaultValue) { 39 | Optional value = world.get(key); 40 | return value.orElse(defaultValue); 41 | } 42 | 43 | public void reconstruct() { 44 | world.reconstruct(); 45 | } 46 | 47 | public Set getKeysStartingWith(final String headerPrefix) { 48 | Set keys = world.keys(); 49 | return keys.stream() 50 | .filter(Objects::nonNull) 51 | .filter(s -> s.startsWith(headerPrefix)) 52 | .collect(Collectors.toSet()); 53 | } 54 | 55 | public void remove(String key) { 56 | world.remove(key); 57 | } 58 | 59 | public Set keys() { 60 | return world.keys(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/context/InflateContext.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.context; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.METHOD, ElementType.TYPE}) 10 | public @interface InflateContext { 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Target(ElementType.PARAMETER) 13 | @interface Ignore {} 14 | } 15 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/di/CukesGuiceModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.di; 2 | 3 | import com.google.inject.matcher.Matchers; 4 | import lv.ctco.cukes.core.extension.AbstractCukesModule; 5 | import lv.ctco.cukes.core.extension.CukesPlugin; 6 | import lv.ctco.cukes.core.internal.context.CaptureContext; 7 | import lv.ctco.cukes.core.internal.context.CaptureContextInterceptor; 8 | import lv.ctco.cukes.core.internal.context.InflateContext; 9 | import lv.ctco.cukes.core.internal.context.InflateContextInterceptor; 10 | import lv.ctco.cukes.core.internal.switches.SwitchedBy; 11 | import lv.ctco.cukes.core.internal.switches.SwitchedByInterceptor; 12 | import org.aopalliance.intercept.MethodInterceptor; 13 | 14 | import java.lang.annotation.Annotation; 15 | 16 | public class CukesGuiceModule extends AbstractCukesModule { 17 | 18 | @Override 19 | protected void configure() { 20 | bindInterceptor(new InflateContextInterceptor(), InflateContext.class); 21 | bindInterceptor(new CaptureContextInterceptor(), CaptureContext.class); 22 | bindInterceptor(new SwitchedByInterceptor(), SwitchedBy.class); 23 | 24 | bindPlugins(CukesPlugin.class); 25 | } 26 | 27 | private void bindInterceptor(MethodInterceptor interceptor, Class annotationType) { 28 | requestInjection(interceptor); 29 | bindInterceptor(Matchers.annotatedWith(annotationType), Matchers.any(), interceptor); 30 | bindInterceptor(Matchers.any(), Matchers.annotatedWith(annotationType), interceptor); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/helpers/IO.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.helpers; 2 | 3 | import java.io.*; 4 | import java.nio.charset.Charset; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class IO { 9 | 10 | public static void closeQuietly(InputStream closeable) { 11 | try { 12 | if (closeable != null) { 13 | closeable.close(); 14 | } 15 | } catch (IOException ignored) { 16 | } 17 | } 18 | 19 | public static List readLines(InputStream inputStream, Charset encoding) throws IOException { 20 | InputStreamReader input = new InputStreamReader(inputStream, encoding == null ? Charset.defaultCharset() : encoding); 21 | BufferedReader reader = toBufferedReader(input); 22 | List list = new ArrayList<>(); 23 | 24 | for (String line = reader.readLine(); line != null; line = reader.readLine()) { 25 | list.add(line); 26 | } 27 | 28 | return list; 29 | } 30 | 31 | public static BufferedReader toBufferedReader(Reader reader) { 32 | return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/helpers/Strings.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.helpers; 2 | 3 | public class Strings { 4 | 5 | public static String escapeRegex(String regex) { 6 | return regex.replace("{", "\\{").replace("}", "\\}"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/helpers/Time.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.helpers; 2 | 3 | public class Time { 4 | 5 | private final int value; 6 | private final TimeUnitDictionary unit; 7 | 8 | public Time(int value, TimeUnitDictionary unit) { 9 | this.value = value; 10 | this.unit = unit; 11 | } 12 | 13 | public static Time of(int value, String unit) { 14 | return new Time(value, TimeUnitDictionary.of(unit)); 15 | } 16 | 17 | public int getValue() { 18 | return value; 19 | } 20 | 21 | public TimeUnitDictionary getUnitDict() { 22 | return unit; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/helpers/TimeUnitDictionary.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.helpers; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public enum TimeUnitDictionary { 8 | MILLISECONDS(TimeUnit.MILLISECONDS, "ms", "milli", "millisecond", "milliseconds"), 9 | SECONDS(TimeUnit.SECONDS, "s", "sec", "second", "seconds"), 10 | MINUTES(TimeUnit.MINUTES, "m", "min", "minute", "minutes"), 11 | HOURS(TimeUnit.HOURS, "h", "hour", "hours"); 12 | 13 | 14 | private final List keys; 15 | private final TimeUnit timeUnit; 16 | 17 | TimeUnitDictionary(TimeUnit timeUnit, String... keys) { 18 | this.timeUnit = timeUnit; 19 | this.keys = Arrays.asList(keys); 20 | } 21 | 22 | public static TimeUnitDictionary of(String key) { 23 | for (TimeUnitDictionary timeUnit : values()) { 24 | if (timeUnit.keys.contains(key.toLowerCase())) return timeUnit; 25 | } 26 | throw new IllegalArgumentException("No TimeUnit found for " + key); 27 | } 28 | 29 | public TimeUnit getTimeUnit() { 30 | return timeUnit; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/matchers/ContainsPattern.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.matchers; 2 | 3 | import lv.ctco.cukes.core.internal.helpers.Strings; 4 | import org.hamcrest.BaseMatcher; 5 | import org.hamcrest.Description; 6 | import org.hamcrest.Matcher; 7 | 8 | import java.util.regex.Pattern; 9 | 10 | public class ContainsPattern extends BaseMatcher { 11 | 12 | private final Pattern p; 13 | private final boolean match; 14 | 15 | public ContainsPattern(Pattern p, boolean match) { 16 | this.p = p; 17 | this.match = match; 18 | } 19 | 20 | public ContainsPattern(String regex, boolean match) { 21 | this(Pattern.compile(Strings.escapeRegex(regex)), match); 22 | } 23 | 24 | public static Matcher containsPattern(String regex) { 25 | return new ContainsPattern(regex, false); 26 | } 27 | 28 | public static Matcher matchesPattern(String regex) { 29 | return new ContainsPattern(regex, true); 30 | } 31 | 32 | public static Matcher matchesPattern(Pattern p) { 33 | return new ContainsPattern(p, true); 34 | } 35 | 36 | @Override 37 | public void describeTo(Description description) { 38 | description.appendText("a string ") 39 | .appendText(match ? "matching" : "containing") 40 | .appendText(" /") 41 | .appendText(p.pattern()) 42 | .appendText("/"); 43 | } 44 | 45 | @Override 46 | public boolean matches(Object o) { 47 | boolean isString = o instanceof String; 48 | CharSequence item = (isString) ? (CharSequence) o : String.valueOf(o); 49 | if (match) { 50 | return p.matcher(item).matches(); 51 | } else { 52 | return p.matcher(item).find(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/matchers/EndsWithRegexp.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.matchers; 2 | 3 | import org.hamcrest.BaseMatcher; 4 | import org.hamcrest.Description; 5 | import org.hamcrest.Matcher; 6 | 7 | import java.util.regex.Pattern; 8 | 9 | public class EndsWithRegexp { 10 | 11 | public static Matcher endsWithRegexp(final String regexp) { 12 | return new BaseMatcher() { 13 | 14 | @Override 15 | public void describeTo(Description description) { 16 | description.appendText("matches pattern " + regexp); 17 | } 18 | 19 | @Override 20 | public boolean matches(Object item) { 21 | String value = (String) item; 22 | java.util.regex.Matcher matcher = Pattern.compile(".*" + regexp).matcher(value); 23 | 24 | return matcher.matches(); 25 | } 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/matchers/MiscMatchers.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.matchers; 2 | 3 | import org.hamcrest.Matcher; 4 | 5 | import static org.hamcrest.core.Is.is; 6 | 7 | public class MiscMatchers { 8 | 9 | public static Matcher that(Matcher matcher) { 10 | return is(matcher); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/resources/FilePathService.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.resources; 2 | 3 | import com.google.inject.Inject; 4 | import lv.ctco.cukes.core.CukesOptions; 5 | import lv.ctco.cukes.core.internal.context.GlobalWorldFacade; 6 | import lv.ctco.cukes.core.internal.helpers.Files; 7 | 8 | import java.io.File; 9 | 10 | public class FilePathService { 11 | 12 | @Inject 13 | GlobalWorldFacade world; 14 | 15 | public String normalize(String path) { 16 | if (Files.isRelative(path)) { 17 | // TODO: Put correct RESOURCE_ROOT 18 | String resourceRoot = world.get(CukesOptions.RESOURCES_ROOT, "resources"); 19 | return new File(resourceRoot, path).getAbsolutePath(); 20 | } 21 | return new File(path).getAbsolutePath(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/resources/ResourceFileReader.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.resources; 2 | 3 | import com.google.common.base.Joiner; 4 | import com.google.inject.Inject; 5 | import lv.ctco.cukes.core.CukesRuntimeException; 6 | import lv.ctco.cukes.core.internal.context.InflateContext; 7 | import lv.ctco.cukes.core.internal.helpers.Files; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.util.List; 12 | 13 | @InflateContext 14 | public class ResourceFileReader { 15 | 16 | @Inject 17 | FilePathService pathService; 18 | 19 | public String read(String path) { 20 | return Joiner.on("").join(readLines(path)); 21 | } 22 | 23 | public List readLines(String path) { 24 | try { 25 | File file = getResourceFile(path); 26 | return Files.readLines(file); 27 | } catch (IOException e) { 28 | throw new CukesRuntimeException(e); 29 | } 30 | } 31 | 32 | public byte[] readBytes(String path) { 33 | try { 34 | File file = getResourceFile(path); 35 | return Files.readBytes(file); 36 | } catch (IOException e) { 37 | throw new CukesRuntimeException(e); 38 | } 39 | } 40 | 41 | public File getResourceFile(String relativePath) { 42 | String normalizedPath = pathService.normalize(relativePath); 43 | return new File(normalizedPath); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/switches/SwitchedBy.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.switches; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.METHOD, ElementType.TYPE}) 10 | public @interface SwitchedBy { 11 | String value(); 12 | } 13 | -------------------------------------------------------------------------------- /cukes-core/src/main/java/lv/ctco/cukes/core/internal/templating/TemplatingEngine.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.templating; 2 | 3 | import java.util.Optional; 4 | import com.google.inject.Inject; 5 | import com.google.inject.Singleton; 6 | import lv.ctco.cukes.core.CukesOptions; 7 | import lv.ctco.cukes.core.internal.context.GlobalWorldFacade; 8 | import lv.ctco.cukes.core.internal.context.InflateContext; 9 | import org.rythmengine.Rythm; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | @Singleton 15 | @InflateContext 16 | public class TemplatingEngine { 17 | 18 | @Inject 19 | GlobalWorldFacade world; 20 | 21 | private Boolean isBodyTemplatesEnabled() { 22 | return world.getBoolean(CukesOptions.REQUEST_BODY_TEMPLATES_ENABLED); 23 | } 24 | 25 | public String processBody(String body) { 26 | if (isBodyTemplatesEnabled()) { 27 | final Map rythmParams = new HashMap<>(); 28 | for (String key : world.keys()) { 29 | Optional value = world.get(key); 30 | if (value.isPresent() && body.contains("@" + key)) rythmParams.put(key, value.get()); 31 | } 32 | return Rythm.render(body, rythmParams); 33 | } 34 | return body; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cukes-core/src/main/resources/META-INF/services/io.cucumber.core.backend.ObjectFactory: -------------------------------------------------------------------------------- 1 | lv.ctco.cukes.core.internal.di.SingletonObjectFactory 2 | -------------------------------------------------------------------------------- /cukes-core/src/main/resources/cucumber.properties: -------------------------------------------------------------------------------- 1 | cucumber.object-factory=lv.ctco.cukes.core.internal.di.SingletonObjectFactory 2 | -------------------------------------------------------------------------------- /cukes-core/src/test/java/lv/ctco/cukes/core/RunCukesTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core; 2 | 3 | import io.cucumber.junit.Cucumber; 4 | import io.cucumber.junit.CucumberOptions; 5 | import org.junit.runner.RunWith; 6 | 7 | @RunWith(Cucumber.class) 8 | @CucumberOptions( 9 | plugin = {"pretty", "json:target/cucumber.json"}, 10 | features = {"classpath:features"}, 11 | glue = {"lv.ctco.cukes"} 12 | ) 13 | public class RunCukesTest { 14 | } 15 | -------------------------------------------------------------------------------- /cukes-core/src/test/java/lv/ctco/cukes/core/TestCukesGuiceModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core; 2 | 3 | import lv.ctco.cukes.core.extension.AbstractCukesModule; 4 | import lv.ctco.cukes.core.extension.CukesInjectableModule; 5 | import lv.ctco.cukes.core.facade.RandomGeneratorFacade; 6 | import lv.ctco.cukes.core.facade.RandomGeneratorFacadeImpl; 7 | import lv.ctco.cukes.core.facade.VariableFacade; 8 | import lv.ctco.cukes.core.facade.VariableFacadeImpl; 9 | 10 | @CukesInjectableModule 11 | public class TestCukesGuiceModule extends AbstractCukesModule { 12 | 13 | @Override 14 | protected void configure() { 15 | bind(VariableFacade.class).to(VariableFacadeImpl.class); 16 | bind(RandomGeneratorFacade.class).to(RandomGeneratorFacadeImpl.class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cukes-core/src/test/java/lv/ctco/cukes/core/api/TestSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.api; 2 | 3 | import com.google.inject.Singleton; 4 | import io.cucumber.java.en.Then; 5 | import lv.ctco.cukes.core.internal.context.InflateContext; 6 | 7 | @Singleton 8 | @InflateContext 9 | public class TestSteps { 10 | 11 | @Then("^variable \"(.+)\" is set to \"(.+)\"$") 12 | public void variableIsSet(String variableName, String variableValue) { 13 | System.out.println(variableName + "=" + variableValue); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cukes-core/src/test/java/lv/ctco/cukes/core/facade/RandomGeneratorFacadeImplTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.facade; 2 | 3 | import lv.ctco.cukes.core.CukesRuntimeException; 4 | import lv.ctco.cukes.core.internal.matchers.ContainsPattern; 5 | import org.junit.Test; 6 | 7 | import java.util.regex.Pattern; 8 | 9 | import static org.hamcrest.MatcherAssert.assertThat; 10 | 11 | public class RandomGeneratorFacadeImplTest { 12 | 13 | private final RandomGeneratorFacadeImpl generator = new RandomGeneratorFacadeImpl(); 14 | 15 | @Test(expected = CukesRuntimeException.class) 16 | public void byInvalidPattern() { 17 | generator.byPattern("b"); 18 | } 19 | 20 | @Test 21 | public void byPattern1() { 22 | assertThat(generator.byPattern("A"), ContainsPattern.matchesPattern("[A-Z]")); 23 | assertThat(generator.byPattern("a"), ContainsPattern.matchesPattern("[a-z]")); 24 | assertThat(generator.byPattern("0"), ContainsPattern.matchesPattern("[0-9]")); 25 | 26 | assertThat(generator.byPattern("0Aa"), ContainsPattern.matchesPattern(Pattern.compile("[0-9][A-Z][a-z]"))); 27 | } 28 | 29 | @Test 30 | public void withLength() { 31 | assertThat(generator.withLength(5), ContainsPattern.matchesPattern(Pattern.compile("[A-Za-z0-9]{5}"))); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cukes-core/src/test/java/lv/ctco/cukes/core/internal/context/BaseContextHandlerTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.internal.context; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.mockito.InjectMocks; 6 | import org.mockito.junit.MockitoJUnitRunner; 7 | 8 | import java.util.List; 9 | 10 | import static org.hamcrest.MatcherAssert.assertThat; 11 | import static org.hamcrest.Matchers.*; 12 | 13 | 14 | @RunWith(MockitoJUnitRunner.class) 15 | public class BaseContextHandlerTest { 16 | 17 | @InjectMocks 18 | ContextCapturer capturer; 19 | 20 | @Test 21 | public void shouldExtractNoGroupsInPattern() { 22 | List groups = capturer.extractGroups("(hello)"); 23 | assertThat(groups, is(empty())); 24 | } 25 | 26 | @Test 27 | public void shouldExtractSingleGroupInPattern() { 28 | List groups = capturer.extractGroups("{(hello)}"); 29 | assertThat(groups, contains("hello")); 30 | } 31 | 32 | @Test 33 | public void shouldExtractTwoGroupsInPattern() { 34 | List groups = capturer.extractGroups("{(hello)}, {(world)}"); 35 | assertThat(groups, contains("hello", "world")); 36 | } 37 | 38 | @Test 39 | public void shouldNotExtractGroupsInPatternWithSpacesInName() { 40 | List groups = capturer.extractGroups("{(hello world)}"); 41 | assertThat(groups, is(empty())); 42 | } 43 | 44 | @Test 45 | public void shouldExtractGroupsInPatternWithUnderscoreInName() { 46 | List groups = capturer.extractGroups("{(hello_world)}"); 47 | assertThat(groups, contains("hello_world")); 48 | } 49 | 50 | @Test 51 | public void shouldExtractDotSeparatedName() { 52 | List groups = capturer.extractGroups("{(hello.world)}"); 53 | assertThat(groups, contains("hello.world")); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cukes-core/src/test/resources/features/issue92.feature: -------------------------------------------------------------------------------- 1 | Feature: Issue 92 2 | 3 | Scenario: should generate proper report 4 | Given let variable "var1" to be random UUID 5 | And let variable "var2" to be random UUID 6 | Then variable "var1" is set to "{(var1)}" 7 | Then variable "var2" is set to "{(var2)}" 8 | Then variable "var1" is set to "{(var1)}" 9 | -------------------------------------------------------------------------------- /cukes-core/src/test/resources/features/passwordGeneration.feature: -------------------------------------------------------------------------------- 1 | Feature: Password generation - by given length 2 | 3 | Scenario: should generate password with provided length 4 | When let variable "var1" to be random password with length 8 5 | Then variable "var1" is set to "{(var1)}" 6 | -------------------------------------------------------------------------------- /cukes-documentation-generator/readme.md: -------------------------------------------------------------------------------- 1 | # Documentation generator 2 | 3 | This utility helps to generate documentation for all available cukes steps. 4 | 5 | Just run `lv.ctco.cukes.docgen.DocumentationGenerator` and check out `target/steps.md` for generated documentation. 6 | -------------------------------------------------------------------------------- /cukes-documentation-generator/src/main/java/lv/ctco/cukes/docgen/CukesComponent.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.docgen; 2 | 3 | import java.util.Comparator; 4 | import java.util.Map; 5 | 6 | public enum CukesComponent { 7 | 8 | core("General", "lv.ctco.cukes.core", "cukes-core"), 9 | http("HTTP", "lv.ctco.cukes.http.api", "cukes-http"), 10 | graphQL("GraphQL", "lv.ctco.cukes.graphql", "cukes-graphql"), 11 | ldap("LDAP", "lv.ctco.cukes.ldap", "cukes-ldap"), 12 | rabbitMQ("RabbitMQ", "lv.ctco.cukes.rabbitmq", "cukes-rabbitmq"), 13 | rest("REST", "lv.ctco.cukes.rest", "cukes-rest"), 14 | httpMock("HTTP Mock", "lv.ctco.cukes.http.mock", "cukes-http-mock"); 15 | 16 | public static final Comparator comparator = Comparator.comparing(CukesComponent::ordinal); 17 | public static final Comparator> mapKeyComparator = (o1, o2) -> comparator.compare(o1.getKey(), o2.getKey()); 18 | 19 | private final String name; 20 | private final String basePackage; 21 | private final String moduleName; 22 | 23 | CukesComponent(String name, String basePackage, String moduleName) { 24 | this.name = name; 25 | this.basePackage = basePackage; 26 | this.moduleName = moduleName; 27 | } 28 | 29 | public static CukesComponent findByClassName(String className) { 30 | for (CukesComponent component : values()) { 31 | if (className.startsWith(component.basePackage)) { 32 | return component; 33 | } 34 | } 35 | throw new IllegalArgumentException("Unexpected class name with steps - " + className); 36 | } 37 | 38 | public String getName() { 39 | return name; 40 | } 41 | 42 | public String getModuleName() { 43 | return moduleName; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /cukes-documentation-generator/src/main/java/lv/ctco/cukes/docgen/StepDefinition.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.docgen; 2 | 3 | import java.util.Comparator; 4 | 5 | public class StepDefinition { 6 | 7 | public static final Comparator comparator = Comparator.comparing(StepDefinition::getPatten); 8 | 9 | private final String patten; 10 | private final String description; 11 | 12 | public StepDefinition(String patten, String description) { 13 | this.patten = patten; 14 | this.description = description; 15 | } 16 | 17 | public String getPatten() { 18 | return patten; 19 | } 20 | 21 | public String getDescription() { 22 | return description; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /cukes-documentation-generator/src/main/java/lv/ctco/cukes/docgen/StepType.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.docgen; 2 | 3 | import io.cucumber.java.en.Given; 4 | import io.cucumber.java.en.Then; 5 | import io.cucumber.java.en.When; 6 | import lv.ctco.cukes.core.CukesDocs; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.lang.reflect.Method; 10 | import java.util.Comparator; 11 | import java.util.function.Function; 12 | 13 | public enum StepType { 14 | 15 | 16 | given(Given.class, method -> method.getAnnotation(Given.class).value()), 17 | when(When.class, method -> method.getAnnotation(When.class).value()), 18 | then(Then.class, method -> method.getAnnotation(Then.class).value()); 19 | 20 | public static final Comparator comparator = Comparator.comparing(Enum::ordinal); 21 | 22 | private final Class annotation; 23 | private final Function patternProvider; 24 | 25 | StepType(Class annotation, Function patternProvider) { 26 | this.annotation = annotation; 27 | this.patternProvider = patternProvider; 28 | } 29 | 30 | public static StepType getTypeForMethod(Method method) { 31 | for (StepType stepType : values()) { 32 | if (method.getAnnotation(stepType.annotation) != null) { 33 | return stepType; 34 | } 35 | } 36 | return null; 37 | } 38 | 39 | public String getPattern(Method method) { 40 | return patternProvider.apply(method); 41 | } 42 | 43 | public String getDescription(Method method) { 44 | CukesDocs docs = method.getAnnotation(CukesDocs.class); 45 | 46 | return docs == null ? null : docs.value(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cukes-documentation-generator/src/main/resources/header.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This is an autogenerated documentation about all available cukes step definitions. 4 | 5 | Each step contains a regular expression pattern, and a short description of what this step is intended to be used for. 6 | -------------------------------------------------------------------------------- /cukes-environment-variables-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Environment variables Plugin for Cukes 2 | 3 | This plugin is created to populate cukes-specific Environment Variables (prefixed with `CUKES_`) to GlobalWorld 4 | -------------------------------------------------------------------------------- /cukes-environment-variables-plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | cukes 5 | lv.ctco.cukes 6 | 1.0.11-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | cukes-environment-variables-plugin 11 | Cukes Environment Variables plugin 12 | Make cukes-specific Environment Variables available in GlobalWorld 13 | 14 | 15 | 16 | lv.ctco.cukes 17 | cukes-core 18 | ${project.version} 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /cukes-environment-variables-plugin/src/main/java/lv/ctco/cukes/core/extension/EnvironmentVariablesPlugin.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.core.extension; 2 | 3 | import com.google.inject.Inject; 4 | import lv.ctco.cukes.core.internal.context.GlobalWorldFacade; 5 | 6 | import java.util.Map; 7 | 8 | public class EnvironmentVariablesPlugin implements CukesPlugin { 9 | public static final String CUKES_PREFIX = "CUKES_"; 10 | @Inject 11 | GlobalWorldFacade world; 12 | 13 | @Override 14 | public void beforeAllTests() { 15 | // Do nothing 16 | } 17 | 18 | @Override 19 | public void afterAllTests() { 20 | // Do nothing 21 | } 22 | 23 | @Override 24 | public void beforeScenario() { 25 | Map envVars = System.getenv(); 26 | envVars.entrySet().stream(). 27 | filter(ev -> ev.getKey().toUpperCase().startsWith(CUKES_PREFIX)). 28 | forEach(ev -> world.put( 29 | ev.getKey().substring(CUKES_PREFIX.length()), 30 | ev.getValue() 31 | )); 32 | } 33 | 34 | @Override 35 | public void afterScenario() { 36 | // Do nothing 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /cukes-graphql/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | cukes-graphql 6 | Cukes GraphQL extension 7 | 8 | 9 | lv.ctco.cukes 10 | cukes 11 | 1.0.11-SNAPSHOT 12 | 13 | 14 | 15 | 16 | lv.ctco.cukes 17 | cukes-http 18 | ${project.version} 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /cukes-graphql/src/main/java/lv/ctco/cukes/graphql/CukesGraphQLGuiceModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.graphql; 2 | 3 | import lv.ctco.cukes.core.extension.CukesInjectableModule; 4 | import lv.ctco.cukes.graphql.internal.PreprocessGraphQLRequestBody; 5 | import lv.ctco.cukes.http.extension.AbstractCukesHttpModule; 6 | 7 | @CukesInjectableModule 8 | public class CukesGraphQLGuiceModule extends AbstractCukesHttpModule { 9 | 10 | @Override 11 | protected void configure() { 12 | registerHttpPlugin(PreprocessGraphQLRequestBody.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cukes-graphql/src/main/java/lv/ctco/cukes/graphql/CukesGraphQLHooks.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.graphql; 2 | 3 | import com.google.inject.Inject; 4 | import io.cucumber.java.After; 5 | import lv.ctco.cukes.graphql.facade.GQLRequestFacade; 6 | 7 | public class CukesGraphQLHooks { 8 | 9 | @Inject 10 | GQLRequestFacade requestFacade; 11 | 12 | @After 13 | public void afterScenario() { 14 | requestFacade.initNewSpecification(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cukes-graphql/src/main/java/lv/ctco/cukes/graphql/api/GivenSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.graphql.api; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.Given; 6 | import lv.ctco.cukes.core.internal.resources.ResourceFileReader; 7 | import lv.ctco.cukes.graphql.facade.GQLRequestFacade; 8 | 9 | @Singleton 10 | public class GivenSteps { 11 | 12 | @Inject 13 | private GQLRequestFacade requestFacade; 14 | 15 | @Inject 16 | private ResourceFileReader fileReader; 17 | 18 | @Given("^query from file \"(.+)\"$") 19 | public void requestQueryFromFile(String path) { 20 | String body = this.fileReader.read(path); 21 | this.requestQuery(body); 22 | } 23 | 24 | @Given("^query:$") 25 | public void requestQuery(String query) { 26 | this.requestFacade.queryBody(query); 27 | } 28 | 29 | @Given("^query variables from file \"(.+)\"$") 30 | public void requestQueryVariablesFromFile(String path) { 31 | String body = this.fileReader.read(path); 32 | this.requestFacade.getGraphQLRequest().setVariables(body); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /cukes-graphql/src/main/java/lv/ctco/cukes/graphql/api/WhenSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.graphql.api; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.When; 6 | import io.restassured.http.ContentType; 7 | import lv.ctco.cukes.http.facade.HttpRequestFacade; 8 | import lv.ctco.cukes.http.facade.HttpResponseFacade; 9 | 10 | @Singleton 11 | public class WhenSteps { 12 | 13 | @Inject 14 | private HttpRequestFacade requestFacade; 15 | 16 | @Inject 17 | private HttpResponseFacade responseFacade; 18 | 19 | @When("^the query is executed$") 20 | public void execute_Query() throws Throwable { 21 | String contentType = ContentType.JSON.toString(); 22 | requestFacade.accept(contentType); 23 | requestFacade.contentType(contentType); 24 | responseFacade.setResponsePrefix("data."); 25 | responseFacade.doRequest("POST", ""); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /cukes-graphql/src/main/java/lv/ctco/cukes/graphql/facade/GQLRequestFacade.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.graphql.facade; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.restassured.specification.RequestSpecification; 6 | import lv.ctco.cukes.core.internal.context.InflateContext; 7 | import lv.ctco.cukes.graphql.internal.GraphQLRequest; 8 | import lv.ctco.cukes.http.facade.HttpRequestFacade; 9 | 10 | @Singleton 11 | @InflateContext 12 | public class GQLRequestFacade { 13 | 14 | private GraphQLRequest graphQLRequest = new GraphQLRequest(); 15 | 16 | @Inject 17 | private HttpRequestFacade requestFacade; 18 | 19 | public void initNewSpecification() { 20 | graphQLRequest = new GraphQLRequest(); 21 | } 22 | 23 | public void queryBody(String body) { 24 | graphQLRequest.setQuery(body); 25 | } 26 | 27 | public void body(GraphQLRequest request) { 28 | specification().body(request); 29 | } 30 | 31 | private RequestSpecification specification() { 32 | return requestFacade.value(); 33 | } 34 | 35 | public GraphQLRequest getGraphQLRequest() { 36 | return graphQLRequest; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /cukes-graphql/src/main/java/lv/ctco/cukes/graphql/internal/GraphQLRequest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.graphql.internal; 2 | 3 | public class GraphQLRequest { 4 | 5 | private String operationName; 6 | private String query; 7 | private String variables; 8 | 9 | public String getOperationName() { 10 | return operationName; 11 | } 12 | 13 | public void setOperationName(String operationName) { 14 | this.operationName = operationName; 15 | } 16 | 17 | public String getQuery() { 18 | return query; 19 | } 20 | 21 | public void setQuery(String query) { 22 | this.query = query; 23 | } 24 | 25 | public String getVariables() { 26 | return variables; 27 | } 28 | 29 | public void setVariables(String variables) { 30 | this.variables = variables; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cukes-graphql/src/main/java/lv/ctco/cukes/graphql/internal/PreprocessGraphQLRequestBody.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.graphql.internal; 2 | 3 | import com.google.common.base.Strings; 4 | import com.google.inject.Inject; 5 | import com.google.inject.Singleton; 6 | import io.restassured.response.Response; 7 | import io.restassured.specification.RequestSpecification; 8 | import lv.ctco.cukes.core.internal.templating.TemplatingEngine; 9 | import lv.ctco.cukes.graphql.facade.GQLRequestFacade; 10 | import lv.ctco.cukes.http.extension.CukesHttpPlugin; 11 | 12 | @Singleton 13 | public class PreprocessGraphQLRequestBody implements CukesHttpPlugin { 14 | 15 | @Inject 16 | GQLRequestFacade requestFacade; 17 | 18 | @Inject 19 | TemplatingEngine templatingEngine; 20 | 21 | @Override 22 | public void beforeRequest(RequestSpecification requestSpecification) { 23 | GraphQLRequest graphQLRequest = requestFacade.getGraphQLRequest(); 24 | if (!Strings.isNullOrEmpty(graphQLRequest.getQuery())) { 25 | graphQLRequest.setQuery(templatingEngine.processBody(graphQLRequest.getQuery())); 26 | } 27 | if (!Strings.isNullOrEmpty(graphQLRequest.getVariables())) { 28 | graphQLRequest.setVariables(templatingEngine.processBody(graphQLRequest.getVariables())); 29 | } 30 | requestSpecification.body(graphQLRequest); 31 | } 32 | 33 | @Override 34 | public void afterRequest(Response response) { 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cukes-http-mock/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | cukes-http-mock 6 | Cukes http mock extension 7 | 8 | 9 | lv.ctco.cukes 10 | cukes 11 | 1.0.11-SNAPSHOT 12 | 13 | 14 | 15 | 16 | lv.ctco.cukes 17 | cukes-core 18 | ${project.version} 19 | 20 | 21 | org.mock-server 22 | mockserver-netty 23 | 5.4.1 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /cukes-http-mock/src/main/java/lv/ctco/cukes/http/mock/extension/HttpMockPlugin.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http.mock.extension; 2 | 3 | import com.google.inject.Inject; 4 | import lv.ctco.cukes.core.extension.CukesPlugin; 5 | import lv.ctco.cukes.http.mock.internal.MockClientServerFacade; 6 | 7 | public class HttpMockPlugin implements CukesPlugin { 8 | @Inject 9 | private MockClientServerFacade mockClientServerFacade; 10 | 11 | @Override 12 | public void beforeAllTests() { 13 | mockClientServerFacade.startAllServers(); 14 | } 15 | 16 | @Override 17 | public void afterAllTests() { 18 | mockClientServerFacade.stopAllServers(); 19 | } 20 | 21 | @Override 22 | public void beforeScenario() { 23 | mockClientServerFacade.resetAllServers(); 24 | } 25 | 26 | @Override 27 | public void afterScenario() { 28 | //TODO verifyNoMoreIterations 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cukes-http-mock/src/main/java/lv/ctco/cukes/http/mock/internal/MockResponse.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http.mock.internal; 2 | 3 | public class MockResponse { 4 | 5 | private String httpMethod; 6 | private String url; 7 | private String receivedBody; 8 | 9 | public void setHttpMethod(String httpMethod) { 10 | this.httpMethod = httpMethod; 11 | } 12 | 13 | public String getHttpMethod() { 14 | return httpMethod; 15 | } 16 | 17 | public void setUrl(String url) { 18 | this.url = url; 19 | } 20 | 21 | public String getUrl() { 22 | return url; 23 | } 24 | 25 | public void setReceivedBody(String receivedBody) { 26 | this.receivedBody = receivedBody; 27 | } 28 | 29 | public String getRequestBody() { 30 | return receivedBody; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cukes-http-mock/src/test/resources/cukes.properties: -------------------------------------------------------------------------------- 1 | cukes.http.mock.port=6667 2 | cukes.http.mock.host=localhost 3 | -------------------------------------------------------------------------------- /cukes-http/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | cukes-http 6 | Cukes HTTP 7 | 8 | 9 | lv.ctco.cukes 10 | cukes 11 | 1.0.11-SNAPSHOT 12 | 13 | 14 | 15 | 16 | lv.ctco.cukes 17 | cukes-core 18 | ${project.version} 19 | 20 | 21 | 22 | 23 | io.rest-assured 24 | rest-assured 25 | ${rest.assured.version} 26 | 27 | 28 | 29 | 30 | com.google.code.gson 31 | gson 32 | 33 | 34 | 35 | 36 | com.jayway.awaitility 37 | awaitility 38 | 1.7.0 39 | 40 | 41 | org.hamcrest 42 | hamcrest-core 43 | 44 | 45 | org.hamcrest 46 | hamcrest-library 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/AwaitCondition.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http; 2 | 3 | import io.restassured.response.Response; 4 | import lv.ctco.cukes.core.internal.helpers.Time; 5 | import org.hamcrest.Matcher; 6 | 7 | public class AwaitCondition { 8 | 9 | private final Time waitTime; 10 | private final Time interval; 11 | private final Matcher successMatcher; 12 | private final Matcher failureMatcher; 13 | 14 | public AwaitCondition(Time waitTime, Time interval, Matcher successMatcher) { 15 | this.waitTime = waitTime; 16 | this.interval = interval; 17 | this.successMatcher = successMatcher; 18 | failureMatcher = null; 19 | } 20 | 21 | public AwaitCondition(Time waitTime, Time interval, Matcher successMatcher, Matcher failureMatcher) { 22 | this.waitTime = waitTime; 23 | this.interval = interval; 24 | this.successMatcher = successMatcher; 25 | this.failureMatcher = failureMatcher; 26 | } 27 | 28 | public Time getWaitTime() { 29 | return waitTime; 30 | } 31 | 32 | public Time getInterval() { 33 | return interval; 34 | } 35 | 36 | public Matcher getSuccessMatcher() { 37 | return successMatcher; 38 | } 39 | 40 | public Matcher getFailureMatcher() { 41 | return failureMatcher; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/CukesHttpGuiceModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http; 2 | 3 | import lv.ctco.cukes.core.extension.CukesInjectableModule; 4 | import lv.ctco.cukes.core.facade.RandomGeneratorFacade; 5 | import lv.ctco.cukes.core.facade.RandomGeneratorFacadeImpl; 6 | import lv.ctco.cukes.core.facade.VariableFacade; 7 | import lv.ctco.cukes.core.facade.VariableFacadeImpl; 8 | import lv.ctco.cukes.http.extension.AbstractCukesHttpModule; 9 | import lv.ctco.cukes.http.extension.CukesHttpPlugin; 10 | import lv.ctco.cukes.http.facade.HttpAssertionFacade; 11 | import lv.ctco.cukes.http.facade.HttpAssertionFacadeImpl; 12 | import lv.ctco.cukes.http.logging.HttpLoggingPlugin; 13 | 14 | @CukesInjectableModule 15 | public class CukesHttpGuiceModule extends AbstractCukesHttpModule { 16 | 17 | @Override 18 | protected void configure() { 19 | bind(HttpAssertionFacade.class).to(HttpAssertionFacadeImpl.class); 20 | bind(VariableFacade.class).to(VariableFacadeImpl.class); 21 | bind(RandomGeneratorFacade.class).to(RandomGeneratorFacadeImpl.class); 22 | 23 | registerHttpPlugin(HttpLoggingPlugin.class); 24 | bindPlugins(CukesHttpPlugin.class); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/CukesHttpHooks.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http; 2 | 3 | import com.google.inject.Inject; 4 | import io.cucumber.java.After; 5 | import lv.ctco.cukes.http.facade.HttpRequestFacade; 6 | 7 | public class CukesHttpHooks { 8 | 9 | @Inject 10 | HttpRequestFacade requestFacade; 11 | 12 | @After 13 | public void afterScenario() { 14 | requestFacade.initNewSpecification(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/HttpMethod.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http; 2 | 3 | import io.restassured.response.Response; 4 | import io.restassured.specification.RequestSpecification; 5 | import lv.ctco.cukes.core.CukesRuntimeException; 6 | 7 | public enum HttpMethod { 8 | GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH; 9 | 10 | public static HttpMethod parse(String string) { 11 | for (HttpMethod httpMethod : values()) { 12 | if (httpMethod.toString().equalsIgnoreCase(string)) { 13 | return httpMethod; 14 | } 15 | } 16 | throw new IllegalArgumentException(); 17 | } 18 | 19 | 20 | public Response doRequest(RequestSpecification when) { 21 | return doRequest(when, ""); 22 | } 23 | 24 | public Response doRequest(RequestSpecification when, String url) { 25 | switch (this) { 26 | case GET: return when.get(url); 27 | case POST: return when.post(url); 28 | case PUT: return when.put(url); 29 | case DELETE: return when.delete(url); 30 | case OPTIONS: return when.options(url); 31 | case HEAD: return when.head(url); 32 | case PATCH: return when.patch(url); 33 | } 34 | throw new CukesRuntimeException("This Http Method is nos supported"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/extension/AbstractCukesHttpModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http.extension; 2 | 3 | import com.google.inject.multibindings.Multibinder; 4 | import lv.ctco.cukes.core.extension.AbstractCukesModule; 5 | 6 | public abstract class AbstractCukesHttpModule extends AbstractCukesModule { 7 | 8 | protected void registerHttpPlugin(Class pluginClass) { 9 | Multibinder multibinder = Multibinder.newSetBinder(binder(), CukesHttpPlugin.class); 10 | multibinder.addBinding().to(pluginClass); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/extension/CukesHttpPlugin.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http.extension; 2 | 3 | import io.restassured.response.Response; 4 | import io.restassured.specification.RequestSpecification; 5 | 6 | public interface CukesHttpPlugin { 7 | 8 | void beforeRequest(RequestSpecification requestSpecification); 9 | 10 | void afterRequest(Response response); 11 | } 12 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/facade/ResponseContentProvider.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http.facade; 2 | 3 | import io.restassured.response.Response; 4 | import lv.ctco.cukes.core.internal.matchers.JsonMatchers; 5 | 6 | public class ResponseContentProvider implements JsonMatchers.ContentProvider { 7 | 8 | public static final ResponseContentProvider INSTANCE = new ResponseContentProvider(); 9 | 10 | @Override 11 | public String getValue(Object o) { 12 | return ((Response) o).getBody().asString(); 13 | } 14 | 15 | @Override 16 | public String getContentType(Object o) { 17 | return ((Response) o).getContentType(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/https/TrustAllTrustManager.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http.https; 2 | 3 | import javax.net.ssl.TrustManager; 4 | import java.security.KeyManagementException; 5 | import java.security.NoSuchAlgorithmException; 6 | import java.security.cert.X509Certificate; 7 | 8 | @SuppressWarnings("SameReturnValue") 9 | public class TrustAllTrustManager implements TrustManager, javax.net.ssl.X509TrustManager { 10 | 11 | /** 12 | * Method to trust all the HTTPS certificates. To be used only in the 13 | * development environment for convenience sake 14 | */ 15 | public static void trustAllHttpsCertificates() { 16 | try { 17 | javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1]; 18 | javax.net.ssl.TrustManager tm = new TrustAllTrustManager(); 19 | trustAllCerts[0] = tm; 20 | javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL"); 21 | javax.net.ssl.SSLSessionContext serverSessionContext = sc.getServerSessionContext(); 22 | serverSessionContext.setSessionTimeout(0); 23 | sc.init(null, trustAllCerts, null); 24 | javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 25 | } catch (KeyManagementException | NoSuchAlgorithmException | IllegalArgumentException e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | 30 | @Override 31 | public X509Certificate[] getAcceptedIssuers() { 32 | return null; 33 | } 34 | 35 | @Override 36 | public void checkServerTrusted(X509Certificate[] certs, String authType) { 37 | // Explicitly do nothing 38 | } 39 | 40 | @Override 41 | public void checkClientTrusted(X509Certificate[] certs, String authType) { 42 | // Explicitly do nothing 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/json/JsonParser.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http.json; 2 | 3 | import com.google.gson.stream.JsonToken; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import static com.google.gson.stream.JsonToken.*; 9 | 10 | public class JsonParser { 11 | 12 | public Map parsePathToValueMap(String json) { 13 | Map result = new HashMap<>(); 14 | SafeJsonReader reader = new SafeJsonReader(json); 15 | for (JsonToken token : reader) { 16 | if (BEGIN_ARRAY == token) reader.beginArray(); 17 | else if (END_ARRAY == token) reader.endArray(); 18 | else if (BEGIN_OBJECT == token) reader.beginObject(); 19 | else if (END_OBJECT == token) reader.endObject(); 20 | else if (NAME == token) reader.nextName(); 21 | else if (STRING == token) add(reader.getCurrentPath(), reader.nextString(), result); 22 | else if (NUMBER == token) add(reader.getCurrentPath(), reader.nextString(), result); 23 | else if (BOOLEAN == token) add(reader.getCurrentPath(), Boolean.toString(reader.nextBoolean()), result); 24 | else if (NULL == token) reader.nextNull(); 25 | } 26 | reader.close(); 27 | return result; 28 | } 29 | 30 | static private void add(String path, String value, Map result) { 31 | if (path.startsWith("$.")) { 32 | path = path.substring(2); 33 | } else { 34 | path = path.substring(1); 35 | } 36 | result.put(path, value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/logging/LoggerPrintStream.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http.logging; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.slf4j.Logger; 5 | import org.slf4j.event.Level; 6 | 7 | import java.io.OutputStream; 8 | import java.io.PrintStream; 9 | 10 | public class LoggerPrintStream extends PrintStream { 11 | 12 | public LoggerPrintStream(Logger logger, Level level) { 13 | super(new LoggerOutputStream(logger, level), true); 14 | } 15 | 16 | private static class LoggerOutputStream extends OutputStream { 17 | 18 | private final Logger logger; 19 | private final Level level; 20 | private StringBuilder stringBuilder = new StringBuilder(); 21 | 22 | LoggerOutputStream(Logger logger, Level level) { 23 | this.logger = logger; 24 | this.level = level; 25 | } 26 | 27 | @Override 28 | public void write(int b) { 29 | stringBuilder.append((char) b); 30 | } 31 | 32 | @Override 33 | public void flush() { 34 | log(logger, level, stringBuilder.toString()); 35 | stringBuilder = new StringBuilder(); 36 | } 37 | 38 | private void log(Logger logger, Level level, String message) { 39 | 40 | if (StringUtils.isBlank(message)) return; 41 | 42 | switch (level) { 43 | case TRACE: 44 | logger.trace(message); 45 | break; 46 | case DEBUG: 47 | logger.debug(message); 48 | break; 49 | case INFO: 50 | logger.info(message); 51 | break; 52 | case WARN: 53 | logger.warn(message); 54 | break; 55 | case ERROR: 56 | logger.error(message); 57 | break; 58 | } 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/matchers/AwaitConditionMatcher.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http.matchers; 2 | 3 | import io.cucumber.core.exception.CucumberException; 4 | import io.restassured.response.Response; 5 | import lv.ctco.cukes.http.AwaitCondition; 6 | import org.hamcrest.Description; 7 | import org.hamcrest.TypeSafeMatcher; 8 | 9 | public class AwaitConditionMatcher extends TypeSafeMatcher { 10 | 11 | private final AwaitCondition awaitCondition; 12 | 13 | public AwaitConditionMatcher(AwaitCondition awaitCondition) { 14 | this.awaitCondition = awaitCondition; 15 | } 16 | 17 | @Override 18 | public void describeTo(Description description) { 19 | description.appendText("Matches successful or failure response."); 20 | } 21 | 22 | @Override 23 | protected boolean matchesSafely(Response response) { 24 | if (awaitCondition.getSuccessMatcher() != null && awaitCondition.getSuccessMatcher().matches(response)) { 25 | return true; 26 | } 27 | if (awaitCondition.getFailureMatcher() != null && awaitCondition.getFailureMatcher().matches(response)) { 28 | throw new CucumberException("Expected successful response but was failed."); 29 | } 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cukes-http/src/main/java/lv/ctco/cukes/http/matchers/StatusCodeMatcher.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http.matchers; 2 | 3 | import io.restassured.response.Response; 4 | import org.hamcrest.Description; 5 | import org.hamcrest.TypeSafeDiagnosingMatcher; 6 | 7 | import static java.lang.String.format; 8 | 9 | public class StatusCodeMatcher extends TypeSafeDiagnosingMatcher { 10 | 11 | private final Integer expectedStatusCode; 12 | private final Response response; 13 | private final boolean appendBody; 14 | private final Integer maxSize; 15 | 16 | public StatusCodeMatcher(Integer expectedStatusCode, Response response, boolean appendBody, Integer maxSize) { 17 | this.expectedStatusCode = expectedStatusCode; 18 | this.response = response; 19 | this.appendBody = appendBody; 20 | this.maxSize = maxSize; 21 | } 22 | 23 | @Override 24 | protected boolean matchesSafely(Integer statusCode, Description description) { 25 | description.appendText(format("was \"%d\"", statusCode)); 26 | 27 | if (appendBody) { 28 | final String body = response.body().asString(); 29 | final int size = body.length(); 30 | 31 | if (response.getContentType().equals("application/octet-stream")) { 32 | description.appendText(" with body "); 33 | } else if (maxSize != null && size > maxSize) { 34 | description.appendText(" with body "); 35 | } else { 36 | description.appendText(format(" with body:\n\"\"\"\n%s\n\"\"\"", body)); 37 | } 38 | } 39 | 40 | return expectedStatusCode.equals(statusCode); 41 | } 42 | 43 | @Override 44 | public void describeTo(Description description) { 45 | description.appendText(format("\"%d\"", expectedStatusCode)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cukes-http/src/test/java/lv/ctco/cukes/http/CustomMatchers.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http; 2 | 3 | import java.util.Optional; 4 | import org.hamcrest.Description; 5 | import org.hamcrest.Matcher; 6 | import org.hamcrest.TypeSafeMatcher; 7 | import org.hamcrest.core.IsEqual; 8 | 9 | import java.util.Map; 10 | 11 | public class CustomMatchers { 12 | 13 | public static Matcher> hasSize(final int size) { 14 | return new TypeSafeMatcher>() { 15 | @Override 16 | public boolean matchesSafely(Map kvMap) { 17 | return kvMap.size() == size; 18 | } 19 | 20 | @Override 21 | public void describeTo(Description description) { 22 | description.appendText(" has ").appendValue(size).appendText(" key/value pairs"); 23 | } 24 | }; 25 | } 26 | 27 | public static Matcher> equalToOptional(final T operand) { 28 | return new TypeSafeMatcher>() { 29 | 30 | private Matcher equalTo; 31 | 32 | @Override 33 | public void describeTo(Description description) { 34 | IsEqual.equalTo(equalTo).describeTo(description); 35 | } 36 | 37 | @Override 38 | protected boolean matchesSafely(Optional t) { 39 | if (t.isPresent()) { 40 | equalTo = IsEqual.equalTo(t.get()); 41 | return equalTo.matches(operand); 42 | } 43 | return false; 44 | } 45 | }; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /cukes-http/src/test/java/lv/ctco/cukes/http/RequestBody.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http; 2 | 3 | import io.restassured.config.RestAssuredConfig; 4 | import io.restassured.internal.ResponseParserRegistrar; 5 | import io.restassured.internal.RestAssuredResponseOptionsImpl; 6 | 7 | public class RequestBody extends RestAssuredResponseOptionsImpl { 8 | 9 | public RequestBody(String contentType, String content) { 10 | setRpr(new ResponseParserRegistrar()); 11 | setConfig(new RestAssuredConfig()); 12 | setContentType(contentType); 13 | setContent(content); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cukes-http/src/test/java/lv/ctco/cukes/http/matchers/EndsWithRegexpTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.http.matchers; 2 | 3 | import lv.ctco.cukes.core.internal.matchers.EndsWithRegexp; 4 | import org.hamcrest.Matchers; 5 | import org.junit.Test; 6 | 7 | import static org.hamcrest.MatcherAssert.assertThat; 8 | 9 | public class EndsWithRegexpTest { 10 | 11 | @Test 12 | public void matchesDirectMatch() { 13 | assertThat("hello", EndsWithRegexp.endsWithRegexp("hello")); 14 | } 15 | 16 | @Test 17 | public void matchesEndWith() { 18 | assertThat("hello world", EndsWithRegexp.endsWithRegexp("world")); 19 | } 20 | 21 | @Test 22 | public void matchesEndWithRegexp() { 23 | assertThat("hello world", EndsWithRegexp.endsWithRegexp("el.*world")); 24 | } 25 | 26 | @Test 27 | public void matchesNotEndWith() { 28 | assertThat("hello world", Matchers.not(EndsWithRegexp.endsWithRegexp("hello"))); 29 | } 30 | 31 | @Test 32 | public void matchesNotEndWithRegexp() { 33 | assertThat("hello world", Matchers.not(EndsWithRegexp.endsWithRegexp("h.*o"))); 34 | } 35 | 36 | @Test 37 | public void matchesLocationUrl() { 38 | assertThat("http://company.com:80/webapp/orx/rest/index/types/CLIENT/nodes/6f1155df-644b-4228-89af" + 39 | "-7d24b8fe1a8d", EndsWithRegexp.endsWithRegexp("/index/types/CLIENT/nodes/.+")); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /cukes-ldap/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | cukes 5 | lv.ctco.cukes 6 | 1.0.11-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | cukes-ldap 11 | Cukes LDAP extension 12 | 13 | 14 | 15 | lv.ctco.cukes 16 | cukes-core 17 | ${project.version} 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /cukes-ldap/src/main/java/lv/ctco/cukes/ldap/CukesLDAPGuiceModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap; 2 | 3 | import lv.ctco.cukes.core.extension.AbstractCukesModule; 4 | import lv.ctco.cukes.core.extension.CukesInjectableModule; 5 | import lv.ctco.cukes.core.facade.RandomGeneratorFacade; 6 | import lv.ctco.cukes.core.facade.RandomGeneratorFacadeImpl; 7 | import lv.ctco.cukes.core.facade.VariableFacade; 8 | import lv.ctco.cukes.core.facade.VariableFacadeImpl; 9 | 10 | @CukesInjectableModule 11 | public class CukesLDAPGuiceModule extends AbstractCukesModule { 12 | 13 | @Override 14 | protected void configure() { 15 | bind(VariableFacade.class).to(VariableFacadeImpl.class); 16 | bind(RandomGeneratorFacade.class).to(RandomGeneratorFacadeImpl.class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cukes-ldap/src/main/java/lv/ctco/cukes/ldap/CukesLDAPHooks.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.After; 6 | import lv.ctco.cukes.ldap.facade.EntityFacade; 7 | import lv.ctco.cukes.ldap.facade.ModificationFacade; 8 | import lv.ctco.cukes.ldap.facade.SetupFacade; 9 | 10 | @Singleton 11 | public class CukesLDAPHooks { 12 | 13 | @Inject 14 | SetupFacade setupFacade; 15 | @Inject 16 | EntityFacade entityFacade; 17 | @Inject 18 | ModificationFacade modificationFacade; 19 | 20 | @After 21 | public void afterScenario() { 22 | setupFacade.initConfiguration(); 23 | entityFacade.initConfiguration(); 24 | modificationFacade.reset(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /cukes-ldap/src/main/java/lv/ctco/cukes/ldap/api/GivenSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap.api; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.Given; 6 | import lv.ctco.cukes.core.internal.context.InflateContext; 7 | import lv.ctco.cukes.ldap.facade.EntityFacade; 8 | import lv.ctco.cukes.ldap.facade.ModificationFacade; 9 | import lv.ctco.cukes.ldap.facade.SetupFacade; 10 | 11 | @Singleton 12 | @InflateContext 13 | public class GivenSteps { 14 | 15 | @Inject 16 | SetupFacade setupFacade; 17 | @Inject 18 | EntityFacade entityFacade; 19 | @Inject 20 | ModificationFacade modificationFacade; 21 | 22 | @Given("^LDAP server URL is \"(.+)\"$") 23 | public void setUrl(String url) { 24 | setupFacade.setUrl(url); 25 | } 26 | 27 | @Given("^LDAP server can be connected by user with DN \"(.+)\" and password \"(.+)\"$") 28 | public void setUserDn(String userDn, String password) { 29 | setupFacade.setUserDn(userDn); 30 | setupFacade.setPassword(password); 31 | } 32 | 33 | @Given("^the client imports LDIF:$") 34 | public void importLdif(String ldif) { 35 | entityFacade.importLdif(ldif); 36 | } 37 | 38 | @Given("^prepare new entity modification$") 39 | public void prepareNewModification() { 40 | modificationFacade.reset(); 41 | } 42 | 43 | @Given("^change attribute \"(.+)\" (add|remove|replace) value \"(.+)\"$") 44 | public void addModification(String attribute, String operation, String value) { 45 | modificationFacade.add(attribute, operation, value); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /cukes-ldap/src/main/java/lv/ctco/cukes/ldap/api/WhenSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap.api; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.When; 6 | import lv.ctco.cukes.core.internal.context.InflateContext; 7 | import lv.ctco.cukes.ldap.facade.EntityFacade; 8 | import lv.ctco.cukes.ldap.facade.ModificationFacade; 9 | 10 | @Singleton 11 | @InflateContext 12 | public class WhenSteps { 13 | 14 | @Inject 15 | EntityFacade entityFacade; 16 | @Inject 17 | ModificationFacade modificationFacade; 18 | 19 | @When("^the client retrieves entity by DN \"(.+)\"$") 20 | public void readEntityByDn(String dn) { 21 | entityFacade.readEntityByDn(dn); 22 | } 23 | 24 | @When("^the client creates entity using LDIF:$") 25 | public void createEntityFromLdif(String ldif) { 26 | entityFacade.importLdif(ldif); 27 | } 28 | 29 | @When("^the client creates entity using LDIF from file \"(.+)\"$") 30 | public void createEntityFromLdifFile(String ldifFile) { 31 | entityFacade.importLdifFromFile(ldifFile); 32 | } 33 | 34 | @When("^the client deletes entity with DN \"(.+)\"$") 35 | public void deleteEntityWithDn(String dn) { 36 | entityFacade.deleteEntityByDn(dn); 37 | } 38 | 39 | @When("^the client updates entity with DN \"(.+)\" using prepared modifications$") 40 | public void modifyEntityWithDn(String dn) { 41 | modificationFacade.execute(dn); 42 | } 43 | 44 | @When("^the client searches entities within DN \"(.+)\" by filter \"(.+)\"$") 45 | public void clientSearchEntitiesByFilter(String dn, String filter) { 46 | entityFacade.searchByFilter(dn, filter); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cukes-ldap/src/main/java/lv/ctco/cukes/ldap/facade/ModificationFacade.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap.facade; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lv.ctco.cukes.core.CukesRuntimeException; 6 | import lv.ctco.cukes.ldap.internal.EntityService; 7 | 8 | import javax.naming.directory.BasicAttribute; 9 | import javax.naming.directory.DirContext; 10 | import javax.naming.directory.ModificationItem; 11 | import java.util.ArrayList; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | @Singleton 17 | public class ModificationFacade { 18 | 19 | private static final Map OP_MAPPING = new HashMap<>(); 20 | 21 | static { 22 | OP_MAPPING.put("add", DirContext.ADD_ATTRIBUTE); 23 | OP_MAPPING.put("remove", DirContext.REMOVE_ATTRIBUTE); 24 | OP_MAPPING.put("replace", DirContext.REPLACE_ATTRIBUTE); 25 | } 26 | 27 | @Inject 28 | EntityService entityService; 29 | 30 | private final List modifications = new ArrayList<>(); 31 | 32 | public void add(String attribute, String operation, String value) { 33 | Integer op = OP_MAPPING.get(operation); 34 | if (op == null) { 35 | throw new CukesRuntimeException("Unknown operation: " + operation); 36 | } 37 | modifications.add(new ModificationItem(op, new BasicAttribute(attribute, value))); 38 | } 39 | 40 | public void execute(String dn) { 41 | entityService.modifyByDn(dn, modifications); 42 | reset(); 43 | } 44 | 45 | public void reset() { 46 | modifications.clear(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cukes-ldap/src/main/java/lv/ctco/cukes/ldap/facade/SetupFacade.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap.facade; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lv.ctco.cukes.core.internal.context.GlobalWorldFacade; 6 | import lv.ctco.cukes.ldap.internal.ConnectionService; 7 | 8 | @Singleton 9 | public class SetupFacade { 10 | @Inject 11 | GlobalWorldFacade world; 12 | @Inject 13 | ConnectionService connectionService; 14 | 15 | public void initConfiguration() { 16 | connectionService.close(); 17 | } 18 | 19 | public void setUrl(String url) { 20 | world.put(ConnectionService.URL, url); 21 | connectionService.close(); 22 | } 23 | 24 | public void setUserDn(String userDn) { 25 | world.put(ConnectionService.USER, userDn); 26 | connectionService.close(); 27 | } 28 | 29 | public void setPassword(String password) { 30 | world.put(ConnectionService.PASSWORD, password); 31 | connectionService.close(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cukes-ldap/src/main/java/lv/ctco/cukes/ldap/internal/ConnectionService.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap.internal; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lv.ctco.cukes.core.CukesRuntimeException; 6 | import lv.ctco.cukes.core.internal.context.GlobalWorldFacade; 7 | 8 | import javax.naming.Context; 9 | import javax.naming.NamingException; 10 | import javax.naming.ldap.InitialLdapContext; 11 | import javax.naming.ldap.LdapContext; 12 | import java.util.Hashtable; 13 | 14 | @Singleton 15 | public class ConnectionService { 16 | 17 | public static final String URL = "ldap.url"; 18 | public static final String USER = "ldap.user"; 19 | public static final String PASSWORD = "ldap.password"; 20 | 21 | @Inject 22 | GlobalWorldFacade world; 23 | 24 | private LdapContext context; 25 | 26 | public LdapContext getContext() { 27 | if (context == null) { 28 | Hashtable environment = new Hashtable<>(); 29 | environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 30 | environment.put(Context.PROVIDER_URL, world.get(URL, "ldap://localhost:389")); 31 | environment.put(Context.SECURITY_AUTHENTICATION, "simple"); 32 | environment.put(Context.SECURITY_PRINCIPAL, world.get(USER, "cn=admin")); 33 | environment.put(Context.SECURITY_CREDENTIALS, world.get(PASSWORD, "password")); 34 | try { 35 | context = new InitialLdapContext(environment, null); 36 | } catch (NamingException e) { 37 | throw new CukesRuntimeException(e); 38 | } 39 | } 40 | return context; 41 | } 42 | 43 | public void close() { 44 | if (context != null) { 45 | try { 46 | context.close(); 47 | } catch (NamingException e) { 48 | throw new CukesRuntimeException(e); 49 | } 50 | } 51 | context = null; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /cukes-ldap/src/main/java/lv/ctco/cukes/ldap/internal/DnComparator.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap.internal; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import java.util.Comparator; 6 | 7 | public class DnComparator implements Comparator { 8 | private final boolean reverse; 9 | 10 | public DnComparator(boolean reverse) { 11 | this.reverse = reverse; 12 | } 13 | 14 | @Override 15 | public int compare(String o1, String o2) { 16 | return (reverse ? -1 : 1) * StringUtils.reverse(o1).compareTo(StringUtils.reverse(o2)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cukes-ldap/src/test/java/lv/ctco/cukes/ldap/internal/DnComparatorTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap.internal; 2 | 3 | import org.hamcrest.Matcher; 4 | import org.hamcrest.Matchers; 5 | import org.junit.Test; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | import static org.hamcrest.CoreMatchers.is; 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | 14 | public class DnComparatorTest { 15 | 16 | DnComparator comparator = new DnComparator(false); 17 | 18 | @Test 19 | public void compare_sameTree() { 20 | assertThat(comparator.compare("cn=root", "cn=b,cn=root"), is(more())); 21 | assertThat(comparator.compare("cn=a,cn=root", "cn=root"), is(less())); 22 | assertThat(comparator.compare("cn=a,cn=root", "cn=a,cn=root"), is(same())); 23 | } 24 | 25 | @Test 26 | public void compare_differentTrees() { 27 | assertThat(comparator.compare("cn=a,cn=root", "cn=b,cn=root"), is(more())); 28 | assertThat(comparator.compare("cn=b,cn=root", "cn=a,cn=root"), is(less())); 29 | } 30 | 31 | @Test 32 | public void sort() { 33 | List dns = new ArrayList<>(Arrays.asList( 34 | "cn=root", 35 | "cn=a,cn=root", 36 | "cn=b,cn=root", 37 | "cn=c,cn=a,cn=root" 38 | )); 39 | dns.sort(comparator); 40 | assertThat(dns.get(0), is("cn=root")); 41 | assertThat(dns.get(1), is("cn=a,cn=root")); 42 | assertThat(dns.get(2), is("cn=c,cn=a,cn=root")); 43 | assertThat(dns.get(3), is("cn=b,cn=root")); 44 | } 45 | 46 | static Matcher more() { 47 | return Matchers.lessThan(0); 48 | } 49 | 50 | static Matcher less() { 51 | return Matchers.greaterThan(0); 52 | } 53 | 54 | static Matcher same() { 55 | return Matchers.is(0); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /cukes-ldap/src/test/resources/example.ldif: -------------------------------------------------------------------------------- 1 | dn: dc=example,dc=com 2 | objectClass: domain 3 | objectClass: top 4 | dc: example 5 | 6 | dn: ou=Users,dc=example,dc=com 7 | objectClass: organizationalUnit 8 | objectClass: top 9 | ou: Users 10 | 11 | dn: ou=Groups,dc=example,dc=com 12 | objectClass: organizationalUnit 13 | objectClass: top 14 | ou: Groups 15 | 16 | dn: cn=Micha Kops,ou=Users,dc=example,dc=com 17 | objectClass: inetOrgPerson 18 | objectClass: organizationalPerson 19 | objectClass: person 20 | objectClass: top 21 | cn: Micha 22 | Kops 23 | sn: Kops 24 | uid: mkops 25 | userPassword: abcdefg 26 | -------------------------------------------------------------------------------- /cukes-oauth/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | cukes 5 | lv.ctco.cukes 6 | 1.0.11-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | cukes-oauth 11 | Cukes OAuth extension 12 | 13 | 14 | 15 | lv.ctco.cukes 16 | cukes-http 17 | ${project.version} 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /cukes-oauth/src/main/java/lv/ctco/cukes/oauth/GrantType.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.oauth; 2 | 3 | import lv.ctco.cukes.core.internal.context.GlobalWorldFacade; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public enum GrantType { 9 | 10 | client_credentials, 11 | password(OAuthCukesConstants.USER_NAME, OAuthCukesConstants.PASSWORD); 12 | 13 | private static final Map attributeNameMapping = new HashMap<>(); 14 | 15 | static { 16 | attributeNameMapping.put(OAuthCukesConstants.USER_NAME, "username"); 17 | attributeNameMapping.put(OAuthCukesConstants.PASSWORD, "password"); 18 | } 19 | 20 | private final String[] requiredAttributes; 21 | 22 | GrantType(String... requiredAttributes) { 23 | this.requiredAttributes = requiredAttributes; 24 | } 25 | 26 | public Map getParameters(GlobalWorldFacade world) { 27 | Map parameters = new HashMap<>(); 28 | parameters.put("grant_type", name()); 29 | for (String attribute : requiredAttributes) { 30 | parameters.put(attributeNameMapping.get(attribute), world.getOrThrow(attribute)); 31 | } 32 | return parameters; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /cukes-oauth/src/main/java/lv/ctco/cukes/oauth/OAuthCukesConstants.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.oauth; 2 | 3 | public class OAuthCukesConstants { 4 | 5 | public static final String ENABLED = "oauth.enabled"; 6 | public static final String GRANT_TYPE = "oauth.grant_type"; 7 | public static final String AUTH_SERVER = "oauth.auth_server"; 8 | public static final String CLIENT_ID = "oauth.client_id"; 9 | public static final String CLIENT_SECRET = "oauth.client_secret"; 10 | public static final String USER_NAME = "oauth.user_name"; 11 | public static final String PASSWORD = "oauth.password"; 12 | public static final String SCOPE = "oauth.scope"; 13 | 14 | public static final String CACHED_TOKEN = "oauth.token"; 15 | public static final String TOKEN_EXPIRES_ON = "oauth.expires"; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /cukes-oauth/src/main/java/lv/ctco/cukes/oauth/OAuthCukesGuiceModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.oauth; 2 | 3 | import lv.ctco.cukes.core.extension.CukesInjectableModule; 4 | import lv.ctco.cukes.http.extension.AbstractCukesHttpModule; 5 | 6 | @CukesInjectableModule 7 | public class OAuthCukesGuiceModule extends AbstractCukesHttpModule { 8 | 9 | @Override 10 | protected void configure() { 11 | registerHttpPlugin(OAuthCukesHttpPlugin.class); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /cukes-oauth/src/main/java/lv/ctco/cukes/oauth/OAuthCukesHttpPlugin.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.oauth; 2 | 3 | import com.google.inject.Inject; 4 | import io.restassured.response.Response; 5 | import io.restassured.specification.RequestSpecification; 6 | import lv.ctco.cukes.core.CukesOptions; 7 | import lv.ctco.cukes.core.CukesRuntimeException; 8 | import lv.ctco.cukes.core.internal.context.GlobalWorldFacade; 9 | import lv.ctco.cukes.http.extension.CukesHttpPlugin; 10 | import lv.ctco.cukes.oauth.internal.OAuthTokenRetriever; 11 | 12 | import java.io.IOException; 13 | import java.util.Optional; 14 | 15 | public class OAuthCukesHttpPlugin implements CukesHttpPlugin { 16 | 17 | @Inject 18 | OAuthTokenRetriever tokenRetriever; 19 | @Inject 20 | GlobalWorldFacade worldFacade; 21 | 22 | @Override 23 | public void beforeRequest(RequestSpecification requestSpecification) { 24 | java.util.Optional authType = worldFacade.get(CukesOptions.AUTH_TYPE); 25 | if (authType.isPresent()) { 26 | if (!"OAuth".equalsIgnoreCase(authType.get())) { 27 | return; 28 | } 29 | } else { 30 | return; 31 | } 32 | try { 33 | Optional authorizationHeader = tokenRetriever.getAuthorizationHeader(); 34 | authorizationHeader.ifPresent(s -> requestSpecification.auth().oauth2(s)); 35 | } catch (IOException e) { 36 | throw new CukesRuntimeException("Cannot get OAuth token", e); 37 | } 38 | } 39 | 40 | @Override 41 | public void afterRequest(Response response) { 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /cukes-oauth/src/main/java/lv/ctco/cukes/oauth/api/GivenSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.oauth.api; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.Given; 6 | import lv.ctco.cukes.core.CukesDocs; 7 | import lv.ctco.cukes.http.facade.HttpRequestFacade; 8 | import lv.ctco.cukes.oauth.facade.OAuthFacade; 9 | 10 | @Singleton 11 | public class GivenSteps { 12 | 13 | @Inject 14 | HttpRequestFacade httpRequestFacade; 15 | @Inject 16 | OAuthFacade oAuthFacade; 17 | 18 | @Given("^using OAuth$") 19 | @CukesDocs("The following scenario will use OAuth") 20 | public void usingOAuth() { 21 | httpRequestFacade.authenticationType("OAuth"); 22 | } 23 | 24 | @Given("^using (.+) grant type$") 25 | @CukesDocs("Specify which grant type is used") 26 | public void usingGrantType(String grantType) { 27 | oAuthFacade.setGrantType(grantType); 28 | } 29 | 30 | @Given("^using \"(.+)\" scopes$") 31 | @CukesDocs("Specify for which scopes access token will be requested") 32 | public void usingScopes(String scopes) { 33 | oAuthFacade.setScopes(scopes); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cukes-oauth/src/main/java/lv/ctco/cukes/oauth/facade/OAuthFacade.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.oauth.facade; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lv.ctco.cukes.core.internal.context.GlobalWorldFacade; 6 | import lv.ctco.cukes.oauth.OAuthCukesConstants; 7 | 8 | @Singleton 9 | public class OAuthFacade { 10 | 11 | @Inject 12 | GlobalWorldFacade worldFacade; 13 | 14 | public void setGrantType(String grantType) { 15 | worldFacade.put(OAuthCukesConstants.GRANT_TYPE, grantType); 16 | invalidateToken(); 17 | } 18 | 19 | public void setScopes(String scopes) { 20 | worldFacade.put(OAuthCukesConstants.SCOPE, scopes); 21 | invalidateToken(); 22 | } 23 | 24 | private void invalidateToken() { 25 | worldFacade.remove(OAuthCukesConstants.CACHED_TOKEN); 26 | worldFacade.remove(OAuthCukesConstants.TOKEN_EXPIRES_ON); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cukes-rabbitmq/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | cukes-rabbitmq 6 | Cukes RabbitMQ extension 7 | 8 | lv.ctco.cukes 9 | cukes 10 | 1.0.11-SNAPSHOT 11 | 12 | 13 | 14 | 15 | 16 | lv.ctco.cukes 17 | cukes-core 18 | ${project.version} 19 | 20 | 21 | 22 | 23 | com.rabbitmq 24 | amqp-client 25 | 4.8.0 26 | 27 | 28 | 29 | 30 | org.projectlombok 31 | lombok 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/ConfigurationParameters.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq; 2 | 3 | public class ConfigurationParameters { 4 | // Connection 5 | public static final String HOST = "rabbitmq.host"; 6 | public static final String PORT = "rabbitmq.port"; 7 | public static final String USER = "rabbitmq.user"; 8 | public static final String PASSWORD = "rabbitmq.password"; 9 | public static final String VIRTUAL_HOST = "rabbitmq.vhost"; 10 | 11 | // Defaults 12 | public static final String DEFAULT_EXCHANGE_NAME_ATTRIBUTE = "rabbitmq.exchange.default"; 13 | public static final String DEFAULT_READ_TIMEOUT = "rabbitmq.read-timeout.default"; 14 | public static final String CONTENT_TYPE = "rabbitmq.content-type"; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/CukesRabbitMQGuiceModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq; 2 | 3 | import lv.ctco.cukes.core.extension.AbstractCukesModule; 4 | import lv.ctco.cukes.core.extension.CukesInjectableModule; 5 | import lv.ctco.cukes.core.facade.RandomGeneratorFacade; 6 | import lv.ctco.cukes.core.facade.RandomGeneratorFacadeImpl; 7 | import lv.ctco.cukes.core.facade.VariableFacade; 8 | import lv.ctco.cukes.core.facade.VariableFacadeImpl; 9 | 10 | @CukesInjectableModule 11 | public class CukesRabbitMQGuiceModule extends AbstractCukesModule { 12 | 13 | @Override 14 | protected void configure() { 15 | bind(VariableFacade.class).to(VariableFacadeImpl.class); 16 | bind(RandomGeneratorFacade.class).to(RandomGeneratorFacadeImpl.class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/CukesRabbitMQHooks.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.After; 6 | import lv.ctco.cukes.rabbitmq.internal.ConnectionService; 7 | 8 | @Singleton 9 | public class CukesRabbitMQHooks { 10 | 11 | @Inject 12 | ConnectionService connectionService; 13 | 14 | @After 15 | public void afterScenario() { 16 | connectionService.initConfiguration(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/api/NamePatterns.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.api; 2 | 3 | public class NamePatterns { 4 | 5 | public static final String EXCHANGE_NAME = "([a-zA-Z0-9_\\-\\.:]+)"; 6 | public static final String QUEUE_NAME = "([a-zA-Z0-9_\\-\\.:]+)"; 7 | 8 | } 9 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/api/given/BindQueueSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.api.given; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.Given; 6 | import lv.ctco.cukes.rabbitmq.api.NamePatterns; 7 | import lv.ctco.cukes.rabbitmq.facade.SetupFacade; 8 | 9 | import java.util.Optional; 10 | 11 | @Singleton 12 | public class BindQueueSteps { 13 | 14 | @Inject 15 | SetupFacade setupFacade; 16 | 17 | @Given("^bind queue \"" + NamePatterns.QUEUE_NAME + "\" to exchange \"" + NamePatterns.EXCHANGE_NAME + "\" with routing key \"(.+)\"$") 18 | public void bindQueueToExchangeWithRoutingKey(String queueName, String exchange, String routingKey) { 19 | setupFacade.declareQueue(queueName, Optional.of(exchange), routingKey); 20 | } 21 | 22 | @Given("^bind queue \"" + NamePatterns.QUEUE_NAME + "\" with routing key \"(.+)\"$") 23 | public void bindQueueToDefaultExchangeWithRoutingKey(String queueName, String routingKey) { 24 | setupFacade.declareQueue(queueName, Optional.empty(), routingKey); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/api/given/ExchangeConfigurationSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.api.given; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.Given; 6 | import lv.ctco.cukes.rabbitmq.api.NamePatterns; 7 | import lv.ctco.cukes.rabbitmq.facade.SetupFacade; 8 | 9 | @Singleton 10 | public class ExchangeConfigurationSteps { 11 | 12 | @Inject 13 | SetupFacade setupFacade; 14 | 15 | @Given("^declare( durable)? exchange \"" + NamePatterns.EXCHANGE_NAME + "\"$") 16 | public void declareExchange(String durable, String exchange) { 17 | this.declareExchangeWithType(durable, exchange, "direct"); 18 | } 19 | 20 | @Given("^declare( durable)? exchange \"" + NamePatterns.EXCHANGE_NAME + "\" of type \"(topic|direct|fanout)\"$") 21 | public void declareExchangeWithType(String durable, String exchange, String type) { 22 | setupFacade.declareExchange(exchange, type, durable != null); 23 | } 24 | 25 | @Given("^use exchange \"" + NamePatterns.EXCHANGE_NAME + "\" by default$") 26 | public void useExchangeByDefault(String exchange) { 27 | setupFacade.setDefaultExchange(exchange); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/api/given/PrepareMessageSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.api.given; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.Given; 6 | import lv.ctco.cukes.core.internal.context.InflateContext; 7 | import lv.ctco.cukes.rabbitmq.facade.RequestFacade; 8 | 9 | @Singleton 10 | @InflateContext 11 | public class PrepareMessageSteps { 12 | 13 | @Inject 14 | RequestFacade requestFacade; 15 | 16 | @Given("^prepare new message$") 17 | public void prepareNewMessage() { 18 | requestFacade.initRequestMessage(); 19 | } 20 | 21 | @Given("^message body:$") 22 | public void setMessageBody(String body) { 23 | requestFacade.setBody(body); 24 | } 25 | 26 | @Given("^message body is \"(.+)\"$") 27 | public void setMessageBodyInline(String body) { 28 | requestFacade.setBody(body); 29 | } 30 | 31 | @Given("^reply-to is \"(.+)\"$") 32 | public void setReplyTo(String replyTo) { 33 | requestFacade.setReplyTo(replyTo); 34 | } 35 | 36 | @Given("^content-type is \"(.+)\"$") 37 | public void setContentType(String contentType) { 38 | requestFacade.setContentType(contentType); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/api/given/SetupConnectionSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.api.given; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.Given; 6 | import lv.ctco.cukes.rabbitmq.facade.SetupFacade; 7 | 8 | @Singleton 9 | public class SetupConnectionSteps { 10 | 11 | @Inject 12 | SetupFacade setupFacade; 13 | 14 | @Given("^connecting to host \"(.+)\"$") 15 | public void setHost(String host) { 16 | setupFacade.setHost(host); 17 | } 18 | 19 | @Given("^server responds on port (\\d+)$") 20 | public void setPort(int port) { 21 | setupFacade.setPort(port); 22 | } 23 | 24 | @Given("^connecting using username \"(.+)\" and password \"(.+)\"$") 25 | public void setUsernameAndPassword(String username, String password) { 26 | setupFacade.setUsername(username); 27 | setupFacade.setPassword(password); 28 | } 29 | 30 | @Given("^virtual host is \"(.+)\"$") 31 | public void setVirtualHost(String virtualHost) { 32 | setupFacade.setVirtualHost(virtualHost); 33 | } 34 | 35 | @Given("^using SSL$") 36 | public void setUseSSL() { 37 | setupFacade.setSsl(true); 38 | } 39 | 40 | @Given("^not using SSL$") 41 | public void setDontUseSSL() { 42 | setupFacade.setSsl(false); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/api/then/ReceiveMessageSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.api.then; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.Then; 6 | import lv.ctco.cukes.rabbitmq.api.NamePatterns; 7 | import lv.ctco.cukes.rabbitmq.facade.ResponseFacade; 8 | 9 | import java.util.Optional; 10 | 11 | @Singleton 12 | public class ReceiveMessageSteps { 13 | 14 | @Inject 15 | ResponseFacade responseFacade; 16 | 17 | 18 | @Then("^wait for message in queue \"" + NamePatterns.QUEUE_NAME + "\"$") 19 | public void waitForMessageInQueue(String queue) { 20 | responseFacade.waitForMessage(queue, Optional.empty()); 21 | } 22 | 23 | @Then("^wait for message in queue \"" + NamePatterns.QUEUE_NAME + "\" for not more than (\\d+) seconds$") 24 | public void waitForMessageInQueue(String queue, Integer timeout) { 25 | responseFacade.waitForMessage(queue, Optional.of(timeout)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/api/when/SendMessageSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.api.when; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.When; 6 | import lv.ctco.cukes.rabbitmq.api.NamePatterns; 7 | import lv.ctco.cukes.rabbitmq.facade.RequestFacade; 8 | 9 | import java.util.Optional; 10 | 11 | @Singleton 12 | public class SendMessageSteps { 13 | 14 | @Inject 15 | RequestFacade requestFacade; 16 | 17 | @When("^the client sends message with routing key \"(.+)\"$") 18 | public void sendMessage(String routingKey) { 19 | requestFacade.sendMessage(Optional.empty(), routingKey); 20 | } 21 | 22 | @When("^the client sends message to exchange \"" + NamePatterns.EXCHANGE_NAME + "\" with routing key \"(.+)\"$") 23 | public void sendMessageToExchange(String exchange, String routingKey) { 24 | requestFacade.sendMessage(Optional.of(exchange), routingKey); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/facade/RequestFacade.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.facade; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lv.ctco.cukes.core.internal.context.GlobalWorldFacade; 6 | import lv.ctco.cukes.rabbitmq.internal.ExchangeService; 7 | import lv.ctco.cukes.rabbitmq.internal.MessageService; 8 | import lv.ctco.cukes.rabbitmq.internal.MessageWrapper; 9 | 10 | import java.util.Optional; 11 | 12 | import static lv.ctco.cukes.rabbitmq.ConfigurationParameters.CONTENT_TYPE; 13 | 14 | @Singleton 15 | public class RequestFacade { 16 | 17 | @Inject 18 | MessageService messageService; 19 | @Inject 20 | ExchangeService exchangeService; 21 | @Inject 22 | GlobalWorldFacade globalWorldFacade; 23 | 24 | private MessageWrapper message; 25 | 26 | @Inject 27 | public RequestFacade(GlobalWorldFacade globalWorldFacade) { 28 | this.globalWorldFacade = globalWorldFacade; 29 | initRequestMessage(); 30 | } 31 | 32 | public void initRequestMessage() { 33 | message = new MessageWrapper(); 34 | message.getProperties().contentType(globalWorldFacade.get(CONTENT_TYPE, null)); 35 | } 36 | 37 | public void setBody(String body) { 38 | message.setBody(body); 39 | } 40 | 41 | public void setReplyTo(String replyTo) { 42 | message.getProperties().replyTo(replyTo); 43 | } 44 | 45 | public void setContentType(String contentType) { 46 | message.getProperties().contentType(contentType); 47 | } 48 | 49 | public MessageWrapper getMessage() { 50 | return message; 51 | } 52 | 53 | public void sendMessage(Optional exchange, String routingKey) { 54 | messageService.sendMessage(exchange.orElse(exchangeService.getDefaultExchange()), routingKey, message); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/facade/SetupFacade.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.facade; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lv.ctco.cukes.rabbitmq.internal.ConnectionService; 6 | import lv.ctco.cukes.rabbitmq.internal.ExchangeService; 7 | import lv.ctco.cukes.rabbitmq.internal.QueueService; 8 | 9 | import java.util.Optional; 10 | 11 | @Singleton 12 | public class SetupFacade { 13 | 14 | @Inject 15 | ConnectionService connectionService; 16 | @Inject 17 | ExchangeService exchangeService; 18 | @Inject 19 | QueueService queueService; 20 | 21 | public void setHost(String host) { 22 | connectionService.setHost(host); 23 | } 24 | 25 | public void setPort(int port) { 26 | connectionService.setPort(port); 27 | } 28 | 29 | public void setUsername(String username) { 30 | connectionService.setUsername(username); 31 | } 32 | 33 | public void setPassword(String password) { 34 | connectionService.setPassword(password); 35 | } 36 | 37 | public void setVirtualHost(String virtualHost) { 38 | connectionService.setVirtualHost(virtualHost); 39 | } 40 | 41 | public void setSsl(boolean ssl) { 42 | connectionService.setSsl(ssl); 43 | } 44 | 45 | public void declareExchange(String name, String type, boolean durable) { 46 | exchangeService.declareExchange(name, type, durable); 47 | } 48 | 49 | public void setDefaultExchange(String exchange) { 50 | exchangeService.setDefaultExchange(exchange); 51 | } 52 | 53 | public void declareQueue(String queueName, Optional exchange, String routingKey) { 54 | queueService.declareQueue(queueName, exchange.orElse(exchangeService.getDefaultExchange()), routingKey); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/internal/MessageService.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.internal; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import com.rabbitmq.client.AMQP; 6 | import com.rabbitmq.client.Channel; 7 | import com.rabbitmq.client.DefaultConsumer; 8 | import com.rabbitmq.client.Envelope; 9 | import lombok.SneakyThrows; 10 | 11 | import java.io.IOException; 12 | import java.util.concurrent.ArrayBlockingQueue; 13 | import java.util.concurrent.BlockingQueue; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | @Singleton 17 | public class MessageService { 18 | 19 | @Inject 20 | ConnectionService connectionService; 21 | 22 | @SneakyThrows(IOException.class) 23 | public void sendMessage(String exchange, String routingKey, MessageWrapper message) { 24 | Channel channel = connectionService.getChannel(); 25 | channel.basicPublish(exchange, routingKey, message.getProperties().build(), message.getBody().getBytes()); 26 | } 27 | 28 | @SneakyThrows({IOException.class, InterruptedException.class}) 29 | public MessageWrapper receiveMessage(String queue, int timeoutInSeconds) { 30 | Channel channel = connectionService.getChannel(); 31 | BlockingQueue result = new ArrayBlockingQueue<>(1); 32 | channel.basicConsume(queue, true, new DefaultConsumer(channel) { 33 | @Override 34 | public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) { 35 | String response = new String(body); 36 | MessageWrapper messageWrapper = new MessageWrapper(response, properties); 37 | result.add(messageWrapper); 38 | } 39 | }); 40 | return result.poll(timeoutInSeconds, TimeUnit.SECONDS); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/internal/MessageWrapper.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.internal; 2 | 3 | import com.rabbitmq.client.AMQP; 4 | 5 | public class MessageWrapper { 6 | 7 | private String body; 8 | private final AMQP.BasicProperties.Builder properties; 9 | 10 | public MessageWrapper() { 11 | this.properties = new AMQP.BasicProperties().builder(); 12 | } 13 | 14 | public MessageWrapper(String body, AMQP.BasicProperties properties) { 15 | this.body = body; 16 | this.properties = properties.builder(); 17 | } 18 | 19 | public String getBody() { 20 | return body; 21 | } 22 | 23 | public void setBody(String body) { 24 | this.body = body; 25 | } 26 | 27 | public AMQP.BasicProperties.Builder getProperties() { 28 | return properties; 29 | } 30 | 31 | public String getContentType() { 32 | return properties.build().getContentType(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/internal/MessageWrapperContentProvider.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.internal; 2 | 3 | import lv.ctco.cukes.core.internal.matchers.JsonMatchers; 4 | 5 | public class MessageWrapperContentProvider implements JsonMatchers.ContentProvider { 6 | 7 | public static final MessageWrapperContentProvider INSTANCE = new MessageWrapperContentProvider(); 8 | 9 | @Override 10 | public String getValue(Object o) { 11 | return ((MessageWrapper) o).getBody(); 12 | } 13 | 14 | @Override 15 | public String getContentType(Object o) { 16 | return ((MessageWrapper) o).getContentType(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /cukes-rabbitmq/src/main/java/lv/ctco/cukes/rabbitmq/internal/QueueService.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.internal; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import com.rabbitmq.client.Channel; 6 | import lombok.SneakyThrows; 7 | 8 | import java.io.IOException; 9 | 10 | @Singleton 11 | public class QueueService { 12 | 13 | @Inject 14 | ConnectionService connectionService; 15 | 16 | @SneakyThrows(IOException.class) 17 | public void declareQueue(String queueName, String exchange, String routingKey) { 18 | Channel channel = connectionService.getChannel(); 19 | channel.queueDeclare(queueName, false, true, true, null); 20 | channel.queueBind(queueName, exchange, routingKey); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /cukes-rest/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | cukes-rest 6 | Cukes RESTful extension 7 | 8 | 9 | lv.ctco.cukes 10 | cukes 11 | 1.0.11-SNAPSHOT 12 | 13 | 14 | 15 | 16 | lv.ctco.cukes 17 | cukes-http 18 | ${project.version} 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /cukes-rest/src/main/java/lv/ctco/cukes/rest/CukesRestGuiceModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest; 2 | 3 | import lv.ctco.cukes.core.extension.CukesInjectableModule; 4 | import lv.ctco.cukes.http.extension.AbstractCukesHttpModule; 5 | import lv.ctco.cukes.rest.internal.PreprocessRestRequestBody; 6 | 7 | @CukesInjectableModule 8 | public class CukesRestGuiceModule extends AbstractCukesHttpModule { 9 | 10 | @Override 11 | protected void configure() { 12 | registerHttpPlugin(PreprocessRestRequestBody.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cukes-rest/src/main/java/lv/ctco/cukes/rest/CukesRestHooks.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest; 2 | 3 | import com.google.inject.Inject; 4 | import io.cucumber.java.After; 5 | import lv.ctco.cukes.rest.facade.RestRequestFacade; 6 | 7 | public class CukesRestHooks { 8 | 9 | @Inject 10 | RestRequestFacade requestFacade; 11 | 12 | @After 13 | public void afterScenario() { 14 | requestFacade.initNewSpecification(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cukes-rest/src/main/java/lv/ctco/cukes/rest/api/WhenSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.api; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.cucumber.java.en.When; 6 | import lv.ctco.cukes.core.internal.context.InflateContext; 7 | import lv.ctco.cukes.http.facade.HttpResponseFacade; 8 | 9 | @Singleton 10 | @InflateContext 11 | public class WhenSteps { 12 | 13 | @Inject 14 | HttpResponseFacade facade; 15 | 16 | @When("^the client performs (.+) request on \"(.+)\"$") 17 | public void perform_Http_Request(String httpMethod, String url) throws Throwable { 18 | facade.setResponsePrefix(""); 19 | facade.doRequest(httpMethod, url); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /cukes-rest/src/main/java/lv/ctco/cukes/rest/internal/PreprocessRestRequestBody.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.internal; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import io.restassured.response.Response; 6 | import io.restassured.specification.RequestSpecification; 7 | import lv.ctco.cukes.core.internal.templating.TemplatingEngine; 8 | import lv.ctco.cukes.http.extension.CukesHttpPlugin; 9 | import lv.ctco.cukes.rest.facade.RestRequestFacade; 10 | 11 | @Singleton 12 | public class PreprocessRestRequestBody implements CukesHttpPlugin { 13 | 14 | @Inject 15 | TemplatingEngine templatingEngine; 16 | @Inject 17 | RestRequestFacade requestFacade; 18 | 19 | @Override 20 | public void beforeRequest(RequestSpecification requestSpecification) { 21 | String requestBody = this.requestFacade.getRequestBody(); 22 | if (requestBody != null) { 23 | String processed = templatingEngine.processBody(requestBody); 24 | requestSpecification.body(processed); 25 | } 26 | } 27 | 28 | @Override 29 | public void afterRequest(Response response) { 30 | this.requestFacade.clearRequestBody(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cukes-samples/cukes-graphql-sample/graphql.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "README_schema": "Specifies how to load the GraphQL schema that completion, error highlighting, and documentation is based on in the IDE", 3 | "schema": { 4 | "README_file": "Remove 'file' to use request url below. A relative or absolute path to the JSON from a schema introspection query, e.g. '{ data: ... }'. Changes to the file are watched.", 5 | "file": "graphql.schema.json", 6 | "README_request": "To request the schema from a url instead, remove the 'file' JSON property above (and optionally delete the default graphql.schema.json file).", 7 | "request": { 8 | "url": "https://graphql-europe.org/graphql", 9 | "method": "POST", 10 | "README_postIntrospectionQuery": "Whether to POST an introspectionQuery to the url. If the url always returns the schema JSON, set to false and consider using GET", 11 | "postIntrospectionQuery": true, 12 | "README_options": "See the 'Options' section at https://github.com/then/then-request", 13 | "options": { 14 | "headers": { 15 | "user-agent": "JS GraphQL" 16 | } 17 | } 18 | } 19 | }, 20 | "README_endpoints": "A list of GraphQL endpoints that can be queried from '.graphql' files in the IDE", 21 | "endpoints": [ 22 | { 23 | "name": "Default (https://graphql-europe.org/graphql)", 24 | "url": "https://graphql-europe.org/graphqll", 25 | "options": { 26 | "headers": { 27 | "user-agent": "JS GraphQL" 28 | } 29 | } 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /cukes-samples/cukes-graphql-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | cukes-graphql-sample 6 | Cukes GraphQL Sample Project 7 | 8 | 9 | lv.ctco.cukes 10 | cukes-samples 11 | 1.0.11-SNAPSHOT 12 | 13 | 14 | 15 | 16 | 17 | lv.ctco.cukes 18 | cukes-graphql 19 | ${project.version} 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /cukes-samples/cukes-graphql-sample/src/test/java/lv/ctco/cukes/graphql/RunCukesGraphQLTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.graphql; 2 | 3 | import io.cucumber.junit.Cucumber; 4 | import io.cucumber.junit.CucumberOptions; 5 | import org.junit.Ignore; 6 | import org.junit.runner.RunWith; 7 | 8 | @RunWith(Cucumber.class) 9 | @CucumberOptions( 10 | plugin = {"pretty", "json:target/cucumber.json", "json:target/cucumber2.json"}, 11 | features = {"classpath:features/conference/"}, 12 | glue = "lv.ctco.cukes.graphql.api" 13 | ) 14 | @Ignore 15 | public class RunCukesGraphQLTest { 16 | } 17 | -------------------------------------------------------------------------------- /cukes-samples/cukes-graphql-sample/src/test/resources/cukes.properties: -------------------------------------------------------------------------------- 1 | cukes.base_uri=https://graphql-europe.org/graphql 2 | cukes.resources_root=src/test/resources/features/conference 3 | cukes.relaxed_https=true 4 | -------------------------------------------------------------------------------- /cukes-samples/cukes-graphql-sample/src/test/resources/features/conference/Conference.feature: -------------------------------------------------------------------------------- 1 | Feature: GraphQL Conference 2 | 3 | Scenario: Should retrieve general information about the event 4 | Given query from file "queries/Berlin2017Conference.graphql" 5 | When the query is executed 6 | Then response contains property "conference.name" with value "GraphQL-Europe" 7 | And response contains property "conference.dateStart" with value "2017-05-21" 8 | And response contains an array "conference.speakers" of size > 10 9 | And response contains an array "conference.sponsors" with object having property "name" with value "Facebook" 10 | 11 | Scenario: Should retrieve ticket options 12 | Given query: 13 | """ 14 | { 15 | conference(edition: Berlin2017) { 16 | tickets { 17 | name 18 | } 19 | } 20 | } 21 | """ 22 | When the query is executed 23 | And response contains an array "conference.tickets" of size 3 24 | And response contains an array "conference.tickets" with object having property "name" with value "Early Bird" 25 | And response contains an array "conference.tickets" with object having property "name" with value "Regular" 26 | And response contains an array "conference.tickets" with object having property "name" with value "Late Bird" 27 | -------------------------------------------------------------------------------- /cukes-samples/cukes-graphql-sample/src/test/resources/features/conference/queries/Berlin2017Conference.graphql: -------------------------------------------------------------------------------- 1 | query ConferenceDetails { 2 | conference(edition: Berlin2017) { 3 | name 4 | dateStart 5 | 6 | tickets { 7 | name 8 | price 9 | } 10 | 11 | schedule { 12 | startTime 13 | duration 14 | entryType 15 | 16 | ... on Talk { 17 | title 18 | description 19 | 20 | speakers { 21 | name 22 | company 23 | twitter 24 | github 25 | } 26 | } 27 | } 28 | 29 | speakers { 30 | name 31 | company 32 | twitter 33 | github 34 | } 35 | 36 | sponsors { 37 | name 38 | url 39 | description 40 | } 41 | 42 | team { 43 | name 44 | description 45 | twitter 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cukes-samples/cukes-http-mock-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | cukes-http-mock-sample 6 | Cukes REST mock Sample Project 7 | 8 | 9 | lv.ctco.cukes 10 | cukes-samples 11 | 1.0.11-SNAPSHOT 12 | 13 | 14 | 15 | 16 | 17 | lv.ctco.cukes 18 | cukes-http-mock 19 | ${project.version} 20 | 21 | 22 | lv.ctco.cukes 23 | cukes-rest 24 | ${project.version} 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /cukes-samples/cukes-http-mock-sample/src/test/java/lv/ctco/cukes/rest/run/RunCustomCukesTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.run; 2 | 3 | 4 | import io.cucumber.junit.Cucumber; 5 | import io.cucumber.junit.CucumberOptions; 6 | import org.junit.runner.RunWith; 7 | 8 | @RunWith(Cucumber.class) 9 | @CucumberOptions( 10 | plugin = {"pretty"}, 11 | features = "classpath:features/custom/", 12 | glue = {"lv.ctco.cukes"} 13 | ) 14 | public class RunCustomCukesTest { 15 | } 16 | -------------------------------------------------------------------------------- /cukes-samples/cukes-http-mock-sample/src/test/resources/cukes.properties: -------------------------------------------------------------------------------- 1 | cukes.plugins=lv.ctco.cukes.http.mock.extension.HttpMockPlugin 2 | 3 | cukes.http.mock.services=mockService1,mockService2 4 | 5 | #mock config 6 | cukes.http.mock.services.mockService1.port=10666 7 | cukes.http.mock.services.mockService1.host=localhost 8 | cukes.http.mock.services.mockService2.port=10777 9 | cukes.http.mock.services.mockService2.host=localhost 10 | 11 | #cukes-rest config 12 | cukes.base_uri=http://localhost:10666 13 | cukes.resources_root=src/test/resources/features 14 | -------------------------------------------------------------------------------- /cukes-samples/cukes-http-mock-sample/src/test/resources/features/custom/custom.feature: -------------------------------------------------------------------------------- 1 | Feature: Custom integration 2 | 3 | Scenario: Simple example 4 | Given requesting mock for service "mockService1" and url "/ping" with method "GET" 5 | * mock responds with status code "200" 6 | 7 | When the client performs GET request on "/ping" 8 | Then status code is 200 9 | 10 | Scenario: More complex example 11 | ##### Prepare matching request 12 | Given requesting mock for service "mockService1" and url "/api/ping" with method "GET" 13 | * with header "MockedHeader" with value "HeaderValue" 14 | ##### Prepare mock response matcing request 15 | * mock response will have header "expected" with value "hello" 16 | * mock response will have body 17 | """ 18 | pong 19 | """ 20 | * mock responds with status code "200" 21 | 22 | # Test 23 | Given header MockedHeader with value "HeaderValue" 24 | When the client performs GET request on "/api/ping" 25 | Then status code is 200 26 | And response equals to "pong" 27 | And header "expected" contains "hello" 28 | -------------------------------------------------------------------------------- /cukes-samples/cukes-http-mock-sample/src/test/resources/features/custom/mockingSameResource.feature: -------------------------------------------------------------------------------- 1 | Feature: Changing behaviour of same endpoint 2 | 3 | Scenario: Changing behaviour of same endpoint 4 | Given requesting mock for service "mockService1" and url "/ping" with method "GET" 5 | * mock responds with status code "200" 6 | Given requesting mock for service "mockService1" and url "/ping" with method "GET" 7 | * mock responds with status code "418" exactly 1 time 8 | Given requesting mock for service "mockService1" and url "/ping" with method "GET" 9 | * mock responds with status code "429" exactly 1 time 10 | 11 | When the client performs GET request on "/ping" 12 | Then status code is 418 13 | When the client performs GET request on "/ping" 14 | Then status code is 429 15 | When the client performs GET request on "/ping" 16 | Then status code is 200 17 | -------------------------------------------------------------------------------- /cukes-samples/cukes-http-mock-sample/src/test/resources/features/custom/mockingSameResourceComplex.feature: -------------------------------------------------------------------------------- 1 | Feature: Changing behaviour of same endpoint 2 | 3 | Scenario: Changing behaviour of same endpoint 4 | Given requesting mock for service "mockService1" and url "/ping" with method "GET" 5 | * mock responds with status code "200" 6 | 7 | When the client performs GET request on "/ping" 8 | Then status code is 200 9 | When the client performs GET request on "/ping" 10 | Then status code is 200 11 | 12 | Given requesting mock for service "mockService1" and url "/ping" with method "GET" 13 | * mock responds with status code "418" exactly 1 time 14 | Given requesting mock for service "mockService1" and url "/ping" with method "GET" 15 | * mock responds with status code "429" exactly 2 times 16 | 17 | When the client performs GET request on "/ping" 18 | Then status code is 418 19 | When the client performs GET request on "/ping" 20 | Then status code is 429 21 | When the client performs GET request on "/ping" 22 | Then status code is 429 23 | When the client performs GET request on "/ping" 24 | Then status code is 200 25 | When the client performs GET request on "/ping" 26 | Then status code is 200 27 | 28 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | cukes-samples 5 | lv.ctco.cukes 6 | 1.0.11-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | cukes-ldap-sample 11 | Cukes LDAP Sample Project 12 | 13 | 14 | 15 | 16 | lv.ctco.cukes 17 | cukes-ldap 18 | ${project.version} 19 | 20 | 21 | 22 | 23 | 24 | org.apache.directory.server 25 | apacheds-all 26 | 2.0.0-M24 27 | 28 | 29 | ch.qos.logback 30 | logback-classic 31 | 1.2.3 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/readme.md: -------------------------------------------------------------------------------- 1 | # Cukes LDAP Sample 2 | 3 | This is a sample project showing how to use **[Cukes LDAP extensions](../../cukes-ldap)** 4 | 5 | ## Overall design 6 | 7 | For simplicity and to avoid any external dependencies the test suite runs own instance of in-memory LDAP server with predefined schema. 8 | This avoids need of hosting it externally and brings more autonomy. 9 | 10 | [Apache DS](http://directory.apache.org/apacheds/) is used as in-memory LDAP server. 11 | 12 | If you are interested how it works you can check **[lv.ctco.cukes.ldap.sample.EmbeddedLDAPServer](src/main/java/lv/ctco/cukes/ldap/sample/EmbeddedLDAPServer.java)**. 13 | 14 | **[cukes.properties](src/test/resources/cukes.properties)** is configured in a way to connect to in-memory LDAP server. 15 | 16 | ## System under test 17 | 18 | The system under test is very simple - it does nothing except for providing some popular LDAP schemas. 19 | 20 | ## Running tests 21 | 22 | There is a JUnit-based test runner configured - [lv.ctco.cukes.ldap.sample.RunCukesLDAPTest](src/test/java/lv/ctco/cukes/ldap/sample/RunCukesLDAPTest.java). 23 | 24 | It starts in-memory LDAP server first, then runs all tests and in the end shuts down LDAP server. 25 | 26 | There are 2 options to run test suite: 27 | 28 | 1. Run it as JUnit test - `lv.ctco.cukes.ldap.sample.RunCukesLDAPTest` 29 | 2. Run using Maven - `mvn clean test` 30 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/main/java/lv/ctco/cukes/ldap/sample/Application.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap.sample; 2 | 3 | public class Application { 4 | 5 | public static void main(String[] args) throws Exception { 6 | EmbeddedLDAPServer server = new EmbeddedLDAPServer(); 7 | server.start(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/test/java/lv/ctco/cukes/ldap/sample/CukesLDAPBootstrap.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap.sample; 2 | 3 | import lv.ctco.cukes.core.CukesRuntimeException; 4 | import lv.ctco.cukes.core.extension.CukesPlugin; 5 | 6 | public class CukesLDAPBootstrap implements CukesPlugin { 7 | 8 | private EmbeddedLDAPServer server; 9 | 10 | @Override 11 | public void beforeAllTests() { 12 | server = new EmbeddedLDAPServer(); 13 | try { 14 | server.start(); 15 | server.loadLDIF("init.ldif"); 16 | } catch (Exception e) { 17 | throw new CukesRuntimeException(e); 18 | } 19 | } 20 | 21 | @Override 22 | public void afterAllTests() { 23 | try { 24 | server.stop(); 25 | } catch (Exception e) { 26 | throw new CukesRuntimeException(e); 27 | } 28 | } 29 | 30 | @Override 31 | public void beforeScenario() { 32 | 33 | } 34 | 35 | @Override 36 | public void afterScenario() { 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/test/java/lv/ctco/cukes/ldap/sample/RunCukesLDAPTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.ldap.sample; 2 | 3 | import io.cucumber.junit.Cucumber; 4 | import io.cucumber.junit.CucumberOptions; 5 | import org.junit.AfterClass; 6 | import org.junit.BeforeClass; 7 | import org.junit.runner.RunWith; 8 | 9 | @RunWith(Cucumber.class) 10 | @CucumberOptions( 11 | plugin = {"pretty", "json:target/cucumber.json", "json:target/cucumber.json"}, 12 | features = {"classpath:features/"}, 13 | glue = {"lv.ctco.cukes.ldap", "lv.ctco.cukes.core.api"} 14 | ) 15 | public class RunCukesLDAPTest { 16 | 17 | private static final CukesLDAPBootstrap bootstrap = new CukesLDAPBootstrap(); 18 | 19 | @BeforeClass 20 | public static void setUp() { 21 | bootstrap.beforeAllTests(); 22 | } 23 | 24 | @AfterClass 25 | public static void tearDown() { 26 | bootstrap.afterAllTests(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/test/resources/cukes.properties: -------------------------------------------------------------------------------- 1 | cukes.ldap.url:ldap://localhost:10389 2 | cukes.ldap.user:uid=admin,ou=system 3 | cukes.ldap.password:secret 4 | 5 | cukes.plugins:lv.ctco.cukes.ldap.sample.CukesLDAPBootstrap 6 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/test/resources/features/CreateEntity.feature: -------------------------------------------------------------------------------- 1 | Feature: Create entity showcase 2 | 3 | Scenario: Should create entity from LDIF inline 4 | When the client creates entity using LDIF: 5 | """ 6 | dn: cn=inline,ou=Users,dc=example,dc=com 7 | objectClass: inetOrgPerson 8 | objectClass: organizationalPerson 9 | objectClass: person 10 | objectClass: top 11 | gn: test 12 | sn: test 13 | cn: inline 14 | """ 15 | And the client retrieves entity by DN "cn=inline,ou=Users,dc=example,dc=com" 16 | Then entity exists 17 | 18 | Scenario: Should create entity from LDIF file 19 | When the client creates entity using LDIF from file "ldif/test.ldif" 20 | And the client retrieves entity by DN "cn=from-file,ou=Users,dc=example,dc=com" 21 | Then entity exists 22 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/test/resources/features/DeleteEntity.feature: -------------------------------------------------------------------------------- 1 | Feature: Delete entity showcase 2 | 3 | Scenario: Should delete entity by DN 4 | Given let variable "cn" to be random UUID 5 | And the client imports LDIF: 6 | """ 7 | dn: cn={(cn)},ou=Users,dc=example,dc=com 8 | objectClass: inetOrgPerson 9 | objectClass: organizationalPerson 10 | objectClass: person 11 | objectClass: top 12 | gn: test 13 | sn: test 14 | cn: {(cn)} 15 | """ 16 | When the client retrieves entity by DN "cn={(cn)},ou=Users,dc=example,dc=com" 17 | Then entity exists 18 | And entity matches LDIF: 19 | """ 20 | dn: cn={(cn)},ou=Users,dc=example,dc=com 21 | cn: {(cn)} 22 | """ 23 | When the client deletes entity with DN "cn={(cn)},ou=Users,dc=example,dc=com" 24 | And the client retrieves entity by DN "cn={(cn)},ou=Users,dc=example,dc=com" 25 | Then entity does not exist 26 | 27 | Scenario: Should delete subtree 28 | Given let variable "group" to be random UUID 29 | Given let variable "cn" to be random UUID 30 | And the client imports LDIF: 31 | """ 32 | dn: ou={(group)},dc=example,dc=com 33 | objectClass: organizationalUnit 34 | objectClass: top 35 | ou: {(group)} 36 | 37 | dn: cn={(cn)},ou={(group)},dc=example,dc=com 38 | objectClass: inetOrgPerson 39 | objectClass: organizationalPerson 40 | objectClass: person 41 | objectClass: top 42 | gn: test 43 | sn: test 44 | cn: {(cn)} 45 | """ 46 | When the client retrieves entity by DN "ou={(group)},dc=example,dc=com" 47 | Then entity exists 48 | When the client deletes entity with DN "ou={(group)},dc=example,dc=com" 49 | And the client retrieves entity by DN "ou={(group)},dc=example,dc=com" 50 | Then entity does not exist 51 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/test/resources/features/LDIF.feature: -------------------------------------------------------------------------------- 1 | Feature: LDIF showcase 2 | 3 | Scenario: Import LDIF and verify response against LDIF 4 | Given the client imports LDIF: 5 | """ 6 | dn: cn=John Smith,ou=Users,dc=example,dc=com 7 | objectClass: inetOrgPerson 8 | objectClass: organizationalPerson 9 | objectClass: person 10 | objectClass: top 11 | cn: John Smith 12 | gn: John 13 | sn: Smith 14 | uid: johnsmith 15 | """ 16 | When the client retrieves entity by DN "cn=John Smith,ou=Users,dc=example,dc=com" 17 | Then entity contains attribute "sn" with value "Smith" 18 | And entity contains attribute "objectclass" with 4 values 19 | And entity contains attribute "objectclass" with value "top" 20 | And entity contains attribute "objectclass" with value "person" 21 | And entity contains attribute "objectclass" with value "inetOrgPerson" 22 | And entity contains attribute "objectclass" with value "organizationalPerson" 23 | And entity matches LDIF: 24 | """ 25 | dn: cn=John Smith,ou=Users,dc=example,dc=com 26 | cn: John Smith 27 | uid: johnsmith 28 | objectClass: person 29 | """ 30 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/test/resources/features/ReadEntity.feature: -------------------------------------------------------------------------------- 1 | Feature: Read entity showcase 2 | 3 | Scenario: Read admin user 4 | When the client retrieves entity by DN "uid=admin,ou=system" 5 | Then entity contains attribute "uid" with value "admin" 6 | And entity contains attribute "uid" 7 | And entity contains attribute "displayname" with value "Directory Superuser" 8 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/test/resources/features/SearchEntities.feature: -------------------------------------------------------------------------------- 1 | Feature: Search entities showcase 2 | 3 | Scenario: Should search by filter expression 4 | When the client searches entities within DN "ou=system" by filter "(uid=admin)" 5 | Then search result has size 1 6 | And take entity with index 1 from search results 7 | And entity contains attribute "uid" with value "admin" 8 | And entity contains attribute "uid" 9 | And entity contains attribute "displayname" with value "Directory Superuser" 10 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/test/resources/init.ldif: -------------------------------------------------------------------------------- 1 | dn: dc=example,dc=com 2 | objectClass: domain 3 | objectClass: top 4 | dc: example 5 | 6 | dn: ou=Users,dc=example,dc=com 7 | objectClass: organizationalUnit 8 | objectClass: top 9 | ou: Users 10 | 11 | dn: ou=Groups,dc=example,dc=com 12 | objectClass: organizationalUnit 13 | objectClass: top 14 | ou: Groups 15 | -------------------------------------------------------------------------------- /cukes-samples/cukes-ldap-sample/src/test/resources/ldif/test.ldif: -------------------------------------------------------------------------------- 1 | dn: cn=from-file,ou=Users,dc=example,dc=com 2 | objectClass: inetOrgPerson 3 | objectClass: organizationalPerson 4 | objectClass: person 5 | objectClass: top 6 | gn: test 7 | sn: test 8 | cn: from-file 9 | -------------------------------------------------------------------------------- /cukes-samples/cukes-oauth-sample/readme.md: -------------------------------------------------------------------------------- 1 | # Cukes OAuth Sample 2 | 3 | This is a sample project showing how to use **[Cukes OAuth extensions](../../cukes-oauth)** 4 | 5 | ## Overall design 6 | 7 | For simplicity and to avoid any external dependencies the test suite runs own instance of in-memory OAuth server and a resource provider with a couple of secured endpoints. 8 | This avoids need of hosting it externally and brings more autonomy. 9 | 10 | **[cukes.properties](src/test/resources/cukes.properties)** is configured in a way to connect to use in-memory OAuth server. 11 | 12 | ## System under test 13 | 14 | The system under test is very simple - only a couple of OAuth secured endpoints. 15 | 16 | ## Running tests 17 | 18 | There is a JUnit-based test runner configured - [lv.ctco.cukes.oauth.sample.RunCukesOAuthTest](src/test/java/lv/ctco/cukes/oauth/sample/RunCukesOAuthTest.java). 19 | 20 | It starts in-memory OAuth server and resource provider first, then runs all tests and in the end shuts down OAuth server and resource provider. 21 | 22 | There are 2 options to run test suite: 23 | 24 | 1. Run it as JUnit test - `lv.ctco.cukes.oauth.sample.RunCukesOAuthTest` 25 | 2. Run using Maven - `mvn clean test` 26 | -------------------------------------------------------------------------------- /cukes-samples/cukes-oauth-sample/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | security.oauth2.client.client-id=clientid 2 | security.oauth2.client.client-secret=secret 3 | security.oauth2.client.authorized-grant-types=password,client_credentials 4 | security.oauth2.client.scope=openid,read,write 5 | -------------------------------------------------------------------------------- /cukes-samples/cukes-oauth-sample/src/test/java/lv/ctco/cukes/oauth/sample/CukesOAuthBootstrap.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.oauth.sample; 2 | 3 | import lv.ctco.cukes.core.extension.CukesPlugin; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.context.ConfigurableApplicationContext; 6 | 7 | public class CukesOAuthBootstrap implements CukesPlugin { 8 | 9 | private ConfigurableApplicationContext applicationContext; 10 | 11 | @Override 12 | public void beforeAllTests() { 13 | applicationContext = SpringApplication.run(OAuthSampleApplication.class); 14 | } 15 | 16 | @Override 17 | public void afterAllTests() { 18 | applicationContext.close(); 19 | } 20 | 21 | @Override 22 | public void beforeScenario() { 23 | 24 | } 25 | 26 | @Override 27 | public void afterScenario() { 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cukes-samples/cukes-oauth-sample/src/test/java/lv/ctco/cukes/oauth/sample/RunCukesOAuthTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.oauth.sample; 2 | 3 | import io.cucumber.junit.Cucumber; 4 | import io.cucumber.junit.CucumberOptions; 5 | import org.junit.AfterClass; 6 | import org.junit.BeforeClass; 7 | import org.junit.runner.RunWith; 8 | 9 | @RunWith(Cucumber.class) 10 | @CucumberOptions( 11 | plugin = {"pretty", "json:target/cucumber.json", "json:target/cucumber.json"}, 12 | features = {"classpath:features/"}, 13 | glue = {"lv.ctco.cukes"} 14 | ) 15 | public class RunCukesOAuthTest { 16 | 17 | private static final CukesOAuthBootstrap bootstrap = new CukesOAuthBootstrap(); 18 | 19 | @BeforeClass 20 | public static void setUp() { 21 | bootstrap.beforeAllTests(); 22 | } 23 | 24 | @AfterClass 25 | public static void tearDown() { 26 | bootstrap.afterAllTests(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cukes-samples/cukes-oauth-sample/src/test/resources/cukes.properties: -------------------------------------------------------------------------------- 1 | cukes.base_uri=http://localhost:8080 2 | 3 | cukes.oauth.enabled=true 4 | cukes.oauth.client_id=clientid 5 | cukes.oauth.client_secret=secret 6 | cukes.oauth.grant_type=client_credentials 7 | cukes.oauth.auth_server=http://localhost:8080/oauth/token 8 | cukes.oauth.scope=read write 9 | -------------------------------------------------------------------------------- /cukes-samples/cukes-oauth-sample/src/test/resources/features/OAuthShowcase.feature: -------------------------------------------------------------------------------- 1 | Feature: Call OAuth-protected resources 2 | 3 | Scenario: Read with allowed scopes 4 | When using OAuth 5 | Then the client performs GET request on "/read" 6 | And status code is 200 7 | And response contains "Read success" 8 | 9 | Scenario: Write with allowed scopes 10 | When using OAuth 11 | Then the client performs GET request on "/write" 12 | And status code is 200 13 | And response contains "Write success" 14 | 15 | Scenario: Call resource with less scopes 16 | When using OAuth 17 | And using "read" scopes 18 | Then the client performs GET request on "/write" 19 | And status code is 403 20 | -------------------------------------------------------------------------------- /cukes-samples/cukes-plugin-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | cukes-samples 5 | lv.ctco.cukes 6 | 1.0.11-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | cukes-plugin-sample 11 | Cukes Plugins Sample Project 12 | 13 | 14 | 15 | 16 | lv.ctco.cukes 17 | cukes-core 18 | ${project.version} 19 | 20 | 21 | 22 | 23 | ch.qos.logback 24 | logback-classic 25 | 1.2.3 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /cukes-samples/cukes-plugin-sample/readme.md: -------------------------------------------------------------------------------- 1 | # Cukes Plugins Sample 2 | 3 | This is a sample to show how you can build your own plugin. 4 | 5 | ## Use cases 6 | 7 | Plugins are powerful thing that allows you to execute some code before/after all scenarios or features. 8 | 9 | Some of the possible usage scenarios might be: 10 | 11 | - Clean database before each scenario 12 | - Setup pre-defined entries in database 13 | - Retrieve OAuth token before running all tests 14 | 15 | ## Implementation 16 | 17 | In this sample a simple plugin is implemented (`lv.ctco.cukes.plugins.SamplePlugin`) that just logs how long execution of a scenario took. 18 | 19 | Run [showcase.feature](src/test/resources/features/showcase.feature) and check the logs - execution times will be displayed. 20 | 21 | Please note that the plugin should be enabled explicitly in [cukes.properties](src/test/resources/cukes.properties) 22 | -------------------------------------------------------------------------------- /cukes-samples/cukes-plugin-sample/src/test/java/lv/ctco/cukes/plugins/RunCukesTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.plugins; 2 | 3 | import io.cucumber.junit.Cucumber; 4 | import io.cucumber.junit.CucumberOptions; 5 | import org.junit.runner.RunWith; 6 | 7 | @RunWith(Cucumber.class) 8 | @CucumberOptions( 9 | plugin = {"pretty", "json:target/cucumber2.json"}, 10 | features = {"classpath:features"}, 11 | glue = {"lv.ctco.cukes"} 12 | ) 13 | public class RunCukesTest { 14 | } 15 | -------------------------------------------------------------------------------- /cukes-samples/cukes-plugin-sample/src/test/java/lv/ctco/cukes/plugins/SamplePlugin.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.plugins; 2 | 3 | import lv.ctco.cukes.core.extension.CukesPlugin; 4 | import org.apache.commons.lang3.time.StopWatch; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | public class SamplePlugin implements CukesPlugin { 9 | 10 | private static final Logger logger = LoggerFactory.getLogger(SamplePlugin.class); 11 | 12 | private final StopWatch stopWatch = new StopWatch(); 13 | 14 | @Override 15 | public void beforeAllTests() { 16 | 17 | } 18 | 19 | @Override 20 | public void afterAllTests() { 21 | 22 | } 23 | 24 | @Override 25 | public void beforeScenario() { 26 | stopWatch.reset(); 27 | stopWatch.start(); 28 | } 29 | 30 | @Override 31 | public void afterScenario() { 32 | stopWatch.stop(); 33 | long executionTime = stopWatch.getTime(); 34 | logger.info("Scenario executed in {} ms", executionTime); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cukes-samples/cukes-plugin-sample/src/test/java/lv/ctco/cukes/plugins/TestCukesGuiceModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.plugins; 2 | 3 | import lv.ctco.cukes.core.extension.AbstractCukesModule; 4 | import lv.ctco.cukes.core.extension.CukesInjectableModule; 5 | import lv.ctco.cukes.core.facade.RandomGeneratorFacade; 6 | import lv.ctco.cukes.core.facade.RandomGeneratorFacadeImpl; 7 | import lv.ctco.cukes.core.facade.VariableFacade; 8 | import lv.ctco.cukes.core.facade.VariableFacadeImpl; 9 | 10 | @CukesInjectableModule 11 | public class TestCukesGuiceModule extends AbstractCukesModule { 12 | 13 | @Override 14 | protected void configure() { 15 | bind(VariableFacade.class).to(VariableFacadeImpl.class); 16 | bind(RandomGeneratorFacade.class).to(RandomGeneratorFacadeImpl.class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cukes-samples/cukes-plugin-sample/src/test/java/lv/ctco/cukes/plugins/api/TestSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.plugins.api; 2 | 3 | import io.cucumber.java.en.Given; 4 | 5 | public class TestSteps { 6 | 7 | @Given("^wait for (\\d+) seconds?$") 8 | public void waitForSeconds(int seconds) throws InterruptedException { 9 | Thread.sleep(seconds * 1000); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /cukes-samples/cukes-plugin-sample/src/test/resources/cukes.properties: -------------------------------------------------------------------------------- 1 | cukes.plugins:lv.ctco.cukes.plugins.SamplePlugin 2 | -------------------------------------------------------------------------------- /cukes-samples/cukes-plugin-sample/src/test/resources/features/showcase.feature: -------------------------------------------------------------------------------- 1 | Feature: Plugins showcase 2 | 3 | Scenario: fast scenario 4 | Given let variable "test" to be random UUID 5 | 6 | Scenario: slow scenario 7 | Given wait for 1 second 8 | 9 | Scenario: very slow scenario 10 | Given wait for 5 seconds 11 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/main/java/lv/ctco/cukes/rabbitmq/sample/Application.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.sample; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.ConfigurableApplicationContext; 7 | 8 | @SpringBootApplication 9 | @Slf4j 10 | public class Application { 11 | 12 | public static void main(String[] args) { 13 | run(); 14 | } 15 | 16 | public static ConfigurableApplicationContext run() { 17 | return SpringApplication.run(Application.class, 18 | "--rabbitmq.host=localhost", 19 | "--rabbitmq.port=5672", 20 | "--rabbitmq.username=guest", 21 | "--rabbitmq.password=guest", 22 | "--rabbitmq.virtual-host=default", 23 | "--rabbitmq.use-ssl=false" 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/main/java/lv/ctco/cukes/rabbitmq/sample/configuration/InMemoryAMQPBroker.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.sample.configuration; 2 | 3 | import org.apache.qpid.server.Broker; 4 | import org.apache.qpid.server.BrokerOptions; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.annotation.PostConstruct; 8 | import javax.annotation.PreDestroy; 9 | 10 | @Component("amqpBroker") 11 | public class InMemoryAMQPBroker { 12 | 13 | private final Broker broker = new Broker(); 14 | 15 | BrokerOptions brokerOptions() { 16 | BrokerOptions brokerOptions = new BrokerOptions(); 17 | 18 | brokerOptions.setConfigProperty("qpid.amqp_port", "5672"); 19 | brokerOptions.setInitialConfigurationLocation(getClass().getResource("/initial-config.json").getFile()); 20 | brokerOptions.setStartupLoggedToSystemOut(false); 21 | return brokerOptions; 22 | } 23 | 24 | @PostConstruct 25 | public void start() throws Exception { 26 | broker.startup(brokerOptions()); 27 | } 28 | 29 | @PreDestroy 30 | public void stop() { 31 | broker.shutdown(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/main/java/lv/ctco/cukes/rabbitmq/sample/listeners/PrependHello.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.sample.listeners; 2 | 3 | import lv.ctco.cukes.rabbitmq.sample.configuration.RabbitMQConfiguration; 4 | import lv.ctco.cukes.rabbitmq.sample.message.StringMessage; 5 | import org.springframework.amqp.core.ExchangeTypes; 6 | import org.springframework.amqp.core.Message; 7 | import org.springframework.amqp.rabbit.annotation.Exchange; 8 | import org.springframework.amqp.rabbit.annotation.Queue; 9 | import org.springframework.amqp.rabbit.annotation.QueueBinding; 10 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 11 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Component; 14 | 15 | @Component 16 | public class PrependHello { 17 | 18 | @Autowired 19 | RabbitTemplate template; 20 | 21 | @RabbitListener(bindings = { 22 | @QueueBinding( 23 | value = @Queue, 24 | exchange = @Exchange(value = RabbitMQConfiguration.EXCHANGE_NAME, type = ExchangeTypes.TOPIC), 25 | key = "prepend" 26 | ) 27 | }) 28 | public void onMessage(StringMessage msg, Message message) { 29 | String text = msg.getBody(); 30 | System.out.println("PrependHello.onMessage - " + text); 31 | String result = "hello, " + text; 32 | template.convertAndSend(message.getMessageProperties().getReplyTo(), new StringMessage(result)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/main/java/lv/ctco/cukes/rabbitmq/sample/listeners/ToUpperCase.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.sample.listeners; 2 | 3 | import lv.ctco.cukes.rabbitmq.sample.configuration.RabbitMQConfiguration; 4 | import org.springframework.amqp.core.ExchangeTypes; 5 | import org.springframework.amqp.core.Message; 6 | import org.springframework.amqp.rabbit.annotation.Exchange; 7 | import org.springframework.amqp.rabbit.annotation.Queue; 8 | import org.springframework.amqp.rabbit.annotation.QueueBinding; 9 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 10 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | @Component 15 | public class ToUpperCase { 16 | 17 | @Autowired 18 | RabbitTemplate template; 19 | 20 | @RabbitListener(bindings = { 21 | @QueueBinding( 22 | value = @Queue, 23 | exchange = @Exchange(value = RabbitMQConfiguration.EXCHANGE_NAME, type = ExchangeTypes.TOPIC), 24 | key = "upper" 25 | ) 26 | }) 27 | public void onMessage(Message message) { 28 | String text = new String(message.getBody()); 29 | System.out.println("ToUpperCase.onMessage - " + text); 30 | String result = text.toUpperCase(); 31 | template.convertAndSend(message.getMessageProperties().getReplyTo(), result); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/main/java/lv/ctco/cukes/rabbitmq/sample/message/StringMessage.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq.sample.message; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class StringMessage { 11 | 12 | private String body; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/main/resources/initial-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mock broker", 3 | "modelVersion": "2.0", 4 | "defaultVirtualHost": "default", 5 | "authenticationproviders": [ 6 | { 7 | "name": "password", 8 | "type": "Plain", 9 | "secureOnlyMechanisms": [], 10 | "users": [ 11 | { 12 | "name": "guest", 13 | "password": "guest", 14 | "type": "managed" 15 | } 16 | ] 17 | } 18 | ], 19 | "ports": [ 20 | { 21 | "name": "AMQP", 22 | "port": "${qpid.amqp_port}", 23 | "authenticationProvider": "password", 24 | "protocols": [ 25 | "AMQP_0_9_1" 26 | ] 27 | } 28 | ], 29 | "virtualhostnodes": [ 30 | { 31 | "name": "default", 32 | "type": "JSON", 33 | "virtualHostInitialConfiguration": "{ \"type\" : \"Memory\" }" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %date{HH:mm:ss.SSS} [%-20.20logger{0}] %.-1level: %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/test/java/lv/ctco/cukes/rabbitmq/RunCukesRabbmitMQTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rabbitmq; 2 | 3 | import io.cucumber.junit.Cucumber; 4 | import io.cucumber.junit.CucumberOptions; 5 | import lv.ctco.cukes.rabbitmq.sample.Application; 6 | import org.junit.AfterClass; 7 | import org.junit.BeforeClass; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.context.ConfigurableApplicationContext; 11 | 12 | @RunWith(Cucumber.class) 13 | @CucumberOptions( 14 | plugin = {"pretty", "json:target/cucumber.json", "json:target/cucumber2.json"}, 15 | features = {"classpath:features/"}, 16 | glue = {"lv.ctco.cukes.rabbitmq", "lv.ctco.cukes.core.api"} 17 | ) 18 | public class RunCukesRabbmitMQTest { 19 | 20 | private static ConfigurableApplicationContext context; 21 | 22 | @BeforeClass 23 | public static void setUp() { 24 | context = Application.run(); 25 | } 26 | 27 | @AfterClass 28 | public static void tearDown() { 29 | context.close(); 30 | } 31 | 32 | @Test 33 | public void name() { 34 | System.out.println("aaaa"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/test/resources/cukes.properties: -------------------------------------------------------------------------------- 1 | # Connection parameters 2 | cukes.rabbitmq.host=localhost 3 | cukes.rabbitmq.port=5672 4 | cukes.rabbitmq.user=guest 5 | cukes.rabbitmq.password=guest 6 | cukes.rabbitmq.vhost=default 7 | 8 | # Default content type for messages 9 | cukes.rabbitmq.content-type=application/json 10 | 11 | # Default waiting timeout 12 | cukes.rabbitmq.read-timeout.default=1 13 | 14 | # Predefine exchange 15 | cukes.rabbitmq.exchange.0.name=exchange 16 | cukes.rabbitmq.exchange.0.type=topic 17 | 18 | # Use exchange by default 19 | cukes.rabbitmq.exchange.default=exchange 20 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/test/resources/features/example.feature: -------------------------------------------------------------------------------- 1 | Feature: DSL showcase 2 | 3 | Background: 4 | Given connecting to host "localhost" 5 | And server responds on port 5672 6 | And connecting using username "guest" and password "guest" 7 | And virtual host is "default" 8 | And not using SSL 9 | And declare exchange "exchange" of type "topic" 10 | # And use exchange "exchange" by default 11 | 12 | Scenario: Should convert message to upper case 13 | Given prepare new message 14 | And content-type is "text/plain" 15 | And message body is "hello" 16 | And reply-to is "out" 17 | And bind queue "out" to exchange "exchange" with routing key "out" 18 | When the client sends message to exchange "exchange" with routing key "upper" 19 | Then wait for message in queue "out" for not more than 5 seconds 20 | # Double quotes here are just because String to JSON conversion during payload processing 21 | And message body equals to ""HELLO"" 22 | 23 | Scenario: Should prepend 'hello' to message 24 | Given use exchange "exchange" by default 25 | And prepare new message 26 | And message body: 27 | """ 28 | { 29 | "body": "world" 30 | } 31 | """ 32 | And content-type is "application/json" 33 | And reply-to is "out" 34 | And bind queue "out" with routing key "out" 35 | When the client sends message with routing key "prepend" 36 | Then wait for message in queue "out" for not more than 5 seconds 37 | And message body contains property "body" with value "hello, world" 38 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/test/resources/features/minimalistic.feature: -------------------------------------------------------------------------------- 1 | Feature: Minimalistic configuration 2 | 3 | Scenario: Should prepend 'hello' to message 4 | Given prepare new message 5 | And message body: 6 | """ 7 | { 8 | "body": "world" 9 | } 10 | """ 11 | And reply-to is "out" 12 | And bind queue "out" with routing key "out" 13 | When the client sends message with routing key "prepend" 14 | Then wait for message in queue "out" 15 | And message body contains property "body" with value "hello, world" 16 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rabbitmq-sample/src/test/resources/features/variables.feature: -------------------------------------------------------------------------------- 1 | Feature: Variables usage showcase 2 | 3 | Scenario: Should prepend 'hello' to message 4 | Given let variable "name" to be random UUID 5 | And prepare new message 6 | And message body: 7 | """ 8 | { 9 | "body": "{(name)}" 10 | } 11 | """ 12 | And reply-to is "out" 13 | And bind queue "out" with routing key "out" 14 | When the client sends message with routing key "prepend" 15 | Then wait for message in queue "out" for not more than 5 seconds 16 | And message body contains property "body" with value "hello, {(name)}" 17 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/server.yml: -------------------------------------------------------------------------------- 1 | http: 2 | port: 8080 3 | adminPort: 8888 4 | requestLog: 5 | console: 6 | enabled: false 7 | file: 8 | enabled: false 9 | syslog: 10 | enabled: false 11 | 12 | logging: 13 | level: INFO 14 | console: 15 | enabled: false 16 | file: 17 | enabled: false 18 | syslog: 19 | enabled: false 20 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/SampleConfiguration.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest; 2 | 3 | import com.yammer.dropwizard.config.Configuration; 4 | 5 | public class SampleConfiguration extends Configuration { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/SampleHealthCheck.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest; 2 | 3 | import com.yammer.metrics.core.HealthCheck; 4 | 5 | public class SampleHealthCheck extends HealthCheck { 6 | 7 | protected SampleHealthCheck() { 8 | super("sample"); 9 | } 10 | 11 | @Override 12 | protected Result check() { 13 | return Result.healthy(); // To Disable Dropwizard warnings 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/common/InMemoryStorage.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.common; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lv.ctco.cukes.rest.gadgets.dto.GadgetDto; 6 | import lv.ctco.cukes.rest.gadgets.dto.GadgetType; 7 | import lv.ctco.cukes.rest.gadgets.dto.Owner; 8 | 9 | import java.util.Collections; 10 | import java.util.Date; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | @Singleton 15 | public class InMemoryStorage { 16 | 17 | private final Map gadgets = new HashMap<>(); 18 | 19 | @Inject 20 | public void init() { 21 | initGadget(1857, GadgetType.SMARTPHONE, "LG Nexus 5", "Lisa", 8); 22 | initGadget(1858, GadgetType.LAPTOP, "Macbook Air", "Homer", 38); 23 | initGadget(1859, GadgetType.LAPTOP, "Alienware 17 R3", "Bart", 10); 24 | initGadget(1860, GadgetType.SMART_WATCH, "Apple Watch", "Marge", 36); 25 | initGadget(1861, GadgetType.TABLET, "Samsung Galaxy Tab 2", "Maggie", 2); 26 | } 27 | 28 | private void initGadget(Integer id, GadgetType type, String gadgetName, String name, Integer age) { 29 | GadgetDto gadget = new GadgetDto(); 30 | 31 | gadget.setId(id); 32 | gadget.setType(type); 33 | gadget.setName(gadgetName); 34 | gadget.setOwner(new Owner(name, "Simpson", age, Collections.singletonList("Mr.Burns' slave"))); 35 | gadget.setCreatedDate(new Date()); 36 | 37 | this.gadgets.put(id, gadget); 38 | } 39 | 40 | public Map getGadgets() { 41 | return this.gadgets; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/common/RestUtils.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.common; 2 | 3 | import com.google.inject.Inject; 4 | import com.yammer.dropwizard.config.Configuration; 5 | 6 | import javax.ws.rs.core.Response; 7 | 8 | public class RestUtils { 9 | 10 | @Inject 11 | Configuration config; 12 | 13 | public Response ok() { 14 | return buildResponse(200); 15 | } 16 | 17 | public Response ok(Object data) { 18 | return buildResponse(200, data); 19 | } 20 | 21 | public Response created(Integer id, String resource) { 22 | int port = config.getHttpConfiguration().getPort(); 23 | return Response 24 | .status(201) 25 | .header("Location", String.format("http://localhost:%s%s/%s", port, resource, id)) 26 | .build(); 27 | } 28 | 29 | public Response notFound() { 30 | return buildResponse(404, "Object not found in the database"); 31 | } 32 | 33 | public Response badRequest(String message) { 34 | return buildResponse(400, message); 35 | } 36 | 37 | public Response buildResponse(int statusCode) { 38 | return buildResponse(statusCode, null); 39 | } 40 | 41 | public Response buildResponse(int statusCode, Object data) { 42 | return Response 43 | .status(statusCode) 44 | .entity(data) 45 | .build(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/gadgets/dto/GadgetData.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.gadgets.dto; 2 | 3 | import java.util.Collection; 4 | 5 | public class GadgetData { 6 | 7 | private Collection gadgets; 8 | 9 | public GadgetData(Collection gadgets) { 10 | this.gadgets = gadgets; 11 | } 12 | 13 | public Collection getGadgets() { 14 | return gadgets; 15 | } 16 | 17 | public void setGadgets(Collection gadgets) { 18 | this.gadgets = gadgets; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/gadgets/dto/GadgetDto.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.gadgets.dto; 2 | 3 | import java.util.Date; 4 | 5 | public class GadgetDto { 6 | private Integer id; 7 | private GadgetType type; 8 | private String name; 9 | private Owner owner; 10 | private Date createdDate; 11 | private Date updatedDate; 12 | 13 | public Integer getId() { 14 | return id; 15 | } 16 | 17 | public void setId(Integer id) { 18 | this.id = id; 19 | } 20 | 21 | public GadgetType getType() { 22 | return type; 23 | } 24 | 25 | public void setType(GadgetType type) { 26 | this.type = type; 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | public Owner getOwner() { 38 | return owner; 39 | } 40 | 41 | public void setOwner(Owner owner) { 42 | this.owner = owner; 43 | } 44 | 45 | public Date getCreatedDate() { 46 | return createdDate; 47 | } 48 | 49 | public void setCreatedDate(Date createdDate) { 50 | this.createdDate = createdDate; 51 | } 52 | 53 | public Date getUpdatedDate() { 54 | return updatedDate; 55 | } 56 | 57 | public void setUpdatedDate(Date updatedDate) { 58 | this.updatedDate = updatedDate; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/gadgets/dto/GadgetType.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.gadgets.dto; 2 | 3 | public enum GadgetType { 4 | LAPTOP, 5 | SMARTPHONE, 6 | TABLET, 7 | SMART_WATCH, 8 | BOOK_READER 9 | } 10 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/gadgets/dto/Owner.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.gadgets.dto; 2 | 3 | import java.util.List; 4 | 5 | public class Owner { 6 | private String name; 7 | private String surname; 8 | private Integer age; 9 | private List roles; 10 | 11 | public Owner() { 12 | } 13 | 14 | public Owner(String name, String surname, Integer age, List roles) { 15 | this.name = name; 16 | this.surname = surname; 17 | this.age = age; 18 | this.roles = roles; 19 | } 20 | 21 | public String getName() { 22 | return this.name; 23 | } 24 | 25 | public void setName(String name) { 26 | this.name = name; 27 | } 28 | 29 | public String getSurname() { 30 | return this.surname; 31 | } 32 | 33 | public void setSurname(String surname) { 34 | this.surname = surname; 35 | } 36 | 37 | public Integer getAge() { 38 | return this.age; 39 | } 40 | 41 | public void setAge(Integer age) { 42 | this.age = age; 43 | } 44 | 45 | public List getRoles() { 46 | return this.roles; 47 | } 48 | 49 | public void setRoles(List roles) { 50 | this.roles = roles; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/healthcheck/CustomHeadersResource.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.healthcheck; 2 | 3 | import javax.ws.rs.Consumes; 4 | import javax.ws.rs.GET; 5 | import javax.ws.rs.Path; 6 | import javax.ws.rs.Produces; 7 | import javax.ws.rs.core.MediaType; 8 | import javax.ws.rs.core.Response; 9 | 10 | @Path(CustomHeadersResource.API) 11 | @Consumes(MediaType.APPLICATION_JSON) 12 | @Produces(MediaType.APPLICATION_JSON) 13 | public class CustomHeadersResource { 14 | 15 | protected static final String API = "/customHeaders"; 16 | 17 | @GET 18 | public Response headers() { 19 | return Response.ok() 20 | .header("Custom-Header", "2000000000000") 21 | .build(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/healthcheck/StaticTypesResource.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.healthcheck; 2 | 3 | import javax.ws.rs.Consumes; 4 | import javax.ws.rs.GET; 5 | import javax.ws.rs.Path; 6 | import javax.ws.rs.Produces; 7 | import javax.ws.rs.core.MediaType; 8 | import javax.ws.rs.core.Response; 9 | 10 | @SuppressWarnings("SameReturnValue") 11 | @Path(StaticTypesResource.API) 12 | @Consumes(MediaType.APPLICATION_JSON) 13 | @Produces(MediaType.APPLICATION_JSON) 14 | public class StaticTypesResource { 15 | 16 | protected static final String API = "/staticTypes"; 17 | 18 | @GET 19 | public Response staticTypes() { 20 | return Response.ok("{" + 21 | " \"prop\": [" + 22 | " {" + 23 | " \"int\": 1," + 24 | " \"float\": 26.505515," + 25 | " \"long\": 2000000000000," + 26 | " \"string\": \"{(isolated)}\"" + 27 | " }" + 28 | "]" + 29 | "}").build(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/multipart/BinaryStreamResource.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.multipart; 2 | 3 | import com.google.inject.Inject; 4 | import lv.ctco.cukes.rest.common.RestUtils; 5 | import org.apache.commons.io.IOUtils; 6 | 7 | import javax.ws.rs.Consumes; 8 | import javax.ws.rs.POST; 9 | import javax.ws.rs.Path; 10 | import javax.ws.rs.core.MediaType; 11 | import javax.ws.rs.core.Response; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | 15 | @Path("/binary") 16 | public class BinaryStreamResource { 17 | 18 | @Inject 19 | RestUtils rest; 20 | 21 | @POST 22 | @Consumes(MediaType.APPLICATION_OCTET_STREAM) 23 | public Response upload(InputStream is) throws IOException { 24 | byte[] bytes = IOUtils.toByteArray(is); 25 | System.out.println("Read " + bytes.length + " byte(s)"); 26 | return rest.ok(String.valueOf(bytes.length)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/main/java/lv/ctco/cukes/rest/multipart/MultipartResource.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.multipart; 2 | 3 | import com.google.inject.Inject; 4 | import com.sun.jersey.multipart.FormDataParam; 5 | import lv.ctco.cukes.rest.common.RestUtils; 6 | import org.apache.commons.io.IOUtils; 7 | 8 | import javax.ws.rs.Consumes; 9 | import javax.ws.rs.POST; 10 | import javax.ws.rs.Path; 11 | import javax.ws.rs.core.MediaType; 12 | import javax.ws.rs.core.Response; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | 16 | @Path("/multipart") 17 | public class MultipartResource { 18 | 19 | @Inject 20 | RestUtils rest; 21 | 22 | @POST 23 | @Consumes(MediaType.MULTIPART_FORM_DATA) 24 | public Response upload(@FormDataParam("file") InputStream is) throws IOException { 25 | byte[] bytes = IOUtils.toByteArray(is); 26 | System.out.println("Read " + bytes.length + " byte(s)"); 27 | return rest.ok(new String(bytes)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/java/lv/ctco/cukes/rest/run/RunCukesTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.run; 2 | 3 | import io.cucumber.junit.Cucumber; 4 | import io.cucumber.junit.CucumberOptions; 5 | import lv.ctco.cukes.rest.SampleApplication; 6 | import org.junit.BeforeClass; 7 | import org.junit.runner.RunWith; 8 | 9 | @RunWith(Cucumber.class) 10 | @CucumberOptions( 11 | plugin = {"pretty", "json:target/cucumber.json", "json:target/cucumber2.json"}, 12 | features = {"classpath:features/gadgets/", "classpath:features/healthcheck/", "classpath:features/variables/", "classpath:features/multipart/"}, 13 | glue = "lv.ctco.cukes" 14 | ) 15 | public class RunCukesTest { 16 | 17 | @BeforeClass 18 | public static void setUp() throws Exception { 19 | new SampleApplication().run(new String[]{"server", "server.yml"}); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/java/lv/ctco/cukes/rest/run/RunCustomCukesTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.run; 2 | 3 | 4 | import io.cucumber.guice.ScenarioScoped; 5 | import io.cucumber.junit.Cucumber; 6 | import io.cucumber.junit.CucumberOptions; 7 | import lv.ctco.cukes.core.internal.di.SingletonObjectFactory; 8 | import lv.ctco.cukes.rest.SampleApplication; 9 | import lv.ctco.cukes.rest.run.custom.steps.CustomSteps; 10 | import org.junit.BeforeClass; 11 | import org.junit.runner.RunWith; 12 | 13 | @RunWith(Cucumber.class) 14 | @CucumberOptions( 15 | plugin = {"pretty"}, 16 | features = "classpath:features/custom/", 17 | glue = {"lv.ctco.cukes"} 18 | ) 19 | public class RunCustomCukesTest { 20 | 21 | @BeforeClass 22 | public static void setUp() throws Exception { 23 | SingletonObjectFactory.instance().addModule(binder -> binder.bind(CustomSteps.StateSaver.class).in(ScenarioScoped.class)); 24 | 25 | new SampleApplication().run(new String[]{"server", "server.yml"}); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/java/lv/ctco/cukes/rest/run/custom/steps/CustomSteps.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.rest.run.custom.steps; 2 | 3 | import com.google.common.collect.ForwardingMap; 4 | import com.google.common.collect.Maps; 5 | import com.google.inject.Inject; 6 | import io.cucumber.java.en.Given; 7 | import io.cucumber.java.en.Then; 8 | 9 | import java.util.Map; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | public class CustomSteps { 14 | 15 | @Inject 16 | private StateSaver stateSaver; 17 | 18 | @Given("^save state for \"(.*)\" as (STARTED|WORKING|STOPPED)$") 19 | public void saveState(String object, String state) { 20 | stateSaver.put(object, State.valueOf(state)); 21 | } 22 | 23 | @Then("^state for \"(.*)\" should be (STARTED|WORKING|STOPPED)$") 24 | public void checkState(String object, String state) { 25 | assertEquals(State.valueOf(state), stateSaver.get(object)); 26 | } 27 | 28 | @Then("^state should be missing for \"(.*)\"$") 29 | public void checkState(String object) { 30 | assertFalse(stateSaver.containsKey(object)); 31 | } 32 | 33 | public static class StateSaver extends ForwardingMap { 34 | 35 | private final Map states = Maps.newHashMap(); 36 | 37 | @Override 38 | protected Map delegate() { 39 | return states; 40 | } 41 | } 42 | 43 | public enum State { 44 | STARTED, WORKING, STOPPED 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/cukes.properties: -------------------------------------------------------------------------------- 1 | ### 2 | # common 3 | ### 4 | 5 | #default - http://localhost:80 6 | cukes.base_uri=http://localhost:8080 7 | 8 | #default - src/test/resources/ 9 | cukes.resources_root=src/test/resources/features 10 | 11 | #cukes.proxy 12 | 13 | #default - false 14 | #cukes.url_encoding_enabled=false 15 | #cukes.relaxed_https=false 16 | 17 | #cukes.auth_type 18 | #cukes.username=username 19 | #cukes.password=password 20 | 21 | ### 22 | # asserts 23 | ### 24 | 25 | #default - false 26 | #cukes.asserts.status_code.display_body=true 27 | 28 | #default - no max 29 | #cukes.asserts.status_code.max_size=1024 30 | 31 | ### 32 | # logging 33 | ### 34 | 35 | # default - lv.ctco.cukes.http 36 | #cukes.logging.http.logger.name=lv.ctco.cukes.http 37 | 38 | # default - none 39 | # options - all, body, cookies, headers, method, params, uri 40 | #cukes.logging.http.requests.include=method,uri,params 41 | 42 | # default - none 43 | # options - all, body, cookies, headers, status 44 | #cukes.logging.http.responses.include=headers,status 45 | 46 | ### 47 | # load runner 48 | ### 49 | 50 | #default - true 51 | #cukes.context_inflating_enabled=true 52 | 53 | #default - false 54 | #cukes.assertions_disabled=false 55 | 56 | #default - false 57 | #cukes.plugins: com.mycompany.cukes.MyPlugin 58 | cukes.url_encoding_enabled=true 59 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/features/custom/custom.feature: -------------------------------------------------------------------------------- 1 | Feature: Custom integration 2 | 3 | Background: 4 | Given baseUri is "http://localhost:8888/" 5 | 6 | Scenario: Should persist state within scenario 7 | And save state for "healthcheck" as STARTED 8 | When the client performs GET request on "/healthcheck" 9 | And save state for "healthcheck" as WORKING 10 | Then status code is 200 11 | And save state for "healthcheck" as STOPPED 12 | And state for "healthcheck" should be STOPPED 13 | 14 | Scenario: Should not have have old state 15 | Then state should be missing for "healthcheck" 16 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/features/gadgets/Delete Gadgets.feature: -------------------------------------------------------------------------------- 1 | Feature: It is able to remove newly created Gadgets from the database 2 | 3 | Scenario: Should remove newly created Gadget 4 | Given request body from file "gadgets/requests/newGadget.json" 5 | And content type is "application/json" 6 | 7 | When the client performs POST request on "/gadgets" 8 | Then status code is 201 9 | And let variable "gadgetURL" equal to header "Location" value 10 | 11 | When the client performs DELETE request on "{(gadgetURL)}" 12 | Then status code is 200 13 | 14 | When the client performs GET request on "{(gadgetURL)}" 15 | Then status code is 404 16 | And response equals to "Object not found in the database" 17 | 18 | Scenario: Validation should fail if removal of Gadget is requested by invalid ID 19 | When the client performs DELETE request on "/gadgets/123456" 20 | Then status code is 404 21 | And response equals to "Object not found in the database" 22 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/features/gadgets/requests/newGadget.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 2000, 3 | "type": "TABLET", 4 | "name": "Nexus 9", 5 | "owner": { 6 | "name": "Ned", 7 | "surname": "Flanders", 8 | "roles": [ 9 | "maLL-owner", 10 | "EvanGEliST" 11 | ], 12 | "age": 43 13 | }, 14 | "createdDate": 1455114963103, 15 | "updatedDate": 1456326973103 16 | } 17 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/features/gadgets/requests/updatedGadget.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 3000, 3 | "type": "LAPTOP", 4 | "name": "Acer Aspire R1", 5 | "owner": { 6 | "name": "Mr.", 7 | "surname": "Burns", 8 | "age": 65 9 | }, 10 | "createdDate": 1455114963103, 11 | "updatedDate": 1456326973103 12 | } 13 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/features/healthcheck/healthcheck.feature: -------------------------------------------------------------------------------- 1 | Feature: Server is healthy 2 | 3 | Background: 4 | Given baseUri is "http://localhost:8888/" 5 | 6 | Scenario: Should return pong response on /healthcheck 7 | When the client performs GET request on "/healthcheck" 8 | Then status code is 200 9 | And let variable "var" equal to header "Content-Length" value 10 | 11 | Scenario: Scenario is isolated from another 12 | When the client performs GET request on "/healthcheck" 13 | Then status code is 200 14 | Given request body "Hello. I'm Isolated!" 15 | Given queryParam "param" is "isolated" 16 | 17 | Scenario: GET is performed without extra request body 18 | When the client performs GET request on "/healthcheck" 19 | Then status code is 200 20 | And let variable "{(isolated)}" equal to "not_isolated" 21 | 22 | Scenario: Float values should be compared correctly 23 | Given baseUri is "http://localhost:8080/" 24 | When the client performs GET request on "/staticTypes" 25 | Then status code is 200 26 | Then response contains property "prop[0].float" with value "26.505515" 27 | Then response contains property "prop[0].string" with value "{(isolated)}" 28 | 29 | Scenario: Auto-cached variables are cleaned after each request 30 | Given baseUri is "http://localhost:8080/" 31 | When the client performs GET request on "/customHeaders" 32 | Then status code is 200 33 | 34 | When the client performs GET request on "/staticTypes" 35 | Then status code is 200 36 | # {(header.Custom-Header)} should be empty 37 | Then response contains property "prop[0].long" not matching pattern "{(header.Custom-Header)}" 38 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/features/multipart/BinaryStream.feature: -------------------------------------------------------------------------------- 1 | Feature: Binary upload showcase 2 | 3 | Scenario: Excel Octet-Stream upload 4 | Given request body from binary file "multipart/testExcel.xlsx" 5 | Given header Content-Type with value "application/octet-stream" 6 | When the client performs POST request on "/binary" 7 | Then status code is 200 8 | And response contains "6180" 9 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/features/multipart/Mutlipart.feature: -------------------------------------------------------------------------------- 1 | Feature: Multipart upload showcase 2 | 3 | Scenario: Multipart upload 4 | Given request body is a multipart file "multipart/test.txt" 5 | When the client performs POST request on "/multipart" 6 | Then status code is 200 7 | And response contains "Hello, world" 8 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/features/multipart/test.txt: -------------------------------------------------------------------------------- 1 | Hello, world 2 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/features/multipart/testExcel.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctco/cukes/ddede60dccfc8c1d7666cb02c50461738b5990a5/cukes-samples/cukes-rest-sample/src/test/resources/features/multipart/testExcel.xlsx -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/features/variables/Dynamic File Name.feature: -------------------------------------------------------------------------------- 1 | Feature: It is able to use variable in file names 2 | 3 | Background: 4 | Given let variable "fileName" equal to "newGadget.json" 5 | 6 | Scenario: Should remove newly created Gadget 7 | Given request body from file "variables/requests/{(fileName)}" 8 | And content type is "application/json" 9 | 10 | When the client performs POST request on "/gadgets" 11 | Then status code is 201 12 | And let variable "gadgetURL" equal to header "Location" value 13 | 14 | When the client performs DELETE request on "{(gadgetURL)}" 15 | Then status code is 200 16 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/features/variables/requests/newGadget.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 2000, 3 | "type": "TABLET", 4 | "name": "Nexus 9", 5 | "owner": { 6 | "name": "Ned", 7 | "surname": "Flanders", 8 | "roles": [ 9 | "maLL-owner", 10 | "EvanGEliST" 11 | ], 12 | "age": 43 13 | }, 14 | "createdDate": 1455114963103, 15 | "updatedDate": 1456326973103 16 | } 17 | -------------------------------------------------------------------------------- /cukes-samples/cukes-rest-sample/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/readme.md: -------------------------------------------------------------------------------- 1 | # Cukes SOAP Sample 2 | 3 | This is a sample project showing how to use **[Cukes REST extensions](../../cukes-rest)** for testing of SOAP-based web-services 4 | 5 | ## System under test 6 | 7 | The system under test is very trivial - it exposes a calculator API that can add two integers. 8 | 9 | The WSDL can be found under [http://localhost:8080/ws/calculator.wsdl](http://localhost:8080/ws/calculator.wsdl) 10 | 11 | ## Running tests 12 | 13 | There is a JUnit-based test runner configured - [RunCukesSOAPTest](src/test/java/lv/ctco/cukes/soap/sample/RunCukesSOAPTest.java). 14 | 15 | It starts calculator web-service first and then after all tests are run the web-service is stopped. 16 | 17 | There are 2 options to run test suite: 18 | 19 | 1. Run it as JUnit test - `lv.ctco.cukes.soap.sample.RunCukesSOAPTest` 20 | 2. Run using Maven - `mvn clean test` 21 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/src/main/java/lv/ctco/cukes/soap/sample/Application.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.soap.sample; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.ws.config.annotation.EnableWs; 6 | 7 | @SpringBootApplication 8 | @EnableWs 9 | public class Application { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(Application.class); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/src/main/java/lv/ctco/cukes/soap/sample/CalculatorEndpoint.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.soap.sample; 2 | 3 | import lv.ctco.cukes.soap.sample.dto.CalculatorRequest; 4 | import lv.ctco.cukes.soap.sample.dto.CalculatorResponse; 5 | import org.springframework.ws.server.endpoint.annotation.Endpoint; 6 | import org.springframework.ws.server.endpoint.annotation.PayloadRoot; 7 | import org.springframework.ws.server.endpoint.annotation.RequestPayload; 8 | import org.springframework.ws.server.endpoint.annotation.ResponsePayload; 9 | 10 | @Endpoint 11 | public class CalculatorEndpoint { 12 | 13 | @PayloadRoot(namespace = "http://github.com/ctco/cukes/cukes-soap-sample", localPart = "calculatorRequest") 14 | @ResponsePayload 15 | public CalculatorResponse calculate(@RequestPayload CalculatorRequest request) { 16 | CalculatorResponse response = new CalculatorResponse(); 17 | response.setResult(request.getA() + request.getB()); 18 | return response; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/src/main/java/lv/ctco/cukes/soap/sample/dto/CalculatorRequest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.soap.sample.dto; 2 | 3 | import lombok.Data; 4 | 5 | import javax.xml.bind.annotation.XmlAccessType; 6 | import javax.xml.bind.annotation.XmlAccessorType; 7 | import javax.xml.bind.annotation.XmlRootElement; 8 | import javax.xml.bind.annotation.XmlType; 9 | import java.io.Serializable; 10 | 11 | @Data 12 | @XmlAccessorType(XmlAccessType.FIELD) 13 | @XmlType(name = "calculatorRequest") 14 | @XmlRootElement 15 | public class CalculatorRequest implements Serializable { 16 | public Integer a; 17 | public Integer b; 18 | } 19 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/src/main/java/lv/ctco/cukes/soap/sample/dto/CalculatorResponse.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.soap.sample.dto; 2 | 3 | import lombok.Data; 4 | 5 | import javax.xml.bind.annotation.XmlAccessType; 6 | import javax.xml.bind.annotation.XmlAccessorType; 7 | import javax.xml.bind.annotation.XmlRootElement; 8 | import javax.xml.bind.annotation.XmlType; 9 | 10 | @Data 11 | @XmlAccessorType(XmlAccessType.FIELD) 12 | @XmlType 13 | @XmlRootElement 14 | public class CalculatorResponse { 15 | private Integer result; 16 | } 17 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/src/main/java/lv/ctco/cukes/soap/sample/dto/package-info.java: -------------------------------------------------------------------------------- 1 | @javax.xml.bind.annotation.XmlSchema(namespace = "http://github.com/ctco/cukes/cukes-soap-sample") 2 | package lv.ctco.cukes.soap.sample.dto; 3 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/src/test/java/lv/ctco/cukes/soap/sample/CukesBootstrap.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.soap.sample; 2 | 3 | import lv.ctco.cukes.core.extension.CukesPlugin; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.context.ConfigurableApplicationContext; 6 | 7 | public class CukesBootstrap implements CukesPlugin { 8 | 9 | private ConfigurableApplicationContext applicationContext; 10 | 11 | @Override 12 | public void beforeAllTests() { 13 | this.applicationContext = SpringApplication.run(Application.class); 14 | } 15 | 16 | @Override 17 | public void afterAllTests() { 18 | applicationContext.close(); 19 | } 20 | 21 | @Override 22 | public void beforeScenario() { 23 | 24 | } 25 | 26 | @Override 27 | public void afterScenario() { 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/src/test/java/lv/ctco/cukes/soap/sample/RunCukesSOAPTest.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.soap.sample; 2 | 3 | import io.cucumber.junit.Cucumber; 4 | import io.cucumber.junit.CucumberOptions; 5 | import org.junit.runner.RunWith; 6 | 7 | @RunWith(Cucumber.class) 8 | @CucumberOptions( 9 | plugin = {"pretty", "json:target/cucumber.json", "json:target/cucumber2.json"}, 10 | features = {"classpath:features/"}, 11 | glue = {"lv.ctco.cukes"} 12 | ) 13 | public class RunCukesSOAPTest { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/src/test/java/lv/ctco/cukes/soap/sample/TestGuiceModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.soap.sample; 2 | 3 | import lv.ctco.cukes.core.extension.CukesInjectableModule; 4 | import lv.ctco.cukes.http.extension.AbstractCukesHttpModule; 5 | 6 | @CukesInjectableModule 7 | public class TestGuiceModule extends AbstractCukesHttpModule { 8 | 9 | @Override 10 | protected void configure() { 11 | registerHttpPlugin(TestPlugin.class); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/src/test/java/lv/ctco/cukes/soap/sample/TestPlugin.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.soap.sample; 2 | 3 | import io.restassured.response.Response; 4 | import io.restassured.specification.RequestSpecification; 5 | import lv.ctco.cukes.core.extension.CukesPlugin; 6 | import lv.ctco.cukes.http.extension.CukesHttpPlugin; 7 | 8 | public class TestPlugin implements CukesHttpPlugin, CukesPlugin { 9 | 10 | @Override 11 | public void beforeAllTests() { 12 | System.out.println("TestPlugin.beforeAllTests"); 13 | } 14 | 15 | @Override 16 | public void afterAllTests() { 17 | System.out.println("TestPlugin.afterAllTests"); 18 | } 19 | 20 | @Override 21 | public void beforeScenario() { 22 | System.out.println("TestPlugin.beforeScenario"); 23 | } 24 | 25 | @Override 26 | public void afterScenario() { 27 | System.out.println("TestPlugin.afterScenario"); 28 | } 29 | 30 | @Override 31 | public void beforeRequest(RequestSpecification requestSpecification) { 32 | System.out.println("TestPlugin.beforeRequest"); 33 | } 34 | 35 | @Override 36 | public void afterRequest(Response response) { 37 | System.out.println("TestPlugin.afterRequest"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/src/test/resources/cukes.properties: -------------------------------------------------------------------------------- 1 | cukes.base_uri=http://localhost:8080 2 | 3 | cukes.plugins=lv.ctco.cukes.soap.sample.CukesBootstrap 4 | -------------------------------------------------------------------------------- /cukes-samples/cukes-soap-sample/src/test/resources/features/calculate.feature: -------------------------------------------------------------------------------- 1 | Feature: Calculate 2 | 3 | Background: 4 | Given content type is "text/xml" 5 | And accept "text/xml" mediaTypes 6 | 7 | Scenario: Should sum 2 numbers 8 | Given request body: 9 | """ 10 | 11 | 12 | 13 | 14 | 1 15 | 2 16 | 17 | 18 | 19 | """ 20 | When the client performs POST request on "/ws" 21 | Then status code is 200 22 | And response contains property "Envelope.Body.calculatorResponse.result" with value "3" 23 | -------------------------------------------------------------------------------- /cukes-samples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | cukes-samples 6 | Cukes Sample Projects 7 | 8 | cukes-graphql-sample 9 | cukes-ldap-sample 10 | cukes-rabbitmq-sample 11 | cukes-rest-sample 12 | cukes-soap-sample 13 | cukes-plugin-sample 14 | cukes-http-mock-sample 15 | cukes-oauth-sample 16 | 17 | pom 18 | 19 | 20 | lv.ctco.cukes 21 | cukes 22 | 1.0.11-SNAPSHOT 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /cukes-samples/readme.md: -------------------------------------------------------------------------------- 1 | # Cukes Samples 2 | 3 | The following samples are provided 4 | 5 | | Sample | Description | 6 | |-----|----| 7 | | [cukes-graphql-sample](cukes-graphql-sample) | Testing GraphQL-based endpoints | 8 | | [cukes-http-mock-sample](cukes-http-mock-sample)| Showcase how to mock HTTP requests for tests | 9 | | [cukes-ldap-sample](cukes-ldap-sample)| Testing LDAP-based backends | 10 | | [cukes-oauth-sample](cukes-oauth-sample)| Showcase how to test OAuth-based services | 11 | | [cukes-plugin-sample](cukes-plugin-sample)| Showcase how to build own cukes plugins | 12 | | [cukes-rabbitmq-sample](cukes-rabbitmq-sample)| Testing RabbitMQ-based messaging systems | 13 | | [cukes-rest-sample](cukes-rest-sample)| Testing RESTful endpoints | 14 | | [cukes-soap-sample](cukes-soap-sample)| Testing SOAP-based web services | 15 | -------------------------------------------------------------------------------- /cukes-sql/Readme.md: -------------------------------------------------------------------------------- 1 | #Cukes SQL 2 | 3 | **Cukes SQL** is an extension to cukes framework that allows simplified testing of data stored in DB 4 | 5 | ##Sample Step 6 | 7 | **Cukes SQL** support exact matching with Table values 8 | 9 | ```gherkin 10 | Then DB table Person should match: 11 | | id | name | surname | age | 12 | | 1 | Anju | Ujna | 5 | 13 | | 2 | Sonia | Ainos | 19 | 14 | | 3 | Asha | Tear | 35 | 15 | ``` 16 | 17 | And can check whether target Table contains subset of values 18 | 19 | ```gherkin 20 | And DB table Person should contain: 21 | | name | age | 22 | | Anju | 5 | 23 | | Sonia | 19 | 24 | ``` 25 | 26 | Also **Cukes SQL** can be used with ContextInflater 27 | 28 | ```gherkin 29 | Then DB table Person should match: 30 | | id | name | surname | age | 31 | | 1 | {(name)} | Ujna | 5 | 32 | | 2 | Sonia | {(surname)} | 19 | 33 | | 3 | Asha | Tear | 35 | 34 | ``` 35 | 36 | It is possible to check content of the Table without using exact values to compare 37 | 38 | ```gherkin 39 | Then DB table Person row count should be = 3 40 | ``` 41 | 42 | ```gherkin 43 | Then DB table Person row count should not be empty 44 | ``` 45 | 46 | It is possible to create content of the Table with values, as well as with SQL query 47 | 48 | ```gherkin 49 | When the client creates DB entities in table Person with values: 50 | | id | name | surname | age | 51 | | 4 | SomeName | SomeSurname | 0 | 52 | | 5 | Hello | World | 46 | 53 | ``` 54 | 55 | ```gherkin 56 | When the client creates DB entities by SQL query: 57 | """ 58 | INSERT INTO Person(id, name, surname, age) VALUES(4, 'SomeName', 'SomeSurname', 0); 59 | INSERT INTO Person(id, name, surname, age) VALUES(5, 'Hello', 'World', 46); 60 | """ 61 | ``` 62 | ## Prerequisites 63 | 64 | - Java-based project 65 | - Java 1.8 66 | - Maven or Gradle as build system 67 | -------------------------------------------------------------------------------- /cukes-sql/src/main/java/lv/ctco/cukes/sql/ConnectionFactory.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.sql; 2 | 3 | import java.sql.Connection; 4 | 5 | public interface ConnectionFactory { 6 | Connection getConnection(); 7 | } 8 | -------------------------------------------------------------------------------- /cukes-sql/src/test/java/lv/ctco/cukes/sql/CucumberBootstrap.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.sql; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lombok.extern.slf4j.Slf4j; 6 | import lv.ctco.cukes.core.extension.CukesPlugin; 7 | 8 | import java.sql.SQLException; 9 | 10 | @Slf4j 11 | @Singleton 12 | public class CucumberBootstrap implements CukesPlugin { 13 | 14 | @Inject 15 | private H2MemoryDatabase database; 16 | 17 | @Override 18 | public void beforeAllTests() { 19 | database.openConnection(); 20 | } 21 | 22 | @Override 23 | public void afterAllTests() { 24 | try { 25 | database.closeConnection(); 26 | } catch (SQLException e) { 27 | log.error(e.getMessage()); 28 | } 29 | } 30 | 31 | @Override 32 | public void beforeScenario() { 33 | try { 34 | database.clearDataBase(); 35 | database.createAndFillTable(); 36 | } catch (SQLException e) { 37 | log.error(e.getMessage()); 38 | } 39 | } 40 | 41 | @Override 42 | public void afterScenario() {} 43 | } 44 | -------------------------------------------------------------------------------- /cukes-sql/src/test/java/lv/ctco/cukes/sql/CucumberTests.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.sql; 2 | 3 | import io.cucumber.junit.Cucumber; 4 | import io.cucumber.junit.CucumberOptions; 5 | import org.junit.runner.RunWith; 6 | 7 | @RunWith(Cucumber.class) 8 | @CucumberOptions( 9 | tags = "not @Ignore", 10 | features = {"src/test/resources/features"}, 11 | glue = "lv.ctco.cukes", 12 | plugin = {"pretty", "junit:build/cucumber-report/junit.xml", "json:build/cucumber-report/report.json"}, 13 | monochrome = true 14 | ) 15 | public class CucumberTests { 16 | } 17 | -------------------------------------------------------------------------------- /cukes-sql/src/test/java/lv/ctco/cukes/sql/TestModule.java: -------------------------------------------------------------------------------- 1 | package lv.ctco.cukes.sql; 2 | 3 | import lv.ctco.cukes.core.extension.CukesInjectableModule; 4 | import lv.ctco.cukes.core.facade.RandomGeneratorFacade; 5 | import lv.ctco.cukes.core.facade.RandomGeneratorFacadeImpl; 6 | import lv.ctco.cukes.core.facade.VariableFacade; 7 | import lv.ctco.cukes.core.facade.VariableFacadeImpl; 8 | import lv.ctco.cukes.core.internal.di.CukesGuiceModule; 9 | 10 | @CukesInjectableModule 11 | public class TestModule extends CukesGuiceModule { 12 | 13 | @Override 14 | protected void configure() { 15 | super.configure(); 16 | bind(ConnectionFactory.class).to(H2MemoryDatabase.class); 17 | bind(VariableFacade.class).to(VariableFacadeImpl.class); 18 | bind(RandomGeneratorFacade.class).to(RandomGeneratorFacadeImpl.class); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cukes-sql/src/test/resources/cukes.properties: -------------------------------------------------------------------------------- 1 | cukes.plugins:lv.ctco.cukes.sql.CucumberBootstrap 2 | -------------------------------------------------------------------------------- /cukes-sql/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %date{HH:mm:ss.SSS} %X{correlationId} [%-20.20logger{0}] %.-1level %X{user}: %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | # Cukes documentation 2 | 3 | Please find information about available Cukes steps: 4 | 5 | - [SNAPSHOT version](current/readme.md) - unreleased content 6 | - For version-specific steps just go in particular directory inside 7 | --------------------------------------------------------------------------------