├── .gitignore ├── .semaphore ├── docker-build.yml └── semaphore.yml ├── Dockerfile ├── LICENSE ├── README.md ├── assets └── pipeline-result.png ├── docker-hub-secret.yml ├── pom.xml └── src ├── it └── java │ └── com │ └── example │ └── springpipelinedemo │ ├── controller │ ├── AdminControllerTest.java │ └── UserControllerTest.java │ └── repository │ └── UserRepositoryTest.java ├── main ├── java │ └── com │ │ └── example │ │ └── springpipelinedemo │ │ ├── SpringPipelineDemoApplication.java │ │ ├── config │ │ ├── JpaConfig.java │ │ ├── UtilityConfig.java │ │ └── WebSecurityConfig.java │ │ ├── controller │ │ ├── AdminController.java │ │ ├── UserController.java │ │ └── forms │ │ │ └── SignupForm.java │ │ ├── dto │ │ ├── UserDto.java │ │ └── mapper │ │ │ ├── Mapper.java │ │ │ └── UserMapper.java │ │ ├── model │ │ └── User.java │ │ ├── repository │ │ └── UserRepository.java │ │ ├── service │ │ └── UserService.java │ │ └── tasks │ │ └── InitializeDefaultUser.java └── resources │ ├── application.properties │ ├── jmeter │ ├── HTTP Request.jmx │ └── auth.txt │ └── templates │ └── home.html └── test └── java └── com └── example └── springpipelinedemo ├── SpringPipelineDemoApplicationTests.java └── service └── UserServiceTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/maven,gradle,eclipse,intellij 3 | # Edit at https://www.gitignore.io/?templates=maven,gradle,eclipse,intellij 4 | 5 | ### Eclipse ### 6 | 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .settings/ 16 | .loadpath 17 | .recommenders 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # PyDev specific (Python IDE for Eclipse) 26 | *.pydevproject 27 | 28 | # CDT-specific (C/C++ Development Tooling) 29 | .cproject 30 | 31 | # CDT- autotools 32 | .autotools 33 | 34 | # Java annotation processor (APT) 35 | .factorypath 36 | 37 | # PDT-specific (PHP Development Tools) 38 | .buildpath 39 | 40 | # sbteclipse plugin 41 | .target 42 | 43 | # Tern plugin 44 | .tern-project 45 | 46 | # TeXlipse plugin 47 | .texlipse 48 | 49 | # STS (Spring Tool Suite) 50 | .springBeans 51 | 52 | # Code Recommenders 53 | .recommenders/ 54 | 55 | # Annotation Processing 56 | .apt_generated/ 57 | 58 | # Scala IDE specific (Scala & Java development for Eclipse) 59 | .cache-main 60 | .scala_dependencies 61 | .worksheet 62 | 63 | ### Eclipse Patch ### 64 | # Eclipse Core 65 | .project 66 | 67 | # JDT-specific (Eclipse Java Development Tools) 68 | .classpath 69 | 70 | # Annotation Processing 71 | .apt_generated 72 | 73 | .sts4-cache/ 74 | 75 | ### Intellij ### 76 | .idea 77 | *.iml 78 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 79 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 80 | 81 | # User-specific stuff 82 | .idea/**/workspace.xml 83 | .idea/**/tasks.xml 84 | .idea/**/usage.statistics.xml 85 | .idea/**/dictionaries 86 | .idea/**/shelf 87 | 88 | # Generated files 89 | .idea/**/contentModel.xml 90 | 91 | # Sensitive or high-churn files 92 | .idea/**/dataSources/ 93 | .idea/**/dataSources.ids 94 | .idea/**/dataSources.local.xml 95 | .idea/**/sqlDataSources.xml 96 | .idea/**/dynamic.xml 97 | .idea/**/uiDesigner.xml 98 | .idea/**/dbnavigator.xml 99 | 100 | # Gradle 101 | .idea/**/gradle.xml 102 | .idea/**/libraries 103 | 104 | # Gradle and Maven with auto-import 105 | # When using Gradle or Maven with auto-import, you should exclude module files, 106 | # since they will be recreated, and may cause churn. Uncomment if using 107 | # auto-import. 108 | # .idea/modules.xml 109 | # .idea/*.iml 110 | # .idea/modules 111 | 112 | # CMake 113 | cmake-build-*/ 114 | 115 | # Mongo Explorer plugin 116 | .idea/**/mongoSettings.xml 117 | 118 | # File-based project format 119 | *.iws 120 | 121 | # IntelliJ 122 | out/ 123 | 124 | # mpeltonen/sbt-idea plugin 125 | .idea_modules/ 126 | 127 | # JIRA plugin 128 | atlassian-ide-plugin.xml 129 | 130 | # Cursive Clojure plugin 131 | .idea/replstate.xml 132 | 133 | # Crashlytics plugin (for Android Studio and IntelliJ) 134 | com_crashlytics_export_strings.xml 135 | crashlytics.properties 136 | crashlytics-build.properties 137 | fabric.properties 138 | 139 | # Editor-based Rest Client 140 | .idea/httpRequests 141 | 142 | # Android studio 3.1+ serialized cache file 143 | .idea/caches/build_file_checksums.ser 144 | 145 | ### Intellij Patch ### 146 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 147 | 148 | # *.iml 149 | # modules.xml 150 | # .idea/misc.xml 151 | # *.ipr 152 | 153 | # Sonarlint plugin 154 | .idea/sonarlint 155 | 156 | ### Maven ### 157 | target/ 158 | pom.xml.tag 159 | pom.xml.releaseBackup 160 | pom.xml.versionsBackup 161 | pom.xml.next 162 | release.properties 163 | dependency-reduced-pom.xml 164 | buildNumber.properties 165 | .mvn/timing.properties 166 | .mvn/wrapper/maven-wrapper.jar 167 | 168 | ### Gradle ### 169 | .gradle 170 | build/ 171 | 172 | # Ignore Gradle GUI config 173 | gradle-app.setting 174 | 175 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 176 | !gradle-wrapper.jar 177 | 178 | # Cache of project 179 | .gradletasknamecache 180 | 181 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 182 | # gradle/wrapper/gradle-wrapper.properties 183 | 184 | ### Gradle Patch ### 185 | **/build/ 186 | 187 | # End of https://www.gitignore.io/api/maven,gradle,eclipse,intellij 188 | 189 | /testResults 190 | -------------------------------------------------------------------------------- /.semaphore/docker-build.yml: -------------------------------------------------------------------------------- 1 | version: v1.0 2 | name: "Dockerize pipeline \U0001F433" 3 | agent: 4 | machine: 5 | type: e1-standard-2 6 | os_image: ubuntu2004 7 | blocks: 8 | - name: Build 9 | task: 10 | env_vars: 11 | - name: MAVEN_OPTS 12 | value: '-Dmaven.repo.local=.m2' 13 | - name: ENVIRONMENT 14 | value: dev 15 | secrets: 16 | - name: dockerhub 17 | prologue: 18 | commands: 19 | - checkout 20 | - 'cache restore spring-pipeline-build-$SEMAPHORE_GIT_BRANCH-$(checksum pom.xml),spring-pipeline-build-$SEMAPHORE_GIT_BRANCH,spring-pipeline-build' 21 | - cache restore 22 | jobs: 23 | - name: Docker build 24 | commands: 25 | - mvn -q package -Dmaven.test.skip=true 26 | - echo "$DOCKER_PASSWORD" | docker login --username "$DOCKER_USERNAME" --password-stdin 27 | - 'docker pull "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest || true' 28 | - 'docker build --cache-from "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest --build-arg ENVIRONMENT="${ENVIRONMENT}" -t "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest .' 29 | - 'docker push "$DOCKER_USERNAME"/semaphore-demo-java-spring:latest' 30 | -------------------------------------------------------------------------------- /.semaphore/semaphore.yml: -------------------------------------------------------------------------------- 1 | version: v1.0 2 | name: Java Spring example CI pipeline on Semaphore 3 | agent: 4 | machine: 5 | type: e1-standard-2 6 | os_image: ubuntu2004 7 | blocks: 8 | - name: Build 9 | task: 10 | env_vars: 11 | - name: MAVEN_OPTS 12 | value: '-Dmaven.repo.local=.m2' 13 | jobs: 14 | - name: Build 15 | commands: 16 | - sem-version java 17 17 | - checkout 18 | - cache restore 19 | - 'mvn -q package jmeter:configure -Dmaven.test.skip=true' 20 | - cache store 21 | - name: Test 22 | task: 23 | env_vars: 24 | - name: MAVEN_OPTS 25 | value: '-Dmaven.repo.local=.m2' 26 | prologue: 27 | commands: 28 | - sem-version java 17 29 | - checkout 30 | - cache restore 31 | - mvn -q test-compile -Dmaven.test.skip=true 32 | jobs: 33 | - name: Unit tests 34 | commands: 35 | - mvn test 36 | - name: Integration tests 37 | commands: 38 | - mvn test -Pintegration-testing 39 | - name: Performance tests 40 | task: 41 | env_vars: 42 | - name: MAVEN_OPTS 43 | value: '-Dmaven.repo.local=.m2' 44 | prologue: 45 | commands: 46 | - sem-version java 17 47 | - checkout 48 | - cache restore 49 | jobs: 50 | - name: Benchmark 51 | commands: 52 | - java -version 53 | - java -jar target/spring-pipeline-demo.jar > /dev/null & 54 | - sleep 20 55 | - 'mvn -q jmeter:jmeter' 56 | - 'mvn jmeter:results' 57 | promotions: 58 | - name: "Dockerize \U0001F433" 59 | pipeline_file: docker-build.yml 60 | auto_promote_on: 61 | - result: passed 62 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17-jdk-alpine 2 | ARG ENVIRONMENT 3 | ENV ENVIRONMENT ${ENVIRONMENT} 4 | COPY target/*.jar app.jar 5 | ENTRYPOINT ["java","-Dspring.profiles.active=${ENVIRONMENT}", "-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Rendered Text 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Semaphore demo CI/CD pipeline using Java Spring 2 | 3 | [![Build Status](https://semaphore-demos.semaphoreci.com/badges/semaphore-demo-java-spring/branches/master.svg)](https://semaphore-demos.semaphoreci.com/projects/semaphore-demo-java-spring) 4 | 5 | Example Spring Boot application and CI/CD pipeline showing how to run a Java 6 | project on [Semaphore 2.0](https://semaphoreci.com). 7 | 8 | ## Application overview 9 | 10 | ### Features 11 | 12 | - Simple login screen 13 | - User registration 14 | - An endpoint secured with `Basic` authentification layer 15 | - Persistence layer for storing users in database 16 | 17 | ### Endpoints 18 | 19 | - `"/admin/home"` a secured endpoint that returns a webpage in a form of `text/html`, generated with thymeleaf. [AdminController](src/main/java/com/example/springpipelinedemo/controller/AdminController.java) 20 | - `"/login"` standard spring login endpoint 21 | - `"/logout"` rest endpoint, ends user session, redirects to `"/login"` 22 | - `"/users/signup"` rest endpoint, adds a new user to the system. [UserController](src/main/java/com/example/springpipelinedemo/controller/UserController.java) 23 | 24 | ### Persistence 25 | 26 | Persistence for this project is set up using Spring Data JPA, and utilizes `m2` database, 27 | which is a runtime database for the ease of testing and continuous integration, however is fully compatible with many 28 | standard database technologies like Postgres. 29 | 30 | There is a single database entity [User](src/main/java/com/example/springpipelinedemo/model/User.java) 31 | and a corresponding repository [UserRepository](src/main/java/com/example/springpipelinedemo/repository/UserRepository.java) 32 | 33 | ### Tests 34 | 35 | Tests are separated into two classpaths (in order to run them as separate tasks): 36 | - [src/test](src/test) holds the unit tests 37 | - [src/it](src/it) holds the integration tests, in this case repository and rest endpoint tests. 38 | 39 | 40 | ## CI/CD pipeline on Semaphore 41 | 42 | The Semaphore pipeline is configured to: 43 | 44 | 1. Build the project 45 | 2. Run tests 46 | 3. Build Docker image 47 | 4. Push image to `hub.docker.com` 48 | 49 | Semaphore pipeline configuration is located at 50 | [.semaphore/semaphore.yml](.semaphore/semaphore.yml) 51 | 52 | ### Setting up 53 | 54 | To set up this pipeline on your Semaphore account: 55 | 56 | 1. If you don't have `sem` command line tool installed, do so using `curl 57 | https://storage.googleapis.com/sem-cli-releases/get.sh | bash` and then 58 | connect to your account using `sem connect .semaphoreci.com `. You can get the private 60 | key from your account dashboard at `semaphoreci.com`. 61 | 2. Add the project to Semaphore using `sem init`. 62 | 3. This pipeline relies on public Docker repository to push artifacts of successful builds. Create an account on `https://hub.docker.com/` if you don't have one. 63 | 4. Add your `hub.docker.com` credentials to `./docker-hub-secret.yml`. The credentials should remain private, so don't publish them to your Git repository by mistake. 64 | 5. Add your `./docker-hub-secret.yml` credentials to Semaphore with `sem create -f docker-hub-secret.yml` 65 | 66 | 67 | After pushing a new commit to master, Semaphore will initiate a workflow: 68 | 69 | ![alt text](assets/pipeline-result.png) 70 | 71 | 72 | ## Build configuration 73 | 74 | This project is set up using Maven. Build configuration can be found at `pom.xml`. 75 | 76 | ##### Running the project 77 | 78 | `mvn spring-boot:run` 79 | 80 | ##### Running tests 81 | 82 | Tests are separated into two classpaths: `src/test` for unit tests, and `src/it` for integration tests. 83 | 84 | To run unit tests: 85 | 86 | `mvn clean test` 87 | 88 | To run integration tests 89 | 90 | `mvn clean test -Pintegration-testing` 91 | 92 | To run performance tests 93 | 94 | `mvn clean jmeter:jmeter` 95 | 96 | ##### JMeter GUI 97 | `mvn jmeter:gui` 98 | 99 | ## License 100 | 101 | Copyright (c) 2022 Rendered Text 102 | 103 | Distributed under the MIT License. See the file LICENSE. 104 | -------------------------------------------------------------------------------- /assets/pipeline-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaphoreci-demos/semaphore-demo-java-spring/1f5532644e6dc0f50bf39ebf242ece8c27164acf/assets/pipeline-result.png -------------------------------------------------------------------------------- /docker-hub-secret.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1beta 2 | kind: Secret 3 | metadata: 4 | name: docker-hub 5 | data: 6 | env_vars: 7 | - name: DOCKER_USERNAME 8 | value: "username" 9 | - name: DOCKER_PASSWORD 10 | value: "secret" -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.2.RELEASE 9 | 10 | 11 | com.example 12 | spring-pipeline-demo 13 | 0.0.1-SNAPSHOT 14 | spring-pipeline-demo 15 | Demo project for Spring Boot 16 | jar 17 | 18 | 19 | ${project.basedir}/src/test/java 20 | 1.8 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-data-jpa 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-security 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-thymeleaf 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-web 39 | 40 | 41 | org.apache.commons 42 | commons-lang3 43 | 3.12.0 44 | 45 | 46 | com.fasterxml.jackson.core 47 | jackson-databind 48 | 49 | 50 | 51 | 52 | com.h2database 53 | h2 54 | runtime 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-test 59 | test 60 | 61 | 62 | org.assertj 63 | assertj-core 64 | 3.22.0 65 | test 66 | 67 | 68 | 69 | 70 | ${myTestSourceDirectory} 71 | spring-pipeline-demo 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-maven-plugin 76 | 77 | 78 | com.lazerycode.jmeter 79 | jmeter-maven-plugin 80 | 3.5.0 81 | 82 | 83 | configuration 84 | 85 | configure 86 | 87 | 88 | 89 | jmeter-tests 90 | 91 | jmeter 92 | 93 | 94 | 95 | jmeter-check-results 96 | 97 | results 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | integration-testing 108 | 109 | false 110 | ${project.basedir}/src/it/java 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /src/it/java/com/example/springpipelinedemo/controller/AdminControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.controller; 2 | 3 | import com.example.springpipelinedemo.SpringPipelineDemoApplication; 4 | import com.example.springpipelinedemo.model.User; 5 | import com.example.springpipelinedemo.service.UserService; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import org.apache.tomcat.util.codec.binary.Base64; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import org.springframework.boot.test.mock.mockito.MockBean; 14 | import org.springframework.http.HttpHeaders; 15 | import org.springframework.security.crypto.password.PasswordEncoder; 16 | import org.springframework.test.annotation.DirtiesContext; 17 | import org.springframework.test.context.TestPropertySource; 18 | import org.springframework.test.context.junit4.SpringRunner; 19 | import org.springframework.test.context.web.WebAppConfiguration; 20 | import org.springframework.test.web.servlet.MockMvc; 21 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 22 | import org.springframework.web.context.WebApplicationContext; 23 | 24 | import javax.servlet.Filter; 25 | 26 | import java.nio.charset.Charset; 27 | 28 | import static org.mockito.Mockito.doReturn; 29 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 30 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 31 | 32 | /** 33 | * Created by Rimantas Jacikevičius on 19.2.14. 34 | */ 35 | @RunWith(SpringRunner.class) 36 | @WebAppConfiguration 37 | @SpringBootTest(classes = SpringPipelineDemoApplication.class) 38 | @TestPropertySource(properties = "testContext=true") 39 | @DirtiesContext 40 | public class AdminControllerTest { 41 | 42 | @MockBean 43 | UserService userService; 44 | 45 | @Autowired 46 | PasswordEncoder encoder; 47 | @Autowired 48 | private WebApplicationContext webApplicationContext; 49 | @Autowired 50 | private Filter springSecurityFilterChain; 51 | 52 | private MockMvc mockMvc; 53 | private ObjectMapper objectMapper = new ObjectMapper(); 54 | 55 | @Before 56 | public void setup() throws Exception { 57 | mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) 58 | .addFilter(springSecurityFilterChain) 59 | .build(); 60 | } 61 | 62 | @Test 63 | public void home() throws Exception { 64 | String username = "hello@world.com"; 65 | String pass = "pass123"; 66 | 67 | User user = new User(username, encoder.encode(pass)); 68 | doReturn(user).when(userService).loadUserByUsername(username); 69 | 70 | String auth = username+":"+pass; 71 | 72 | String token = "Basic "+ new String(Base64.encodeBase64( 73 | auth.getBytes(Charset.forName("US-ASCII")))); 74 | 75 | mockMvc.perform( 76 | get("/admin/home") 77 | .header(HttpHeaders.AUTHORIZATION, token)) 78 | .andExpect( 79 | status().isOk()); 80 | } 81 | 82 | @Test 83 | public void home_unauthorised() throws Exception { 84 | mockMvc.perform( 85 | get("/admin/home")) 86 | .andExpect( 87 | status().isUnauthorized()); 88 | } 89 | } -------------------------------------------------------------------------------- /src/it/java/com/example/springpipelinedemo/controller/UserControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.controller; 2 | 3 | import com.example.springpipelinedemo.SpringPipelineDemoApplication; 4 | import com.example.springpipelinedemo.controller.forms.SignupForm; 5 | import com.example.springpipelinedemo.dto.UserDto; 6 | import com.example.springpipelinedemo.service.UserService; 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import org.springframework.boot.test.mock.mockito.MockBean; 14 | import org.springframework.http.MediaType; 15 | import org.springframework.test.annotation.DirtiesContext; 16 | import org.springframework.test.context.TestPropertySource; 17 | import org.springframework.test.context.junit4.SpringRunner; 18 | import org.springframework.test.context.web.WebAppConfiguration; 19 | import org.springframework.test.web.servlet.MockMvc; 20 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 21 | import org.springframework.web.context.WebApplicationContext; 22 | 23 | import javax.servlet.Filter; 24 | 25 | import static org.hamcrest.Matchers.is; 26 | import static org.mockito.Mockito.*; 27 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 28 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 29 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 30 | 31 | /** 32 | * Created by Rimantas Jacikevičius on 19.2.14. 33 | */ 34 | @RunWith(SpringRunner.class) 35 | @WebAppConfiguration 36 | @SpringBootTest(classes = SpringPipelineDemoApplication.class) 37 | @TestPropertySource(properties = "testContext=true") 38 | @DirtiesContext 39 | public class UserControllerTest { 40 | 41 | @MockBean 42 | UserService userService; 43 | 44 | @Autowired 45 | private WebApplicationContext webApplicationContext; 46 | @Autowired 47 | private Filter springSecurityFilterChain; 48 | 49 | private MockMvc mockMvc; 50 | private ObjectMapper objectMapper = new ObjectMapper(); 51 | 52 | @Before 53 | public void setup() throws Exception { 54 | mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) 55 | .addFilter(springSecurityFilterChain) 56 | .build(); 57 | } 58 | 59 | @Test 60 | public void signup() throws Exception { 61 | SignupForm form = new SignupForm("a@b.test","somepassword"); 62 | 63 | doReturn(new UserDto(form.email)).when(userService).createUser(form.email, form.password); 64 | 65 | mockMvc.perform( 66 | post("/users") 67 | .contentType(MediaType.APPLICATION_JSON) 68 | .content(objectMapper.writeValueAsBytes(form))) 69 | .andExpect( 70 | status().isOk()) 71 | .andExpect( 72 | jsonPath("$.username", is(form.email)) 73 | ); 74 | 75 | verify(userService).createUser(form.email, form.password); 76 | verifyNoMoreInteractions(userService); 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /src/it/java/com/example/springpipelinedemo/repository/UserRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.repository; 2 | 3 | import com.example.springpipelinedemo.model.User; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.dao.DataIntegrityViolationException; 10 | import org.springframework.test.context.TestPropertySource; 11 | import org.springframework.test.context.junit4.SpringRunner; 12 | 13 | import java.util.Date; 14 | import java.util.Optional; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | import static org.assertj.core.api.Assertions.fail; 18 | 19 | 20 | /** 21 | * Created by Rimantas Jacikevičius on 19.2.12. 22 | */ 23 | @RunWith(SpringRunner.class) 24 | @SpringBootTest 25 | @TestPropertySource(properties = "testContext=true") 26 | public class UserRepositoryTest { 27 | 28 | @Autowired 29 | UserRepository userRepository; 30 | 31 | @Before 32 | public void setUp() { 33 | userRepository.deleteAll(); 34 | } 35 | 36 | @Test 37 | public void save() { 38 | 39 | Date timestamp = new Date(); 40 | 41 | User user = new User("email", "pass"); 42 | User save = userRepository.save(user); 43 | 44 | assertThat(save.getId()).isNotNull(); 45 | 46 | Optional result = userRepository.findById(save.getId()); 47 | 48 | assertThat(result).isPresent(); 49 | 50 | assertThat(result.get().getEmail()).isEqualTo(user.getEmail()); 51 | assertThat(result.get().getPassword()).isEqualTo(user.getPassword()); 52 | assertThat(result.get().getId()).isEqualTo(save.getId()); 53 | assertThat(result.get().getCreatedDate()).isCloseTo(timestamp, 1000); 54 | assertThat(result.get().getModifiedDate()).isCloseTo(timestamp, 1000); 55 | } 56 | 57 | @Test(expected = DataIntegrityViolationException.class) 58 | public void save_duplicateKeys() { 59 | String email = "email"; 60 | 61 | try { 62 | userRepository.save(new User(email, "pass1")); 63 | } catch (DataIntegrityViolationException e) { 64 | fail("Not expected at this point"); 65 | } 66 | 67 | userRepository.save(new User(email, "pass2")); 68 | } 69 | 70 | 71 | @Test 72 | public void findByEmail() { 73 | String email = "email"; 74 | 75 | User user = userRepository.save(new User(email, "pass1")); 76 | 77 | Optional result = userRepository.findByEmail(email); 78 | 79 | assertThat(result).isPresent(); 80 | assertThat(result.get()).isEqualToIgnoringGivenFields(user, "createdDate", "modifiedDate"); 81 | } 82 | 83 | @Test 84 | public void findByEmail_notFound() { 85 | Optional result = userRepository.findByEmail("email"); 86 | assertThat(result).isEmpty(); 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/SpringPipelineDemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringPipelineDemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringPipelineDemoApplication.class, args); 11 | } 12 | 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/config/JpaConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 6 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 7 | 8 | /** 9 | * Created by Rimantas Jacikevičius on 19.2.12. 10 | */ 11 | @Configuration 12 | @EnableJpaAuditing 13 | public class JpaConfig { 14 | 15 | @Bean 16 | public AuditingEntityListener createAuditingListener() { 17 | return new AuditingEntityListener(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/config/UtilityConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 6 | import org.springframework.security.crypto.password.PasswordEncoder; 7 | 8 | /** 9 | * Created by Rimantas Jacikevičius on 19.2.12. 10 | */ 11 | @Configuration 12 | public class UtilityConfig { 13 | 14 | @Bean 15 | public PasswordEncoder passwordEncoder() { 16 | return new BCryptPasswordEncoder(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/config/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.config; 2 | 3 | import com.example.springpipelinedemo.service.UserService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 11 | import org.springframework.security.core.userdetails.UserDetailsService; 12 | 13 | /** 14 | * Created by Rimantas Jacikevičius on 18.12.29. 15 | */ 16 | 17 | @Configuration 18 | @EnableWebSecurity 19 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 20 | 21 | private final UserService userService; 22 | 23 | public WebSecurityConfig(UserService userService) { 24 | this.userService = userService; 25 | } 26 | 27 | @Override 28 | protected void configure(HttpSecurity http) throws Exception { 29 | http.csrf().disable() 30 | .authorizeRequests() 31 | .antMatchers("/admin/**").authenticated() 32 | .anyRequest().permitAll() 33 | .and() 34 | .formLogin() 35 | .permitAll() 36 | .and() 37 | .logout() 38 | .permitAll().and() 39 | .httpBasic(); 40 | } 41 | 42 | @Autowired 43 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 44 | auth.userDetailsService(userService); 45 | } 46 | 47 | @Bean 48 | @Override 49 | public UserDetailsService userDetailsService() { 50 | return userService; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/controller/AdminController.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.controller; 2 | 3 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 4 | import org.springframework.security.core.userdetails.UserDetails; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.ui.Model; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.servlet.ModelAndView; 10 | 11 | /** 12 | * Created by Rimantas Jacikevičius on 19.2.14. 13 | */ 14 | @Controller 15 | @RequestMapping("/admin") 16 | public class AdminController { 17 | 18 | @GetMapping("/home") 19 | public ModelAndView home(@AuthenticationPrincipal UserDetails details, Model model) { 20 | model.addAttribute("user", details); 21 | 22 | return new ModelAndView("home"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.controller; 2 | 3 | import com.example.springpipelinedemo.controller.forms.SignupForm; 4 | import com.example.springpipelinedemo.dto.UserDto; 5 | import com.example.springpipelinedemo.service.UserService; 6 | import org.springframework.web.bind.annotation.PostMapping; 7 | import org.springframework.web.bind.annotation.RequestBody; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import javax.validation.Valid; 12 | 13 | /** 14 | * Created by Rimantas Jacikevičius on 19.2.14. 15 | */ 16 | @RestController 17 | @RequestMapping("/users") 18 | public class UserController { 19 | 20 | private final UserService userService; 21 | 22 | public UserController(UserService userService) { 23 | this.userService = userService; 24 | } 25 | 26 | @PostMapping 27 | public UserDto signup(@RequestBody @Valid SignupForm signupForm) { 28 | return userService.createUser(signupForm.email, signupForm.password); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/controller/forms/SignupForm.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.controller.forms; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import javax.validation.constraints.Email; 7 | import javax.validation.constraints.NotNull; 8 | 9 | /** 10 | * Created by Rimantas Jacikevičius on 19.2.14. 11 | */ 12 | public class SignupForm { 13 | 14 | public final String email; 15 | public final String password; 16 | 17 | @JsonCreator 18 | public SignupForm( 19 | @JsonProperty("email") @Email @NotNull String email, 20 | @JsonProperty("password") @NotNull String password) { 21 | this.email = email; 22 | this.password = password; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "SignupForm{" + 28 | "email='" + email + '\'' + 29 | ", password='" + password + '\'' + 30 | '}'; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/dto/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | /** 8 | * Created by Rimantas Jacikevičius on 19.2.12. 9 | */ 10 | @JsonIgnoreProperties(ignoreUnknown = true) 11 | public class UserDto { 12 | 13 | public final String username; 14 | 15 | @JsonCreator 16 | public UserDto(@JsonProperty("username") String username) { 17 | this.username = username; 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "UserDto{" + 23 | "username='" + username + '\'' + 24 | '}'; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/dto/mapper/Mapper.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.dto.mapper; 2 | 3 | import java.util.Collection; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | /** 8 | * Created by Rimantas Jacikevičius on 18.8.8. 9 | */ 10 | public interface Mapper { 11 | 12 | To map(From from); 13 | 14 | default List map(Collection from) { 15 | if (from == null) { 16 | return null; 17 | } 18 | return from.stream().map(this::map).collect(Collectors.toList()); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/dto/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.dto.mapper; 2 | 3 | import com.example.springpipelinedemo.dto.UserDto; 4 | import com.example.springpipelinedemo.model.User; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * Created by Rimantas Jacikevičius on 19.2.12. 9 | */ 10 | @Component 11 | public class UserMapper implements Mapper { 12 | 13 | @Override 14 | public UserDto map(User user) { 15 | return new UserDto(user.getUsername()); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/model/User.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.model; 2 | 3 | import org.apache.commons.lang3.builder.EqualsBuilder; 4 | import org.apache.commons.lang3.builder.HashCodeBuilder; 5 | import org.springframework.data.annotation.CreatedDate; 6 | import org.springframework.data.annotation.LastModifiedDate; 7 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 8 | import org.springframework.security.core.GrantedAuthority; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | 11 | import javax.persistence.*; 12 | import java.util.ArrayList; 13 | import java.util.Collection; 14 | import java.util.Date; 15 | 16 | /** 17 | * Created by Rimantas Jacikevičius on 19.2.12. 18 | */ 19 | @Table(name = "users") 20 | @EntityListeners({AuditingEntityListener.class}) 21 | @Entity 22 | public class User implements UserDetails { 23 | 24 | public User() { 25 | } 26 | 27 | public User(String email, String password) { 28 | this.email = email; 29 | this.password = password; 30 | } 31 | 32 | @Id 33 | @GeneratedValue(strategy = GenerationType.AUTO) 34 | private Long id; 35 | @Column(unique = true) 36 | private String email; 37 | private String password; 38 | 39 | @CreatedDate 40 | private Date createdDate; 41 | @LastModifiedDate 42 | private Date modifiedDate; 43 | 44 | public Long getId() { 45 | return id; 46 | } 47 | 48 | public void setId(Long id) { 49 | this.id = id; 50 | } 51 | 52 | public String getEmail() { 53 | return email; 54 | } 55 | 56 | public void setEmail(String email) { 57 | this.email = email; 58 | } 59 | 60 | public String getPassword() { 61 | return password; 62 | } 63 | 64 | // getAuthorities() { 68 | // TODO 69 | return new ArrayList<>(); 70 | } 71 | 72 | @Override 73 | public String getUsername() { 74 | return email; 75 | } 76 | 77 | @Override 78 | public boolean isAccountNonExpired() { 79 | // TODO 80 | return true; 81 | } 82 | 83 | @Override 84 | public boolean isAccountNonLocked() { 85 | // TODO 86 | return true; 87 | } 88 | 89 | @Override 90 | public boolean isCredentialsNonExpired() { 91 | // TODO 92 | return true; 93 | } 94 | 95 | @Override 96 | public boolean isEnabled() { 97 | // TODO 98 | return true; 99 | } 100 | 101 | // 102 | 103 | public void setPassword(String password) { 104 | this.password = password; 105 | } 106 | 107 | public Date getCreatedDate() { 108 | return createdDate; 109 | } 110 | 111 | public void setCreatedDate(Date createdDate) { 112 | this.createdDate = createdDate; 113 | } 114 | 115 | public Date getModifiedDate() { 116 | return modifiedDate; 117 | } 118 | 119 | public void setModifiedDate(Date modifiedDate) { 120 | this.modifiedDate = modifiedDate; 121 | } 122 | 123 | @Override 124 | public boolean equals(Object o) { 125 | if (this == o) return true; 126 | 127 | if (o == null || getClass() != o.getClass()) return false; 128 | 129 | User user = (User) o; 130 | 131 | return new EqualsBuilder() 132 | .append(createdDate, user.createdDate) 133 | .append(modifiedDate, user.modifiedDate) 134 | .append(id, user.id) 135 | .append(email, user.email) 136 | .append(password, user.password) 137 | .isEquals(); 138 | } 139 | 140 | @Override 141 | public int hashCode() { 142 | return new HashCodeBuilder(17, 37) 143 | .append(id) 144 | .append(email) 145 | .append(password) 146 | .append(createdDate) 147 | .append(modifiedDate) 148 | .toHashCode(); 149 | } 150 | 151 | @Override 152 | public String toString() { 153 | return "User{" + 154 | "id='" + id + '\'' + 155 | ", email='" + email + '\'' + 156 | ", password='" + password + '\'' + 157 | ", createdDate=" + createdDate + 158 | ", modifiedDate=" + modifiedDate + 159 | '}'; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.repository; 2 | 3 | import com.example.springpipelinedemo.model.User; 4 | import org.springframework.data.repository.CrudRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.Optional; 8 | 9 | /** 10 | * Created by Rimantas Jacikevičius on 19.2.12. 11 | */ 12 | @Repository 13 | public interface UserRepository extends CrudRepository { 14 | 15 | Optional findByEmail(String email); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.service; 2 | 3 | import com.example.springpipelinedemo.dto.UserDto; 4 | import com.example.springpipelinedemo.dto.mapper.UserMapper; 5 | import com.example.springpipelinedemo.model.User; 6 | import com.example.springpipelinedemo.repository.UserRepository; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.security.core.userdetails.UserDetailsService; 9 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 10 | import org.springframework.security.crypto.password.PasswordEncoder; 11 | import org.springframework.stereotype.Service; 12 | 13 | import static org.springframework.util.Assert.notNull; 14 | 15 | /** 16 | * Created by Rimantas Jacikevičius on 19.2.12. 17 | */ 18 | @Service 19 | public class UserService implements UserDetailsService { 20 | 21 | private final UserRepository userRepository; 22 | private final PasswordEncoder passwordEncoder; 23 | private final UserMapper userMapper; 24 | 25 | public UserService(UserRepository userRepository, 26 | PasswordEncoder passwordEncoder, 27 | UserMapper userMapper) { 28 | this.userRepository = userRepository; 29 | this.passwordEncoder = passwordEncoder; 30 | this.userMapper = userMapper; 31 | } 32 | 33 | @Override 34 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 35 | return userRepository.findByEmail(username).orElseThrow( 36 | () -> new UsernameNotFoundException(String.format("User '%s' was not found", username))); 37 | } 38 | 39 | public UserDto createUser(String email, String password) { 40 | notNull(email, "User must have an email"); 41 | notNull(password, "User must have a password"); 42 | 43 | User user = userRepository.save(new User(email, passwordEncoder.encode(password))); 44 | return userMapper.map(user); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/example/springpipelinedemo/tasks/InitializeDefaultUser.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.tasks; 2 | 3 | import com.example.springpipelinedemo.service.UserService; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 6 | import org.springframework.boot.context.event.ApplicationReadyEvent; 7 | import org.springframework.context.ApplicationListener; 8 | import org.springframework.stereotype.Component; 9 | 10 | /** 11 | * Created by Rimantas Jacikevičius on 19.2.12. 12 | */ 13 | @Component 14 | @ConditionalOnExpression("not ${testContext:false}") 15 | public class InitializeDefaultUser implements ApplicationListener { 16 | 17 | private final UserService userService; 18 | private final String defaultUsername; 19 | private final String defaultPassword; 20 | 21 | public InitializeDefaultUser(UserService userService, 22 | @Value("${defaultUser}") String defaultUsername, 23 | @Value("${defaultPassword}") String defaultPassword) { 24 | this.userService = userService; 25 | this.defaultUsername = defaultUsername; 26 | this.defaultPassword = defaultPassword; 27 | } 28 | 29 | @Override 30 | public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { 31 | userService.createUser(defaultUsername, defaultPassword); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | defaultUser=test@email.com 2 | defaultPassword=testPass123 -------------------------------------------------------------------------------- /src/main/resources/jmeter/HTTP Request.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | continue 16 | 17 | false 18 | 10 19 | 20 | 100 21 | 10 22 | 1550154649000 23 | 1550154649000 24 | false 25 | 26 | 27 | 28 | 29 | 30 | localhost 31 | 8080 32 | 33 | 34 | 35 | false 36 | false 37 | false 38 | 39 | 40 | 41 | 42 | 43 | http://localhost:8080/admin/home 44 | test@email.com 45 | testPass123 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | localhost 57 | 8080 58 | 59 | 60 | /admin/home 61 | GET 62 | true 63 | false 64 | true 65 | false 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 200 74 | 75 | Assertion.response_code 76 | false 77 | 1 78 | 79 | 80 | 81 | false 82 | 83 | saveConfig 84 | 85 | 86 | true 87 | true 88 | true 89 | 90 | true 91 | true 92 | true 93 | true 94 | false 95 | true 96 | true 97 | false 98 | false 99 | false 100 | true 101 | false 102 | false 103 | false 104 | true 105 | 0 106 | true 107 | true 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | false 119 | 120 | saveConfig 121 | 122 | 123 | true 124 | true 125 | true 126 | 127 | true 128 | true 129 | true 130 | true 131 | false 132 | true 133 | true 134 | false 135 | false 136 | false 137 | true 138 | false 139 | false 140 | false 141 | true 142 | 0 143 | true 144 | true 145 | true 146 | true 147 | true 148 | 149 | 150 | 151 | 152 | 153 | 154 | false 155 | 156 | saveConfig 157 | 158 | 159 | true 160 | true 161 | true 162 | 163 | true 164 | true 165 | true 166 | true 167 | false 168 | true 169 | true 170 | false 171 | false 172 | false 173 | true 174 | false 175 | false 176 | false 177 | true 178 | 0 179 | true 180 | true 181 | true 182 | true 183 | true 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | true 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /src/main/resources/jmeter/auth.txt: -------------------------------------------------------------------------------- 1 | # JMeter generated Authorization file 2 | http://localhost:8080/admin/home test@email.com BASIC_DIGEST 3 | -------------------------------------------------------------------------------- /src/main/resources/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |
7 |

