mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 06:09:55 +01:00
[headless] expose ContactManager methods for adding contacts remotely
This commit is contained in:
@@ -71,10 +71,79 @@ Returns a JSON array of contacts:
|
|||||||
|
|
||||||
### Adding a contact
|
### Adding a contact
|
||||||
|
|
||||||
*Not yet implemented*
|
The first step is to get your own link:
|
||||||
|
|
||||||
The only workaround is to add a contact to the Briar app running on a rooted Android phone
|
`GET /v1/contacts/add/link`
|
||||||
and then move its database (and key files) to the headless peer.
|
|
||||||
|
Returns a JSON object with a `briar://` link that needs to be sent to the contact you want to add
|
||||||
|
outside of Briar via an external channel.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"link": "briar://wvui4uvhbfv4tzo6xwngknebsxrafainnhldyfj63x6ipp4q2vigy"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have received the link of your future contact, you can add them
|
||||||
|
by posting the link together with an arbitrary nickname (or alias):
|
||||||
|
|
||||||
|
`POST /v1/contacts/add`
|
||||||
|
|
||||||
|
The link and the alias should be posted as a JSON object:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"link": "briar://ddnsyffpsenoc3yzlhr24aegfq2pwan7kkselocill2choov6sbhs",
|
||||||
|
"alias": "A nickname for the new contact"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This starts the process of adding the contact.
|
||||||
|
Until it is completed, a pending contact is returned as JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=",
|
||||||
|
"alias": "ztatsaajzeegraqcizbbfftofdekclatyht",
|
||||||
|
"state": "adding_contact",
|
||||||
|
"timestamp": 1557838312175
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The state can be one of these values:
|
||||||
|
|
||||||
|
* `waiting_for_connection`
|
||||||
|
* `connected`
|
||||||
|
* `adding_contact`
|
||||||
|
* `failed`
|
||||||
|
|
||||||
|
If you want to get informed about state changes,
|
||||||
|
you can use the Websocket API (below) to listen for events.
|
||||||
|
|
||||||
|
The following events are relevant here:
|
||||||
|
|
||||||
|
* `PendingContactStateChangedEvent`
|
||||||
|
* `PendingContactRemovedEvent`
|
||||||
|
* `ContactAddedRemotelyEvent` (when the pending contact becomes an actual contact)
|
||||||
|
|
||||||
|
It is possible to get a list of all pending contacts:
|
||||||
|
|
||||||
|
`GET /v1/contacts/add/pending`
|
||||||
|
|
||||||
|
This will return a JSON array of pending contacts formatted as shown above.
|
||||||
|
|
||||||
|
To remove a pending contact and abort the process of adding it:
|
||||||
|
|
||||||
|
`DELETE /v1/contacts/add/pending`
|
||||||
|
|
||||||
|
The `pendingContactId` of the pending contact to delete
|
||||||
|
needs to be provided in the request body as follows:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U="
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Removing a contact
|
### Removing a contact
|
||||||
|
|
||||||
@@ -204,3 +273,9 @@ it will send a JSON object to connected websocket clients:
|
|||||||
|
|
||||||
Note that the JSON object in `data` is exactly what the REST API returns
|
Note that the JSON object in `data` is exactly what the REST API returns
|
||||||
when listing private messages.
|
when listing private messages.
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
* PendingContactStateChangedEvent
|
||||||
|
* PendingContactRemovedEvent
|
||||||
|
* ContactAddedRemotelyEvent
|
||||||
@@ -25,6 +25,7 @@ dependencies {
|
|||||||
|
|
||||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||||
|
testImplementation project(path: ':briar-core', configuration: 'testOutput')
|
||||||
|
|
||||||
def junitVersion = '5.4.2'
|
def junitVersion = '5.4.2'
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||||
|
|||||||
@@ -64,6 +64,16 @@ constructor(
|
|||||||
path("/v1") {
|
path("/v1") {
|
||||||
path("/contacts") {
|
path("/contacts") {
|
||||||
get { ctx -> contactController.list(ctx) }
|
get { ctx -> contactController.list(ctx) }
|
||||||
|
path("add") {
|
||||||
|
post { ctx -> contactController.addPendingContact(ctx) }
|
||||||
|
path("link") {
|
||||||
|
get { ctx -> contactController.link(ctx) }
|
||||||
|
}
|
||||||
|
path("pending") {
|
||||||
|
get { ctx -> contactController.listPendingContacts(ctx) }
|
||||||
|
delete { ctx -> contactController.removePendingContact(ctx) }
|
||||||
|
}
|
||||||
|
}
|
||||||
path("/:contactId") {
|
path("/:contactId") {
|
||||||
delete { ctx -> contactController.delete(ctx) }
|
delete { ctx -> contactController.delete(ctx) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import io.javalin.Context
|
|||||||
interface ContactController {
|
interface ContactController {
|
||||||
|
|
||||||
fun list(ctx: Context): Context
|
fun list(ctx: Context): Context
|
||||||
|
fun link(ctx: Context): Context
|
||||||
|
fun addPendingContact(ctx: Context): Context
|
||||||
|
fun listPendingContacts(ctx: Context): Context
|
||||||
|
fun removePendingContact(ctx: Context): Context
|
||||||
fun delete(ctx: Context): Context
|
fun delete(ctx: Context): Context
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
package org.briarproject.briar.headless.contact
|
package org.briarproject.briar.headless.contact
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import io.javalin.Context
|
import io.javalin.Context
|
||||||
import io.javalin.NotFoundResponse
|
import io.javalin.NotFoundResponse
|
||||||
import org.briarproject.bramble.api.contact.ContactManager
|
import org.briarproject.bramble.api.contact.ContactManager
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException
|
import org.briarproject.bramble.api.db.NoSuchContactException
|
||||||
|
import org.briarproject.bramble.api.db.NoSuchPendingContactException
|
||||||
import org.briarproject.briar.headless.getContactIdFromPathParam
|
import org.briarproject.briar.headless.getContactIdFromPathParam
|
||||||
|
import org.briarproject.briar.headless.getFromJson
|
||||||
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
|
import org.spongycastle.util.encoders.Base64
|
||||||
|
import org.spongycastle.util.encoders.DecoderException
|
||||||
import javax.annotation.concurrent.Immutable
|
import javax.annotation.concurrent.Immutable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@@ -13,7 +20,8 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
internal class ContactControllerImpl
|
internal class ContactControllerImpl
|
||||||
@Inject
|
@Inject
|
||||||
constructor(private val contactManager: ContactManager) : ContactController {
|
constructor(private val contactManager: ContactManager, private val objectMapper: ObjectMapper) :
|
||||||
|
ContactController {
|
||||||
|
|
||||||
override fun list(ctx: Context): Context {
|
override fun list(ctx: Context): Context {
|
||||||
val contacts = contactManager.contacts.map { contact ->
|
val contacts = contactManager.contacts.map { contact ->
|
||||||
@@ -22,6 +30,44 @@ constructor(private val contactManager: ContactManager) : ContactController {
|
|||||||
return ctx.json(contacts)
|
return ctx.json(contacts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun link(ctx: Context): Context {
|
||||||
|
val linkDict = JsonDict("link" to contactManager.handshakeLink)
|
||||||
|
return ctx.json(linkDict)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addPendingContact(ctx: Context): Context {
|
||||||
|
val link = ctx.getFromJson(objectMapper, "link")
|
||||||
|
val alias = ctx.getFromJson(objectMapper, "alias")
|
||||||
|
val pendingContact = contactManager.addPendingContact(link, alias)
|
||||||
|
return ctx.json(pendingContact.output())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun listPendingContacts(ctx: Context): Context {
|
||||||
|
val pendingContacts = contactManager.pendingContacts.map { pendingContact ->
|
||||||
|
pendingContact.output()
|
||||||
|
}
|
||||||
|
return ctx.json(pendingContacts)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removePendingContact(ctx: Context): Context {
|
||||||
|
// construct and check PendingContactId
|
||||||
|
val pendingContactString = ctx.getFromJson(objectMapper, "pendingContactId")
|
||||||
|
val pendingContactBytes = try {
|
||||||
|
Base64.decode(pendingContactString)
|
||||||
|
} catch (e: DecoderException) {
|
||||||
|
throw NotFoundResponse()
|
||||||
|
}
|
||||||
|
if (pendingContactBytes.size != PendingContactId.LENGTH) throw NotFoundResponse()
|
||||||
|
val id = PendingContactId(pendingContactBytes)
|
||||||
|
// remove
|
||||||
|
try {
|
||||||
|
contactManager.removePendingContact(id)
|
||||||
|
} catch (e: NoSuchPendingContactException) {
|
||||||
|
throw NotFoundResponse()
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
override fun delete(ctx: Context): Context {
|
override fun delete(ctx: Context): Context {
|
||||||
val contactId = ctx.getContactIdFromPathParam()
|
val contactId = ctx.getContactIdFromPathParam()
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.briarproject.briar.headless.contact
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContact
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactState.*
|
||||||
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
|
|
||||||
|
internal fun PendingContact.output() = JsonDict(
|
||||||
|
"pendingContactId" to id.bytes,
|
||||||
|
"alias" to alias,
|
||||||
|
"state" to when(state) {
|
||||||
|
WAITING_FOR_CONNECTION -> "waiting_for_connection"
|
||||||
|
CONNECTED -> "connected"
|
||||||
|
ADDING_CONTACT -> "adding_contact"
|
||||||
|
FAILED -> "failed"
|
||||||
|
else -> throw AssertionError()
|
||||||
|
},
|
||||||
|
"timestamp" to timestamp
|
||||||
|
)
|
||||||
@@ -4,6 +4,7 @@ import dagger.Component
|
|||||||
import org.briarproject.bramble.BrambleCoreEagerSingletons
|
import org.briarproject.bramble.BrambleCoreEagerSingletons
|
||||||
import org.briarproject.bramble.BrambleCoreModule
|
import org.briarproject.bramble.BrambleCoreModule
|
||||||
import org.briarproject.bramble.account.AccountModule
|
import org.briarproject.bramble.account.AccountModule
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent
|
||||||
import org.briarproject.bramble.event.DefaultEventExecutorModule
|
import org.briarproject.bramble.event.DefaultEventExecutorModule
|
||||||
import org.briarproject.bramble.test.TestSecureRandomModule
|
import org.briarproject.bramble.test.TestSecureRandomModule
|
||||||
import org.briarproject.briar.BriarCoreEagerSingletons
|
import org.briarproject.briar.BriarCoreEagerSingletons
|
||||||
@@ -25,5 +26,7 @@ import javax.inject.Singleton
|
|||||||
internal interface BriarHeadlessTestApp : BrambleCoreEagerSingletons, BriarCoreEagerSingletons {
|
internal interface BriarHeadlessTestApp : BrambleCoreEagerSingletons, BriarCoreEagerSingletons {
|
||||||
fun getRouter(): Router
|
fun getRouter(): Router
|
||||||
|
|
||||||
|
fun getCryptoComponent(): CryptoComponent
|
||||||
|
|
||||||
fun getTestDataCreator(): TestDataCreator
|
fun getTestDataCreator(): TestDataCreator
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import io.javalin.Javalin
|
|||||||
import io.javalin.core.util.Header.AUTHORIZATION
|
import io.javalin.core.util.Header.AUTHORIZATION
|
||||||
import khttp.responses.Response
|
import khttp.responses.Response
|
||||||
import org.briarproject.bramble.BrambleCoreModule
|
import org.briarproject.bramble.BrambleCoreModule
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent
|
||||||
import org.briarproject.briar.BriarCoreModule
|
import org.briarproject.briar.BriarCoreModule
|
||||||
import org.briarproject.briar.api.test.TestDataCreator
|
import org.briarproject.briar.api.test.TestDataCreator
|
||||||
import org.junit.jupiter.api.AfterAll
|
import org.junit.jupiter.api.AfterAll
|
||||||
@@ -22,6 +23,7 @@ abstract class IntegrationTest {
|
|||||||
private val dataDir = File("tmp")
|
private val dataDir = File("tmp")
|
||||||
|
|
||||||
protected lateinit var api: Javalin
|
protected lateinit var api: Javalin
|
||||||
|
protected lateinit var crypto: CryptoComponent
|
||||||
protected lateinit var testDataCreator: TestDataCreator
|
protected lateinit var testDataCreator: TestDataCreator
|
||||||
private lateinit var router: Router
|
private lateinit var router: Router
|
||||||
|
|
||||||
@@ -33,6 +35,7 @@ abstract class IntegrationTest {
|
|||||||
BrambleCoreModule.initEagerSingletons(app)
|
BrambleCoreModule.initEagerSingletons(app)
|
||||||
BriarCoreModule.initEagerSingletons(app)
|
BriarCoreModule.initEagerSingletons(app)
|
||||||
router = app.getRouter()
|
router = app.getRouter()
|
||||||
|
crypto = app.getCryptoComponent()
|
||||||
testDataCreator = app.getTestDataCreator()
|
testDataCreator = app.getTestDataCreator()
|
||||||
|
|
||||||
api = router.start(token, port, false)
|
api = router.start(token, port, false)
|
||||||
@@ -52,10 +55,18 @@ abstract class IntegrationTest {
|
|||||||
return khttp.get(url, getAuthTokenHeader("wrongToken"))
|
return khttp.get(url, getAuthTokenHeader("wrongToken"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun post(url: String, data: String) : Response {
|
||||||
|
return khttp.post(url, getAuthTokenHeader(token), data = data)
|
||||||
|
}
|
||||||
|
|
||||||
protected fun delete(url: String) : Response {
|
protected fun delete(url: String) : Response {
|
||||||
return khttp.delete(url, getAuthTokenHeader(token))
|
return khttp.delete(url, getAuthTokenHeader(token))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun delete(url: String, data: String) : Response {
|
||||||
|
return khttp.delete(url, getAuthTokenHeader(token), data = data)
|
||||||
|
}
|
||||||
|
|
||||||
protected fun deleteWithWrongToken(url: String) : Response {
|
protected fun deleteWithWrongToken(url: String) : Response {
|
||||||
return khttp.delete(url, getAuthTokenHeader("wrongToken"))
|
return khttp.delete(url, getAuthTokenHeader("wrongToken"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package org.briarproject.briar.headless.contact
|
package org.briarproject.briar.headless.contact
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES
|
||||||
import org.briarproject.briar.headless.IntegrationTest
|
import org.briarproject.briar.headless.IntegrationTest
|
||||||
import org.briarproject.briar.headless.url
|
import org.briarproject.briar.headless.url
|
||||||
|
import org.briarproject.briar.test.BriarTestUtils.getRealHandshakeLink
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
class ContactControllerIntegrationTest: IntegrationTest() {
|
class ContactControllerIntegrationTest: IntegrationTest() {
|
||||||
@@ -33,6 +36,51 @@ class ContactControllerIntegrationTest: IntegrationTest() {
|
|||||||
assertEquals(testContactName, author.getString("name"))
|
assertEquals(testContactName, author.getString("name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `returns own handshake link`() {
|
||||||
|
val response = get("$url/contacts/add/link")
|
||||||
|
assertEquals(200, response.statusCode)
|
||||||
|
val link = response.jsonObject.getString("link")
|
||||||
|
assertTrue(link.startsWith("briar://"))
|
||||||
|
assertEquals(BASE32_LINK_BYTES + 8, link.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `returns list of pending contacts`() {
|
||||||
|
// retrieve empty list of pending contacts
|
||||||
|
var response = get("$url/contacts/add/pending")
|
||||||
|
assertEquals(200, response.statusCode)
|
||||||
|
assertEquals(0, response.jsonArray.length())
|
||||||
|
|
||||||
|
// add one pending contact
|
||||||
|
val alias = "AliasFoo"
|
||||||
|
val json = """{
|
||||||
|
"link": "${getRealHandshakeLink(crypto)}",
|
||||||
|
"alias": "$alias"
|
||||||
|
}"""
|
||||||
|
response = post("$url/contacts/add", json)
|
||||||
|
assertEquals(200, response.statusCode)
|
||||||
|
|
||||||
|
// get added contact as only list item
|
||||||
|
response = get("$url/contacts/add/pending")
|
||||||
|
assertEquals(200, response.statusCode)
|
||||||
|
assertEquals(1, response.jsonArray.length())
|
||||||
|
val jsonObject = response.jsonArray.getJSONObject(0)
|
||||||
|
assertEquals(alias, jsonObject.getString("alias"))
|
||||||
|
assertEquals("waiting_for_connection", jsonObject.getString("state"))
|
||||||
|
|
||||||
|
// remove pending contact again
|
||||||
|
val idString = jsonObject.getString("pendingContactId")
|
||||||
|
val deleteJson = """{"pendingContactId": "$idString"}"""
|
||||||
|
response = delete("$url/contacts/add/pending", deleteJson)
|
||||||
|
assertEquals(200, response.statusCode)
|
||||||
|
|
||||||
|
// list of pending contacts should be empty now
|
||||||
|
response = get("$url/contacts/add/pending")
|
||||||
|
assertEquals(200, response.statusCode)
|
||||||
|
assertEquals(0, response.jsonArray.length())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `deleting contact need authentication token`() {
|
fun `deleting contact need authentication token`() {
|
||||||
val response = deleteWithWrongToken("$url/contacts/1")
|
val response = deleteWithWrongToken("$url/contacts/1")
|
||||||
|
|||||||
@@ -7,15 +7,21 @@ import io.mockk.every
|
|||||||
import io.mockk.just
|
import io.mockk.just
|
||||||
import org.briarproject.bramble.api.contact.Contact
|
import org.briarproject.bramble.api.contact.Contact
|
||||||
import org.briarproject.bramble.api.contact.ContactId
|
import org.briarproject.bramble.api.contact.ContactId
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException
|
import org.briarproject.bramble.api.db.NoSuchContactException
|
||||||
|
import org.briarproject.bramble.api.db.NoSuchPendingContactException
|
||||||
import org.briarproject.bramble.identity.output
|
import org.briarproject.bramble.identity.output
|
||||||
|
import org.briarproject.bramble.test.TestUtils.getPendingContact
|
||||||
|
import org.briarproject.bramble.test.TestUtils.getRandomBytes
|
||||||
import org.briarproject.briar.headless.ControllerTest
|
import org.briarproject.briar.headless.ControllerTest
|
||||||
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
import org.junit.jupiter.api.Assertions.assertThrows
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
internal class ContactControllerTest : ControllerTest() {
|
internal class ContactControllerTest : ControllerTest() {
|
||||||
|
|
||||||
private val controller = ContactControllerImpl(contactManager)
|
private val controller = ContactControllerImpl(contactManager, objectMapper)
|
||||||
|
private val pendingContact = getPendingContact()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEmptyContactList() {
|
fun testEmptyContactList() {
|
||||||
@@ -31,6 +37,79 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
controller.list(ctx)
|
controller.list(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLink() {
|
||||||
|
val link = "briar://link"
|
||||||
|
every { contactManager.handshakeLink } returns link
|
||||||
|
every { ctx.json(JsonDict("link" to link)) } returns ctx
|
||||||
|
controller.link(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAddPendingContact() {
|
||||||
|
val link = "briar://link123"
|
||||||
|
val alias = "Alias123"
|
||||||
|
val body = """{
|
||||||
|
"link": "$link",
|
||||||
|
"alias": "$alias"
|
||||||
|
}"""
|
||||||
|
every { ctx.body() } returns body
|
||||||
|
every { contactManager.addPendingContact(link, alias) } returns pendingContact
|
||||||
|
every { ctx.json(pendingContact.output()) } returns ctx
|
||||||
|
controller.addPendingContact(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testListPendingContacts() {
|
||||||
|
every { contactManager.pendingContacts } returns listOf(pendingContact)
|
||||||
|
every { ctx.json(listOf(pendingContact.output())) } returns ctx
|
||||||
|
controller.listPendingContacts(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRemovePendingContact() {
|
||||||
|
val id = pendingContact.id
|
||||||
|
every { ctx.body() } returns """{"pendingContactId": ${toJson(id.bytes)}}"""
|
||||||
|
every { contactManager.removePendingContact(id) } just Runs
|
||||||
|
controller.removePendingContact(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRemovePendingContactInvalidId() {
|
||||||
|
every { ctx.body() } returns """{"pendingContactId": "foo"}"""
|
||||||
|
assertThrows(NotFoundResponse::class.java) {
|
||||||
|
controller.removePendingContact(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRemovePendingContactTooShortId() {
|
||||||
|
val bytes = getRandomBytes(PendingContactId.LENGTH - 1)
|
||||||
|
every { ctx.body() } returns """{"pendingContactId": ${toJson(bytes)}}"""
|
||||||
|
assertThrows(NotFoundResponse::class.java) {
|
||||||
|
controller.removePendingContact(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRemovePendingContactTooLongId() {
|
||||||
|
val bytes = getRandomBytes(PendingContactId.LENGTH + 1)
|
||||||
|
every { ctx.body() } returns """{"pendingContactId": ${toJson(bytes)}}"""
|
||||||
|
assertThrows(NotFoundResponse::class.java) {
|
||||||
|
controller.removePendingContact(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRemovePendingContactNonexistentId() {
|
||||||
|
val id = pendingContact.id
|
||||||
|
every { ctx.body() } returns """{"pendingContactId": ${toJson(id.bytes)}}"""
|
||||||
|
every { contactManager.removePendingContact(id) } throws NoSuchPendingContactException()
|
||||||
|
assertThrows(NotFoundResponse::class.java) {
|
||||||
|
controller.removePendingContact(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDelete() {
|
fun testDelete() {
|
||||||
every { ctx.pathParam("contactId") } returns "1"
|
every { ctx.pathParam("contactId") } returns "1"
|
||||||
@@ -80,4 +159,17 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
assertJsonEquals(json, author.output())
|
assertJsonEquals(json, author.output())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOutputPendingContact() {
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"pendingContactId": ${toJson(pendingContact.id.bytes)},
|
||||||
|
"alias": "${pendingContact.alias}",
|
||||||
|
"state": "${pendingContact.state.name.toLowerCase()}",
|
||||||
|
"timestamp": ${pendingContact.timestamp}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
assertJsonEquals(json, pendingContact.output())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user