writting some tests
continuous-integration/drone/push Build is failing Details

drone-setup
Override-6 2 years ago
parent b94b4493e2
commit 8692ed55c3

@ -1,7 +1,7 @@
package org.tbasket package org.tbasket
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.tbasket.auth.Authentificator import org.tbasket.auth.Authenticator
import org.tbasket.config.{FileServerConfig, ServerConfig} import org.tbasket.config.{FileServerConfig, ServerConfig}
import org.tbasket.data.Database import org.tbasket.data.Database
import org.tbasket.endpoint.Endpoint import org.tbasket.endpoint.Endpoint
@ -44,7 +44,7 @@ object Main extends ZIOAppDefault:
private def setupAuth(config: ServerConfig) = ZIO.attempt { private def setupAuth(config: ServerConfig) = ZIO.attempt {
val publicKey = config.emitterCertificate.getPublicKey val publicKey = config.emitterCertificate.getPublicKey
val auth = new Authentificator(config.emitterURL, publicKey, config.emitterKeyAlgorithm) val auth = new Authenticator(config.emitterURL, publicKey, config.emitterKeyAlgorithm)
ZLayer.succeed(auth) ZLayer.succeed(auth)
} }

@ -9,7 +9,7 @@ import io.getquill.context.qzio.ZioJdbcContext
import io.getquill.context.sql.idiom.SqlIdiom import io.getquill.context.sql.idiom.SqlIdiom
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.tbasket.InternalBasketServerException import org.tbasket.InternalBasketServerException
import org.tbasket.auth.Authentificator.* import org.tbasket.auth.Authenticator.*
import org.tbasket.data.{DatabaseContext, User} import org.tbasket.data.{DatabaseContext, User}
import org.tbasket.error.AuthException.* import org.tbasket.error.AuthException.*
import org.tbasket.error.ExceptionEnum import org.tbasket.error.ExceptionEnum
@ -31,18 +31,20 @@ import javax.sql.DataSource
import scala.collection.immutable.HashMap import scala.collection.immutable.HashMap
case class JwtContent(uuid: UUID)
object Authentificator: object Authenticator:
private final val LOG = LogManager.getLogger("Authentification") private final val LOG = LogManager.getLogger("Authentification")
private final val ValidMailPattern = "^\\w+([.-]?\\w+)*@\\w+([.-]?\\w+)*(\\.\\w{2,3})+$".r private final val ValidMailPattern = "^\\w+([.-]?\\w+)*@\\w+([.-]?\\w+)*(\\.\\w{2,3})+$".r
private final val ValidPasswordPattern = ".{6,}".r private final val ValidPasswordPattern = ".{6,}".r
case class JwtContent(uuid: UUID)
class Authentificator(url: URL, key: PublicKey, algorithm: JwtAsymmetricAlgorithm) { class Authenticator(url: URL, key: PublicKey, algorithm: JwtAsymmetricAlgorithm) {
private def defineCustomClaims(user: User): String = { private def defineCustomClaims(user: User): String = {
JwtContent(user.id).asJson.noSpaces.toString JwtContent(user.id).asJson.noSpaces
} }
private def mkRequest(user: User): Request = { private def mkRequest(user: User): Request = {

@ -5,7 +5,7 @@ import io.getquill.context.sql.idiom.SqlIdiom
import io.getquill.idiom.Idiom import io.getquill.idiom.Idiom
import io.getquill.{Literal, NamingStrategy, SqliteDialect} import io.getquill.{Literal, NamingStrategy, SqliteDialect}
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.tbasket.auth.Authentificator import org.tbasket.auth.Authenticator
import org.tbasket.data.DatabaseContext import org.tbasket.data.DatabaseContext
import org.tbasket.endpoint.Endpoint.LOG import org.tbasket.endpoint.Endpoint.LOG
import org.tbasket.handler.LoginPageHandler import org.tbasket.handler.LoginPageHandler
@ -77,7 +77,7 @@ class Endpoint(port: Int):
Server.install(app).flatMap { port => Server.install(app).flatMap { port =>
LOG.info(s"Listening API entries on $port") LOG.info(s"Listening API entries on $port")
ZIO.never ZIO.never
}.provideSome[DatabaseContext & Authentificator & DataSource]( }.provideSome[DatabaseContext & Authenticator & DataSource](
Scope.default, Scope.default,
serverConfigLayer, serverConfigLayer,
ConnectionPool.fixed(4), ConnectionPool.fixed(4),

@ -5,7 +5,7 @@ import io.getquill.context.ZioJdbc.*
import io.getquill.context.qzio.{ZioContext, ZioJdbcContext} import io.getquill.context.qzio.{ZioContext, ZioJdbcContext}
import io.getquill.context.sql.idiom.SqlIdiom import io.getquill.context.sql.idiom.SqlIdiom
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.tbasket.auth.Authentificator import org.tbasket.auth.Authenticator
import org.tbasket.data.{DatabaseContext, User} import org.tbasket.data.{DatabaseContext, User}
import org.tbasket.error.AuthException.* import org.tbasket.error.AuthException.*
import org.tbasket.error.ExceptionEnum import org.tbasket.error.ExceptionEnum
@ -17,7 +17,7 @@ import zio.http.model.{Cookie, Header, Headers, Status}
import zio.json.* import zio.json.*
import zio.json.ast.Json.Str import zio.json.ast.Json.Str
import zio.json.ast.{Json, JsonCursor} import zio.json.ast.{Json, JsonCursor}
import zio.{ZEnvironment, ZIO, *} import zio.*
import java.sql.SQLException import java.sql.SQLException
import java.util.UUID import java.util.UUID
@ -28,7 +28,7 @@ object LoginPageHandler extends PageHandler:
private val LOG = LogManager.getLogger("Login") private val LOG = LogManager.getLogger("Login")
private def getUser(json: Json) = private def getUser(json: Json) =
ZIO.serviceWithZIO[Authentificator] { auth => ZIO.serviceWithZIO[Authenticator] { auth =>
for for
mail <- HandlerUtils.parseAttribute(json, "email", JsonCursor.field("email").isString) mail <- HandlerUtils.parseAttribute(json, "email", JsonCursor.field("email").isString)
password <- HandlerUtils.parseAttribute(json, "password", JsonCursor.field("password").isString) password <- HandlerUtils.parseAttribute(json, "password", JsonCursor.field("password").isString)
@ -49,7 +49,7 @@ object LoginPageHandler extends PageHandler:
.mapError(InvalidRequest("Invalid JSON body", _)) .mapError(InvalidRequest("Invalid JSON body", _))
user <- getUser(json) user <- getUser(json)
jwt <- ZIO.serviceWithZIO[Authentificator](_.requestNewJwt(user)) jwt <- ZIO.serviceWithZIO[Authenticator](_.requestNewJwt(user))
yield Response( yield Response(
status = Status.Found, status = Status.Found,
headers = Headers.location("/") ++ //login successful, go back to main page headers = Headers.location("/") ++ //login successful, go back to main page

@ -1,6 +1,6 @@
package org.tbasket.handler package org.tbasket.handler
import org.tbasket.auth.Authentificator import org.tbasket.auth.Authenticator
import org.tbasket.data.User import org.tbasket.data.User
import org.tbasket.error.RegularException.InvalidRequest import org.tbasket.error.RegularException.InvalidRequest
import org.tbasket.handler.HandlerUtils.parseAttribute import org.tbasket.handler.HandlerUtils.parseAttribute
@ -25,8 +25,8 @@ object RegisterPageHandler extends PageHandler {
mail <- parseAttribute(json, "email", JsonCursor.field("email").isString) mail <- parseAttribute(json, "email", JsonCursor.field("email").isString)
password <- parseAttribute(json, "password", JsonCursor.field("password").isString) password <- parseAttribute(json, "password", JsonCursor.field("password").isString)
user <- ZIO.serviceWithZIO[Authentificator](_.registerUser(name, forename, mail, password)) user <- ZIO.serviceWithZIO[Authenticator](_.registerUser(name, forename, mail, password))
jwt <- ZIO.serviceWithZIO[Authentificator](_.requestNewJwt(user)) jwt <- ZIO.serviceWithZIO[Authenticator](_.requestNewJwt(user))
yield Response( yield Response(
status = Status.Found, status = Status.Found,
headers = Headers.location("/") ++ //register successful, go back to main page headers = Headers.location("/") ++ //register successful, go back to main page

@ -3,6 +3,6 @@ FROM ubuntu:latest
RUN apt update RUN apt update
RUN apt install openjdk-19-jdk -y RUN apt install openjdk-19-jdk -y
RUN apt install openssh-client -y RUN apt install openssh-client curl wget -y

@ -3,42 +3,50 @@
{ {
"name" : "Test Task name not available here/\/login page handler/login situation tests/login with unknown account", "name" : "Test Task name not available here/\/login page handler/login situation tests/login with unknown account",
"status" : "Success", "status" : "Success",
"durationMillis" : "3658", "durationMillis" : "4103",
"annotations" : "", "annotations" : "",
"fullyQualifiedClassName" : "Test Task name not available here", "fullyQualifiedClassName" : "Test Task name not available here",
"labels" : ["\/login page handler", "login situation tests", "login with unknown account"] "labels" : ["\/login page handler", "login situation tests", "login with unknown account"]
}, },
{ {
"name" : "Test Task name not available here/\/login page handler/erroned request body tests/with invalid json", "name" : "Test Task name not available here/\/login page handler/login situation tests/login with known account",
"status" : "Success", "status" : "Failure",
"durationMillis" : "2587", "durationMillis" : "1",
"annotations" : "", "annotations" : "",
"fullyQualifiedClassName" : "Test Task name not available here", "fullyQualifiedClassName" : "Test Task name not available here",
"labels" : ["\/login page handler", "erroned request body tests", "with invalid json"] "labels" : ["\/login page handler", "login situation tests", "login with known account"]
}, },
{ {
"name" : "Test Task name not available here/\/login page handler/erroned request body tests/with no mail attribute", "name" : "Test Task name not available here/\/login page handler/erroned request body tests/with no password attribute",
"status" : "Success", "status" : "Success",
"durationMillis" : "2629", "durationMillis" : "2681",
"annotations" : "", "annotations" : "",
"fullyQualifiedClassName" : "Test Task name not available here", "fullyQualifiedClassName" : "Test Task name not available here",
"labels" : ["\/login page handler", "erroned request body tests", "with no mail attribute"] "labels" : ["\/login page handler", "erroned request body tests", "with no password attribute"]
}, },
{ {
"name" : "Test Task name not available here/\/login page handler/erroned request body tests/empty packet", "name" : "Test Task name not available here/\/login page handler/erroned request body tests/empty packet",
"status" : "Success", "status" : "Success",
"durationMillis" : "2633", "durationMillis" : "2697",
"annotations" : "", "annotations" : "",
"fullyQualifiedClassName" : "Test Task name not available here", "fullyQualifiedClassName" : "Test Task name not available here",
"labels" : ["\/login page handler", "erroned request body tests", "empty packet"] "labels" : ["\/login page handler", "erroned request body tests", "empty packet"]
}, },
{ {
"name" : "Test Task name not available here/\/login page handler/erroned request body tests/with no password attribute", "name" : "Test Task name not available here/\/login page handler/erroned request body tests/with no mail attribute",
"status" : "Success", "status" : "Success",
"durationMillis" : "2619", "durationMillis" : "2702",
"annotations" : "", "annotations" : "",
"fullyQualifiedClassName" : "Test Task name not available here", "fullyQualifiedClassName" : "Test Task name not available here",
"labels" : ["\/login page handler", "erroned request body tests", "with no password attribute"] "labels" : ["\/login page handler", "erroned request body tests", "with no mail attribute"]
},
{
"name" : "Test Task name not available here/\/login page handler/erroned request body tests/with invalid json",
"status" : "Success",
"durationMillis" : "2717",
"annotations" : "",
"fullyQualifiedClassName" : "Test Task name not available here",
"labels" : ["\/login page handler", "erroned request body tests", "with invalid json"]
} }
] ]

@ -2,16 +2,16 @@
echo GENERATING TEMPORARY KEY PAIRS FOR TESTS echo GENERATING TEMPORARY KEY PAIRS FOR TESTS
rm -r /tmp/keys rm -r /tmp/keys &> /dev/null
mkdir /tmp/keys mkdir -p /tmp/keys
cd /tmp/keys cd /tmp/keys
keytool -genkey -noprompt \ keytool -genkey -noprompt \
-alias key \ -alias key \
-keyalg RSA \ -keyalg RSA \
-validity 1 \ -validity 2 \
-keystore test.keystore \ -keystore test.keystore \
-storetype PKCS12 \ -storetype PKCS12 \
-dname "CN=mqttserver.ibm.com, OU=ID, O=IBM, L=Hursley, S=Hants, C=GB" \ -dname "CN=x.y.com, OU=TB, O=TBA, L=dzqdz, S=dqzdzq, C=GB" \
-storepass 123456789 \ -storepass 123456789 \
-keypass 123456789 -keypass 123456789
@ -23,5 +23,5 @@ keytool -noprompt -export \
-keypass 123456789 \ -keypass 123456789 \
-storepass 123456789 -storepass 123456789
echo "123456789" | openssl pkcs12 -in test.keystore -nodes -nocerts -out private.pcks 2> /dev/null openssl pkcs12 -in test.keystore -nodes -nocerts -out private.pcks -passin pass:123456789
echo "123456789" | openssl pkcs12 -in test.keystore -nokeys -out public.cert 2> /dev/null openssl pkcs12 -in test.keystore -nokeys -out public.cert -passin pass:123456789

@ -0,0 +1,3 @@
INSERT INTO user
VALUES ('1daf7878-8ede-46ed-8b3e-120d37bf6819','maximebatista18@gmail.com', 'batista', 'maxime', 1450575459)

@ -1,10 +1,13 @@
package org.tbasket.test package org.tbasket.test
import org.tbasket.auth.Authentificator import org.tbasket.auth.Authenticator
import org.tbasket.config.ServerConfig import org.tbasket.config.ServerConfig
import org.tbasket.data.Database import org.tbasket.data.Database
import zio.{Task, ZLayer} import zio.{Task, ZLayer}
import java.nio.file.{Files, Path}
import java.sql.{DriverManager, Statement}
/* /*
* Defines required test service layers * Defines required test service layers
* */ * */
@ -13,13 +16,33 @@ object TestLayers {
val auth = { val auth = {
val publicKey = TestServerConfig.emitterCertificate.getPublicKey val publicKey = TestServerConfig.emitterCertificate.getPublicKey
val auth = new Authentificator(TestServerConfig.emitterURL, publicKey, TestServerConfig.emitterKeyAlgorithm) val auth = new Authenticator(TestServerConfig.emitterURL, publicKey, TestServerConfig.emitterKeyAlgorithm)
ZLayer.succeed(auth) ZLayer.succeed(auth)
} }
val db = { val db = {
new Database(TestServerConfig) //ensure that the test table is always new in order to make tests on the same dataset all the time.
Files.deleteIfExists(Path.of("/tmp/test-database.sqlite"))
//open database
val db = new Database(TestServerConfig)
//fill the test database with a test dataset
val connection = DriverManager.getConnection("jdbc:sqlite:/tmp/test-database.sqlite")
val stmnt = connection.createStatement()
executeFile(stmnt, "table_init.sql")
executeFile(stmnt, "test_dataset.sql")
stmnt.close()
connection.close()
db
} }
private def executeFile(statement: Statement, url: String) =
val in = getClass.getClassLoader.getResourceAsStream(url)
val bytes = in.readAllBytes()
in.close()
val requests = new String(bytes).split(';')
requests.foreach(statement.execute)
} }

@ -2,13 +2,14 @@ package org.tbasket.test.pages
import io.getquill.jdbczio.Quill import io.getquill.jdbczio.Quill
import io.getquill.{SnakeCase, SqliteZioJdbcContext} import io.getquill.{SnakeCase, SqliteZioJdbcContext}
import org.tbasket.auth.Authentificator import org.tbasket.auth.Authenticator
import org.tbasket.data.{Database, DatabaseContext} import org.tbasket.data.{Database, DatabaseContext}
import org.tbasket.error.RegularException.InvalidRequest import org.tbasket.error.RegularException.InvalidRequest
import org.tbasket.handler.HandlerUtils.parseAttribute import org.tbasket.handler.HandlerUtils.parseAttribute
import org.tbasket.handler.LoginPageHandler import org.tbasket.handler.LoginPageHandler
import org.tbasket.handler.LoginPageHandler.post import org.tbasket.handler.LoginPageHandler.post
import org.tbasket.test.TestLayers import org.tbasket.test.TestLayers
import org.tbasket.test.pages.LoginPageHandlerTests.test
import zio.* import zio.*
import zio.http.netty.client.ConnectionPool import zio.http.netty.client.ConnectionPool
import zio.http.* import zio.http.*
@ -16,10 +17,11 @@ import zio.http.model.{HeaderNames, Headers}
import zio.http.model.Headers.Header import zio.http.model.Headers.Header
import zio.json.* import zio.json.*
import zio.json.ast.{Json, JsonCursor} import zio.json.ast.{Json, JsonCursor}
import zio.test.{TestAspect, *} import zio.test.*
import zio.test.Assertion.* import zio.test.Assertion.*
object LoginPageHandlerTests extends ZIOSpecDefault { object LoginPageHandlerTests extends ZIOSpecDefault {
import LoginPageHandler.post import LoginPageHandler.post
import TestLayers.* import TestLayers.*
@ -48,29 +50,38 @@ object LoginPageHandlerTests extends ZIOSpecDefault {
) )
private def loginSpec = { private def loginSpec = {
suite("login situation tests") ( suite("login situation tests")(
test("login with unknown account") { test("login with unknown account") {
for for
response <- post(Request.post(Body.fromString("""{"password":"123456","email":"maximebatista18@gmail.com"}"""), URL.empty)) response <- post(Request.post(Body.fromString("""{"password":"123456","email":"unknownaccount@gmail.com"}"""), URL.empty))
json <- getJsonBody(response) json <- getJsonBody(response)
errorType <- parseAttribute(json, "error", JsonCursor.field("error").isString) errorType <- parseAttribute(json, "error", JsonCursor.field("error").isString)
yield yield
//assert that the response error is of type unauthorized and headers are Location: /register //assert that the response error is of type unauthorized and headers are Location: /register
assert(errorType)(equalTo("unauthorized")) assert(errorType)(equalTo("unauthorized"))
&& assert(response)(hasField("headers", _.headers, hasSameElements(Headers.location("/register")))) && assert(response)(hasField("headers", _.headers, hasSameElements(Headers.location("/register"))))
},
test("login with known account") {
for
response <- post(Request.post(Body.fromString("""{"password":"123456","email":"maximebatista18@gmail.com"}"""), URL.empty))
json <- getJsonBody(response)
//errorType <- parseAttribute(json, "error", JsonCursor.field("error").isString)
yield
assert(response)(hasField("headers", _.headers, exists(hasField("key", _.key, equalTo(HeaderNames.setCookie)))))
} }
) )
} }
override def spec = suite("/login page handler") ( override def spec = suite("/login page handler")(
requestsSpec, requestsSpec,
loginSpec loginSpec
).provide( ).provide(
db.datasourceLayer, db.datasourceLayer,
db.contextLayer, db.contextLayer,
auth, auth,
ConnectionPool.fixed(1), ConnectionPool.fixed(1),
Scope.default, Scope.default,
ClientConfig.default, ClientConfig.default,
Client.live) Client.live)
} }