14 Commits

Author SHA1 Message Date
hlaeja
7e89e799ee [RELEASE] - Bump version 2025-08-18 11:00:19 +00:00
hlaeja
f3f10dcfc8 [RELEASE] - Release version: 0.7.0 2025-08-18 11:00:17 +00:00
119d14eb46 add nodes endpoint
- add NodesController
  - add NodesEndpoint
  - add NodesControllerTest
  - add NodesController
  - add nodes.http

- add NodeEntity toNodesResponse in Mapping.kt

- add getNodes to NodeService

- add findAll to NodeRepository
2025-08-18 12:59:14 +02:00
19aa9c8b6b Extract default page and size 2025-08-18 12:59:14 +02:00
14d36f21db add devices by type endpoint
- update DevicesController

  - update DevicesEndpoint
  - update DevicesControllerTest
  - add getDevicesByType to DevicesController
  - update devices.http

- add getDevicesByType to DeviceService

- add findAllByType to DeviceRepository
2025-08-18 12:59:14 +02:00
6f44c05330 add devices endpoint
- fix missing coroutine in
  - TypeRepository
  - TypesController
  - TypeService
  - TypesControllerTest
  - TypeServiceTest
- add DevicesEndpoint
- add DevicesControllerTest
- add devices.http
- add DevicesController
- add DeviceEntity.toDevicesResponse() to Mapping.kt
- add PostgresTestContainer to DeviceService
- update DeviceRepository with find all
- update version catalog
  - update container annotation in DeviceEndpoint
  - update container annotation in IdentityEndpoint
  - update container annotation in NodeEndpoint
  - update container annotation in TypeEndpoint
  - update container annotation in TypesEndpoint
  - update version in gradle.properties
2025-08-18 12:59:14 +02:00
hlaeja
fc95f5d4b8 [RELEASE] - Bump version 2025-07-29 18:14:37 +00:00
hlaeja
83bce6d873 [RELEASE] - Release version: 0.6.0 2025-07-29 18:14:35 +00:00
4393d160e1 update gradlew 2025-07-29 20:13:59 +02:00
5d735ccfbe update project 2025-07-29 20:13:59 +02:00
cf8ca86c32 add GitHub action
- update release in README.md
- add action run checks
- add action release
- remove release.sh
2025-07-29 20:13:59 +02:00
58848e5b54 add actuator.http 2025-07-29 20:13:59 +02:00
59dd6286fe update sql files 2025-07-29 20:13:59 +02:00
9e9dc0fef6 [RELEASE] - bump version 2025-04-05 12:55:27 +02:00
50 changed files with 705 additions and 178 deletions

12
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
name: Release
on:
workflow_dispatch:
jobs:
release:
uses: swordsteel/hlaeja-common-workflows/.github/workflows/release.yml@master
secrets:
CI_BOT_PAT: ${{ secrets.CI_BOT_PAT }}
with:
TYPE: service

12
.github/workflows/run-checks.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
name: Pull request validation
on:
pull_request:
paths-ignore:
- '.github/**'
jobs:
validate:
uses: swordsteel/hlaeja-common-workflows/.github/workflows/run-checks.yml@master
secrets:
CI_BOT_PAT: ${{ secrets.CI_BOT_PAT }}

View File

@@ -16,7 +16,7 @@ Classes crafted, identities bestowed, Each device recorded, their functions unfo
## Releasing Service ## Releasing Service
Run `release.sh` script from `master` branch. Run release pipeline from `master` branch.
## Development Information ## Development Information

View File