8 | Welcome ''! 9 |

10 |
11 | 12 |
-------------------------------------------------------------------------------- /src/test/java/com/example/springpipelinedemo/SpringPipelineDemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringPipelineDemoApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/test/java/com/example/springpipelinedemo/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.example.springpipelinedemo.service; 2 | 3 | import com.example.springpipelinedemo.dto.UserDto; 4 | import com.example.springpipelinedemo.model.User; 5 | import com.example.springpipelinedemo.repository.UserRepository; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.mockito.ArgumentCaptor; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.boot.test.mock.mockito.MockBean; 12 | import org.springframework.security.core.userdetails.UserDetails; 13 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 14 | import org.springframework.security.crypto.password.PasswordEncoder; 15 | import org.springframework.test.annotation.DirtiesContext; 16 | import org.springframework.test.context.TestPropertySource; 17 | import org.springframework.test.context.junit4.SpringRunner; 18 | 19 | import java.util.Optional; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.mockito.Mockito.*; 23 | 24 | /** 25 | * Created by Rimantas Jacikevičius on 19.2.14. 26 | */ 27 | @RunWith(SpringRunner.class) 28 | @SpringBootTest 29 | @DirtiesContext 30 | @TestPropertySource(properties = "testContext=true") 31 | public class UserServiceTest { 32 | 33 | @MockBean 34 | UserRepository userRepository; 35 | 36 | @Autowired 37 | UserService userService; 38 | @Autowired 39 | PasswordEncoder encoder; 40 | 41 | @Test 42 | public void findByName() { 43 | String name = "name"; 44 | 45 | User user = mock(User.class); 46 | doReturn(Optional.of(user)).when(userRepository).findByEmail(name); 47 | 48 | UserDetails result = userService.loadUserByUsername(name); 49 | 50 | assertThat(user).isEqualTo(result); 51 | 52 | verify(userRepository).findByEmail(name); 53 | verifyNoMoreInteractions(userRepository); 54 | } 55 | 56 | @Test(expected = UsernameNotFoundException.class) 57 | public void findByNameNotFound() { 58 | 59 | doReturn(Optional.empty()).when(userRepository).findByEmail(any()); 60 | 61 | userService.loadUserByUsername("name"); 62 | } 63 | 64 | @Test 65 | public void createUser() { 66 | String email = "email"; 67 | String password = "pass"; 68 | 69 | User user = mock(User.class); 70 | doReturn(email).when(user).getEmail(); 71 | doReturn(email).when(user).getUsername(); 72 | doReturn(user).when(userRepository).save(any()); 73 | 74 | UserDto result = userService.createUser(email, password); 75 | 76 | assertThat(result.username).isEqualTo(email); 77 | 78 | ArgumentCaptor captor = ArgumentCaptor.forClass(User.class); 79 | 80 | verify(userRepository).save(captor.capture()); 81 | verifyNoMoreInteractions(userRepository); 82 | 83 | User captured = captor.getValue(); 84 | assertThat(captured.getEmail()).isEqualTo(email); 85 | assertThat(encoder.matches(password,captured.getPassword())).isTrue(); 86 | 87 | } 88 | 89 | } --------------------------------------------------------------------------------