fixs and move files

This commit is contained in:
2019-08-03 00:57:00 +02:00
parent 63a50dcb92
commit 7c3028eca2
60 changed files with 171 additions and 105 deletions

View File

@@ -47,9 +47,3 @@ dependencies {
testImplementation("io.cucumber:cucumber-java8:4.3.1")
testImplementation("io.cucumber:cucumber-junit:4.3.1")
}
kotlin.sourceSets["main"].kotlin.srcDirs("src")
kotlin.sourceSets["test"].kotlin.srcDirs("test")
sourceSets["main"].resources.srcDirs("resources")
sourceSets["test"].resources.srcDirs("testresources")

View File

@@ -1,18 +0,0 @@
create or replace procedure insert_user(inout resource json) language plpgsql as
$$
declare
new_id uuid;
begin
insert into "user" (username, password, blocked_at)
select
username,
crypt(resource->>'plain_password', gen_salt('bf', 8)),
case when blocked_at is not null then now() else null end
from json_populate_record(null::"user", resource)
returning id into new_id;
select find_user_by_id(new_id) into resource;
end;
$$;
-- drop procedure if exists insert_user(inout json);

View File

@@ -1,2 +0,0 @@
-- User
drop view if exists user_lite;

View File

@@ -1,5 +0,0 @@
-- User
create or replace view user_lite as
select u.id, u.created_at, u.blocked_at, u.username
from "user" u
where u.blocked_at is null;

View File

@@ -1,26 +0,0 @@
package fr.dcproject
import fr.postgresjson.connexion.Requester
import io.ktor.util.KtorExperimentalAPI
import org.koin.dsl.module
import java.io.File
import fr.dcproject.repository.Article as ArticleRepository
val config = Config()
@KtorExperimentalAPI
val Module = module {
single { config }
single { Requester.RequesterFactory(
host = config.host,
database = config.database,
username = config.username,
password = config.password,
port = config.port,
functionsDirectory = File(this::class.java.getResource("/sql/functions").toURI())
).createRequester() }
single { ArticleRepository(get<Requester>()) }
}

View File

@@ -52,8 +52,8 @@ fun Application.module() {
convert<Article> {
decode { values, _ ->
val id = values.singleOrNull()?.let { UUID.fromString(it) }
?: throw InternalError("Cannot convert $values to Article")
get<RepositoryArticle>().findById(id)
?: throw InternalError("Cannot convert $values to UUID")
get<RepositoryArticle>().findById(id) ?: throw InternalError("Article $values not found")
}
}
}

View File

@@ -1,14 +1,16 @@
package fr.dcproject
import com.typesafe.config.ConfigFactory
import java.io.File
class Config {
private var config = ConfigFactory.load()
val sqlFiles = File(this::class.java.getResource("/sql").toURI())
val envName: String = config.getString("app.envName")
val host: String = config.getString("db.host")
val database: String = config.getString("db.database")
val username: String = config.getString("db.username")
val password: String = config.getString("db.password")
var database: String = config.getString("db.database")
var username: String = config.getString("db.username")
var password: String = config.getString("db.password")
val port: Int = config.getInt("db.port")
}

View File

@@ -0,0 +1,26 @@
package fr.dcproject
import fr.postgresjson.connexion.Connection
import fr.postgresjson.connexion.Requester
import fr.postgresjson.migration.Migrations
import io.ktor.util.KtorExperimentalAPI
import org.koin.dsl.module
import fr.dcproject.repository.Article as ArticleRepository
val config = Config()
@KtorExperimentalAPI
val Module = module {
single { config }
single { Connection(host = config.host, port = config.port, database = config.database, username = config.username, password = config.password) }
single { Requester.RequesterFactory(
connection = get(),
functionsDirectory = config.sqlFiles.resolve("functions")
).createRequester() }
single { ArticleRepository(get()) }
single { Migrations(connection = get(), directory = config.sqlFiles) }
}

View File

