Skip to content

Commit

Permalink
feat: added missing entities validations and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rodobarcaaa committed Oct 24, 2023
1 parent 08b54ea commit af596b3
Show file tree
Hide file tree
Showing 23 changed files with 447 additions and 102 deletions.
51 changes: 29 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,19 @@ on:
workflow_dispatch:

jobs:
linter:
labeler:
runs-on: ubuntu-latest
timeout-minutes: 1
steps:
- name: Label the PR action
continue-on-error: true
uses: TimonVS/pr-labeler-action@v3
with:
configuration-path: .github/labeler.yml
env:
GITHUB_TOKEN: ${{ secrets.GH_PAT }}

lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
Expand All @@ -19,36 +31,31 @@ jobs:

sonar:
runs-on: ubuntu-latest
needs: [linter]
needs: [ linter ]
timeout-minutes: 25
steps:
- name: Checkout / Setup JDK 11 / SBT-Cache
uses: rodobarcaaa/action-jdk-sbt-cache@main
with:
token: ${{ secrets.GH_PAT }}

- run: docker-compose -f docker-compose-test.yml up -d

- run: sbt -Dsonar.login=${{ secrets.SONAR_TOKEN }} sonar

labeler:
runs-on: ubuntu-latest
if: github.ref != 'refs/heads/main'
timeout-minutes: 1
steps:
- name: Label the PR action
continue-on-error: true
uses: TimonVS/pr-labeler-action@v3
- run: docker rm -f -v bookstore-db

- name: Tests report
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
configuration-path: .github/labeler.yml
env:
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
check_name: Tests report
files: ./**/*Test.xml

release_draft:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
timeout-minutes: 5
steps:
- uses: release-drafter/release-drafter@v5
- name: Coverage report
uses: 5monkeys/cobertura-action@master
with:
config-name: release-drafter.yml
env:
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
path: target/scala-2.13/coverage-report/cobertura.xml
minimum_coverage: 75
fail_below_threshold: true
show_class_names: true
17 changes: 17 additions & 0 deletions .github/workflows/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: release-drafter

on:
push:
branches: [ main ]
workflow_dispatch:

jobs:
release_draft:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: release-drafter/release-drafter@v5
with:
config-name: release-drafter.yml
env:
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,51 @@
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=tapir-http4s-seed&metric=coverage)](https://sonarcloud.io/summary/new_code?id=tapir-http4s-seed)
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=tapir-http4s-seed&metric=bugs)](https://sonarcloud.io/summary/new_code?id=tapir-http4s-seed)
[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=tapir-http4s-seed&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=tapir-http4s-seed)

## Try it! JDK 11

```
docker-compose -f up -d
sbt run
```

Swagger: http://0.0.0.0:8080/docs/

Metrics: http://0.0.0.0:8080/metrics

## Tests run

```
docker-compose -f docker-compose.yml up -d
```

or

```
sh recreate-db
```

- Run all tests: `sbt test`
- Run all tests with coverage check: `sbt tcoverage`
- Run all tests with coverage check and formatting: `sbt +scalafmtAll tcoverage`

Also, you can run only one suite like this

```
sbt "to *SOMETest"
```

## Ref Documentation

- Http4s: https://http4s.org/v0.23/docs/quickstart.html
- Tapir: https://tapir.softwaremill.com/en/latest/quickstart.html
- Macwire: https://github.com/softwaremill/macwire
- Circe: https://circe.github.io/circe/
- Cats Core: https://typelevel.org/cats/
- Cats Effect: https://typelevel.org/cats-effect/
- Slick: http://books.underscore.io/essential-slick/essential-slick-3.html
- Slick-Pg: https://github.com/tminglei/slick-pg
- Fly4s: https://geirolz.github.io/fly4s/
- MUnit: https://scalameta.org/munit/docs/getting-started.html
- Sonar Scala: https://sonar-scala.com/

1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,5 @@ addCommandAlias("tfc", "test:scalafmtCheck")
addCommandAlias("fmt", ";f;tf")
addCommandAlias("fmtCheck", ";fc;tfc")

addCommandAlias("tcoverage", ";coverage;test;coverageReport")
addCommandAlias("sonar", ";clean;coverage;test;coverageReport;sonarScan")
16 changes: 8 additions & 8 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ services:
env_file:
- .env

redis:
container_name: bookstore-cache
image: redis:6-alpine
restart: always
ports:
- '6380:6379'
command: redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD}
# redis:
# container_name: bookstore-cache
# image: redis:6-alpine
# restart: always
# ports:
# - '6380:6379'
# command: redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD}
#

# TODO: S3 localstack


4 changes: 4 additions & 0 deletions recreate-db
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash

docker rm -f -v bookstore-db
docker-compose up -d
8 changes: 0 additions & 8 deletions src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,4 @@ db {

password = "BookStore*24"
password = ${?DB_PASSWORD}

migrations {
table = "flyway"
table = ${?DB_MIGRATIONS_TABLE}

locations = ["db"]
locations = ${?DB_MIGRATIONS_LOCATIONS}
}
}
14 changes: 14 additions & 0 deletions src/test/scala/com/example/books/domain/author/AuthorMother.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.books.domain.author

