Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Добавить checker framework и github action для юнит тестов #541

Draft
wants to merge 17 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Unit Tests

on:
workflow_dispatch:
pull_request:
branches:
- 'master'
- 'develop'
push:
branches:
- 'master'
- 'develop'

jobs:
tests:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: '22'
distribution: 'liberica'
cache: maven

- name: Maven Tests
run: mvn --batch-mode clean test

- name: Test Coverage
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

- name: SonarCloud Analyze
run: >
mvn --batch-mode sonar:sonar
-Dsonar.projectKey=spacious-team_investbook
-Dsonar.organization=spacious-team
-Dsonar.host.url=https://sonarcloud.io
-Dsonar.login=$SONAR_TOKEN
-Dsonar.coverage.jacoco.xmlReportPaths=./target/site/jacoco/jacoco.xml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
[![spring-boot-version](https://img.shields.io/badge/spring--boot-3.3.4-brightgreen?style=flat-square)](https://github.com/spring-projects/spring-boot/releases)
[![hits-of-code](https://img.shields.io/badge/dynamic/json?style=flat-square&color=lightblue&label=hits-of-code&url=https://hitsofcode.com/github/spacious-team/investbook/json?branch=develop&query=$.count)](https://hitsofcode.com/github/spacious-team/investbook/view?branch=develop)
[![github-closed-pull-requests](https://img.shields.io/github/issues-pr-closed/spacious-team/investbook?style=flat-square&color=brightgreen)](https://github.com/spacious-team/investbook/pulls?q=is%3Apr+is%3Aclosed)
[![Unit tests](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fspacious-team%2Finvestbook%2Fbadge%3Fref%3Ddevelop&style=flat-square&label=test&logo=none)](
https://github.com/spacious-team/investbook/actions/workflows/unit-tests.yml)
[![github-workflow-status](https://img.shields.io/github/actions/workflow/status/spacious-team/investbook/publish-docker.yml?style=flat-square&branch=master)](https://github.com/spacious-team/investbook/actions/workflows/publish-docker.yml)
[![github-all-releases](https://img.shields.io/github/downloads/spacious-team/investbook/total?style=flat-square&logo=github&color=lightblue)](https://github.com/spacious-team/investbook/releases/latest)
[![docker-pulls](https://img.shields.io/docker/pulls/spaciousteam/investbook?style=flat-square&logo=docker&color=lightblue&logoColor=white)](https://hub.docker.com/r/spaciousteam/investbook)
Expand Down
113 changes: 112 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@

<properties>
<!-- Valid version is (0-255).(0-255).(0-65535) -->
<win.msi.version>24.2</win.msi.version>
<win.msi.version>24.2</win.msi.version> <!-- TODO: remove dependencyManagement table-wrapper-api before release -->
<java.version>22</java.version>
<lombok.version>1.18.34</lombok.version>
<checkerframework.version>3.48.1</checkerframework.version>
</properties>

<repositories>
Expand All @@ -89,6 +91,12 @@
<!-- version is same as for hibernate-jpamodelgen dep, just scope is changed -->
<scope>compile</scope>
</dependency>
<!-- TODO: remove before release -->
<dependency>
<groupId>com.github.spacious-team</groupId>
<artifactId>table-wrapper-api</artifactId>
<version>f4572e7848</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down Expand Up @@ -209,6 +217,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
Expand All @@ -222,10 +231,92 @@
<artifactId>poi-scratchpad</artifactId>
<version>5.2.5</version> <!-- use same version as table-wrapper-spring-boot-starter provides -->
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>${checkerframework.version}</version>
<scope>provided</scope> <!-- for static analysis tools only -->
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
<version>3.17.1</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.1</version> <!-- JUnit 5 requirement -->
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<fork>true</fork> <!-- Must fork or else JVM arguments are ignored. -->
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>${hibernate.version}</version>
</path>
<path>
<groupId>org.checkerframework</groupId>
<artifactId>checker</artifactId>
<version>${checkerframework.version}</version>
</path>
</annotationProcessorPaths>
<annotationProcessors>
<annotationProcessor>
lombok.launch.AnnotationProcessorHider$AnnotationProcessor
</annotationProcessor>
<annotationProcessor>
org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor
</annotationProcessor>
<annotationProcessor>
org.checkerframework.checker.nullness.NullnessChecker
</annotationProcessor>
</annotationProcessors>
<compilerArgs combine.children="append">
<arg>-Awarns</arg> <!-- TODO Comment after warns fix-->
<arg>-AskipDefs=.*Test</arg>
<arg>-AskipDefs=V2022_1_0_1</arg>
<arg>-AsuppressWarnings=type.anno.before.modifier</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
Expand Down Expand Up @@ -287,6 +378,26 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -93,7 +94,7 @@ public boolean insert(Pojo object) {
log.error("Can't INSERT by optimized deprecated Hibernate method save(): {}", object, e);
}
}
Boolean result = transactionTemplateRequired.execute(_ -> createIfAbsent(object));
@Nullable Boolean result = transactionTemplateRequired.execute(_ -> createIfAbsent(object));
return Boolean.TRUE.equals(result);
}

Expand Down Expand Up @@ -123,14 +124,14 @@ public CreateResult<Pojo> createIfAbsentAndGet(Pojo object) {
* @implSpec Should be called in transaction
*/
private Optional<Entity> createIfAbsentInternal(Pojo object) {
Entity entity = null;
@Nullable Entity entity = null;
if (repository instanceof ConstraintAwareRepository<Entity, ID> caRepository) {
entity = converter.toEntity(object);
if (caRepository.exists(entity)) {
return Optional.empty();
}
} else {
ID id = getId(object);
@Nullable ID id = getId(object);
if (id != null && existsById(id)) {
return Optional.empty();
}
Expand All @@ -153,13 +154,13 @@ private Optional<Entity> createIfAbsentInternal(Pojo object) {
* @implSpec Should be called in transaction
*/
private CreateResult<Pojo> createIfAbsentAndGetInternal(Pojo object) {
Entity entity = null;
@Nullable Entity entity = null;
Optional<Entity> selectedEntity;
if (repository instanceof ConstraintAwareRepository<Entity, ID> caRepository) {
entity = converter.toEntity(object);
selectedEntity = caRepository.findBy(entity);
} else {
ID id = getId(object);
@Nullable ID id = getId(object);
selectedEntity = Optional.ofNullable(id)
.flatMap(repository::findById);
}
Expand Down
33 changes: 20 additions & 13 deletions src/main/java/ru/investbook/api/AbstractRestController.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import jakarta.persistence.GeneratedValue;
import lombok.SneakyThrows;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -32,9 +33,12 @@

import java.net.URI;
import java.util.Objects;
import java.util.Optional;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static org.springframework.http.HttpStatus.CREATED;

public abstract class AbstractRestController<ID, Pojo, Entity> extends AbstractEntityRepositoryService<ID, Pojo, Entity> {

Expand Down Expand Up @@ -73,7 +77,7 @@ protected ResponseEntity<Void> post(Pojo object) {
CreateResult<Pojo> result = createIfAbsentAndGet(object);
Pojo savedObject = result.object();
if (result.created()) {
return createResponseWithLocationHeader(savedObject);
return createResponseWithOptionalLocationHeader(savedObject);
} else {
return createConflictResponse(savedObject);
}
Expand All @@ -85,10 +89,8 @@ protected ResponseEntity<Void> post(Pojo object) {
@NonNull
private ResponseEntity<Void> createConflictResponse(Pojo object) {
ResponseEntity.BodyBuilder response = ResponseEntity.status(HttpStatus.CONFLICT);
if (getId(object) != null) {
URI locationURI = getLocationURI(object);
response.location(locationURI);
}
getLocationURI(object)
.ifPresent(response::location);
return response.build();
}

Expand All @@ -106,14 +108,14 @@ private ResponseEntity<Void> createConflictResponse(Pojo object) {
@Transactional
public ResponseEntity<Void> put(ID id, Pojo object) {
try {
ID objectId = getId(object);
@Nullable ID objectId = getId(object);
if (nonNull(objectId) && !Objects.equals(id, objectId)) {
throw new BadRequestException("Идентификатор объекта, переданный в URI [" + id + "] и в теле " +
"запроса [" + objectId + "] не совпадают");
}
Pojo objectWithId = nonNull(objectId) ? object : updateId(id, object);
return createAndGetIfAbsent(objectWithId)
.map(this::createResponseWithLocationHeader)
.map(this::createResponseWithOptionalLocationHeader)
.orElseGet(() -> {
createOrUpdate(objectWithId);
return ResponseEntity.noContent().build();
Expand All @@ -134,16 +136,21 @@ public ResponseEntity<Void> delete(ID id) {
/**
* @return response entity with http CREATE status, Location http header and body
*/
private ResponseEntity<Void> createResponseWithLocationHeader(Pojo object) {
URI locationURI = getLocationURI(object);
return ResponseEntity
.created(locationURI)
private ResponseEntity<Void> createResponseWithOptionalLocationHeader(Pojo object) {
return getLocationURI(object)
.map(ResponseEntity::created)
.orElseGet(() -> ResponseEntity.status(CREATED))
.build();
}

@SneakyThrows
protected URI getLocationURI(Pojo object) {
return new URI(UriUtils.encodePath(getLocation() + "/" + getId(object), UTF_8));
protected Optional<URI> getLocationURI(Pojo object) {
@Nullable ID id = getId(object);
if (isNull(id)) {
return Optional.empty();
}
URI uri = new URI(UriUtils.encodePath(getLocation() + "/" + id, UTF_8));
return Optional.of(uri);
}

protected abstract String getLocation();
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/ru/investbook/api/EntityRepositoryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@

package ru.investbook.api;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.Optional;

public interface EntityRepositoryService<ID, Pojo> {

@Nullable
ID getId(Pojo object);

boolean existsById(ID id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spacious_team.broker.pojo.EventCashFlow;
import org.springdoc.core.converters.models.PageableAsQueryParam;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -84,7 +85,7 @@ public ResponseEntity<EventCashFlow> get(@PathVariable("id")
@ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)),
@ApiResponse(responseCode = "409"),
@ApiResponse(responseCode = "500", content = @Content)})
public ResponseEntity<Void> post(@Valid @RequestBody EventCashFlow event) {
public ResponseEntity<Void> post(@RequestBody @Valid EventCashFlow event) {
return super.post(event);
}

Expand All @@ -97,8 +98,8 @@ public ResponseEntity<Void> post(@Valid @RequestBody EventCashFlow event) {
public ResponseEntity<Void> put(@PathVariable("id")
@Parameter(description = "Номер события")
Integer id,
@Valid
@RequestBody
@Valid
EventCashFlow event) {
return super.put(id, event);
}
Expand All @@ -115,7 +116,7 @@ public ResponseEntity<Void> delete(@PathVariable("id")
}

@Override
public Integer getId(EventCashFlow object) {
public @Nullable Integer getId(EventCashFlow object) {
return object.getId();
}

Expand Down
Loading
Loading