mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-21 15:19:53 +01:00
[headless] Add first integration test for ContactController
This commit is contained in:
@@ -30,6 +30,9 @@ dependencies {
|
|||||||
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||||
testImplementation "io.mockk:mockk:1.8.12"
|
testImplementation "io.mockk:mockk:1.8.12"
|
||||||
testImplementation "org.skyscreamer:jsonassert:1.5.0"
|
testImplementation "org.skyscreamer:jsonassert:1.5.0"
|
||||||
|
testImplementation 'khttp:khttp:0.1.0'
|
||||||
|
|
||||||
|
kaptTest 'com.google.dagger:dagger-compiler:2.19'
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
@@ -48,6 +51,7 @@ jar {
|
|||||||
idea {
|
idea {
|
||||||
module {
|
module {
|
||||||
sourceDirs += file('build/generated/source/kapt/main')
|
sourceDirs += file('build/generated/source/kapt/main')
|
||||||
|
testSourceDirs += file('build/generated/source/kapt/test')
|
||||||
generatedSourceDirs += file('build/generated/source/kapt/main')
|
generatedSourceDirs += file('build/generated/source/kapt/main')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,22 @@ import javax.annotation.concurrent.Immutable
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
interface BriarService {
|
||||||
|
fun start()
|
||||||
|
fun stop()
|
||||||
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@Singleton
|
@Singleton
|
||||||
internal class BriarService
|
internal class BriarServiceImpl
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
private val accountManager: AccountManager,
|
private val accountManager: AccountManager,
|
||||||
private val lifecycleManager: LifecycleManager,
|
private val lifecycleManager: LifecycleManager,
|
||||||
private val passwordStrengthEstimator: PasswordStrengthEstimator
|
private val passwordStrengthEstimator: PasswordStrengthEstimator
|
||||||
) {
|
) : BriarService {
|
||||||
|
|
||||||
fun start() {
|
override fun start() {
|
||||||
if (!accountManager.accountExists()) {
|
if (!accountManager.accountExists()) {
|
||||||
createAccount()
|
createAccount()
|
||||||
} else {
|
} else {
|
||||||
@@ -38,7 +43,7 @@ constructor(
|
|||||||
lifecycleManager.startServices(dbKey)
|
lifecycleManager.startServices(dbKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
override fun stop() {
|
||||||
lifecycleManager.stopServices()
|
lifecycleManager.stopServices()
|
||||||
lifecycleManager.waitForShutdown()
|
lifecycleManager.waitForShutdown()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ import javax.net.SocketFactory
|
|||||||
)
|
)
|
||||||
internal class HeadlessModule(private val appDir: File) {
|
internal class HeadlessModule(private val appDir: File) {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
internal fun provideBriarService(briarService: BriarServiceImpl): BriarService = briarService
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
internal fun provideDatabaseConfig(): DatabaseConfig {
|
internal fun provideDatabaseConfig(): DatabaseConfig {
|
||||||
@@ -71,30 +75,21 @@ internal class HeadlessModule(private val appDir: File) {
|
|||||||
locationUtils: LocationUtils, eventBus: EventBus, resourceProvider: ResourceProvider,
|
locationUtils: LocationUtils, eventBus: EventBus, resourceProvider: ResourceProvider,
|
||||||
circumventionProvider: CircumventionProvider, batteryManager: BatteryManager, clock: Clock
|
circumventionProvider: CircumventionProvider, batteryManager: BatteryManager, clock: Clock
|
||||||
): PluginConfig {
|
): PluginConfig {
|
||||||
val torDirectory = File(appDir, "tor")
|
val duplex: List<DuplexPluginFactory> = if (isLinux() || isMac()) {
|
||||||
val duplex: List<DuplexPluginFactory>
|
val torDirectory = File(appDir, "tor")
|
||||||
if (isLinux() || isMac()) {
|
|
||||||
val tor = UnixTorPluginFactory(
|
val tor = UnixTorPluginFactory(
|
||||||
ioExecutor, networkManager, locationUtils, eventBus, torSocketFactory,
|
ioExecutor, networkManager, locationUtils, eventBus, torSocketFactory,
|
||||||
backoffFactory, resourceProvider, circumventionProvider, batteryManager, clock,
|
backoffFactory, resourceProvider, circumventionProvider, batteryManager, clock,
|
||||||
torDirectory
|
torDirectory
|
||||||
)
|
)
|
||||||
duplex = listOf(tor)
|
listOf(tor)
|
||||||
} else {
|
} else {
|
||||||
duplex = emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
return object : PluginConfig {
|
return object : PluginConfig {
|
||||||
override fun getDuplexFactories(): Collection<DuplexPluginFactory> {
|
override fun getDuplexFactories(): Collection<DuplexPluginFactory> = duplex
|
||||||
return duplex
|
override fun getSimplexFactories(): Collection<SimplexPluginFactory> = emptyList()
|
||||||
}
|
override fun shouldPoll(): Boolean = true
|
||||||
|
|
||||||
override fun getSimplexFactories(): Collection<SimplexPluginFactory> {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun shouldPoll(): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,21 +99,14 @@ internal class HeadlessModule(private val appDir: File) {
|
|||||||
return object : DevConfig {
|
return object : DevConfig {
|
||||||
override fun getDevPublicKey(): PublicKey {
|
override fun getDevPublicKey(): PublicKey {
|
||||||
try {
|
try {
|
||||||
return crypto.messageKeyParser
|
return crypto.messageKeyParser.parsePublicKey(fromHexString(DEV_PUBLIC_KEY_HEX))
|
||||||
.parsePublicKey(fromHexString(DEV_PUBLIC_KEY_HEX))
|
|
||||||
} catch (e: GeneralSecurityException) {
|
} catch (e: GeneralSecurityException) {
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDevOnionAddress(): String {
|
override fun getDevOnionAddress(): String = DEV_ONION_ADDRESS
|
||||||
return DEV_ONION_ADDRESS
|
override fun getReportDir(): File = File(appDir, "reportDir")
|
||||||
}
|
|
||||||
|
|
||||||
override fun getReportDir(): File {
|
|
||||||
return File(appDir, "reportDir")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ constructor(
|
|||||||
private val logger = getLogger(Router::javaClass.name)
|
private val logger = getLogger(Router::javaClass.name)
|
||||||
private val stopped = AtomicBoolean(false)
|
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()
|
briarService.start()
|
||||||
getRuntime().addShutdownHook(Thread(this::stop))
|
getRuntime().addShutdownHook(Thread(this::stop))
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ constructor(
|
|||||||
webSocketController.sessions.remove(session)
|
webSocketController.sessions.remove(session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.start()
|
return app.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun serverStopped() {
|
private fun serverStopped() {
|
||||||
@@ -112,7 +112,7 @@ constructor(
|
|||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stop() {
|
internal fun stop() {
|
||||||
if (!stopped.getAndSet(true)) {
|
if (!stopped.getAndSet(true)) {
|
||||||
briarService.stop()
|
briarService.stop()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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<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()
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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"))
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user