fixs and move files
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
@@ -1,2 +0,0 @@
|
||||
-- User
|
||||
drop view if exists user_lite;
|
||||
@@ -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;
|
||||
@@ -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>()) }
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
26
src/main/kotlin/fr/dcproject/Module.kt
Normal file
26
src/main/kotlin/fr/dcproject/Module.kt
Normal 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) }
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
19
src/main/resources/sql/functions/user/insert_user.sql
Normal file
19
src/main/resources/sql/functions/user/insert_user.sql
Normal 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);
|
||||
@@ -1,6 +1,4 @@
|
||||
-- Users
|
||||
create extension if not exists pgcrypto;
|
||||
|
||||
create table "user"
|
||||
(
|
||||
id uuid default uuid_generate_v4() not null primary key,
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
@@ -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())
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
Reference in New Issue
Block a user