Commit 0725d207 authored by akwizgran's avatar akwizgran

Merge branch '1432-headless-integration-tests' into 'master'

[headless] Add first integration test for ContactController

See merge request !1008
parents 5a7599a8 a22d03d0
Pipeline #2868 passed with stage
in 11 minutes and 16 seconds
......@@ -30,6 +30,9 @@ dependencies {
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
testImplementation "io.mockk:mockk:1.8.12"
testImplementation "org.skyscreamer:jsonassert:1.5.0"
testImplementation 'khttp:khttp:0.1.0'
kaptTest 'com.google.dagger:dagger-compiler:2.19'
}
jar {
......@@ -48,6 +51,7 @@ jar {
idea {
module {
sourceDirs += file('build/generated/source/kapt/main')
testSourceDirs += file('build/generated/source/kapt/test')
generatedSourceDirs += file('build/generated/source/kapt/main')
}
}
......
......@@ -13,17 +13,22 @@ import javax.annotation.concurrent.Immutable
import javax.inject.Inject
import javax.inject.Singleton
interface BriarService {
fun start()
fun stop()
}
@Immutable
@Singleton
internal class BriarService
internal class BriarServiceImpl
@Inject
constructor(
private val accountManager: AccountManager,
private val lifecycleManager: LifecycleManager,
private val passwordStrengthEstimator: PasswordStrengthEstimator
) {
) : BriarService {
fun start() {
override fun start() {
if (!accountManager.accountExists()) {
createAccount()
} else {
......@@ -36,9 +41,10 @@ constructor(
}
val dbKey = accountManager.databaseKey ?: throw AssertionError()
lifecycleManager.startServices(dbKey)
lifecycleManager.waitForStartup()
}
fun stop() {
override fun stop() {
lifecycleManager.stopServices()
lifecycleManager.waitForShutdown()
}
......
......@@ -56,6 +56,10 @@ import javax.net.SocketFactory
)
internal class HeadlessModule(private val appDir: File) {
@Provides
@Singleton
internal fun provideBriarService(briarService: BriarServiceImpl): BriarService = briarService
@Provides
@Singleton
internal fun provideDatabaseConfig(): DatabaseConfig {
......@@ -71,30 +75,21 @@ internal class HeadlessModule(private val appDir: File) {
locationUtils: LocationUtils, eventBus: EventBus, resourceProvider: ResourceProvider,
circumventionProvider: CircumventionProvider, batteryManager: BatteryManager, clock: Clock
): PluginConfig {
val torDirectory = File(appDir, "tor")
val duplex: List<DuplexPluginFactory>
if (isLinux() || isMac()) {
val duplex: List<DuplexPluginFactory> = if (isLinux() || isMac()) {
val torDirectory = File(appDir, "tor")
val tor = UnixTorPluginFactory(
ioExecutor, networkManager, locationUtils, eventBus, torSocketFactory,
backoffFactory, resourceProvider, circumventionProvider, batteryManager, clock,
torDirectory
)
duplex = listOf(tor)
listOf(tor)
} else {
duplex = emptyList()
emptyList()
}
return object : PluginConfig {
override fun getDuplexFactories(): Collection<DuplexPluginFactory> {
return duplex
}
override fun getSimplexFactories(): Collection<SimplexPluginFactory> {
return emptyList()
}
override fun shouldPoll(): Boolean {
return true
}
override fun getDuplexFactories(): Collection<DuplexPluginFactory> = duplex
override fun getSimplexFactories(): Collection<SimplexPluginFactory> = emptyList()
override fun shouldPoll(): Boolean = true
}
}
......@@ -104,21 +99,14 @@ internal class HeadlessModule(private val appDir: File) {
return object : DevConfig {
override fun getDevPublicKey(): PublicKey {
try {
return crypto.messageKeyParser
.parsePublicKey(fromHexString(DEV_PUBLIC_KEY_HEX))
return crypto.messageKeyParser.parsePublicKey(fromHexString(DEV_PUBLIC_KEY_HEX))
} catch (e: GeneralSecurityException) {
throw RuntimeException(e)
}
}
override fun getDevOnionAddress(): String {
return DEV_ONION_ADDRESS
}
override fun getReportDir(): File {
return File(appDir, "reportDir")
}
override fun getDevOnionAddress(): String = DEV_ONION_ADDRESS
override fun getReportDir(): File = File(appDir, "reportDir")
}
}
......
......@@ -41,7 +41,7 @@ constructor(
private val logger = getLogger(Router::javaClass.name)
private val stopped = AtomicBoolean(false)
fun start(authToken: String, port: Int, debug: Boolean) {
internal fun start(authToken: String, port: Int, debug: Boolean) : Javalin {
briarService.start()
getRuntime().addShutdownHook(Thread(this::stop))
......@@ -104,7 +104,7 @@ constructor(
webSocketController.sessions.remove(session)
}
}
app.start()
return app.start()
}
private fun serverStopped() {
......@@ -112,7 +112,7 @@ constructor(
exit(1)
}
private fun stop() {
internal fun stop() {
if (!stopped.getAndSet(true)) {
briarService.stop()
}
......
package org.briarproject.briar.headless
import dagger.Component
import org.briarproject.bramble.BrambleCoreEagerSingletons
import org.briarproject.bramble.BrambleCoreModule
import org.briarproject.bramble.account.AccountModule
import org.briarproject.bramble.test.TestSecureRandomModule
import org.briarproject.briar.BriarCoreEagerSingletons
import org.briarproject.briar.BriarCoreModule
import org.briarproject.briar.api.test.TestDataCreator
import javax.inject.Singleton
@Component(
modules = [
BrambleCoreModule::class,
BriarCoreModule::class,
TestSecureRandomModule::class,
AccountModule::class,
HeadlessTestModule::class
]
)
@Singleton
internal interface BriarHeadlessTestApp : BrambleCoreEagerSingletons, BriarCoreEagerSingletons {
fun getRouter(): Router
fun getTestDataCreator(): TestDataCreator
}
package org.briarproject.briar.headless
import org.briarproject.bramble.api.account.AccountManager
import org.briarproject.bramble.api.lifecycle.LifecycleManager
import javax.annotation.concurrent.Immutable
import javax.inject.Inject
import javax.inject.Singleton
const val user = "user"
const val pass = "pass"
@Immutable
@Singleton
internal class BriarTestServiceImpl
@Inject
constructor(
private val accountManager: AccountManager,
private val lifecycleManager: LifecycleManager
) : BriarService {
override fun start() {
if (accountManager.accountExists()) {
accountManager.deleteAccount()
}
accountManager.createAccount(user, pass)
if (!accountManager.signIn(pass)) {
throw AssertionError("Password invalid")
}
val dbKey = accountManager.databaseKey ?: throw AssertionError()
lifecycleManager.startServices(dbKey)
lifecycleManager.waitForStartup()
}
override fun stop() {
lifecycleManager.stopServices()
lifecycleManager.waitForShutdown()
}
}
package org.briarproject.briar.headless
import com.fasterxml.jackson.databind.ObjectMapper
import dagger.Module
import dagger.Provides
import org.briarproject.bramble.api.crypto.PublicKey
import org.briarproject.bramble.api.db.DatabaseConfig
import org.briarproject.bramble.api.plugin.PluginConfig
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory
import org.briarproject.bramble.api.reporting.DevConfig
import org.briarproject.bramble.network.JavaNetworkModule
import org.briarproject.bramble.plugin.tor.CircumventionModule
import org.briarproject.bramble.system.JavaSystemModule
import org.briarproject.briar.headless.blogs.HeadlessBlogModule
import org.briarproject.briar.headless.contact.HeadlessContactModule
import org.briarproject.briar.headless.event.HeadlessEventModule
import org.briarproject.briar.headless.forums.HeadlessForumModule
import org.briarproject.briar.headless.messaging.HeadlessMessagingModule
import java.io.File
import java.util.Collections.emptyList
import javax.inject.Singleton
@Module(
includes = [
JavaNetworkModule::class,
JavaSystemModule::class,
CircumventionModule::class,
HeadlessBlogModule::class,
HeadlessContactModule::class,
HeadlessEventModule::class,
HeadlessForumModule::class,
HeadlessMessagingModule::class
]
)
internal class HeadlessTestModule(private val appDir: File) {
@Provides
@Singleton
internal fun provideBriarService(briarService: BriarTestServiceImpl): BriarService =
briarService
@Provides
@Singleton
internal fun provideDatabaseConfig(): DatabaseConfig {
val dbDir = File(appDir, "db")
val keyDir = File(appDir, "key")
return HeadlessDatabaseConfig(dbDir, keyDir)
}
@Provides
internal fun providePluginConfig(): PluginConfig {
return object : PluginConfig {
override fun getDuplexFactories(): Collection<DuplexPluginFactory> = emptyList()
override fun getSimplexFactories(): Collection<SimplexPluginFactory> = emptyList()
override fun shouldPoll(): Boolean = false
}
}
@Provides
@Singleton
internal fun provideDevConfig(): DevConfig = object : DevConfig {
override fun getDevPublicKey(): PublicKey = throw NotImplementedError()
override fun getDevOnionAddress(): String = throw NotImplementedError()
override fun getReportDir(): File = throw NotImplementedError()
}
@Provides
@Singleton
internal fun provideObjectMapper() = ObjectMapper()
}
package org.briarproject.briar.headless
import io.javalin.Javalin
import io.javalin.core.util.Header.AUTHORIZATION
import khttp.responses.Response
import org.briarproject.bramble.BrambleCoreModule
import org.briarproject.briar.BriarCoreModule
import org.briarproject.briar.api.test.TestDataCreator
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
import java.io.File
const val port = 8000
const val url = "http://127.0.0.1:$port/v1"
const val token = "authToken"
@TestInstance(PER_CLASS)
abstract class IntegrationTest {
private val dataDir = File("tmp")
protected lateinit var api: Javalin
protected lateinit var testDataCreator: TestDataCreator
private lateinit var router: Router
@BeforeAll
fun setUp() {
val app = DaggerBriarHeadlessTestApp.builder()
.headlessTestModule(HeadlessTestModule(dataDir))
.build()
BrambleCoreModule.initEagerSingletons(app)
BriarCoreModule.initEagerSingletons(app)
router = app.getRouter()
testDataCreator = app.getTestDataCreator()
api = router.start(token, port, false)
}
@AfterAll
fun tearDown() {
router.stop()
dataDir.deleteRecursively()
}
protected fun get(url: String) : Response {
return khttp.get(url, getAuthTokenHeader(token))
}
protected fun getWithWrongToken(url: String) : Response {
return khttp.get(url, getAuthTokenHeader("wrongToken"))
}
protected fun delete(url: String) : Response {
return khttp.delete(url, getAuthTokenHeader(token))
}
protected fun deleteWithWrongToken(url: String) : Response {
return khttp.delete(url, getAuthTokenHeader("wrongToken"))
}
private fun getAuthTokenHeader(token: String) = mapOf(Pair(AUTHORIZATION, "Bearer $token"))
}
package org.briarproject.briar.headless.contact
import org.briarproject.briar.headless.IntegrationTest
import org.briarproject.briar.headless.url
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class ContactControllerIntegrationTest: IntegrationTest() {
@Test
fun `list of contacts need authentication token`() {
val response = getWithWrongToken("$url/contacts")
assertEquals(401, response.statusCode)
}
@Test
fun `returns list of contacts`() {
// retrieve empty list of contacts
var response = get("$url/contacts")
assertEquals(200, response.statusCode)
assertEquals(0, response.jsonArray.length())
// add one test contact
val testContactName= "testContactName"
testDataCreator.addContact(testContactName)
// retrieve list with one test contact
response = get("$url/contacts")
assertEquals(200, response.statusCode)
assertEquals(1, response.jsonArray.length())
val contact = response.jsonArray.getJSONObject(0)
val author = contact.getJSONObject("author")
assertEquals(testContactName, author.getString("name"))
}
@Test
fun `deleting contact need authentication token`() {
val response = deleteWithWrongToken("$url/contacts/1")
assertEquals(401, response.statusCode)
}
@Test
fun `deleting real and non-existing contact`() {
var response = delete("$url/contacts/1")
assertEquals(200, response.statusCode)
response = delete("$url/contacts/1")
assertEquals(404, response.statusCode)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment