diff --git a/.idea/runConfigurations/All_in_briar_headless.xml b/.idea/runConfigurations/All_in_briar_headless.xml
new file mode 100644
index 000000000..d682a709f
--- /dev/null
+++ b/.idea/runConfigurations/All_in_briar_headless.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/All_tests.xml b/.idea/runConfigurations/All_tests.xml
index a90c8b90e..a1de55c01 100644
--- a/.idea/runConfigurations/All_tests.xml
+++ b/.idea/runConfigurations/All_tests.xml
@@ -24,6 +24,7 @@
+
-
+
\ No newline at end of file
diff --git a/briar-headless/build.gradle b/briar-headless/build.gradle
index 03b09dd06..eae48965f 100644
--- a/briar-headless/build.gradle
+++ b/briar-headless/build.gradle
@@ -1,8 +1,8 @@
plugins {
id 'java'
id 'idea'
- id 'org.jetbrains.kotlin.jvm' version '1.2.61'
- id "org.jetbrains.kotlin.kapt" version "1.2.61"
+ id 'org.jetbrains.kotlin.jvm' version '1.2.70'
+ id 'org.jetbrains.kotlin.kapt' version '1.2.70'
id 'witness'
}
apply from: 'witness.gradle'
@@ -14,7 +14,7 @@ dependencies {
implementation project(path: ':briar-core', configuration: 'default')
implementation project(path: ':bramble-java', configuration: 'default')
- implementation 'io.javalin:javalin:2.1.0'
+ implementation 'io.javalin:javalin:2.2.0'
implementation 'org.slf4j:slf4j-simple:1.7.25'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.6'
implementation 'com.github.ajalt:clikt:1.5.0'
@@ -29,6 +29,7 @@ dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
testImplementation "io.mockk:mockk:1.8.6"
+ testImplementation "org.skyscreamer:jsonassert:1.5.0"
}
jar {
diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/blogs/BlogController.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/blogs/BlogController.kt
index 4b8ccb144..ad44c9b79 100644
--- a/briar-headless/src/main/java/org/briarproject/briar/headless/blogs/BlogController.kt
+++ b/briar-headless/src/main/java/org/briarproject/briar/headless/blogs/BlogController.kt
@@ -4,6 +4,8 @@ import io.javalin.BadRequestResponse
import io.javalin.Context
import org.briarproject.bramble.api.identity.IdentityManager
import org.briarproject.bramble.api.system.Clock
+import org.briarproject.bramble.util.StringUtils
+import org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_BODY_LENGTH
import org.briarproject.briar.api.blog.BlogManager
import org.briarproject.briar.api.blog.BlogPostFactory
import javax.annotation.concurrent.Immutable
@@ -33,7 +35,9 @@ constructor(
fun createPost(ctx: Context): Context {
val text = ctx.formParam("text")
if (text == null || text.isEmpty())
- throw BadRequestResponse("Expecting Blog text")
+ throw BadRequestResponse("Expecting blog post text")
+ if (StringUtils.toUtf8(text).size > MAX_BLOG_POST_BODY_LENGTH)
+ throw BadRequestResponse("Too long blog post text")
val author = identityManager.localAuthor
val blog = blogManager.getPersonalBlog(author)
diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/forums/ForumController.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/forums/ForumController.kt
index 52d6a06c7..54fe88ede 100644
--- a/briar-headless/src/main/java/org/briarproject/briar/headless/forums/ForumController.kt
+++ b/briar-headless/src/main/java/org/briarproject/briar/headless/forums/ForumController.kt
@@ -2,6 +2,8 @@ package org.briarproject.briar.headless.forums
import io.javalin.BadRequestResponse
import io.javalin.Context
+import org.briarproject.bramble.util.StringUtils
+import org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH
import org.briarproject.briar.api.forum.ForumManager
import javax.annotation.concurrent.Immutable
import javax.inject.Inject
@@ -18,8 +20,10 @@ constructor(private val forumManager: ForumManager) {
fun create(ctx: Context): Context {
val name = ctx.formParam("name")
- if (name == null || name.isEmpty())
+ if (name == null || name.isNullOrEmpty())
throw BadRequestResponse("Expecting Forum Name")
+ if (StringUtils.toUtf8(name).size > MAX_FORUM_NAME_LENGTH)
+ throw BadRequestResponse("Forum name is too long")
return ctx.json(forumManager.addForum(name).output())
}
diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/forums/OutputForum.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/forums/OutputForum.kt
index c02f5a9fa..17a9be8d3 100644
--- a/briar-headless/src/main/java/org/briarproject/briar/headless/forums/OutputForum.kt
+++ b/briar-headless/src/main/java/org/briarproject/briar/headless/forums/OutputForum.kt
@@ -4,7 +4,7 @@ import org.briarproject.briar.api.forum.Forum
import javax.annotation.concurrent.Immutable
@Immutable
-internal class OutputForum(
+internal data class OutputForum(
val name: String,
val id: ByteArray
) {
diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/Extensions.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/Extensions.kt
index b9332b457..c0ee4a149 100644
--- a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/Extensions.kt
+++ b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/Extensions.kt
@@ -14,10 +14,10 @@ import org.briarproject.briar.api.sharing.InvitationResponse
internal fun PrivateMessageHeader.output(
contactId: ContactId,
body: String?
-) = OutputPrivateMessage(this, contactId, body)
+) = OutputPrivateMessageHeader(this, contactId, body)
internal fun PrivateMessage.output(contactId: ContactId, body: String) =
- OutputPrivateMessage(this, contactId, body)
+ OutputPrivateMessageHeader(this, contactId, body)
internal fun PrivateMessageReceivedEvent<*>.output(body: String) =
messageHeader.output(contactId, body)
diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt
index 2c545d4ac..0cdf71627 100644
--- a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt
+++ b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt
@@ -20,7 +20,7 @@ import javax.annotation.concurrent.Immutable
import javax.inject.Inject
import javax.inject.Singleton
-private const val EVENT_PRIVATE_MESSAGE =
+internal const val EVENT_PRIVATE_MESSAGE =
"org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent"
@Immutable
@@ -79,7 +79,11 @@ constructor(
private fun getContact(ctx: Context): Contact {
val contactString = ctx.pathParam("contactId")
- val contactInt = Integer.parseInt(contactString)
+ val contactInt = try {
+ Integer.parseInt(contactString)
+ } catch (e: NumberFormatException) {
+ throw NotFoundResponse()
+ }
val contactId = ContactId(contactInt)
return try {
contactManager.getContact(contactId)
diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateMessage.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateMessage.kt
index 2af0a35aa..a4f2e19af 100644
--- a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateMessage.kt
+++ b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateMessage.kt
@@ -1,5 +1,3 @@
-@file:Suppress("MemberVisibilityCanBePrivate", "unused")
-
package org.briarproject.briar.headless.messaging
import org.briarproject.bramble.api.contact.ContactId
@@ -8,47 +6,45 @@ import org.briarproject.briar.api.messaging.PrivateMessageHeader
import javax.annotation.concurrent.Immutable
@Immutable
-internal open class OutputPrivateMessage(
- val body: String?,
- val timestamp: Long,
- val read: Boolean,
- val seen: Boolean,
- val sent: Boolean,
- val local: Boolean,
- val id: ByteArray,
- val groupId: ByteArray,
- val contactId: Int
+internal abstract class OutputPrivateMessage(
+ protected open val iHeader: PrivateMessageHeader,
+ protected open val iContactId: ContactId,
+ open val body: String?
) {
- open val type = "org.briarproject.briar.api.messaging.PrivateMessageHeader"
- internal constructor(
- header: PrivateMessageHeader,
- contactId: ContactId,
- body: String?
- ) : this(
- body = body,
- timestamp = header.timestamp,
- read = header.isRead,
- seen = header.isSeen,
- sent = header.isSent,
- local = header.isLocal,
- id = header.id.bytes,
- groupId = header.groupId.bytes,
- contactId = contactId.int
- )
+ open val type: String get() = throw NotImplementedError()
+ val contactId: Int get() = iContactId.int
+ val timestamp: Long get() = iHeader.timestamp
+ val read: Boolean get() = iHeader.isRead
+ val seen: Boolean get() = iHeader.isSeen
+ val sent: Boolean get() = iHeader.isSent
+ val local: Boolean get() = iHeader.isLocal
+ val id: ByteArray get() = iHeader.id.bytes
+ val groupId: ByteArray get() = iHeader.groupId.bytes
+
+}
+
+@Immutable
+internal data class OutputPrivateMessageHeader(
+ override val iHeader: PrivateMessageHeader,
+ override val iContactId: ContactId,
+ override val body: String?
+) : OutputPrivateMessage(iHeader, iContactId, body) {
+
+ override val type = "org.briarproject.briar.api.messaging.PrivateMessageHeader"
/**
* Only meant for own [PrivateMessage]s directly after creation.
*/
internal constructor(m: PrivateMessage, contactId: ContactId, body: String) : this(
- body = body,
- timestamp = m.message.timestamp,
- read = true,
- seen = true,
- sent = true,
- local = true,
- id = m.message.id.bytes,
- groupId = m.message.groupId.bytes,
- contactId = contactId.int
+ PrivateMessageHeader(
+ m.message.id,
+ m.message.groupId,
+ m.message.timestamp,
+ true,
+ true,
+ true,
+ true
+ ), contactId, body
)
}
diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateRequest.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateRequest.kt
index 2429edae6..1b2bb98da 100644
--- a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateRequest.kt
+++ b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateRequest.kt
@@ -1,4 +1,4 @@
-@file:Suppress("MemberVisibilityCanBePrivate", "unused")
+@file:Suppress("unused")
package org.briarproject.briar.headless.messaging
@@ -21,22 +21,28 @@ internal abstract class OutputPrivateRequest(header: PrivateRequest<*>, contactI
}
@Immutable
-internal class OutputIntroductionRequest(header: IntroductionRequest, contactId: ContactId) :
- OutputPrivateRequest(header, contactId) {
+internal data class OutputIntroductionRequest(
+ override val iHeader: IntroductionRequest,
+ override val iContactId: ContactId
+) : OutputPrivateRequest(iHeader, iContactId) {
override val type = "org.briarproject.briar.api.introduction.IntroductionRequest"
- val alreadyContact = header.isContact
+ val alreadyContact get() = iHeader.isContact
+
}
@Immutable
-internal class OutputInvitationRequest(header: InvitationRequest<*>, contactId: ContactId) :
- OutputPrivateRequest(header, contactId) {
+internal data class OutputInvitationRequest(
+ override val iHeader: InvitationRequest<*>,
+ override val iContactId: ContactId
+) : OutputPrivateRequest(iHeader, iContactId) {
- override val type = when (header) {
+ override val type = when (iHeader) {
is ForumInvitationRequest -> "org.briarproject.briar.api.forum.ForumInvitationRequest"
is BlogInvitationRequest -> "org.briarproject.briar.api.blog.BlogInvitationRequest"
is GroupInvitationRequest -> "org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest"
else -> throw AssertionError("Unknown InvitationRequest")
}
- val canBeOpened = header.canBeOpened()
+ val canBeOpened get() = iHeader.canBeOpened()
+
}
diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateResponse.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateResponse.kt
index e9eab936c..5ac715255 100644
--- a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateResponse.kt
+++ b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateResponse.kt
@@ -21,23 +21,27 @@ internal abstract class OutputPrivateResponse(header: PrivateResponse, contactId
}
@Immutable
-internal class OutputIntroductionResponse(header: IntroductionResponse, contactId: ContactId) :
- OutputPrivateResponse(header, contactId) {
+internal data class OutputIntroductionResponse(
+ override val iHeader: IntroductionResponse,
+ override val iContactId: ContactId
+) : OutputPrivateResponse(iHeader, iContactId) {
override val type = "org.briarproject.briar.api.introduction.IntroductionResponse"
- val introducedAuthor = header.introducedAuthor.output()
- val introducer = header.isIntroducer
+ val introducedAuthor get() = iHeader.introducedAuthor.output()
+ val introducer get() = iHeader.isIntroducer
}
@Immutable
-internal class OutputInvitationResponse(header: InvitationResponse, contactId: ContactId) :
- OutputPrivateResponse(header, contactId) {
+internal data class OutputInvitationResponse(
+ override val iHeader: InvitationResponse,
+ override val iContactId: ContactId
+) : OutputPrivateResponse(iHeader, iContactId) {
- override val type = when (header) {
+ override val type = when (iHeader) {
is ForumInvitationResponse -> "org.briarproject.briar.api.forum.ForumInvitationResponse"
is BlogInvitationResponse -> "org.briarproject.briar.api.blog.BlogInvitationResponse"
is GroupInvitationResponse -> "org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse"
else -> throw AssertionError("Unknown InvitationResponse")
}
- val shareableId: ByteArray = header.shareableId.bytes
+ val shareableId: ByteArray get() = iHeader.shareableId.bytes
}
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/ControllerTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/ControllerTest.kt
new file mode 100644
index 000000000..2067d9f5f
--- /dev/null
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/ControllerTest.kt
@@ -0,0 +1,44 @@
+package org.briarproject.briar.headless
+
+import io.javalin.Context
+import io.javalin.core.util.ContextUtil
+import io.mockk.mockk
+import org.briarproject.bramble.api.contact.Contact
+import org.briarproject.bramble.api.contact.ContactId
+import org.briarproject.bramble.api.contact.ContactManager
+import org.briarproject.bramble.api.identity.Author
+import org.briarproject.bramble.api.identity.IdentityManager
+import org.briarproject.bramble.api.identity.LocalAuthor
+import org.briarproject.bramble.api.sync.Group
+import org.briarproject.bramble.api.sync.Message
+import org.briarproject.bramble.api.system.Clock
+import org.briarproject.bramble.test.TestUtils.*
+import org.briarproject.bramble.util.StringUtils.getRandomString
+import org.skyscreamer.jsonassert.JSONAssert.assertEquals
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+
+abstract class ControllerTest {
+
+ protected val contactManager = mockk()
+ protected val identityManager = mockk()
+ protected val clock = mockk()
+ protected val ctx = mockk()
+
+ private val request = mockk(relaxed = true)
+ private val response = mockk(relaxed = true)
+ private val outputCtx = ContextUtil.init(request, response)
+
+ protected val group: Group = getGroup(getClientId(), 0)
+ protected val author: Author = getAuthor()
+ protected val localAuthor: LocalAuthor = getLocalAuthor()
+ protected val contact = Contact(ContactId(1), author, localAuthor.id, true, true)
+ protected val message: Message = getMessage(group.id)
+ protected val body: String = getRandomString(5)
+ protected val timestamp = 42L
+
+ protected fun assertJsonEquals(json: String, obj: Any) {
+ assertEquals(json, outputCtx.json(obj).resultString(), false)
+ }
+
+}
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt
index 5df4dafa7..df951fa03 100644
--- a/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt
@@ -1,63 +1,128 @@
package org.briarproject.briar.headless.blogs
-import io.javalin.Context
+import io.javalin.BadRequestResponse
+import io.javalin.json.JavalinJson.toJson
+import io.mockk.Runs
import io.mockk.every
+import io.mockk.just
import io.mockk.mockk
-import io.mockk.slot
-import io.mockk.verifySequence
import org.briarproject.bramble.api.identity.Author.Status.OURSELVES
-import org.briarproject.bramble.api.identity.IdentityManager
-import org.briarproject.bramble.api.system.Clock
-import org.briarproject.bramble.test.TestUtils.*
+import org.briarproject.bramble.api.sync.MessageId
import org.briarproject.bramble.util.StringUtils.getRandomString
-import org.briarproject.briar.api.blog.Blog
-import org.briarproject.briar.api.blog.BlogManager
-import org.briarproject.briar.api.blog.BlogPostFactory
-import org.briarproject.briar.api.blog.BlogPostHeader
+import org.briarproject.briar.api.blog.*
+import org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_BODY_LENGTH
import org.briarproject.briar.api.blog.MessageType.POST
-import org.junit.jupiter.api.Assertions.assertEquals
+import org.briarproject.briar.headless.ControllerTest
+import org.briarproject.briar.headless.output
+import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
-class BlogControllerTest {
+internal class BlogControllerTest : ControllerTest() {
private val blogManager = mockk()
private val blogPostFactory = mockk()
- private val identityManager = mockk()
- private val clock = mockk()
- private val ctx = mockk()
- private val blogController =
- BlogController(blogManager, blogPostFactory, identityManager, clock)
+ private val controller =
+ BlogController(blogManager, blogPostFactory, identityManager, clock)
- private val group = getGroup(getClientId(), 0)
- private val author = getAuthor()
private val blog = Blog(group, author, false)
- private val message = getMessage(group.id)
- private val body = getRandomString(5)
+ private val parentId: MessageId? = null
+ private val rssFeed = false
+ private val read = true
+ private val header = BlogPostHeader(
+ POST,
+ group.id,
+ message.id,
+ parentId,
+ message.timestamp,
+ timestamp,
+ author,
+ OURSELVES,
+ rssFeed,
+ read
+ )
+
+ @Test
+ fun testCreate() {
+ val post = BlogPost(message, null, localAuthor)
+
+ every { ctx.formParam("text") } returns body
+ every { identityManager.localAuthor } returns localAuthor
+ every { blogManager.getPersonalBlog(localAuthor) } returns blog
+ every { clock.currentTimeMillis() } returns message.timestamp
+ every {
+ blogPostFactory.createBlogPost(
+ message.groupId,
+ message.timestamp,
+ parentId,
+ localAuthor,
+ body
+ )
+ } returns post
+ every { blogManager.addLocalPost(post) } just Runs
+ every { blogManager.getPostHeader(post.message.groupId, post.message.id) } returns header
+ every { ctx.json(header.output(body)) } returns ctx
+
+ controller.createPost(ctx)
+ }
+
+ @Test
+ fun testCreateNoText() {
+ every { ctx.formParam("text") } returns null
+
+ assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) }
+ }
+
+ @Test
+ fun testCreateEmptyText() {
+ every { ctx.formParam("text") } returns ""
+
+ assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) }
+ }
+
+ @Test
+ fun testCreateTooLongText() {
+ every { ctx.formParam("text") } returns getRandomString(MAX_BLOG_POST_BODY_LENGTH + 1)
+
+ assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) }
+ }
@Test
fun testList() {
- val header = BlogPostHeader(POST, group.id, message.id, null, 0, 0, author, OURSELVES, true,
- true)
- val slot = slot>()
-
every { blogManager.blogs } returns listOf(blog)
- every { blogManager.getPostHeaders(any()) } returns listOf(header)
- every { blogManager.getPostBody(any()) } returns body
- every { ctx.json(capture(slot)) } returns ctx
+ every { blogManager.getPostHeaders(group.id) } returns listOf(header)
+ every { blogManager.getPostBody(message.id) } returns body
+ every { ctx.json(listOf(header.output(body))) } returns ctx
- blogController.listPosts(ctx)
+ controller.listPosts(ctx)
+ }
- assertEquals(1, slot.captured.size)
- assertEquals(header.id.bytes, slot.captured[0].id)
- assertEquals(body, slot.captured[0].body)
+ @Test
+ fun testEmptyList() {
+ every { blogManager.blogs } returns listOf(blog)
+ every { blogManager.getPostHeaders(group.id) } returns emptyList()
+ every { ctx.json(emptyList()) } returns ctx
- verifySequence {
- blogManager.blogs
- blogManager.getPostHeaders(group.id)
- blogManager.getPostBody(message.id)
- ctx.json(slot.captured)
- }
+ controller.listPosts(ctx)
+ }
+
+ @Test
+ fun testOutputBlogPost() {
+ val json = """
+ {
+ "body": "$body",
+ "author": ${toJson(author.output())},
+ "authorStatus": "ourselves",
+ "type": "post",
+ "id": ${toJson(header.id.bytes)},
+ "parentId": $parentId,
+ "read": $read,
+ "rssFeed": $rssFeed,
+ "timestamp": ${message.timestamp},
+ "timestampReceived": $timestamp
+ }
+ """
+ assertJsonEquals(json, header.output(body))
}
}
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerTest.kt
new file mode 100644
index 000000000..2739e49c8
--- /dev/null
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerTest.kt
@@ -0,0 +1,52 @@
+package org.briarproject.briar.headless.contact
+
+import io.javalin.json.JavalinJson.toJson
+import io.mockk.every
+import org.briarproject.bramble.api.contact.Contact
+import org.briarproject.briar.headless.ControllerTest
+import org.briarproject.briar.headless.output
+import org.junit.jupiter.api.Test
+
+internal class ContactControllerTest : ControllerTest() {
+
+ private val controller = ContactController(contactManager)
+
+ @Test
+ fun testEmptyContactList() {
+ every { contactManager.activeContacts } returns emptyList()
+ every { ctx.json(emptyList()) } returns ctx
+ controller.list(ctx)
+ }
+
+ @Test
+ fun testList() {
+ every { contactManager.activeContacts } returns listOf(contact)
+ every { ctx.json(listOf(contact.output())) } returns ctx
+ controller.list(ctx)
+ }
+
+ @Test
+ fun testOutputContact() {
+ val json = """
+ {
+ "id": ${contact.id.int},
+ "author": ${toJson(author.output())},
+ "verified": ${contact.isVerified}
+ }
+ """
+ assertJsonEquals(json, contact.output())
+ }
+
+ @Test
+ fun testOutputAuthor() {
+ val json = """
+ {
+ "id": ${toJson(author.id.bytes)},
+ "name": "${author.name}",
+ "publicKey": ${toJson(author.publicKey)}
+ }
+ """
+ assertJsonEquals(json, author.output())
+ }
+
+}
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/forums/ForumControllerTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/forums/ForumControllerTest.kt
new file mode 100644
index 000000000..f7ac8a89e
--- /dev/null
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/forums/ForumControllerTest.kt
@@ -0,0 +1,61 @@
+package org.briarproject.briar.headless.forums
+
+import io.javalin.BadRequestResponse
+import io.mockk.every
+import io.mockk.mockk
+import org.briarproject.bramble.test.TestUtils.getRandomBytes
+import org.briarproject.bramble.util.StringUtils.getRandomString
+import org.briarproject.briar.api.forum.Forum
+import org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH
+import org.briarproject.briar.api.forum.ForumManager
+import org.briarproject.briar.headless.ControllerTest
+import org.junit.jupiter.api.Assertions.assertThrows
+import org.junit.jupiter.api.Test
+
+internal class ForumControllerTest : ControllerTest() {
+
+ private val forumManager = mockk()
+
+ private val controller = ForumController(forumManager)
+
+ private val forum = Forum(group, getRandomString(5), getRandomBytes(5))
+
+ @Test
+ fun list() {
+ every { forumManager.forums } returns listOf(forum)
+ every { ctx.json(listOf(forum.output())) } returns ctx
+
+ controller.list(ctx)
+ }
+
+ @Test
+ fun create() {
+ every { ctx.formParam("name") } returns forum.name
+ every { forumManager.addForum(forum.name) } returns forum
+ every { ctx.json(forum.output()) } returns ctx
+
+ controller.create(ctx)
+ }
+
+ @Test
+ fun createNoName() {
+ every { ctx.formParam("name") } returns null
+
+ assertThrows(BadRequestResponse::class.java) { controller.create(ctx) }
+ }
+
+ @Test
+ fun createEmptyName() {
+ every { ctx.formParam("name") } returns ""
+
+ assertThrows(BadRequestResponse::class.java) { controller.create(ctx) }
+ }
+
+ @Test
+ fun createTooLongName() {
+ every { ctx.formParam("name") } returns getRandomString(MAX_FORUM_NAME_LENGTH + 1)
+
+ assertThrows(BadRequestResponse::class.java) { controller.create(ctx) }
+ }
+
+}
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt
new file mode 100644
index 000000000..a15bc782c
--- /dev/null
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt
@@ -0,0 +1,180 @@
+package org.briarproject.briar.headless.messaging
+
+import io.javalin.BadRequestResponse
+import io.javalin.Context
+import io.javalin.NotFoundResponse
+import io.javalin.json.JavalinJson.toJson
+import io.mockk.*
+import org.briarproject.bramble.api.contact.ContactId
+import org.briarproject.bramble.api.db.NoSuchContactException
+import org.briarproject.bramble.test.ImmediateExecutor
+import org.briarproject.bramble.util.StringUtils.getRandomString
+import org.briarproject.briar.api.messaging.*
+import org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH
+import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent
+import org.briarproject.briar.headless.ControllerTest
+import org.briarproject.briar.headless.WebSocketController
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertThrows
+import org.junit.jupiter.api.Test
+
+internal class MessagingControllerImplTest : ControllerTest() {
+
+ private val messagingManager = mockk()
+ private val conversationManager = mockk()
+ private val privateMessageFactory = mockk()
+ private val webSocketController = mockk()
+ private val dbExecutor = ImmediateExecutor()
+
+ private val controller = MessagingControllerImpl(
+ messagingManager,
+ conversationManager,
+ privateMessageFactory,
+ contactManager,
+ webSocketController,
+ dbExecutor,
+ clock
+ )
+
+ private val header = PrivateMessageHeader(message.id, group.id, timestamp, true, true, true, true)
+ private val headers = listOf(header)
+
+ @Test
+ fun list() {
+ expectGetContact()
+ every { conversationManager.getMessageHeaders(contact.id) } returns headers
+ every { messagingManager.getMessageBody(message.id) } returns body
+ every { ctx.json(listOf(header.output(contact.id, body))) } returns ctx
+
+ controller.list(ctx)
+ }
+
+ @Test
+ fun emptyList() {
+ every { ctx.pathParam("contactId") } returns contact.id.int.toString()
+ every { contactManager.getContact(contact.id) } returns contact
+ every { conversationManager.getMessageHeaders(contact.id) } returns emptyList()
+ every { ctx.json(emptyList()) } returns ctx
+
+ controller.list(ctx)
+ }
+
+ @Test
+ fun listInvalidContactId() {
+ testInvalidContactId { controller.list(ctx) }
+ }
+
+ @Test
+ fun listNonexistentContactId() {
+ testNonexistentContactId { controller.list(ctx) }
+ }
+
+ @Test
+ fun write() {
+ val privateMessage = PrivateMessage(message)
+ val slot = CapturingSlot()
+
+ expectGetContact()
+ every { ctx.formParam("message") } returns body
+ every { messagingManager.getContactGroup(contact) } returns group
+ every { clock.currentTimeMillis() } returns timestamp
+ every {
+ privateMessageFactory.createPrivateMessage(
+ group.id,
+ timestamp,
+ body
+ )
+ } returns privateMessage
+ every { messagingManager.addLocalMessage(privateMessage) } just runs
+ every { ctx.json(capture(slot)) } returns ctx
+
+ controller.write(ctx)
+
+ val output = slot.captured
+ assertEquals(contact.id.int, output.contactId)
+ assertEquals(body, output.body)
+ assertEquals(message.id.bytes, output.id)
+ }
+
+ @Test
+ fun writeInvalidContactId() {
+ testInvalidContactId { controller.write(ctx) }
+ }
+
+ @Test
+ fun writeNonexistentContactId() {
+ testNonexistentContactId { controller.write(ctx) }
+ }
+
+ @Test
+ fun writeNonexistentBody() {
+ expectGetContact()
+ every { ctx.formParam("message") } returns null
+
+ assertThrows(BadRequestResponse::class.java) { controller.write(ctx) }
+ }
+
+ @Test
+ fun writeEmptyBody() {
+ expectGetContact()
+ every { ctx.formParam("message") } returns ""
+
+ assertThrows(BadRequestResponse::class.java) { controller.write(ctx) }
+ }
+
+ @Test
+ fun writeTooLongBody() {
+ expectGetContact()
+ every { ctx.formParam("message") } returns getRandomString(MAX_PRIVATE_MESSAGE_BODY_LENGTH + 1)
+
+ assertThrows(BadRequestResponse::class.java) { controller.write(ctx) }
+ }
+
+ @Test
+ fun privateMessageEvent() {
+ val event = PrivateMessageReceivedEvent(header, contact.id)
+
+ every { messagingManager.getMessageBody(message.id) } returns body
+ every { webSocketController.sendEvent(EVENT_PRIVATE_MESSAGE, event.output(body)) } just runs
+
+ controller.eventOccurred(event)
+ }
+
+ @Test
+ fun testOutputPrivateMessageHeader() {
+ val json = """
+ {
+ "body": "$body",
+ "type": "org.briarproject.briar.api.messaging.PrivateMessageHeader",
+ "timestamp": $timestamp,
+ "groupId": ${toJson(header.groupId.bytes)},
+ "contactId": ${contact.id.int},
+ "local": ${header.isLocal},
+ "seen": ${header.isSeen},
+ "read": ${header.isRead},
+ "sent": ${header.isSent},
+ "id": ${toJson(header.id.bytes)}
+ }
+ """
+ assertJsonEquals(json, header.output(contact.id, body))
+ }
+
+ private fun expectGetContact() {
+ every { ctx.pathParam("contactId") } returns contact.id.int.toString()
+ every { contactManager.getContact(contact.id) } returns contact
+ }
+
+ private fun testNonexistentContactId(function: () -> Context) {
+ every { ctx.pathParam("contactId") } returns "42"
+ every { contactManager.getContact(ContactId(42)) } throws NoSuchContactException()
+
+ assertThrows(NotFoundResponse::class.java) { function.invoke() }
+ }
+
+ private fun testInvalidContactId(function: () -> Context) {
+ every { ctx.pathParam("contactId") } returns "foo"
+
+ assertThrows(NotFoundResponse::class.java) { function.invoke() }
+ }
+
+}
diff --git a/briar-headless/witness.gradle b/briar-headless/witness.gradle
index 47a15e4c0..9c7d2658b 100644
--- a/briar-headless/witness.gradle
+++ b/briar-headless/witness.gradle
@@ -8,7 +8,8 @@ dependencyVerification {
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
- 'io.javalin:javalin:2.1.0:javalin-2.1.0.jar:d52264afbecddd1a8926a6d3b6f84ca534afa4d3997176c99e96ca2e0874577a',
+ 'com.vaadin.external.google:android-json:0.0.20131108.vaadin1:android-json-0.0.20131108.vaadin1.jar:dfb7bae2f404cfe0b72b4d23944698cb716b7665171812a0a4d0f5926c0fac79',
+ 'io.javalin:javalin:2.2.0:javalin-2.2.0.jar:f7298fa281400559e92f000477a631c75aca9e01776962fd4b392fdb3b714190',
'io.mockk:mockk-agent-api:1.8.6:mockk-agent-api-1.8.6.jar:613512c66538e6349e03df641a868f4ee324f13e2e1dbd67a0ed388aa664a444',
'io.mockk:mockk-agent-common:1.8.6:mockk-agent-common-1.8.6.jar:cb7cb26fae5bfd3c89090858548990f311b27f673b9efa9d0c94f97c463b2863',
'io.mockk:mockk-agent-jvm:1.8.6:mockk-agent-jvm-1.8.6.jar:3f30b98d23ada8b5a44d75b43cd58fc03252fcb96939ff31e7ad659818af1e5d',
@@ -21,30 +22,30 @@ dependencyVerification {
'net.bytebuddy:byte-buddy-agent:1.8.8:byte-buddy-agent-1.8.8.jar:dc1a2dcefe72731fa89ae84e32231c74d545ccf8216c79865096e546f20c57e8',
'net.bytebuddy:byte-buddy:1.8.8:byte-buddy-1.8.8.jar:30aed1ae2ee5261b1d2f0e98ec3fcb40755c3f61b378089fb65d56098df1f16b',
'org.apiguardian:apiguardian-api:1.0.0:apiguardian-api-1.0.0.jar:1f58b77470d8d147a0538d515347dd322f49a83b9e884b8970051160464b65b3',
- 'org.eclipse.jetty.websocket:websocket-api:9.4.11.v20180605:websocket-api-9.4.11.v20180605.jar:924edcf7fb17f2ff2e541afce7fd692305235e51b5a16f7223d7e8b4de77559d',
- 'org.eclipse.jetty.websocket:websocket-client:9.4.11.v20180605:websocket-client-9.4.11.v20180605.jar:d3c812f80ac18d7031ffe2a324f2f83d8b260a99863be81b81668184ad5f2cca',
- 'org.eclipse.jetty.websocket:websocket-common:9.4.11.v20180605:websocket-common-9.4.11.v20180605.jar:f00731b8f9d2f2155bd87a2040cdef991fbcd24ff843ff4ba1ab1829bf62c04c',
- 'org.eclipse.jetty.websocket:websocket-server:9.4.11.v20180605:websocket-server-9.4.11.v20180605.jar:0cfa90029d46618116f986cc6aea1c4f2e7408f3c97ad3eb2f92f74aff37279b',
- 'org.eclipse.jetty.websocket:websocket-servlet:9.4.11.v20180605:websocket-servlet-9.4.11.v20180605.jar:76f52b482ad174944e07f552cbfaaa2ccf498063e0a3837bb930eee8a06373aa',
- 'org.eclipse.jetty:jetty-client:9.4.11.v20180605:jetty-client-9.4.11.v20180605.jar:b096ea6ee2607886323791930a470b2e04fb3327459b287ef99647226bd7a09c',
- 'org.eclipse.jetty:jetty-http:9.4.11.v20180605:jetty-http-9.4.11.v20180605.jar:963b75730aa92b0dfbe65fe8a2e413edc88aeb53e8686ba6b1617d7caeb14067',
- 'org.eclipse.jetty:jetty-io:9.4.11.v20180605:jetty-io-9.4.11.v20180605.jar:75c82d6e542a3518e2517c4084c83d8216ec2d2458f8747b8b5c944355ebd732',
- 'org.eclipse.jetty:jetty-security:9.4.11.v20180605:jetty-security-9.4.11.v20180605.jar:5a12b1c69264466004baff33b14fc1555007c86fb2fece2a420c480aa7f8ef56',
- 'org.eclipse.jetty:jetty-server:9.4.11.v20180605:jetty-server-9.4.11.v20180605.jar:b74af5ac482b05c242ed231e00b7c08a0b6649f76f2e039a0885de0cf1376ef8',
- 'org.eclipse.jetty:jetty-servlet:9.4.11.v20180605:jetty-servlet-9.4.11.v20180605.jar:e24f145a6d95c7653ad2fe0c34cf8ce7311effb7eb8ed9399fae63d8af63eaf4',
- 'org.eclipse.jetty:jetty-util:9.4.11.v20180605:jetty-util-9.4.11.v20180605.jar:936e5ed74275c16164cc1eccaeae55900eb00edd9f1b1d3b83d70782dd25f505',
- 'org.eclipse.jetty:jetty-webapp:9.4.11.v20180605:jetty-webapp-9.4.11.v20180605.jar:858f3f16cecb0891f07a4e8d82554201c513bf058c0f65969b366936155b6a36',
- 'org.eclipse.jetty:jetty-xml:9.4.11.v20180605:jetty-xml-9.4.11.v20180605.jar:1780bdaee2b1908e032fbc286bb856d730c4d0c9de39d5e14a1b9c48028c295e',
- 'org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.2.61:kotlin-annotation-processing-gradle-1.2.61.jar:52ce75c26c94c54490e217b1ed4a6b2d689a7d5b0482c1032624e45ed5e259a7',
- 'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.2.61:kotlin-compiler-embeddable-1.2.61.jar:f8165810c61f440a2de6003ed9857d28a1dfc990bacbecee1436610c8ebe565b',
+ 'org.eclipse.jetty.websocket:websocket-api:9.4.12.v20180830:websocket-api-9.4.12.v20180830.jar:6f7ecb42601058ffe4a6c19c5340cac3ebf0f83e2e252b457558f104238278e3',
+ 'org.eclipse.jetty.websocket:websocket-client:9.4.12.v20180830:websocket-client-9.4.12.v20180830.jar:97c6882c858a75776773eaccc01739757c4e9f60a51613878c1f2b2ba03d91af',
+ 'org.eclipse.jetty.websocket:websocket-common:9.4.12.v20180830:websocket-common-9.4.12.v20180830.jar:3c35aefa720c51e09532c16fdbfaaebd1af3e07dee699dacaba8e0ab0adf88e5',
+ 'org.eclipse.jetty.websocket:websocket-server:9.4.12.v20180830:websocket-server-9.4.12.v20180830.jar:7b1bd39006be8c32d7426a119567d860b3e4a3dc3c01a5c91326450bb0213a03',
+ 'org.eclipse.jetty.websocket:websocket-servlet:9.4.12.v20180830:websocket-servlet-9.4.12.v20180830.jar:8d43e0882759ecd093bd1a5a0ef2b4db38ac279212488a34edb8d7de7c45cc4d',
+ 'org.eclipse.jetty:jetty-client:9.4.12.v20180830:jetty-client-9.4.12.v20180830.jar:62efbbfda88cd4f7644242c4b4df8f3b0a671bfeafea7682dabe00352ba07db7',
+ 'org.eclipse.jetty:jetty-http:9.4.12.v20180830:jetty-http-9.4.12.v20180830.jar:20547da653be9942cc63f57e632a732608559aebde69753bc7312cfe16e8d9c0',
+ 'org.eclipse.jetty:jetty-io:9.4.12.v20180830:jetty-io-9.4.12.v20180830.jar:ab1784abbb9e0ed0869ab6568fe46f1faa79fb5e948cf96450daecd9d27ba1db',
+ 'org.eclipse.jetty:jetty-security:9.4.12.v20180830:jetty-security-9.4.12.v20180830.jar:513184970c785ac830424a9c62c2fadfa77a630f44aa0bdd792f00aaa092887e',
+ 'org.eclipse.jetty:jetty-server:9.4.12.v20180830:jetty-server-9.4.12.v20180830.jar:4833644e5c5a09bbddc85f75c53e0c8ed750de120ba248fffd8508028528252d',
+ 'org.eclipse.jetty:jetty-servlet:9.4.12.v20180830:jetty-servlet-9.4.12.v20180830.jar:7310d4cccf8abf27fde0c3f1a32e19c75fe33c6f1ab558f0704d915f0f01cb07',
+ 'org.eclipse.jetty:jetty-util:9.4.12.v20180830:jetty-util-9.4.12.v20180830.jar:60ad53e118a3e7d10418b155b9944d90b2e4e4c732e53ef4f419473288d3f48c',
+ 'org.eclipse.jetty:jetty-webapp:9.4.12.v20180830:jetty-webapp-9.4.12.v20180830.jar:5301e412a32bf7dddcfad458d952179597c61f8fd531c265873562725c3d4646',
+ 'org.eclipse.jetty:jetty-xml:9.4.12.v20180830:jetty-xml-9.4.12.v20180830.jar:5b8298ab3d43ddaf0941d41f51b82c8ae23a247da055fa161b752ab9495155ed',
+ 'org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.2.70:kotlin-annotation-processing-gradle-1.2.70.jar:820da7e3637066c14eb3d54dc29cd6d4dc4a041ff603d0b15844403de47b7d12',
+ 'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.2.70:kotlin-compiler-embeddable-1.2.70.jar:8958d6f6ce4e49a6cecaaa9a1711a6b03df793fe066a74d88cf4958f20b0f10d',
'org.jetbrains.kotlin:kotlin-reflect:1.2.41:kotlin-reflect-1.2.41.jar:1bab75771dfa2bb5949cd383ceaedf6f8d354fa0d677804fc5a39e320bab70d3',
- 'org.jetbrains.kotlin:kotlin-reflect:1.2.61:kotlin-reflect-1.2.61.jar:a4f1ed542390f9bf967fb9aff2c232b90988f7ce138a4b7bcc70b754821a8943',
- 'org.jetbrains.kotlin:kotlin-script-runtime:1.2.61:kotlin-script-runtime-1.2.61.jar:793e4c54a8d59cf1f1cda68a7bc6be1ad54c41d771a08d308aa28c2cd304f1b1',
- 'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.2.61:kotlin-scripting-compiler-embeddable-1.2.61.jar:99e0ee865f9c7c8ddcc0975185c69392182311067fe2a6faa72e942185631c1c',
- 'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.61:kotlin-stdlib-common-1.2.61.jar:54a6fab7dae0cd7528208e95868fe1869b01b613c3018f2e53dbbad2604f6595',
- 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.61:kotlin-stdlib-jdk7-1.2.61.jar:f02ed7a640323b162c798a7834b35c10590880151e104eeb83acd84b364ef030',
- 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.61:kotlin-stdlib-jdk8-1.2.61.jar:1fdeec7f74c2bae97a2205bb4aff0c6fe9d133bd619d069a0928156098d1b927',
- 'org.jetbrains.kotlin:kotlin-stdlib:1.2.61:kotlin-stdlib-1.2.61.jar:62eaf9cc6e746cef4593abe7cdb4dd48694ef5f817c852e0d9fbbd11fcfc564e',
+ 'org.jetbrains.kotlin:kotlin-reflect:1.2.70:kotlin-reflect-1.2.70.jar:89ef46a458c5ee58b8460d0d22b0bb484eea0589ccffd59d650ef38bcb60e806',
+ 'org.jetbrains.kotlin:kotlin-script-runtime:1.2.70:kotlin-script-runtime-1.2.70.jar:0124dfcf890e39250c3a4481cdd27f038d8321e93ce983730c4cbc10143eadc2',
+ 'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.2.70:kotlin-scripting-compiler-embeddable-1.2.70.jar:cbd88e1cae3f8f2baeb7021d3b76323b30e82663e7b4222a214f33ee314b3653',
+ 'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.70:kotlin-stdlib-common-1.2.70.jar:bb6898bef18e1de5d416d5135ca70dcac6645718c7d84bbddcfeb76ed1c199a1',
+ 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.70:kotlin-stdlib-jdk7-1.2.70.jar:b4ace315288134b52fddb58b4a92636faafb2ab5eb46bad97d3bce7623a8e213',
+ 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.70:kotlin-stdlib-jdk8-1.2.70.jar:88d0c29f4065b6ad34261fb4a04d39f9813051c6942428d718b649378d057ad1',
+ 'org.jetbrains.kotlin:kotlin-stdlib:1.2.70:kotlin-stdlib-1.2.70.jar:7d20d0a56dd0ea6176137759a6aad331bbfae67436b45e5f0a4d8dafb6985c81',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.junit.jupiter:junit-jupiter-api:5.2.0:junit-jupiter-api-5.2.0.jar:47f7d71b35dc331210b9ab219bbb00d54332981aa12eb5effe817de17e1ae7b3',
'org.junit.jupiter:junit-jupiter-engine:5.2.0:junit-jupiter-engine-5.2.0.jar:8f994f4094790e246dc84de86a1ff4194ca85e8b13bedaca0207f727ebfbc813',
@@ -53,6 +54,7 @@ dependencyVerification {
'org.junit.platform:junit-platform-engine:1.2.0:junit-platform-engine-1.2.0.jar:60b102e94ea01556fdc8c041950a05450edc188e3708f032a6bfb1a50ba0bc22',
'org.objenesis:objenesis:2.6:objenesis-2.6.jar:5e168368fbc250af3c79aa5fef0c3467a2d64e5a7bd74005f25d8399aeb0708d',
'org.opentest4j:opentest4j:1.1.0:opentest4j-1.1.0.jar:65a5fd7380f53aac708bcee3091dbe2dba73a9a2e7645b66e70e0804fc36ee3b',
+ 'org.skyscreamer:jsonassert:1.5.0:jsonassert-1.5.0.jar:a310bc79c3f4744e2b2e993702fcebaf3696fec0063643ffdc6b49a8fb03ef39',
'org.slf4j:slf4j-api:1.7.25:slf4j-api-1.7.25.jar:18c4a0095d5c1da6b817592e767bb23d29dd2f560ad74df75ff3961dbde25b79',
'org.slf4j:slf4j-simple:1.7.25:slf4j-simple-1.7.25.jar:0966e86fffa5be52d3d9e7b89dd674d98a03eed0a454fbaf7c1bd9493bd9d874',
]