Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test coverage for backend #28

Open
wants to merge 33 commits into
base: master
from
Open
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6bc3044
Adding GET login test
oleksiyp Nov 1, 2017
c993d0b
Covered Login with tests
oleksiyp Nov 2, 2017
cdac7fb
Refactoring LoginKtTest
oleksiyp Nov 2, 2017
1e8db37
Refactoring LoginKtTest
oleksiyp Nov 2, 2017
d870b53
Covered IndexKt with tests
oleksiyp Nov 2, 2017
b8b3149
Upgrade to MockK 1.2
oleksiyp Nov 2, 2017
19b90d3
Using newer mockDsl in LoginKtTest
oleksiyp Nov 3, 2017
daa5166
Adding PostThoughtKtTest
oleksiyp Nov 3, 2017
73034a1
Adding forbidden case
oleksiyp Nov 3, 2017
c91da67
Added PostThoughtKtTest
oleksiyp Nov 3, 2017
cfbbbca
Ignoring test till next release
oleksiyp Nov 3, 2017
841757a
Revert: gradle-wrapper.properties
oleksiyp Nov 3, 2017
d98ed9d
Adding DeleteKtTest
oleksiyp Nov 4, 2017
f6a7cd3
Adding ViewThoughtKtTest
oleksiyp Nov 4, 2017
313c961
Adding UserPageKt
oleksiyp Nov 4, 2017
00a0500
Adding ApplicationPageTest
oleksiyp Nov 4, 2017
e00e79e
Adding RegisterKtTest
oleksiyp Nov 4, 2017
bd4b582
Using non-Nullable, getting rid of assertk
oleksiyp Nov 5, 2017
c901dbb
Logging config for tests
oleksiyp Nov 5, 2017
90ac181
Logging config for tests
oleksiyp Nov 5, 2017
ad5a990
Release version of MockK
oleksiyp Nov 5, 2017
38e88d1
Adding ThinkerDatabaseTest
oleksiyp Nov 6, 2017
b09f9de
Adding tests to ThinkerDatabaseTest
oleksiyp Nov 6, 2017
a69a90d
Adding tests to ThinkerDatabaseTest
oleksiyp Nov 6, 2017
ccd8402
Revert: gradle-wrapper.properties
oleksiyp Nov 6, 2017
ff25f46
Upgrade to version 1.4
oleksiyp Nov 9, 2017
8e09252
Upgrade to version 1.5
oleksiyp Nov 16, 2017
a25bd5b
Upgrade to version 1.5.1 Auto-hinting
oleksiyp Nov 17, 2017
fac616a
Upgrade to version 1.5.1
oleksiyp Nov 17, 2017
5688aa5
Upgrade to Version 1.5.2
oleksiyp Nov 17, 2017
d3a1ca9
Upgrade to Version 1.5.3
oleksiyp Nov 21, 2017
933f264
Merge branch 'master' of github.com:Kotlin/kotlin-fullstack-sample
oleksiyp Feb 17, 2019
58afe2d
Upgrade to 1.9.1
oleksiyp Feb 17, 2019
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

This file was deleted.

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="" vcs="Git" />
</component>
</project>
@@ -1,10 +1,15 @@
plugins {
id "jacoco"
}

group = 'org.jetbrains.demo.thinkter'
version = '0.0.1-SNAPSHOT'

apply plugin: 'kotlin'
apply plugin: 'application'

