diff --git a/mailbox-core/build.gradle b/mailbox-core/build.gradle index cbf2b5ce91682019cceb3b02207fc2646bfa496b..198bff74dfbca8d44375f04a8b78fe24d8b7a4b1 100644 --- a/mailbox-core/build.gradle +++ b/mailbox-core/build.gradle @@ -23,6 +23,7 @@ dependencies { implementation "io.ktor:ktor-server-core:$ktor_version" implementation "io.ktor:ktor-server-netty:$ktor_version" implementation "io.ktor:ktor-auth:$ktor_version" + implementation "io.ktor:ktor-jackson:$ktor_version" api "org.slf4j:slf4j-api:1.7.32" implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6 @@ -34,6 +35,7 @@ dependencies { testImplementation "io.mockk:mockk:$mockk_version" testImplementation "ch.qos.logback:logback-classic:1.2.5" testImplementation "io.ktor:ktor-client-cio:$ktor_version" + testImplementation "io.ktor:ktor-client-jackson:$ktor_version" testImplementation "com.google.dagger:hilt-core:$hilt_version" kaptTest "com.google.dagger:dagger-compiler:$hilt_version" } diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/api/Contact.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/contacts/Contact.kt similarity index 57% rename from mailbox-core/src/main/java/org/briarproject/mailbox/core/api/Contact.kt rename to mailbox-core/src/main/java/org/briarproject/mailbox/core/contacts/Contact.kt index 86eded1fdf72cb27b6c025e6396725026c5f25a2..2565668dfab29e2636dc4d8d96c990820769df09 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/api/Contact.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/contacts/Contact.kt @@ -1,7 +1,7 @@ -package org.briarproject.mailbox.core.api +package org.briarproject.mailbox.core.contacts data class Contact( - val id: Int, + val contactId: Int, val token: String, val inboxId: String, val outboxId: String, diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/Database.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/Database.kt index e108f9da656e355e7ef445cca88cde27ed214cc1..7fbb0c5c8dc23f5a8925bbf7f1c9d5f65d411b1b 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/Database.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/Database.kt @@ -1,6 +1,6 @@ package org.briarproject.mailbox.core.db -import org.briarproject.mailbox.core.api.Contact +import org.briarproject.mailbox.core.contacts.Contact import org.briarproject.mailbox.core.settings.Settings interface Database : TransactionManager { diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/JdbcDatabase.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/JdbcDatabase.kt index 651263331b3c9c85bf447f1d22a93e188eaeab7c..c85ecb099fcb7700029688f28c1b6490cef5fa6f 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/JdbcDatabase.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/JdbcDatabase.kt @@ -1,6 +1,6 @@ package org.briarproject.mailbox.core.db -import org.briarproject.mailbox.core.api.Contact +import org.briarproject.mailbox.core.contacts.Contact import org.briarproject.mailbox.core.db.DatabaseConstants.Companion.DB_SETTINGS_NAMESPACE import org.briarproject.mailbox.core.db.DatabaseConstants.Companion.DIRTY_KEY import org.briarproject.mailbox.core.db.DatabaseConstants.Companion.LAST_COMPACTED_KEY @@ -449,7 +449,7 @@ abstract class JdbcDatabase(private val dbTypes: DatabaseTypes, private val cloc VALUES (?, ?, ?, ?) """.trimIndent() ps = connection.prepareStatement(sql) - ps.setInt(1, contact.id) + ps.setInt(1, contact.contactId) ps.setString(2, contact.token) ps.setString(3, contact.inboxId) ps.setString(4, contact.outboxId) diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/AuthManager.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/AuthManager.kt index 6d4f5a8a1f533c359862249344b757dfe51018c9..1982b8b503842cbce8db6c2d8ed4d2befd41bc07 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/AuthManager.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/AuthManager.kt @@ -1,7 +1,7 @@ package org.briarproject.mailbox.core.server import io.ktor.auth.Principal -import org.briarproject.mailbox.core.api.Contact +import org.briarproject.mailbox.core.contacts.Contact import org.briarproject.mailbox.core.db.Database import org.briarproject.mailbox.core.server.MailboxPrincipal.ContactPrincipal import org.briarproject.mailbox.core.server.MailboxPrincipal.OwnerPrincipal diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt index 8cecb21ae2b1fa458d5147ed13a6c21dc028bf77..60cd89b501261cbb3135999727565ba09ae18bc5 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt @@ -3,6 +3,8 @@ package org.briarproject.mailbox.core.server import io.ktor.application.install import io.ktor.auth.Authentication import io.ktor.features.CallLogging +import io.ktor.features.ContentNegotiation +import io.ktor.jackson.jackson import io.ktor.server.engine.embeddedServer import io.ktor.server.netty.Netty import org.briarproject.mailbox.core.files.FileManager @@ -41,6 +43,9 @@ internal class WebServerManagerImpl @Inject constructor( } } } + install(ContentNegotiation) { + jackson() + } configureBasicApi() configureContactApi() configureFilesApi(fileManager) diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/system/RandomIdManager.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/system/RandomIdManager.kt index e7379b9903d5ebece7825fad8523a8770e57aec3..66c89d17ca66c261845310241a16d4034abab19c 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/system/RandomIdManager.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/system/RandomIdManager.kt @@ -3,7 +3,7 @@ package org.briarproject.mailbox.core.system import java.security.SecureRandom import javax.inject.Inject -private const val ID_SIZE = 32 +internal const val ID_SIZE = 32 private const val ID_HEX_SIZE = ID_SIZE * 2 /** diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestUtils.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestUtils.kt index 2025f841c43d7de9eae5293f0adc759d9616710f..9ec2c1b1be157711e7f86ff4316e9fb913692c17 100644 --- a/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestUtils.kt +++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/TestUtils.kt @@ -1,7 +1,15 @@ package org.briarproject.mailbox.core +import io.mockk.every +import io.mockk.mockk +import org.briarproject.mailbox.core.contacts.Contact +import org.briarproject.mailbox.core.db.Database +import org.briarproject.mailbox.core.db.Transaction +import org.briarproject.mailbox.core.system.ID_SIZE +import org.briarproject.mailbox.core.system.toHex import org.briarproject.mailbox.core.util.IoUtils import java.io.File +import kotlin.random.Random object TestUtils { @@ -10,4 +18,26 @@ object TestUtils { testDir.parentFile.delete() // Delete if empty } + fun getNewRandomId(): String = Random.nextBytes(ID_SIZE).toHex() + + fun getNewRandomContact(id: Int = Random.nextInt(1, Int.MAX_VALUE)) = Contact( + contactId = id, + token = getNewRandomId(), + inboxId = getNewRandomId(), + outboxId = getNewRandomId(), + ) + + /** + * Allows you to mock [Database] access happening within a [Transaction] more comfortably. + * Calls to [Database.transactionWithResult] will be mocked. + * The given lambda [block] will get captured and invoked. + */ + fun <T> everyTransactionWithResult(db: Database, readOnly: Boolean, block: (Transaction) -> T) { + val txn = Transaction(mockk(), readOnly) + every { db.transactionWithResult<T>(true, captureLambda()) } answers { + lambda<(Transaction) -> T>().captured.invoke(txn) + } + block(txn) + } + } diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/db/JdbcDatabaseTest.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/db/JdbcDatabaseTest.kt index ac4d1a7909a2ab29b4725e08b26121b10e32de6a..67b41d18f3238abd45fa8208c10e17615b04e090 100644 --- a/mailbox-core/src/test/java/org/briarproject/mailbox/core/db/JdbcDatabaseTest.kt +++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/db/JdbcDatabaseTest.kt @@ -1,7 +1,7 @@ package org.briarproject.mailbox.core.db import org.briarproject.mailbox.core.TestUtils.deleteTestDirectory -import org.briarproject.mailbox.core.api.Contact +import org.briarproject.mailbox.core.contacts.Contact import org.briarproject.mailbox.core.settings.Settings import org.briarproject.mailbox.core.system.Clock import org.briarproject.mailbox.core.system.RandomIdManager @@ -40,13 +40,13 @@ abstract class JdbcDatabaseTest { open fun testPersistence() { // Store some records val contact1 = Contact( - id = 1, + contactId = 1, token = randomIdManager.getNewRandomId(), inboxId = randomIdManager.getNewRandomId(), outboxId = randomIdManager.getNewRandomId() ) val contact2 = Contact( - id = 2, + contactId = 2, token = randomIdManager.getNewRandomId(), inboxId = randomIdManager.getNewRandomId(), outboxId = randomIdManager.getNewRandomId() diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/IntegrationTest.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/IntegrationTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..1b2e339914e5008210bc2a507d9271fe74bcf92b --- /dev/null +++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/IntegrationTest.kt @@ -0,0 +1,84 @@ +package org.briarproject.mailbox.core.server + +import io.ktor.client.HttpClient +import io.ktor.client.engine.cio.CIO +import io.ktor.client.features.json.JacksonSerializer +import io.ktor.client.features.json.JsonFeature +import io.ktor.client.request.HttpRequestBuilder +import io.ktor.client.request.headers +import io.ktor.http.HttpHeaders +import org.briarproject.mailbox.core.DaggerTestComponent +import org.briarproject.mailbox.core.TestComponent +import org.briarproject.mailbox.core.TestModule +import org.briarproject.mailbox.core.TestUtils.getNewRandomContact +import org.briarproject.mailbox.core.TestUtils.getNewRandomId +import org.briarproject.mailbox.core.contacts.Contact +import org.briarproject.mailbox.core.server.WebServerManager.Companion.PORT +import org.briarproject.mailbox.core.settings.Settings +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 +import org.junit.jupiter.api.io.TempDir +import java.io.File + +@TestInstance(Lifecycle.PER_CLASS) +abstract class IntegrationTest { + + protected lateinit var testComponent: TestComponent + private val lifecycleManager by lazy { testComponent.getLifecycleManager() } + protected val httpClient = HttpClient(CIO) { + expectSuccess = false // prevents exceptions on non-success responses + install(JsonFeature) { + serializer = JacksonSerializer() + } + } + protected val baseUrl = "http://127.0.0.1:$PORT" + + protected val ownerToken = getNewRandomId() + protected val token = getNewRandomId() + protected val id = getNewRandomId() + protected val contact1 = getNewRandomContact() + protected val contact2 = getNewRandomContact() + + @BeforeAll + fun setUp(@TempDir tempDir: File) { + testComponent = DaggerTestComponent.builder().testModule(TestModule(tempDir)).build() + testComponent.injectCoreEagerSingletons() + lifecycleManager.startServices() + lifecycleManager.waitForStartup() + initDb() + } + + open fun initDb() { + // sub-classes can initialize the DB here as needed + } + + @AfterAll + fun tearDown() { + lifecycleManager.stopServices() + lifecycleManager.waitForShutdown() + } + + protected fun addOwnerToken() { + val settingsManager = testComponent.getSettingsManager() + val settings = Settings() + settings[SETTINGS_OWNER_TOKEN] = ownerToken + settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE_OWNER) + } + + protected fun addContact(c: Contact) { + val db = testComponent.getDatabase() + db.transaction(false) { txn -> + db.addContact(txn, c) + } + } + + protected fun HttpRequestBuilder.authenticateWithToken(t: String) { + headers { + @Suppress("EXPERIMENTAL_API_USAGE_FUTURE_ERROR") + append(HttpHeaders.Authorization, "Bearer $t") + } + } + +} diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/WebServerIntegrationTest.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/WebServerIntegrationTest.kt index 753642437efe430a862737084eff97296ec2cf33..95dc851d1fea703529fdeb4ad69687a9a7dc936d 100644 --- a/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/WebServerIntegrationTest.kt +++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/WebServerIntegrationTest.kt @@ -1,47 +1,13 @@ package org.briarproject.mailbox.core.server -import io.ktor.client.HttpClient -import io.ktor.client.engine.cio.CIO import io.ktor.client.request.get import io.ktor.client.statement.HttpResponse import io.ktor.client.statement.readText import kotlinx.coroutines.runBlocking -import org.briarproject.mailbox.core.DaggerTestComponent -import org.briarproject.mailbox.core.TestComponent -import org.briarproject.mailbox.core.TestModule -import org.briarproject.mailbox.core.server.WebServerManager.Companion.PORT -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance -import org.junit.jupiter.api.TestInstance.Lifecycle -import org.junit.jupiter.api.io.TempDir -import java.io.File import kotlin.test.assertEquals -@TestInstance(Lifecycle.PER_CLASS) -class WebServerIntegrationTest { - - private lateinit var testComponent: TestComponent - private val lifecycleManager by lazy { testComponent.getLifecycleManager() } - private val httpClient = HttpClient(CIO) { - expectSuccess = false // prevents exceptions on non-success responses - } - private val baseUrl = "http://127.0.0.1:$PORT" - - @BeforeAll - fun setUp(@TempDir tempDir: File) { - testComponent = DaggerTestComponent.builder().testModule(TestModule(tempDir)).build() - testComponent.injectCoreEagerSingletons() - lifecycleManager.startServices() - lifecycleManager.waitForStartup() - } - - @AfterAll - fun tearDown() { - lifecycleManager.stopServices() - lifecycleManager.waitForShutdown() - } +class WebServerIntegrationTest : IntegrationTest() { @Test fun routeRespondsWithHelloWorldString(): Unit = runBlocking {