@@ -3,22 +3,23 @@ package fr.dcproject.entity
import fr.postgresjson.entity.EntityCreatedAt
import fr.postgresjson.entity.EntityCreatedAtImp
import fr.postgresjson.entity.UuidEntity
import org.joda.time.DateTime
import java.util.*
class Citizen(
id: UUID?,
id: UUID = UUID.randomUUID(),
var name: Name?,
var birthday: String?,
var userId: String?,
var voteAnnonymous: Boolean?,
var followAnnonymous: Boolean?,
var birthday: DateTime?,
var userId: String? = null,
var voteAnnonymous: Boolean? = null,
var followAnnonymous: Boolean? = null,
var user: User?
) : UuidEntity(id),
EntityCreatedAt by EntityCreatedAtImp() {
data class Name(
var civility: String?,
var firstName: String?,
var lastName: String?,
var firstName: String?
var civility: String? = null
)
}

View File

@@ -5,11 +5,10 @@ import org.joda.time.DateTime
import java.util.*
class User(
id: UUID?,
id: UUID? = UUID.randomUUID(),
var username: String?,
var blockedAt: DateTime?,
override var createdAt: DateTime?,
override var updatedAt: DateTime?
var blockedAt: DateTime? = null,
var plainPassword: String?
) : UuidEntity(id),
EntityCreatedAt by EntityCreatedAtImp(),
EntityUpdatedAt by EntityUpdatedAtImp()

View File

@@ -1,4 +1,4 @@
create or replace procedure upsert_citizen(inout resource json)
create or replace function upsert_citizen(inout resource json)
language plpgsql as
$$
declare
@@ -25,4 +25,4 @@ begin
end;
$$;
-- drop procedure if exists insert_user(inout json);
-- drop function if exists upsert_citizen(inout json);

View File

@@ -0,0 +1,19 @@
create or replace function insert_user(inout resource json) language plpgsql as
$$
declare
new_id uuid;
begin
insert into "user" (id, username, password, blocked_at)
select
coalesce(t.id, uuid_generate_v4()),
t.username,
crypt(resource->>'plain_password', gen_salt('bf', 8)),
case when t.blocked_at is not null then now() else null end
from json_populate_record(null::"user", resource) t
returning id into new_id;
select find_user_by_id(new_id) into resource;
end;
$$;
-- drop function if exists insert_user(inout json);

View File