dependencies {

compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

@@ -18,6 +23,8 @@ dependencies {
testCompile "org.jetbrains.ktor:ktor-test-host:$ktor_version"
testCompile "org.jsoup:jsoup:1.9.1"

testCompile "io.mockk:mockk:1.9.1.kotlin12"

compile "org.jetbrains.ktor:ktor-jetty:$ktor_version"
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0'
}
@@ -42,3 +49,12 @@ kotlin {
}

mainClassName = 'org.jetbrains.ktor.jetty.DevelopmentHost'

jacocoTestReport {
reports {
xml.enabled true
html.enabled true
}
}

check.dependsOn jacocoTestReport
@@ -11,8 +11,6 @@ import org.jetbrains.ktor.util.*

fun Route.register(dao: ThinkterStorage, hashFunction: (String) -> String) {
post<Register> { form ->
val vm = call.request.content.get<ValuesMap>()

val user = call.sessionOrNull<Session>()?.let { dao.user(it.userId) }
if (user != null) {
call.redirect(LoginResponse(user))
@@ -40,6 +38,7 @@ fun Route.register(dao: ThinkterStorage, hashFunction: (String) -> String) {
application.environment.log.error("Failed to register user", e)
call.respond(LoginResponse(error = "Failed to register"))
}
return@post
}

call.session(Session(newUser.userId))
@@ -0,0 +1,35 @@
package org.jetbrains.demo.thinkter

import io.mockk.*
import kotlinx.coroutines.experimental.runBlocking
import org.jetbrains.ktor.application.ApplicationCall
import org.jetbrains.ktor.cio.ByteBufferWriteChannel
import org.jetbrains.ktor.html.HtmlContent
import org.jetbrains.ktor.html.respondHtmlTemplate
import org.junit.Test
import java.nio.charset.Charset

class ApplicationPageTest {
val appCall = mockk<ApplicationCall>()

@Test
fun testRenderHTML() {
coEvery { appCall.respond(any()) } just Runs

runBlocking {
appCall.respondHtmlTemplate(ApplicationPage()) {
caption { +"caption" }
}
}

coVerify {
appCall.respond(coAssert<HtmlContent> {
val channel = ByteBufferWriteChannel()
it.writeTo(channel)
val html = channel.toString(Charset.defaultCharset())
html.contains("caption") && html.contains("yui.yahooapis.com")
})
}

}
}
@@ -0,0 +1,96 @@
package org.jetbrains.demo.thinkter

import io.mockk.*
import org.jetbrains.demo.thinkter.dao.ThinkterStorage
import org.jetbrains.demo.thinkter.model.Thought
import org.jetbrains.demo.thinkter.model.User
import org.jetbrains.ktor.application.ApplicationCall
import org.jetbrains.ktor.http.HttpHeaders
import org.jetbrains.ktor.http.HttpStatusCode
import org.jetbrains.ktor.request.host
import org.jetbrains.ktor.sessions.SessionConfig
import org.jetbrains.ktor.util.AttributeKey
import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter

fun MockKMatcherScope.sessionMatcher(): AttributeKey<Session> =
match { it.name == "Session" }

fun MockKMatcherScope.sessionConfigMatcher(): AttributeKey<SessionConfig<*>> =
match { it.name == "SessionConfig" }


fun ApplicationCall.mockSessionReturningUser(dao: ThinkterStorage) {
every { attributes.contains(sessionMatcher()) } returns true

every {
attributes
.get(sessionMatcher())
} returns Session("userId")

every { dao.user("userId") } returns User("userId",
"email",
"User",
"pwd")
}


fun ApplicationCall.mockSessionReturningNothing() {
every { attributes.contains(sessionMatcher()) } returns false
}


fun ApplicationCall.checkForbiddenIfSesionReturningNothing(handle: () -> Unit) {
mockSessionReturningNothing()

coEvery { respond(any()) } just Runs

handle()

coVerify { respond(HttpStatusCode.Forbidden) }
}

fun ApplicationCall.mockHostReferrerHash(hash: (String) -> String) {
every { request.host() } returns "host"

every { request.headers[HttpHeaders.Referrer] } returns "http://abc/referrer"

every { hash(any()) } answers { firstArg<String>().reversed() }
}


fun mockGetThought(dao: ThinkterStorage, ts: Long) {
every {
dao.getThought(any())
} answers { Thought(firstArg(),
"userId",
"text",
formatDate(ts + firstArg<Int>()),
null) }
}

private fun formatDate(date: Long): String {
return Instant.ofEpochMilli(date)
.atZone(ZoneId.systemDefault())
.toOffsetDateTime()
.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
}


fun mockUser(dao: ThinkterStorage, pwdHash: String? = null): User {
val user = User("abcdef", "abc@def", "Abc Def", pwdHash ?: "")
every { dao.user("abcdef", pwdHash) } returns user
return user
}

fun ApplicationCall.mockPutSession() {
every {
attributes
.get(sessionConfigMatcher())
.sessionType
} returns Session::class

every { attributes.put(sessionMatcher(), any()) } just Runs
}

@@ -0,0 +1,104 @@
package org.jetbrains.demo.thinkter

import io.mockk.*
import org.jetbrains.demo.thinkter.dao.ThinkterStorage
import org.jetbrains.demo.thinkter.model.PostThoughtToken
import org.jetbrains.demo.thinkter.model.RpcData
import org.jetbrains.demo.thinkter.model.Thought
import org.jetbrains.ktor.http.HttpMethod
import org.jetbrains.ktor.locations.Locations
import org.jetbrains.ktor.routing.HttpMethodRouteSelector
import org.jetbrains.ktor.routing.Routing
import org.junit.Before
import org.junit.Test

class DeleteKtTest {
val route = mockk<Routing>()
val dao = mockk<ThinkterStorage>()
val hash = mockk<(String) -> String>()
val locations = mockk<Locations>()

val getThoughtDelete = RouteBlockSlot()
val postThoughtDelete = RouteBlockSlot()

@Before
fun setUp() {
route.mockDsl(locations) {
mockObj<ThoughtDelete> {
mockSelect(HttpMethodRouteSelector(HttpMethod.Get)) {
captureBlock(getThoughtDelete)
}
mockSelect(HttpMethodRouteSelector(HttpMethod.Post)) {
captureBlock(postThoughtDelete)
}
}
}

route.delete(dao, hash)
}

@Test
fun testGetThoughtDeleteOk() {
val data = ThoughtDelete(1, System.currentTimeMillis() - 6000, "abc")
getThoughtDelete.invokeBlock(locations, data) { handle ->
mockSessionReturningUser(dao)
mockHostReferrerHash(hash)

coEvery { respond(any()) } just Runs

handle()

coVerify {
respond(assert<PostThoughtToken> {
it.user == "userId" &&
it.code.contains("cba:tsoh:dIresu")
})
}
}
}

@Test
fun testGetThoughtDeleteNotLoggedIn() {
val data = ThoughtDelete(0, 0, "abc")
getThoughtDelete.invokeBlock(locations, data) { handle ->
checkForbiddenIfSesionReturningNothing(handle)
}
}

@Test
fun testPostThoughtDeleteOk() {
val ts = System.currentTimeMillis() - 6000
val data = ThoughtDelete(1, ts, "cba:tsoh:dIresu:" + ts.toString().reversed())
postThoughtDelete.invokeBlock(locations, data) { handle ->
mockSessionReturningUser(dao)
mockHostReferrerHash(hash)
mockGetThought(dao, ts)

every {
dao.deleteThought(1)
} just Runs

coEvery { respond(any()) } just Runs

handle()

coVerify {
respond(ofType(RpcData::class))
}
}
}

@Test
fun testPostThoughtDeleteNotLoggedIn() {
val data = ThoughtDelete(1, 0, "abc")
val ts = System.currentTimeMillis()
postThoughtDelete.invokeBlock(locations, data) { handle ->
every {
dao.getThought(1)
} answers { Thought(1, "userId", "text", ts.toString(), null) }

checkForbiddenIfSesionReturningNothing(handle)
}
}

}
ProTip! Use n and p to navigate between commits in a pull request.