From d857338ad05815a98fd77db94ca7c9b81453adc7 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 6 Nov 2018 10:52:02 -0300 Subject: [PATCH] [headless] Add first integration test for ContactController --- briar-headless/build.gradle | 4 ++ .../briar/headless/BriarService.kt | 13 ++-- .../briar/headless/HeadlessModule.kt | 40 ++++------- .../org/briarproject/briar/headless/Router.kt | 6 +- .../briar/headless/BriarHeadlessTestApp.kt | 27 +++++++ .../briar/headless/BriarTestServiceImpl.kt | 38 ++++++++++ .../briar/headless/HeadlessTestModule.kt | 72 +++++++++++++++++++ .../briar/headless/IntegrationTest.kt | 65 +++++++++++++++++ .../ContactControllerIntegrationTest.kt | 51 +++++++++++++ 9 files changed, 283 insertions(+), 33 deletions(-) create mode 100644 briar-headless/src/test/java/org/briarproject/briar/headless/BriarHeadlessTestApp.kt create mode 100644 briar-headless/src/test/java/org/briarproject/briar/headless/BriarTestServiceImpl.kt create mode 100644 briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt create mode 100644 briar-headless/src/test/java/org/briarproject/briar/headless/IntegrationTest.kt create mode 100644 briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt diff --git a/briar-headless/build.gradle b/briar-headless/build.gradle index 3ba479575..8d81de23c 100644 --- a/briar-headless/build.gradle +++ b/briar-headless/build.gradle @@ -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') } } diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/BriarService.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/BriarService.kt index 5f13b739e..389f3b531 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/BriarService.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/BriarService.kt @@ -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 { @@ -38,7 +43,7 @@ constructor( lifecycleManager.startServices(dbKey) } - fun stop() { + override fun stop() { lifecycleManager.stopServices() lifecycleManager.waitForShutdown() } diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt index be170446b..a5d567547 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt @@ -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 - if (isLinux() || isMac()) { + val duplex: List = 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 { - return duplex - } - - override fun getSimplexFactories(): Collection { - return emptyList() - } - - override fun shouldPoll(): Boolean { - return true - } + override fun getDuplexFactories(): Collection = duplex + override fun getSimplexFactories(): Collection = 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") } } diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt index 918da2618..e16f3eb4c 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt @@ -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() } diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/BriarHeadlessTestApp.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/BriarHeadlessTestApp.kt new file mode 100644 index 000000000..dedf355de --- /dev/null +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/BriarHeadlessTestApp.kt @@ -0,0 +1,27 @@ +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 +} diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/BriarTestServiceImpl.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/BriarTestServiceImpl.kt new file mode 100644 index 000000000..2d25b62e0 --- /dev/null +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/BriarTestServiceImpl.kt @@ -0,0 +1,38 @@ +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 java.lang.AssertionError("Password invalid") + } + val dbKey = accountManager.databaseKey ?: throw AssertionError() + lifecycleManager.startServices(dbKey) + } + + override fun stop() { + lifecycleManager.stopServices() + lifecycleManager.waitForShutdown() + } + +} diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt new file mode 100644 index 000000000..256a80c76 --- /dev/null +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt @@ -0,0 +1,72 @@ +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 = emptyList() + override fun getSimplexFactories(): Collection = 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() + +} diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/IntegrationTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/IntegrationTest.kt new file mode 100644 index 000000000..0884f34ff --- /dev/null +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/IntegrationTest.kt @@ -0,0 +1,65 @@ +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")) + +} diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt new file mode 100644 index 000000000..f00024620 --- /dev/null +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt @@ -0,0 +1,51 @@ +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) + } + +}