Skip to content

Commit

Permalink
Add usecase testing to backend (#131)
Browse files Browse the repository at this point in the history
Co-authored-by: Jose Miguel Lozano <[email protected]>
Co-authored-by: Arpon <[email protected]>
  • Loading branch information
3 people authored Mar 21, 2023
1 parent 54538b0 commit 1be22fe
Show file tree
Hide file tree
Showing 39 changed files with 1,619 additions and 11 deletions.
10 changes: 10 additions & 0 deletions application/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,14 @@ dependencies {
jvmMainImplementation(libs.exposed.jdbc)
jvmMainImplementation(libs.exposed.java.time)
jvmMainImplementation(libs.kotlinx.datetime)
jvmTestImplementation(libs.kotest.core)
jvmTestImplementation(libs.kotest.property)
jvmTestImplementation(libs.testcontainers.postgresql)
jvmTestImplementation(libs.postgrsql)
}

tasks {
withType<Test> {
useJUnitPlatform()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ object CreateBookingUseCase {
throw IllegalArgumentException("One of the booked entities has a descendant that is already booked")
}

val newBookedEntities = BookableEntity.forEntityIds(createBookingDto.bookedEntityIds.map { EntityID(it, BookableEntities) })
val newBookedEntities =
BookableEntity.forEntityIds(createBookingDto.bookedEntityIds.map { EntityID(it, BookableEntities) })

val booking = Booking.new {
this.start = start
Expand All @@ -70,7 +71,7 @@ object CreateBookingUseCase {
this.bookedEntities = newBookedEntities
}

booking.toDto()
booking.toDto(listOf(Booking::bookedEntities))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ object DeleteBookingUseCase {
currentUserId: String,
) {
return newSuspendedTransaction {
BookedEntities.deleteWhere { BookedEntities.booking_id eq bookingId }
Bookings.deleteWhere { Bookings.id eq bookingId and (Bookings.user_id eq currentUserId) }
BookedEntities.deleteWhere { booking_id eq bookingId }
Bookings.deleteWhere { (Bookings.id eq bookingId) and (user_id eq currentUserId) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ object GetBookingUseCase {

val bookings = query.where?.let { Booking.find { it } } ?: Booking.all()

bookings.orderBy(Bookings.start to SortOrder.ASC).with(Booking::bookedEntities, Booking::user).map { it.toDto(listOf(Booking::bookedEntities, Booking::user)) }
bookings.orderBy(Bookings.start to SortOrder.ASC).with(Booking::bookedEntities, Booking::user)
.map { it.toDto(listOf(Booking::bookedEntities, Booking::user)) }
}

return bookingDtos
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package replace.datastore

import java.io.InputStream

class InMemoryFileStorage : FileStorage {

private val backing: MutableMap<String, ByteArray> = mutableMapOf()

override suspend fun exists(path: String): Boolean {
return backing.containsKey(path)
}

override suspend fun saveFile(path: String, input: InputStream): Boolean {
backing[path] = input.readBytes()
return true
}

override suspend fun deleteFile(path: String): Boolean {
backing.remove(path)
return true
}

override suspend fun copyFile(from: String, to: String): Boolean {
backing[to] = backing[from] ?: return false
return true
}

override suspend fun readFile(path: String): InputStream {
return backing[path]?.inputStream() ?: InputStream.nullInputStream()
}

override suspend fun getFileSize(path: String): Long {
return backing[path]?.size?.toLong() ?: 0L
}
}
44 changes: 44 additions & 0 deletions application/src/jvmTest/kotlin/replace/usecase/DatabaseTesting.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package replace.usecase

import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.transactions.transaction
import org.testcontainers.containers.PostgreSQLContainer
import replace.model.BookableEntities
import replace.model.BookableEntityTypes
import replace.model.BookedEntities
import replace.model.Bookings
import replace.model.Floors
import replace.model.TemporaryFiles
import replace.model.Users

val tables: Array<out Table> = arrayOf(
BookedEntities,
BookableEntities,
BookableEntityTypes,
Bookings,
Floors,
TemporaryFiles,
Users,
)

inline fun useDatabase(block: () -> Unit) {
PostgreSQLContainer("postgres").use { container ->
container.start()
println("Postgres container started @ ${container.jdbcUrl}")
Database.connect(
url = container.jdbcUrl,
driver = "org.postgresql.Driver",
user = "test",
password = "test",
)
println("Successfully connected to postgres")

transaction {
SchemaUtils.create(*tables)
}
println("Successfully created tables ${tables.joinToString { it.tableName }}")
block()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package replace.usecase.bookableentity

import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.property.checkAll
import org.jetbrains.exposed.sql.transactions.transaction
import replace.model.BookableEntity
import replace.usecase.generator.ReplaceArb
import replace.usecase.generator.bookableEntityCreateDto
import replace.usecase.useDatabase
import java.util.UUID

class CreateBookableEntityUseCaseTest : FunSpec(
{
context("happy path") {
test("create a simple bookable entity") {
useDatabase {
checkAll(20, ReplaceArb.bookableEntityCreateDto()) { dto ->
val fromUseCase = CreateBookableEntityUseCase.execute(dto)

fromUseCase.id shouldNotBe null
shouldNotThrowAny { UUID.fromString(fromUseCase.id) }
fromUseCase.name shouldBe dto.name
fromUseCase.posX shouldBe dto.posX
fromUseCase.posY shouldBe dto.posY
fromUseCase.floorId shouldBe dto.floorId
fromUseCase.typeId shouldBe dto.typeId
fromUseCase.index shouldBe dto.index

val fromDb = transaction {
BookableEntity.findById(fromUseCase.id)
}

fromDb shouldNotBe null
fromDb!!

fromDb.id shouldNotBe null
fromDb.name shouldBe dto.name
fromDb.posX shouldBe dto.posX
fromDb.posY shouldBe dto.posY
fromDb.floorId.toString() shouldBe dto.floorId
fromDb.typeId.toString() shouldBe dto.typeId
fromDb.index shouldBe dto.index
}
}
}
}
},
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package replace.usecase.bookableentity

import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.property.Arb
import io.kotest.property.arbitrary.constant
import io.kotest.property.arbitrary.list
import io.kotest.property.arbitrary.next
import io.kotest.property.checkAll
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import replace.dto.UpdateBookableEntityOrderDto
import replace.model.BookableEntities
import replace.model.BookableEntity
import replace.usecase.generator.ReplaceArb
import replace.usecase.generator.bookableEntity
import replace.usecase.generator.floor
import replace.usecase.useDatabase

class UpdateBookableEntityOrderUseCaseTest : FunSpec(
{
context("happy path") {
test("Update a simple floor") {
useDatabase {
checkAll(5, ReplaceArb.floor()) { floor ->
val entities = Arb.list(ReplaceArb.bookableEntity(floorArb = Arb.constant(floor)), 1..10).next()
val shuffled = entities.shuffled()

newSuspendedTransaction {
BookableEntity.find { BookableEntities.floor_id eq floor.id }
.map { it.id.toString() }.sorted() shouldBe shuffled.map { it.id.toString() }.sorted()
}

UpdateBookableEntityOrderUseCase.execute(
UpdateBookableEntityOrderDto(
floorId = floor.id.toString(),
bookableEntityIds = shuffled.map { it.id.toString() },
),
)

newSuspendedTransaction {
BookableEntity.find { BookableEntities.floor_id eq floor.id }
.sortedBy { it.index }
.map { it.id.toString() }
.shouldBe(shuffled.map { it.id.toString() })
}
}
}
}
}
},
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package replace.usecase.bookableentity

import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.property.checkAll
import org.jetbrains.exposed.sql.transactions.transaction
import replace.model.BookableEntity
import replace.usecase.generator.ReplaceArb
import replace.usecase.generator.bookableEntity
import replace.usecase.generator.bookableEntityUpdateDto
import replace.usecase.useDatabase

class UpdateBookableEntityUseCaseTest : FunSpec(
{
context("happy path") {
test("Update a simple bookable entity") {
useDatabase {
checkAll(5, ReplaceArb.bookableEntity()) { original ->
checkAll(5, ReplaceArb.bookableEntityUpdateDto(original.id.toString())) { update ->
val fromUseCase = UpdateBookableEntityUseCase.execute(update)

fromUseCase.id shouldBe update.id
fromUseCase.name shouldBe update.name
fromUseCase.floorId shouldBe update.floorId
fromUseCase.parentId shouldBe update.parentId
fromUseCase.typeId shouldBe update.typeId
fromUseCase.posX shouldBe update.posX
fromUseCase.posY shouldBe update.posY
fromUseCase.index shouldBe update.index

val fromDB = transaction {
BookableEntity.findById(update.id)
}
fromDB shouldNotBe null
fromDB!!

fromDB.id.toString() shouldBe update.id
fromDB.name shouldBe update.name
fromDB.floorId.toString() shouldBe update.floorId
fromDB.parentId?.toString() shouldBe update.parentId
fromDB.typeId?.toString() shouldBe update.typeId
fromDB.posX shouldBe update.posX
fromDB.posY shouldBe update.posY
fromDB.index shouldBe update.index
}
}
}
}
}
},
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package replace.usecase.bookableentitytype

import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.property.checkAll
import org.jetbrains.exposed.sql.transactions.transaction
import replace.model.BookableEntityType
import replace.usecase.generator.ReplaceArb
import replace.usecase.generator.bookableEntityTypeCreateDto
import replace.usecase.useDatabase
import java.util.UUID

class CreateBookableEntityTypeUseCaseTest : FunSpec(
{
context("happy path") {
test("create a simple bookable entity type") {
useDatabase {
checkAll(20, ReplaceArb.bookableEntityTypeCreateDto()) { dto ->
val fromUseCase = CreateBookableEntityTypeUseCase.execute(dto)

fromUseCase.id shouldNotBe null
shouldNotThrowAny { UUID.fromString(fromUseCase.id) }
fromUseCase.name shouldBe dto.name

val fromDb = transaction {
BookableEntityType.findById(fromUseCase.id)
}

fromDb shouldNotBe null
fromDb!!

fromDb.id.toString() shouldBe fromUseCase.id
fromDb.name shouldBe fromUseCase.name
}
}
}
}
},
)
Loading

0 comments on commit 1be22fe

Please sign in to comment.