import com.example.shared.domain.common.{Id, Name}
import com.example.shared.domain.shared.{IdMother, TextMother}

object AuthorMother {
def apply(
id: Id = IdMother.random,
firstName: Name = Name(TextMother.random(25)),
lastName: Name = Name(TextMother.random(25))
): Author = Author(id, firstName, lastName)

def random: Author = apply()
}
20 changes: 20 additions & 0 deletions src/test/scala/com/example/books/domain/book/BookMother.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.books.domain.book

import com.example.shared.domain.common.Id
import com.example.shared.domain.shared.{AlphaNumericMother, IdMother, TextMother}

import java.time.LocalDate

object BookMother {
def apply(
authorId: Id,
publisherId: Id,
id: Id = IdMother.random,
title: BookTitle = BookTitle(TextMother.random(100)),
isbn: BookIsbn = BookIsbn(AlphaNumericMother.random(30)),
description: BookDescription = BookDescription(TextMother.random(500)),
yearMother: BookYear = BookYear(LocalDate.now().getYear)
): Book = Book(id, isbn, title, description, yearMother, publisherId, authorId)

def random(authorId: Id, publisherId: Id): Book = apply(authorId, publisherId)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.books.domain.publisher

import com.example.shared.domain.common.{Id, Name, URL}
import com.example.shared.domain.shared.{IdMother, TextMother}

object PublisherMother {
def apply(
id: Id = IdMother.random,
name: Name = Name(TextMother.random(25)),
url: URL = URL(s"www.${TextMother.random(10)}.com")
): Publisher = Publisher(id, name, url)

def random: Publisher = apply()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.books.infrastructure.helpers

import com.example.books.domain.author.AuthorMother
import com.example.shared.domain.common.Id
import com.example.shared.infrastructure.http.HasHttp4sRoutesSuite

trait AuthorHelper {
self: HasHttp4sRoutesSuite =>

private lazy val authorService = module.authorService

def createRandomAuthor: Id = {
val author = AuthorMother.random
authorService.create(author).unsafeRunSync()
author.id
}

def deleteAuthor(id: Id): Unit = authorService.delete(id).unsafeRunSync()

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.books.infrastructure.helpers

import com.example.books.domain.publisher.PublisherMother
import com.example.shared.domain.common.Id
import com.example.shared.infrastructure.http.HasHttp4sRoutesSuite

trait PublisherHelper {
self: HasHttp4sRoutesSuite =>

private lazy val publisherService = module.publisherService

def createRandomPublisher: Id = {
val publisher = PublisherMother.random
publisherService.create(publisher).unsafeRunSync()
publisher.id
}

def deletePublisher(id: Id): Unit = publisherService.delete(id).unsafeRunSync()

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.example.books.infrastructure.http

import cats.effect.IO
import com.example.books.domain.author._
import com.example.books.infrastructure.codecs.AuthorCodecs
import com.example.shared.domain.page.PageResponse
import com.example.shared.domain.shared.IdMother
import com.example.shared.infrastructure.http.{Fail, HasHttp4sRoutesSuite}
import io.circe.generic.auto._
import org.http4s._
import org.http4s.circe.CirceEntityCodec._

import java.util.UUID

class AuthorApiTest extends HasHttp4sRoutesSuite with AuthorCodecs {

override val routes: HttpRoutes[IO] = module.authorApi.routes

val author: Author = AuthorMother.random
val authorId: UUID = author.id.value

test(POST(author, uri"authors")).alias("CREATE") { response =>
assertEquals(response.status, Status.Created)
}

test(GET(uri"authors" / s"$authorId")).alias("FOUND") { response =>
assertEquals(response.status, Status.Ok)
assertIO(response.as[Author], author)
}

lazy val notfoundId: UUID = IdMother.random.value

test(GET(uri"authors" / s"$notfoundId")).alias("NOT_FOUND") { response =>
assertEquals(response.status, Status.NotFound)
assertIO(response.as[Fail.NotFound], Fail.NotFound(s"Author for id: $notfoundId Not Found"))
}

test(GET(uri"authors")).alias("LIST") { response =>
assertEquals(response.status, Status.Ok)
assertIO(response.as[PageResponse[Author]].map(_.elements.contains(author)), true)
}

lazy val updatedAuthor: Author = AuthorMother.random

test(PUT(updatedAuthor, uri"authors" / s"$authorId")).alias("UPDATE") { response =>
assertEquals(response.status, Status.NoContent)
}

test(GET(uri"authors" / s"$authorId")).alias("UPDATED") { response =>
assertEquals(response.status, Status.Ok)
assertIO(response.as[Author], updatedAuthor.copy(id = author.id))
}

test(DELETE(uri"authors" / s"$authorId")).alias("EXISTS") { response =>
assertEquals(response.status, Status.NoContent)
}

test(DELETE(uri"authors" / s"$notfoundId")).alias("NOT EXISTS") { response =>
assertEquals(response.status, Status.NoContent)
}

}
Loading

0 comments on commit af596b3

Please sign in to comment.