@@ -1,10 +1,10 @@
plugins { plugins {
alias(hlaeja.plugins.kotlin.jvm) alias(hlaeja.plugins.kotlin.jvm)
alias(hlaeja.plugins.kotlin.spring) alias(hlaeja.plugins.kotlin.spring)
alias(hlaeja.plugins.ltd.hlaeja.plugin.certificate) alias(hlaeja.plugins.spring.boot)
alias(hlaeja.plugins.ltd.hlaeja.plugin.service)
alias(hlaeja.plugins.spring.dependency.management) alias(hlaeja.plugins.spring.dependency.management)
alias(hlaeja.plugins.springframework.boot) alias(hlaeja.plugins.certificate)
alias(hlaeja.plugins.service)
} }
dependencies { dependencies {
@@ -31,14 +31,14 @@ dependencies {
testRuntimeOnly(hlaeja.junit.platform.launcher) testRuntimeOnly(hlaeja.junit.platform.launcher)
integrationTestImplementation(hlaeja.assertj.core) testIntegrationImplementation(hlaeja.assertj.core)
integrationTestImplementation(hlaeja.library.test) testIntegrationImplementation(hlaeja.library.test)
integrationTestImplementation(hlaeja.projectreactor.reactor.test) testIntegrationImplementation(hlaeja.projectreactor.reactor.test)
integrationTestImplementation(hlaeja.kotlin.test.junit5) testIntegrationImplementation(hlaeja.kotlin.test.junit5)
integrationTestImplementation(hlaeja.kotlinx.coroutines.test) testIntegrationImplementation(hlaeja.kotlinx.coroutines.test)
integrationTestImplementation(hlaeja.springboot.starter.test) testIntegrationImplementation(hlaeja.springboot.starter.test)
integrationTestRuntimeOnly(hlaeja.junit.platform.launcher) testIntegrationRuntimeOnly(hlaeja.junit.platform.launcher)
} }
group = "ltd.hlaeja" group = "ltd.hlaeja"

View File

@@ -1,4 +1,4 @@
kotlin.code.style=official kotlin.code.style=official
version=0.5.0 version=0.8.0-SNAPSHOT
catalog=0.10.0 catalog=0.12.0
container.port.host=9010 container.port.host=9010

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

9
gradlew vendored
View File

@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -115,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -206,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped. # and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line. # treated as '${Hostname}' itself on the command line.
@@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@" "$@"
# Stop when "xargs" is not available. # Stop when "xargs" is not available.

4
gradlew.bat vendored
View File

@@ -70,11 +70,11 @@ goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell

5
http/actuator.http Normal file
View File

@@ -0,0 +1,5 @@
### get actuator
GET {{hostname}}/actuator
### get actuator health
GET {{hostname}}/actuator/health

17
http/devices.http Normal file
View File

@@ -0,0 +1,17 @@
### get all types
GET {{hostname}}/devices
### get all types
GET {{hostname}}/devices/page-1
### get all types
GET {{hostname}}/devices/page-1/show-2
### get all types
GET {{hostname}}/devices/type-00000000-0000-0000-0000-000000000000
### get all types
GET {{hostname}}/devices/type-00000000-0000-0000-0000-000000000000/page-1
### get all types
GET {{hostname}}/devices/type-00000000-0000-0000-0000-000000000000/page-1/show-2

8
http/nodes.http Normal file
View File

@@ -0,0 +1,8 @@
### get all types
GET {{hostname}}/nodes
### get all types
GET {{hostname}}/nodes/page-1
### get all types
GET {{hostname}}/nodes/page-1/show-2

View File

@@ -1,101 +0,0 @@
#!/bin/sh
### This should be a pipeline, but for this example let use this ###
check_active_branch() {
if [ "$(git rev-parse --abbrev-ref HEAD)" != "$1" ]; then
echo "Error: The current branch is not $1."
exit 1
fi
}
check_uncommitted_changes() {
if [ -n "$(git status --porcelain)" ]; then
echo "Error: There are uncommitted changes in the repository."
exit 1
fi
}
prepare_environment() {
git fetch origin
}
check_last_commit() {
last_commit_message=$(git log -1 --pretty=format:%s)
if [ "$last_commit_message" = "[RELEASE] - bump version" ]; then
echo "Warning: Nothing to release!!!"
exit 1
fi
}
check_differences() {
if ! git diff --quiet origin/"$1" "$1"; then
echo "Error: The branches origin/$1 and $1 have differences."
exit 1
fi
}
un_snapshot_version() {
sed -i "s/\($1\s*=\s*[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/" gradle.properties
}
current_version() {
awk -F '=' '/version\s*=\s*[0-9.]*/ {gsub(/^ +| +$/,"",$2); print $2}' gradle.properties
}
stage_files() {
for file in "$@"; do
if git diff --exit-code --quiet -- "$file"; then
echo "No changes in $file"
else
git add "$file"
echo "Changes in $file staged for commit"
fi
done
}
commit_change() {
stage_files gradle.properties
git commit -m "[RELEASE] - $1"
git push --porcelain origin master
}
add_release_tag() {
gitTag="v$(current_version)"
git tag -a "$gitTag" -m "Release version $gitTag"
git push --porcelain origin "$gitTag"
}
snapshot_version() {
new_version="$(current_version | awk -F '.' '{print $1 "." $2+1 ".0"}')"
sed -i "s/\(version\s*=\s*\)[0-9.]*/\1$new_version-SNAPSHOT/" gradle.properties
}
handle_sql_files() {
version=$(current_version)
sql_dir="sql"
version_dir="${sql_dir}/v${version}"
if [ -d "$sql_dir" ] && [ -n "$(ls -A $sql_dir/*.sql 2>/dev/null)" ]; then
mkdir -p "$version_dir"
mv "$sql_dir"/*.sql "$version_dir/"
git add "$sql_dir"
fi
}
# check and prepare for release
check_active_branch master
check_uncommitted_changes
prepare_environment
check_last_commit
check_differences master
# un-snapshot version for release
un_snapshot_version version
un_snapshot_version catalog
# release changes and prepare for next release
commit_change "release version: $(current_version)"
add_release_tag
handle_sql_files
snapshot_version
commit_change 'bump version'

View File

@@ -56,10 +56,12 @@ CREATE DATABASE device_registry
WITH WITH
OWNER = role_administrator OWNER = role_administrator
ENCODING = 'UTF8' ENCODING = 'UTF8'
LC_COLLATE = 'en_US.utf8' LC_COLLATE = 'en_US.UTF-8'
LC_CTYPE = 'en_US.utf8' LC_CTYPE = 'en_US.UTF-8'
LOCALE_PROVIDER = 'libc' LOCALE_PROVIDER = 'libc'
TABLESPACE = pg_default TABLESPACE = pg_default
CONNECTION LIMIT = -1 CONNECTION LIMIT = -1
IS_TEMPLATE = False; IS_TEMPLATE = False;
COMMENT ON DATABASE device_registry
IS 'Database for managing device types, registered devices, and their deployment as nodes.';

View File

@@ -0,0 +1,43 @@
package ltd.hlaeja.controller
import jakarta.validation.constraints.Min
import java.util.UUID
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import ltd.hlaeja.library.deviceRegistry.Devices
import ltd.hlaeja.service.DeviceService
import ltd.hlaeja.util.Pagination.DEFAULT_PAGE
import ltd.hlaeja.util.Pagination.DEFAULT_SIZE
import ltd.hlaeja.util.toDevicesResponse
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
@RestController
class DevicesController(
private val deviceService: DeviceService,
) {
@GetMapping(
"/devices",
"/devices/page-{page}",
"/devices/page-{page}/show-{show}",
)
suspend fun getDevices(
@PathVariable(required = false) @Min(1) page: Int = DEFAULT_PAGE,
@PathVariable(required = false) @Min(1) show: Int = DEFAULT_SIZE,
): Flow<Devices.Response> = deviceService.getDevices((page - 1) * show, show)
.map { it.toDevicesResponse() }
@GetMapping(
"/devices/type-{type}",
"/devices/type-{type}/page-{page}",
"/devices/type-{type}/page-{page}/show-{show}",
)
suspend fun getDevicesByType(
@PathVariable type: UUID,
@PathVariable(required = false) @Min(1) page: Int = DEFAULT_PAGE,
@PathVariable(required = false) @Min(1) show: Int = DEFAULT_SIZE,
): Flow<Devices.Response> = deviceService.getDevicesByType(type, (page - 1) * show, show)
.map { it.toDevicesResponse() }
}

View File

@@ -0,0 +1,30 @@
package ltd.hlaeja.controller
import jakarta.validation.constraints.Min
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import ltd.hlaeja.library.deviceRegistry.Nodes
import ltd.hlaeja.service.NodeService
import ltd.hlaeja.util.Pagination.DEFAULT_PAGE
import ltd.hlaeja.util.Pagination.DEFAULT_SIZE
import ltd.hlaeja.util.toNodesResponse
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
@RestController
class NodesController(
private val service: NodeService,
) {
@GetMapping(
"/nodes",
"/nodes/page-{page}",
"/nodes/page-{page}/show-{show}",
)
suspend fun getNodes(
@PathVariable(required = false) @Min(1) page: Int = DEFAULT_PAGE,
@PathVariable(required = false) @Min(1) show: Int = DEFAULT_SIZE,
): Flow<Nodes.Response> = service.getNodes((page - 1) * show, show)
.map { it.toNodesResponse() }
}

View File

@@ -5,6 +5,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import ltd.hlaeja.library.deviceRegistry.Types import ltd.hlaeja.library.deviceRegistry.Types
import ltd.hlaeja.service.TypeService import ltd.hlaeja.service.TypeService
import ltd.hlaeja.util.Pagination.DEFAULT_PAGE
import ltd.hlaeja.util.Pagination.DEFAULT_SIZE
import ltd.hlaeja.util.toTypesResponse import ltd.hlaeja.util.toTypesResponse
import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PathVariable
@@ -14,10 +16,6 @@ import org.springframework.web.bind.annotation.RestController
class TypesController( class TypesController(
private val service: TypeService, private val service: TypeService,
) { ) {
companion object {
const val DEFAULT_PAGE: Int = 1
const val DEFAULT_SIZE: Int = 25
}
@GetMapping( @GetMapping(
"/types", "/types",
@@ -27,7 +25,7 @@ class TypesController(
"/types/filter-{filter}/page-{page}", "/types/filter-{filter}/page-{page}",
"/types/filter-{filter}/page-{page}/show-{show}", "/types/filter-{filter}/page-{page}/show-{show}",
) )
fun getTypes( suspend fun getTypes(
@PathVariable(required = false) @Min(1) page: Int = DEFAULT_PAGE, @PathVariable(required = false) @Min(1) page: Int = DEFAULT_PAGE,
@PathVariable(required = false) @Min(1) show: Int = DEFAULT_SIZE, @PathVariable(required = false) @Min(1) show: Int = DEFAULT_SIZE,
@PathVariable(required = false) filter: String? = null, @PathVariable(required = false) filter: String? = null,

View File

@@ -1,9 +1,26 @@
package ltd.hlaeja.repository package ltd.hlaeja.repository
import java.util.UUID import java.util.UUID
import kotlinx.coroutines.flow.Flow
import ltd.hlaeja.entity.DeviceEntity import ltd.hlaeja.entity.DeviceEntity
import org.springframework.data.r2dbc.repository.Query
import org.springframework.data.repository.kotlin.CoroutineCrudRepository import org.springframework.data.repository.kotlin.CoroutineCrudRepository
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@Repository @Repository
interface DeviceRepository : CoroutineCrudRepository<DeviceEntity, UUID> interface DeviceRepository : CoroutineCrudRepository<DeviceEntity, UUID> {
@Query("SELECT * FROM devices LIMIT :limit OFFSET :offset")
fun findAll(
@Param("offset") offset: Int,
@Param("limit") limit: Int,
): Flow<DeviceEntity>
@Query("SELECT * FROM devices WHERE type = :type LIMIT :limit OFFSET :offset")
fun findAllByType(
@Param("type") type: UUID,
@Param("offset") offset: Int,
@Param("limit") limit: Int,
): Flow<DeviceEntity>
}

View File

@@ -1,9 +1,11 @@
package ltd.hlaeja.repository package ltd.hlaeja.repository
import java.util.UUID import java.util.UUID
import kotlinx.coroutines.flow.Flow
import ltd.hlaeja.entity.NodeEntity import ltd.hlaeja.entity.NodeEntity
import org.springframework.data.r2dbc.repository.Query import org.springframework.data.r2dbc.repository.Query
import org.springframework.data.repository.kotlin.CoroutineCrudRepository import org.springframework.data.repository.kotlin.CoroutineCrudRepository
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@Repository @Repository
@@ -11,4 +13,10 @@ interface NodeRepository : CoroutineCrudRepository<NodeEntity, UUID> {
@Query("SELECT * FROM nodes WHERE device = :device") @Query("SELECT * FROM nodes WHERE device = :device")
suspend fun findByDevice(device: UUID): NodeEntity? suspend fun findByDevice(device: UUID): NodeEntity?
@Query("SELECT * FROM nodes LIMIT :limit OFFSET :offset")
suspend fun findAll(
@Param("offset") offset: Int,
@Param("limit") limit: Int,
): Flow<NodeEntity>
} }

View File

@@ -13,13 +13,13 @@ import org.springframework.stereotype.Repository
interface TypeRepository : CoroutineCrudRepository<TypeEntity, UUID> { interface TypeRepository : CoroutineCrudRepository<TypeEntity, UUID> {
@Query("SELECT * FROM types ORDER BY name LIMIT :limit OFFSET :offset") @Query("SELECT * FROM types ORDER BY name LIMIT :limit OFFSET :offset")
fun findAll( suspend fun findAll(
@Param("offset") offset: Int, @Param("offset") offset: Int,
@Param("limit") limit: Int, @Param("limit") limit: Int,
): Flow<TypeEntity> ): Flow<TypeEntity>
@Query("SELECT * FROM types WHERE name ILIKE :filter ORDER BY name LIMIT :limit OFFSET :offset") @Query("SELECT * FROM types WHERE name ILIKE :filter ORDER BY name LIMIT :limit OFFSET :offset")
fun findAllContaining( suspend fun findAllContaining(
@Param("filter") filter: String, @Param("filter") filter: String,
@Param("offset") offset: Int, @Param("offset") offset: Int,
@Param("limit") limit: Int, @Param("limit") limit: Int,

View File

@@ -3,6 +3,7 @@ package ltd.hlaeja.service
import io.github.oshai.kotlinlogging.KotlinLogging import io.github.oshai.kotlinlogging.KotlinLogging
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.util.UUID import java.util.UUID
import kotlinx.coroutines.flow.Flow
import ltd.hlaeja.entity.DeviceEntity import ltd.hlaeja.entity.DeviceEntity
import ltd.hlaeja.repository.DeviceRepository import ltd.hlaeja.repository.DeviceRepository
import org.springframework.dao.DataIntegrityViolationException import org.springframework.dao.DataIntegrityViolationException
@@ -31,4 +32,15 @@ class DeviceService(
suspend fun getDevice(device: UUID): DeviceEntity = deviceRepository.findById(device) suspend fun getDevice(device: UUID): DeviceEntity = deviceRepository.findById(device)
?.also { log.debug { "Get device ${it.id}" } } ?.also { log.debug { "Get device ${it.id}" } }
?: throw ResponseStatusException(NOT_FOUND) ?: throw ResponseStatusException(NOT_FOUND)
suspend fun getDevices(
page: Int,
show: Int,
): Flow<DeviceEntity> = deviceRepository.findAll(page, show)
suspend fun getDevicesByType(
type: UUID,
page: Int,
show: Int,
): Flow<DeviceEntity> = deviceRepository.findAllByType(type, page, show)
} }

View File

@@ -2,6 +2,7 @@ package ltd.hlaeja.service
import io.github.oshai.kotlinlogging.KotlinLogging import io.github.oshai.kotlinlogging.KotlinLogging
import java.util.UUID import java.util.UUID
import kotlinx.coroutines.flow.Flow
import ltd.hlaeja.entity.NodeEntity import ltd.hlaeja.entity.NodeEntity
import ltd.hlaeja.repository.NodeRepository import ltd.hlaeja.repository.NodeRepository
import org.springframework.dao.DataIntegrityViolationException import org.springframework.dao.DataIntegrityViolationException
@@ -29,4 +30,9 @@ class NodeService(
device: UUID, device: UUID,
): NodeEntity = nodeRepository.findByDevice(device) ): NodeEntity = nodeRepository.findByDevice(device)
?: throw ResponseStatusException(NOT_FOUND) ?: throw ResponseStatusException(NOT_FOUND)
suspend fun getNodes(
page: Int,
show: Int,
): Flow<NodeEntity> = nodeRepository.findAll(page, show)
} }

View File

@@ -26,7 +26,7 @@ class TypeService(
private val typeDescriptionRepository: TypeDescriptionRepository, private val typeDescriptionRepository: TypeDescriptionRepository,
) { ) {
fun getTypes( suspend fun getTypes(
page: Int, page: Int,
show: Int, show: Int,
filter: String?, filter: String?,

View File

@@ -2,15 +2,17 @@ package ltd.hlaeja.util
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.util.UUID import java.util.UUID
import ltd.hlaeja.dto.TypeWithDescription
import ltd.hlaeja.entity.DeviceEntity import ltd.hlaeja.entity.DeviceEntity
import ltd.hlaeja.entity.NodeEntity import ltd.hlaeja.entity.NodeEntity
import ltd.hlaeja.entity.TypeEntity
import ltd.hlaeja.dto.TypeWithDescription
import ltd.hlaeja.entity.TypeDescriptionEntity import ltd.hlaeja.entity.TypeDescriptionEntity
import ltd.hlaeja.entity.TypeEntity
import ltd.hlaeja.jwt.service.PrivateJwtService import ltd.hlaeja.jwt.service.PrivateJwtService
import ltd.hlaeja.library.deviceRegistry.Device import ltd.hlaeja.library.deviceRegistry.Device
import ltd.hlaeja.library.deviceRegistry.Devices
import ltd.hlaeja.library.deviceRegistry.Identity import ltd.hlaeja.library.deviceRegistry.Identity
import ltd.hlaeja.library.deviceRegistry.Node import ltd.hlaeja.library.deviceRegistry.Node
import ltd.hlaeja.library.deviceRegistry.Nodes
import ltd.hlaeja.library.deviceRegistry.Type import ltd.hlaeja.library.deviceRegistry.Type
import ltd.hlaeja.library.deviceRegistry.Types import ltd.hlaeja.library.deviceRegistry.Types
import org.springframework.http.HttpStatus.EXPECTATION_FAILED import org.springframework.http.HttpStatus.EXPECTATION_FAILED
@@ -68,3 +70,17 @@ fun DeviceEntity.toDeviceResponse(
type, type,
jwtService.sign("device" to id), jwtService.sign("device" to id),
) )
fun DeviceEntity.toDevicesResponse(): Devices.Response = Devices.Response(
id ?: throw ResponseStatusException(EXPECTATION_FAILED),
type,
timestamp,
)
fun NodeEntity.toNodesResponse(): Nodes.Response = Nodes.Response(
id ?: throw ResponseStatusException(EXPECTATION_FAILED),
timestamp,
client,
device,
name,
)

View File

@@ -0,0 +1,6 @@
package ltd.hlaeja.util
object Pagination {
const val DEFAULT_PAGE: Int = 1
const val DEFAULT_SIZE: Int = 25
}

View File

@@ -10,6 +10,20 @@ spring:
name: "%APP_BUILD_OS_NAME%" name: "%APP_BUILD_OS_NAME%"
version: "%APP_BUILD_OS_VERSION%" version: "%APP_BUILD_OS_VERSION%"
management:
endpoints:
access:
default: none
web:
exposure:
include: "health,info"
endpoint:
health:
show-details: always
access: read_only
info:
access: read_only
jwt: jwt:
private-key: cert/private_key.pem private-key: cert/private_key.pem

View File

@@ -1,11 +0,0 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
<logger level="DEBUG" name="ltd.hlaeja"/>
</configuration>

View File

@@ -3,7 +3,7 @@ package ltd.hlaeja.controller
import java.util.UUID import java.util.UUID
import ltd.hlaeja.library.deviceRegistry.Device import ltd.hlaeja.library.deviceRegistry.Device
import ltd.hlaeja.test.compareToFile import ltd.hlaeja.test.compareToFile
import ltd.hlaeja.test.container.PostgresContainer import ltd.hlaeja.test.container.PostgresTestContainer
import ltd.hlaeja.test.isEqualToUuid import ltd.hlaeja.test.isEqualToUuid
import org.assertj.core.api.SoftAssertions import org.assertj.core.api.SoftAssertions
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
@@ -18,7 +18,7 @@ import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody import org.springframework.test.web.reactive.server.expectBody
@PostgresContainer @PostgresTestContainer
@SpringBootTest(webEnvironment = RANDOM_PORT) @SpringBootTest(webEnvironment = RANDOM_PORT)
@ExtendWith(SoftAssertionsExtension::class) @ExtendWith(SoftAssertionsExtension::class)
class DeviceEndpoint { class DeviceEndpoint {

View File

@@ -0,0 +1,201 @@
package ltd.hlaeja.controller
import java.util.UUID
import ltd.hlaeja.library.deviceRegistry.Devices
import ltd.hlaeja.test.container.PostgresTestContainer
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
@PostgresTestContainer
@SpringBootTest(webEnvironment = RANDOM_PORT)
class DevicesEndpoint {
@LocalServerPort
var port: Int = 0
lateinit var webClient: WebTestClient
@BeforeEach
fun setup() {
webClient = WebTestClient.bindToServer().baseUrl("http://localhost:$port").build()
}
@Nested
inner class GetDevices {
@Test
fun `get devices default - success`() {
// when
val result = webClient.get().uri("/devices").exchange()
// then
result.expectStatus().isOk()
.expectBody<List<Devices.Response>>()
.consumeWith {
assertThat(it.responseBody?.size).isEqualTo(4)
}
}
@ParameterizedTest
@CsvSource(
value = [
"1,4",
"2,0",
],
)
fun `get devices by page - success`(page: Int, expected: Int) {
// when
val result = webClient.get().uri("/devices/page-$page").exchange()
// then
result.expectStatus().isOk()
.expectBody<List<Devices.Response>>()
.consumeWith {
assertThat(it.responseBody?.size).isEqualTo(expected)
}
}
@Test
fun `get devices by pages - fail`() {
// when
val result = webClient.get().uri("/devices/page-0").exchange()
// then
result.expectStatus().isBadRequest
}
@ParameterizedTest
@CsvSource(
value = [
"1,2,2",
"2,2,2",
"3,2,0",
],
)
fun `get devices by page and show - success`(page: Int, show: Int, expected: Int) {
// when
val result = webClient.get().uri("/devices/page-$page/show-$show").exchange()
// then
result.expectStatus().isOk()
.expectBody<List<Devices.Response>>()
.consumeWith {
assertThat(it.responseBody?.size).isEqualTo(expected)
}
}
@ParameterizedTest
@CsvSource(
value = [
"0,1",
"1,0",
],
)
fun `get devices by page and show - fail`(page: Int, show: Int) {
// when
val result = webClient.get().uri("/devices/page-$page/show-$show").exchange()
// then
result.expectStatus().isBadRequest
}
}
@Nested
inner class GetDevicesByType {
@Test
fun `get devices for type default - success`() {
// when
val result = webClient.get()
.uri("/devices/type-00000000-0000-0000-0001-000000000001")
.exchange()
// then
result.expectStatus().isOk()
.expectBody<List<Devices.Response>>()
.consumeWith {
assertThat(it.responseBody?.size).isEqualTo(2)
}
}
@ParameterizedTest
@CsvSource(
value = [
"00000000-0000-0000-0001-000000000001,1,2",
"00000000-0000-0000-0001-000000000001,2,0",
],
)
fun `get devices for type by page - success`(type: UUID, page: Int, expected: Int) {
// when
val result = webClient.get()
.uri("/devices/type-$type/page-$page")
.exchange()
// then
result.expectStatus().isOk()
.expectBody<List<Devices.Response>>()
.consumeWith {
assertThat(it.responseBody?.size).isEqualTo(expected)
}
}
@Test
fun `get devices for type by pages - fail`() {
// when
val result = webClient.get()
.uri("/devices/type-00000000-0000-0000-0001-000000000001/page-0")
.exchange()
// then
result.expectStatus().isBadRequest
}
@ParameterizedTest
@CsvSource(
value = [
"00000000-0000-0000-0001-000000000001,1,1,1",
"00000000-0000-0000-0001-000000000001,2,1,1",
"00000000-0000-0000-0001-000000000001,3,1,0",
],
)
fun `get devices for type by page and show - success`(type: UUID, page: Int, show: Int, expected: Int) {
// when
val result = webClient.get()
.uri("/devices/type-$type/page-$page/show-$show")
.exchange()
// then
result.expectStatus().isOk()
.expectBody<List<Devices.Response>>()
.consumeWith {
assertThat(it.responseBody?.size).isEqualTo(expected)
}
}
@ParameterizedTest
@CsvSource(
value = [
"0,1",
"1,0",
],
)
fun `get devices for type by page and show - fail`(page: Int, show: Int) {
// when
val result = webClient.get()
.uri("/devices/type-00000000-0000-0000-0001-000000000001/page-$page/show-$show")
.exchange()
// then
result.expectStatus().isBadRequest
}
}
}

View File

@@ -2,7 +2,7 @@ package ltd.hlaeja.controller
import java.util.UUID import java.util.UUID
import ltd.hlaeja.library.deviceRegistry.Identity import ltd.hlaeja.library.deviceRegistry.Identity
import ltd.hlaeja.test.container.PostgresContainer import ltd.hlaeja.test.container.PostgresTestContainer
import ltd.hlaeja.test.isEqualToUuid import ltd.hlaeja.test.isEqualToUuid
import org.assertj.core.api.SoftAssertions import org.assertj.core.api.SoftAssertions
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
@@ -16,7 +16,7 @@ import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody import org.springframework.test.web.reactive.server.expectBody
@PostgresContainer @PostgresTestContainer
@SpringBootTest(webEnvironment = RANDOM_PORT) @SpringBootTest(webEnvironment = RANDOM_PORT)
@ExtendWith(SoftAssertionsExtension::class) @ExtendWith(SoftAssertionsExtension::class)
class IdentityEndpoint { class IdentityEndpoint {

View File

@@ -2,7 +2,7 @@ package ltd.hlaeja.controller
import java.util.UUID import java.util.UUID
import ltd.hlaeja.library.deviceRegistry.Node import ltd.hlaeja.library.deviceRegistry.Node
import ltd.hlaeja.test.container.PostgresContainer import ltd.hlaeja.test.container.PostgresTestContainer
import org.assertj.core.api.SoftAssertions import org.assertj.core.api.SoftAssertions
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension
@@ -17,7 +17,7 @@ import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody import org.springframework.test.web.reactive.server.expectBody
@PostgresContainer @PostgresTestContainer
@SpringBootTest(webEnvironment = RANDOM_PORT) @SpringBootTest(webEnvironment = RANDOM_PORT)
@ExtendWith(SoftAssertionsExtension::class) @ExtendWith(SoftAssertionsExtension::class)
class NodeEndpoint { class NodeEndpoint {

View File

@@ -0,0 +1,113 @@
package ltd.hlaeja.controller
import ltd.hlaeja.library.deviceRegistry.Nodes
import ltd.hlaeja.test.container.PostgresTestContainer
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.SoftAssertions
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
@PostgresTestContainer
@SpringBootTest(webEnvironment = RANDOM_PORT)
@ExtendWith(SoftAssertionsExtension::class)
class NodesEndpoint {
@InjectSoftAssertions
lateinit var softly: SoftAssertions
@LocalServerPort
var port: Int = 0
lateinit var webClient: WebTestClient
@BeforeEach
fun setup() {
webClient = WebTestClient.bindToServer().baseUrl("http://localhost:$port").build()
}
@Test
fun `get nodes default - success`() {
// when
val result = webClient.get().uri("/nodes").exchange()
// then
result.expectStatus().isOk()
.expectBody<List<Nodes.Response>>()
.consumeWith {
assertThat(it.responseBody?.size).isEqualTo(3)
}
}
@ParameterizedTest
@CsvSource(
value = [
"1,3",
"2,0",
],
)
fun `get nodes by page - success`(page: Int, expected: Int) {
// when
val result = webClient.get().uri("/nodes/page-$page").exchange()
// then
result.expectStatus().isOk()
.expectBody<List<Nodes.Response>>()
.consumeWith {
assertThat(it.responseBody?.size).isEqualTo(expected)
}
}
@Test
fun `get nodes by pages - fail`() {
// when
val result = webClient.get().uri("/nodes/page-0").exchange()
// then
result.expectStatus().isBadRequest
}
@ParameterizedTest
@CsvSource(
value = [
"1,2,2",
"2,2,1",
"3,2,0",
],
)
fun `get nodes by page and show - success`(page: Int, show: Int, expected: Int) {
// when
val result = webClient.get().uri("/nodes/page-$page/show-$show").exchange()
// then
result.expectStatus().isOk()
.expectBody<List<Nodes.Response>>()
.consumeWith {
assertThat(it.responseBody?.size).isEqualTo(expected)
}
}
@ParameterizedTest
@CsvSource(
value = [
"0,1",
"1,0",
],
)
fun `get nodes by page and show - fail`(page: Int, show: Int) {
// when
val result = webClient.get().uri("/nodes/page-$page/show-$show").exchange()
// then
result.expectStatus().isBadRequest
}
}

View File

@@ -1,7 +1,7 @@
package ltd.hlaeja.controller package ltd.hlaeja.controller
import ltd.hlaeja.library.deviceRegistry.Type import ltd.hlaeja.library.deviceRegistry.Type
import ltd.hlaeja.test.container.PostgresContainer import ltd.hlaeja.test.container.PostgresTestContainer
import ltd.hlaeja.test.isEqualToUuid import ltd.hlaeja.test.isEqualToUuid
import org.assertj.core.api.SoftAssertions import org.assertj.core.api.SoftAssertions
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
@@ -21,7 +21,7 @@ import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody import org.springframework.test.web.reactive.server.expectBody
@PostgresContainer @PostgresTestContainer
@SpringBootTest(webEnvironment = RANDOM_PORT) @SpringBootTest(webEnvironment = RANDOM_PORT)
@ExtendWith(SoftAssertionsExtension::class) @ExtendWith(SoftAssertionsExtension::class)
class TypeEndpoint { class TypeEndpoint {

View File

@@ -1,11 +1,10 @@
package ltd.hlaeja.controller package ltd.hlaeja.controller
import ltd.hlaeja.library.deviceRegistry.Types import ltd.hlaeja.library.deviceRegistry.Types
import ltd.hlaeja.test.container.PostgresContainer import ltd.hlaeja.test.container.PostgresTestContainer
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource import org.junit.jupiter.params.provider.CsvSource
import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.SpringBootTest
@@ -14,9 +13,8 @@ import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody import org.springframework.test.web.reactive.server.expectBody
@PostgresContainer @PostgresTestContainer
@SpringBootTest(webEnvironment = RANDOM_PORT) @SpringBootTest(webEnvironment = RANDOM_PORT)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TypesEndpoint { class TypesEndpoint {
@LocalServerPort @LocalServerPort

View File

@@ -6,10 +6,3 @@ spring:
url: r2dbc:postgresql://placeholder url: r2dbc:postgresql://placeholder
username: placeholder username: placeholder
password: placeholder password: placeholder
container:
postgres:
version: postgres:17
init: postgres/schema.sql
before: postgres/data.sql
after: postgres/reset.sql

View File

@@ -0,0 +1,72 @@
package ltd.hlaeja.controller
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.util.UUID
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.single
import kotlinx.coroutines.test.runTest
import ltd.hlaeja.entity.DeviceEntity
import ltd.hlaeja.service.DeviceService
import ltd.hlaeja.test.isEqualToUuid
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class DevicesControllerTest {
companion object {
const val NIL_UUID: String = "00000000-0000-0000-0000-000000000000"
val id: UUID = UUID.fromString(NIL_UUID)
val type: UUID = UUID.fromString(NIL_UUID)
val timestamp: ZonedDateTime = ZonedDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0, 0, 1), ZoneId.of("UTC"))
}
val service: DeviceService = mockk()
lateinit var controller: DevicesController
@BeforeEach
fun setUp() {
controller = DevicesController(service)
}
@Test
fun `get all devices`() = runTest {
// given
coEvery {
service.getDevices(any(), any())
} returns flowOf(DeviceEntity(id, timestamp, type))
// when
val response = controller.getDevices().single()
// then
coVerify(exactly = 1) { service.getDevices(0, 25) }
assertThat(response.id).isEqualToUuid(NIL_UUID)
assertThat(response.type).isEqualToUuid(NIL_UUID)
assertThat(response.timestamp).isEqualTo(timestamp)
}
@Test
fun `get all devices for type`() = runTest {
// given
coEvery {
service.getDevicesByType(any(), any(), any())
} returns flowOf(DeviceEntity(id, timestamp, type))
// when
val response = controller.getDevicesByType(type).single()
// then
coVerify(exactly = 1) { service.getDevicesByType(type, 0, 25) }
assertThat(response.id).isEqualToUuid(NIL_UUID)
assertThat(response.type).isEqualToUuid(NIL_UUID)
assertThat(response.timestamp).isEqualTo(timestamp)
}
}

View File

@@ -0,0 +1,58 @@
package ltd.hlaeja.controller
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.util.UUID
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.single
import kotlinx.coroutines.test.runTest
import ltd.hlaeja.entity.NodeEntity
import ltd.hlaeja.service.NodeService
import ltd.hlaeja.test.isEqualToUuid
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class NodesControllerTest {
companion object {
const val NAME: String = "My Device"
const val NIL_UUID: String = "00000000-0000-0000-0000-000000000000"
val id: UUID = UUID.fromString(NIL_UUID)
val client: UUID = UUID.fromString(NIL_UUID)
val device: UUID = UUID.fromString(NIL_UUID)
val timestamp: ZonedDateTime = ZonedDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0, 0, 1), ZoneId.of("UTC"))
}
val service: NodeService = mockk()
lateinit var controller: NodesController
@BeforeEach
fun setUp() {
controller = NodesController(service)
}
@Test
fun `get all nodes`() = runTest {
// given
coEvery {
service.getNodes(any(), any())
} returns flowOf(NodeEntity(id, timestamp, client, device, NAME))
// when
val response = controller.getNodes().single()
// then
coVerify(exactly = 1) { service.getNodes(0, 25) }
assertThat(response.id).isEqualToUuid(NIL_UUID)
assertThat(response.timestamp).isEqualTo(timestamp)
assertThat(response.client).isEqualToUuid(NIL_UUID)
assertThat(response.device).isEqualToUuid(NIL_UUID)
assertThat(response.name).isEqualTo(NAME)
}
}

View File

@@ -1,8 +1,8 @@
package ltd.hlaeja.controller package ltd.hlaeja.controller
import io.mockk.every import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneId import java.time.ZoneId
import java.time.ZonedDateTime import java.time.ZonedDateTime
@@ -37,7 +37,7 @@ class TypesControllerTest {
@Test @Test
fun `get all types`() = runTest { fun `get all types`() = runTest {
// given // given
every { coEvery {
service.getTypes(any(), any(), any()) service.getTypes(any(), any(), any())
} returns flowOf(TypeEntity(id, timestamp, NAME)) } returns flowOf(TypeEntity(id, timestamp, NAME))
@@ -45,7 +45,7 @@ class TypesControllerTest {
val response = controller.getTypes().single() val response = controller.getTypes().single()
// then // then
verify(exactly = 1) { service.getTypes(0, 25, null) } coVerify(exactly = 1) { service.getTypes(0, 25, null) }
assertThat(response.id).isEqualToUuid(NIL_UUID) assertThat(response.id).isEqualToUuid(NIL_UUID)
assertThat(response.name).isEqualTo(NAME) assertThat(response.name).isEqualTo(NAME)

View File

@@ -6,7 +6,6 @@ import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkStatic import io.mockk.mockkStatic
import io.mockk.unmockkStatic import io.mockk.unmockkStatic
import io.mockk.verify
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneId import java.time.ZoneId
import java.time.ZonedDateTime import java.time.ZonedDateTime
@@ -57,29 +56,29 @@ class TypeServiceTest {
} }
@Test @Test
fun `get all types`() { fun `get all types`() = runTest {
// given // given
every { typeRepository.findAll(any(), any()) } returns flowOf(mockk<TypeEntity>()) coEvery { typeRepository.findAll(any(), any()) } returns flowOf(mockk<TypeEntity>())
// when // when
service.getTypes(1, 10, null) service.getTypes(1, 10, null)
// then // then
verify(exactly = 1) { typeRepository.findAll(1, 10) } coVerify(exactly = 1) { typeRepository.findAll(1, 10) }
verify(exactly = 0) { typeRepository.findAllContaining(any(), any(), any()) } coVerify(exactly = 0) { typeRepository.findAllContaining(any(), any(), any()) }
} }
@Test @Test
fun `get all types with filter`() { fun `get all types with filter`() = runTest {
// given // given
every { typeRepository.findAllContaining(any(), any(), any()) } returns flowOf(mockk<TypeEntity>()) coEvery { typeRepository.findAllContaining(any(), any(), any()) } returns flowOf(mockk<TypeEntity>())
// when // when
service.getTypes(1, 10, "abc") service.getTypes(1, 10, "abc")
// then // then
verify(exactly = 1) { typeRepository.findAllContaining("%abc%", 1, 10) } coVerify(exactly = 1) { typeRepository.findAllContaining("%abc%", 1, 10) }
verify(exactly = 0) { typeRepository.findAll(any(), any()) } coVerify(exactly = 0) { typeRepository.findAll(any(), any()) }
} }
@Test @Test