@@ -1,6 +1,4 @@
-- Users
create extension if not exists pgcrypto;
create table "user"
(
id uuid default uuid_generate_v4() not null primary key,

View File

@@ -9,7 +9,7 @@ declare
selected_article json;
begin
-- insert user for context
call insert_user(created_user);
select insert_user(created_user) into created_user;
_user_id := created_user->>'id';
created_citizen := jsonb_set(created_citizen::jsonb, '{user}'::text[], jsonb_build_object('id', _user_id::text), true)::json;
assert created_citizen#>>'{user, id}' = _user_id::text, format('userId in citizen must be the same as user, %s = %s', created_citizen#>>'{user, id}', _user_id::text);

View File

@@ -8,7 +8,7 @@ declare
selected_citizen json;
begin
-- insert user for context
call insert_user(created_user);
select insert_user(created_user) into created_user;
_user_id := created_user->>'id';
created_citizen := jsonb_set(created_citizen::jsonb, '{user}'::text[], jsonb_build_object('id', _user_id::text), true)::json;
assert created_citizen#>>'{user, id}' = _user_id::text, format('userId in citizen must be the same as user, %s = %s', created_citizen#>>'{user, id}', _user_id::text);

View File

@@ -28,8 +28,8 @@ declare
_comment_id uuid;
begin
-- insert user for context
call insert_user(created_user);
call insert_user(created_user2);
select insert_user(created_user) into created_user;
select insert_user(created_user2) into created_user2;
created_citizen := jsonb_set(created_citizen::jsonb, '{user}'::text[], jsonb_build_object('id', created_user->>'id'), true)::json;
-- insert new citizen for context

View File

@@ -42,7 +42,7 @@ declare
$json$;
begin
-- insert user for context
call insert_user(created_user);
select insert_user(created_user) into created_user;
_user_id := created_user->>'id';
created_citizen := jsonb_set(created_citizen::jsonb, '{user}'::text[], jsonb_build_object('id', _user_id::text), true)::json;
assert created_citizen#>>'{user, id}' = _user_id::text, format('userId in citizen must be the same as user, %s = %s', created_citizen#>>'{user, id}', _user_id::text);

View File

@@ -25,8 +25,8 @@ declare
$json$;
begin
-- insert user for context
call insert_user(created_user);
call insert_user(created_user2);
select insert_user(created_user) into created_user;
select insert_user(created_user2) into created_user2;
created_citizen := jsonb_set(created_citizen::jsonb, '{user}'::text[], jsonb_build_object('id', created_user->>'id'), true)::json;
created_citizen2 := jsonb_set(created_citizen2::jsonb, '{user}'::text[], jsonb_build_object('id', created_user2->>'id'), true)::json;

View File

@@ -6,7 +6,7 @@ declare
exist_user json;
begin
-- Insert user and check if username and password is correct
call insert_user(created_user);
select insert_user(created_user) into created_user;
assert created_user->>'username' = 'george', 'username must be george';
assert created_user->>'password' is null, 'password must not be returned';

View File

@@ -26,7 +26,7 @@ declare
$json$;
begin
-- insert user for context
call insert_user(created_user);
select insert_user(created_user) into created_user;
created_citizen := jsonb_set(created_citizen::jsonb, '{user}'::text[], jsonb_build_object('id', created_user->>'id'), true)::json;
-- insert new citizen for context

View File

@@ -1,17 +1,51 @@
package fr.dcproject
import fr.postgresjson.migration.Migrations
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.server.testing.handleRequest
import io.ktor.server.testing.setBody
import io.ktor.server.testing.withTestApplication
import kotlin.test.Test
import io.ktor.util.KtorExperimentalAPI
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.test.KoinTest
import org.koin.test.inject
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class ArticleRouteTest {
@KtorExperimentalLocationsAPI
@KtorExperimentalAPI
@TestInstance(PER_CLASS)
class ArticleRouteTest: KoinTest {
private val migrations: Migrations by inject()
@BeforeEach
fun beforeAll() {
startKoin {
modules(Module)
config.database = "test"
config.username = "test"
config.password = "test"
}
migrations.run()
}
@AfterEach
fun afterAll() {
migrations.forceAllDown()
stopKoin()
}
private val article: String = """{
"id" : "8e8dd0aa-2b2b-41e1-bff5-ea613c988774",
"version_id" : "e3ec9ea8-87ac-46ac-8321-8f2bc8c687bc",
@@ -39,7 +73,7 @@ class ArticleRouteTest {
}"""
@Test
fun testRoot() {
fun testRoute() {
withTestApplication({ module() }) {
handleRequest(HttpMethod.Get, "/articles").apply {
assertEquals(HttpStatusCode.OK, response.status())

View File

@@ -3,22 +3,32 @@ import cucumber.api.Scenario
import cucumber.api.java8.En
import cucumber.api.junit.Cucumber
import feature.Context
import fr.dcproject.config
import fr.postgresjson.migration.Migrations
import io.ktor.server.testing.TestApplicationEngine
import io.ktor.server.testing.createTestEnvironment
import org.junit.runner.RunWith
import org.koin.test.KoinTest
import org.koin.test.inject
import java.util.concurrent.TimeUnit
import feature.Context.Companion.current as contextCurrent
@RunWith(Cucumber::class)
@CucumberOptions(plugin = ["pretty"])
class RunCucumberTest: En {
class RunCucumberTest: En, KoinTest {
private val migrations: Migrations by inject()
init {
Before(-1) { scenario: Scenario ->
// config.database = "dc-projectg-test"
config.database = "test"
config.username = "test"
config.password = "test"
contextCurrent = Context(TestApplicationEngine(createTestEnvironment()) {}, scenario)
migrations.run()
}
After { scenario: Scenario ->
migrations.forceAllDown()
contextCurrent.engine.stop(0L, 0L, TimeUnit.MILLISECONDS)
}
}

View File

@@ -3,10 +3,14 @@ package feature
import cucumber.api.Scenario
import fr.dcproject.module
import io.ktor.application.Application
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.server.testing.TestApplicationCall
import io.ktor.server.testing.TestApplicationEngine
import io.ktor.server.testing.TestApplicationRequest
import io.ktor.util.KtorExperimentalAPI
@KtorExperimentalLocationsAPI
@KtorExperimentalAPI
class Context(
val engine: TestApplicationEngine,
val scenario: Scenario

View File

@@ -1,8 +1,11 @@
package feature
import com.google.gson.Gson
import cucumber.api.Scenario
import cucumber.api.java8.En
import fr.dcproject.entity.Citizen
import fr.dcproject.entity.User
import fr.postgresjson.connexion.Requester
import fr.postgresjson.migration.Migrations
import io.cucumber.datatable.DataTable
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
@@ -11,18 +14,46 @@ import io.ktor.http.HttpStatusCode
import io.ktor.server.testing.TestApplicationCall
import io.ktor.server.testing.TestApplicationEngine
import io.ktor.server.testing.setBody
import org.joda.time.DateTime
import org.junit.jupiter.api.Assertions.assertEquals
import org.koin.test.KoinTest
import org.koin.test.inject
import org.opentest4j.AssertionFailedError
import java.util.*
import kotlin.test.asserter
import feature.Context.Companion.current as currentContext
class Request: En, KoinTest {
private val migrations: Migrations by inject()
private val requester: Requester by inject()
init {
Before { scenario: Scenario ->
}
// Before { scenario: Scenario ->
// migrations.run()
// }
//
// After { scenario: Scenario ->
// migrations.forceAllDown()
// }
After { scenario: Scenario ->
When("I have citizen:") { body: DataTable ->
val user = User(username = "jaque", plainPassword = "azerty")
val data = body.asMap<String, String>(String::class.java, String::class.java)
val citizen = Citizen(
id = UUID.fromString(data["id"]),
name = Citizen.Name(data["firstName"], data["lastName"]),
birthday = DateTime.now(),
user = user
)
val test: TestApplicationEngine.() -> Unit = {
requester
.getFunction("insert_user")
.selectOne(user)
requester
.getFunction("upsert_citizen")
.selectOne(citizen)
}
currentContext.engine.test()
}
When("I send a {string} request to {string} with body:") { method: String, uri: String, body: String ->
@@ -55,7 +86,7 @@ class Request: En, KoinTest {
Then("the response status code should be {int}") { statusCode: Int ->
val call: TestApplicationCall = currentContext.call ?: throw AssertionFailedError("No call", statusCode, null)
with(call) {
assertEquals(HttpStatusCode.fromValue(statusCode), response.status())
assertEquals(HttpStatusCode.fromValue(statusCode), response.status(), response.content)
}
}

View File

@@ -4,11 +4,15 @@ Feature: articles routes
When I send a "GET" request to "/articles"
Then the response status code should be 200
Scenario: The route for get article must response a 200
When I send a "GET" request to "/articles/55a24426-139b-4ee7-b1e2-a3d016d66cc2"
Then the response status code should be 200
# Scenario: The route for get article must response a 200
# When I send a "GET" request to "/articles/55a24426-139b-4ee7-b1e2-a3d016d66cc2"
# Then the response status code should be 200
Scenario: The route for get article must response a 200
Given I have citizen:
| id | 64b7b379-2298-43ec-b428-ba134930cabd |
| firstName | Jaque |
| lastName | Dupuis |
When I send a "POST" request to "/articles" with body:
"""
{
@@ -28,9 +32,4 @@ Feature: articles routes
Then the response status code should be 200
And the response should contain object:
| version_id | 09c418b6-63ba-448b-b38b-502b41cd500e |
| title | title2 |
When I send a "GET" request to "/articles/99afd1b1-3555-43c1-80a7-63c56e93d250"
Then the response status code should be 200
And the response should contain object:
| id | 99afd1b1-3555-43c1-80a7-63c56e93d250 |
| title | title2 |
| title | title2 |