Repository: andregasser/bigbone
Pull request #413 (2023-12-29 18:10:09)

Add Admin::DomainAllow methods
# Description

This PR implements the [domain allows API](https://docs.joinmastodon.org/methods/admin/domain_allows/) so that users with role of admin to both see allowed federated domains, remove allowed domain and add new domain to be federated.

This closes https://github.com/andregasser/bigbone/issues/328

# Type of Change

- New feature

# How Has This Been Tested?

Gradle ktlint, gradle check, gradle detect and gradle build

# Mandatory Checklist

- [x] I ran `gradle check` and there were no errors reported
- [x] I have performed a self-review of my code
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] All tests pass locally with my changes
- [x] I have added KDoc documentation to all public methods

# Optional checks

I implemented related RX counter part and implemented unit test.
I also updated the API coverage page with corresponding ones developed in this PR.


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/andregasser/bigbone/pull/413?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Andr%C3%A9+Gasser) Report
Attention: `21 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`90c8259`)](https://app.codecov.io/gh/andregasser/bigbone/commit/90c825942359b3985490c7e7eb3b3d5e42701014?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Andr%C3%A9+Gasser) 49.84% compared to head [(`57d3aad`)](https://app.codecov.io/gh/andregasser/bigbone/pull/413?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Andr%C3%A9+Gasser) 49.86%.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff              @@
##             master     #413      +/-   ##
============================================
+ Coverage     49.84%   49.86%   +0.02%     
- Complexity      661      668       +7     
============================================
  Files           162      165       +3     
  Lines          4484     4528      +44     
  Branches        284      284              
============================================
+ Hits           2235     2258      +23     
- Misses         1996     2016      +20     
- Partials        253      254       +1     
```

| [Files](https://app.codecov.io/gh/andregasser/bigbone/pull/413?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Andr%C3%A9+Gasser) | Coverage Δ | |
|---|---|---|
| [...e/src/main/kotlin/social/bigbone/MastodonClient.kt](https://app.codecov.io/gh/andregasser/bigbone/pull/413?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Andr%C3%A9+Gasser#diff-YmlnYm9uZS9zcmMvbWFpbi9rb3RsaW4vc29jaWFsL2JpZ2JvbmUvTWFzdG9kb25DbGllbnQua3Q=) | `35.45% <100.00%> (+0.16%)` | :arrow_up: |
| [...igbone/api/method/admin/AdminDomainAllowMethods.kt](https://app.codecov.io/gh/andregasser/bigbone/pull/413?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Andr%C3%A9+Gasser#diff-YmlnYm9uZS9zcmMvbWFpbi9rb3RsaW4vc29jaWFsL2JpZ2JvbmUvYXBpL21ldGhvZC9hZG1pbi9BZG1pbkRvbWFpbkFsbG93TWV0aG9kcy5rdA==) | `95.00% <95.00%> (ø)` | |
| [...ocial/bigbone/api/entity/admin/AdminDomainAllow.kt](https://app.codecov.io/gh/andregasser/bigbone/pull/413?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Andr%C3%A9+Gasser#diff-YmlnYm9uZS9zcmMvbWFpbi9rb3RsaW4vc29jaWFsL2JpZ2JvbmUvYXBpL2VudGl0eS9hZG1pbi9BZG1pbkRvbWFpbkFsbG93Lmt0) | `33.33% <33.33%> (ø)` | |
| [...cial/bigbone/rx/admin/RxAdminDomainAllowMethods.kt](https://app.codecov.io/gh/andregasser/bigbone/pull/413?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Andr%C3%A9+Gasser#diff-YmlnYm9uZS1yeC9zcmMvbWFpbi9rb3RsaW4vc29jaWFsL2JpZ2JvbmUvcngvYWRtaW4vUnhBZG1pbkRvbWFpbkFsbG93TWV0aG9kcy5rdA==) | `0.00% <0.00%> (ø)` | |

</details>

Patch:
diff --git a/bigbone-rx/src/main/kotlin/social/bigbone/rx/admin/RxAdminDomainAllowMethods.kt b/bigbone-rx/src/main/kotlin/social/bigbone/rx/admin/RxAdminDomainAllowMethods.kt
new file mode 100644
index 0000000..74af792
--- /dev/null
+++ b/bigbone-rx/src/main/kotlin/social/bigbone/rx/admin/RxAdminDomainAllowMethods.kt
@@ -0,0 +1,57 @@
+package social.bigbone.rx.admin
+
+import io.reactivex.rxjava3.core.Completable
+import io.reactivex.rxjava3.core.Single
+import social.bigbone.MastodonClient
+import social.bigbone.api.Pageable
+import social.bigbone.api.Range
+import social.bigbone.api.entity.admin.AdminDomainAllow
+import social.bigbone.api.method.admin.AdminDomainAllowMethods
+
+/**
+ * Reactive implementation of [AdminDomainAllowMethods].
+ *
+ * Allow certain domains to federate.
+ * @see <a href="https://docs.joinmastodon.org/methods/admin/domain_allows/">Mastodon admin/domain_allows API methods</a>
+ */
+class RxAdminDomainAllowMethods(client: MastodonClient) {
+
+    private val adminAllowMethods = AdminDomainAllowMethods(client)
+
+    /**
+     * Show information about all allowed domains.
+     * @param range optional Range for the pageable return value
+     * @see <a href="https://docs.joinmastodon.org/methods/domain_allows/#get">Mastodon API documentation: methods/domain_allows/#get</a>
+     */
+    @JvmOverloads
+    fun getAllAllowedDomains(range: Range = Range()): Single<Pageable<AdminDomainAllow>> = Single.fromCallable {
+        adminAllowMethods.getAllAllowedDomains(range = range).execute()
+    }
+
+    /**
+     * Show information about a single allowed domain.
+     * @param id The ID of the DomainAllow in the database.
+     * @see <a href="https://docs.joinmastodon.org/methods/domain_allows/#get-one">Mastodon API documentation: methods/domain_allows/#get-one</a>
+     */
+    fun getAllowedDomain(id: String): Single<AdminDomainAllow> = Single.fromCallable {
+        adminAllowMethods.getAllowedDomain(id = id).execute()
+    }
+
+    /**
+     * Add a domain to the list of domains allowed to federate, to be used when the instance is in allow-list federation mode.
+     * @param domain The domain to allow federation with.
+     * @see <a href="https://docs.joinmastodon.org/methods/admin/domain_allows/#create">Mastodon API documentation: admin/domain_allows/#create</a>
+     */
+    fun allowDomainToFederate(domain: String): Single<AdminDomainAllow> = Single.fromCallable {
+        adminAllowMethods.allowDomainToFederate(domain = domain).execute()
+    }
+
+    /**
+     * Delete a domain from the allowed domains list.
+     * @param id The ID of the DomainAllow in the database.
+     * @see <a href="https://docs.joinmastodon.org/methods/admin/domain_allows/#delete">Mastodon API documentation: admin/domain_allows/#delete</a>
+     */
+    fun removeAllowedDomain(id: String): Completable = Completable.fromAction {
+        adminAllowMethods.removeAllowedDomain(id = id)
+    }
+}
diff --git a/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt b/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt
index 641f141..af62f96 100644
--- a/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt
+++ b/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt
@@ -68,6 +68,7 @@ import social.bigbone.api.method.TrendMethods
 import social.bigbone.api.method.admin.AdminAccountMethods
 import social.bigbone.api.method.admin.AdminCanonicalEmailBlockMethods
 import social.bigbone.api.method.admin.AdminDimensionMethods
+import social.bigbone.api.method.admin.AdminDomainAllowMethods
 import social.bigbone.api.method.admin.AdminDomainBlockMethods
 import social.bigbone.api.method.admin.AdminEmailDomainBlockMethods
 import social.bigbone.api.method.admin.AdminIpBlockMethods
@@ -130,6 +131,13 @@ private constructor(
     @get:JvmName("adminDimensions")
     val adminDimensions: AdminDimensionMethods by lazy { AdminDimensionMethods(this) }
 
+    /**
+     * Access API methods under the "admin/domain_allows" endpoint.
+     */
+    @Suppress("unused") // public API
+    @get:JvmName("adminDomainAllow")
+    val adminDomainAllow: AdminDomainAllowMethods by lazy { AdminDomainAllowMethods(this) }
+
     /**
      * Access API methods under the "admin/domain_blocks" endpoint.
      */
diff --git a/bigbone/src/main/kotlin/social/bigbone/api/entity/admin/AdminDomainAllow.kt b/bigbone/src/main/kotlin/social/bigbone/api/entity/admin/AdminDomainAllow.kt
new file mode 100644
index 0000000..1dbde08
--- /dev/null
+++ b/bigbone/src/main/kotlin/social/bigbone/api/entity/admin/AdminDomainAllow.kt
@@ -0,0 +1,32 @@
+package social.bigbone.api.entity.admin
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import social.bigbone.DateTimeSerializer
+import social.bigbone.PrecisionDateTime
+
+/**
+ * Represents a domain allowed to federate.
+ * @see <a href="https://docs.joinmastodon.org/entities/Admin_DomainAllow/">Mastodon API Admin::DomainAllow/</a>
+ */
+@Serializable
+data class AdminDomainAllow(
+    /**
+     * The ID of the DomainAllow in the database.
+     */
+    @SerialName("id")
+    val id: String = "0",
+
+    /**
+     * The domain that is allowed to federate.
+     */
+    @SerialName("domain")
+    val domain: String = "",
+
+    /**
+     * When the domain was allowed to federate.
+     */
+    @SerialName("created_at")
+    @Serializable(with = DateTimeSerializer::class)
+    val createdAt: PrecisionDateTime = PrecisionDateTime.InvalidPrecisionDateTime.Unavailable
+)
diff --git a/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethods.kt b/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethods.kt
new file mode 100644
index 0000000..9567472
--- /dev/null
+++ b/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethods.kt
@@ -0,0 +1,70 @@
+package social.bigbone.api.method.admin
+
+import social.bigbone.MastodonClient
+import social.bigbone.MastodonRequest
+import social.bigbone.Parameters
+import social.bigbone.api.Pageable
+import social.bigbone.api.Range
+import social.bigbone.api.entity.admin.AdminDomainAllow
+
+/**
+ * Allow certain domains to federate.
+ * @see <a href="https://docs.joinmastodon.org/methods/admin/domain_allows/">Mastodon admin/domain_allows API methods</a>
+ */
+class AdminDomainAllowMethods(private val client: MastodonClient) {
+
+    private val adminDomainAllowEndpoint = "api/v1/admin/domain_allows"
+
+    /**
+     * Show information about all allowed domains.
+     * @param range optional Range for the pageable return value
+     * @see <a href="https://docs.joinmastodon.org/methods/domain_allows/#get">Mastodon API documentation: methods/domain_allows/#get</a>
+     */
+    @JvmOverloads
+    fun getAllAllowedDomains(range: Range = Range()): MastodonRequest<Pageable<AdminDomainAllow>> {
+        return client.getPageableMastodonRequest(
+            endpoint = adminDomainAllowEndpoint,
+            method = MastodonClient.Method.GET,
+            parameters = range.toParameters()
+        )
+    }
+
+    /**
+     * Show information about a single allowed domain.
+     * @param id The ID of the DomainAllow in the database.
+     * @see <a href="https://docs.joinmastodon.org/methods/domain_allows/#get-one">Mastodon API documentation: methods/domain_allows/#get-one</a>
+     */
+    fun getAllowedDomain(id: String): MastodonRequest<AdminDomainAllow> {
+        return client.getMastodonRequest(
+            endpoint = "$adminDomainAllowEndpoint/$id",
+            method = MastodonClient.Method.GET
+        )
+    }
+
+    /**
+     * Add a domain to the list of domains allowed to federate, to be used when the instance is in allow-list federation mode.
+     * @param domain The domain to allow federation with.
+     * @see <a href="https://docs.joinmastodon.org/methods/admin/domain_allows/#create">Mastodon API documentation: admin/domain_allows/#create</a>
+     */
+    fun allowDomainToFederate(domain: String): MastodonRequest<AdminDomainAllow> {
+        return client.getMastodonRequest(
+            endpoint = adminDomainAllowEndpoint,
+            method = MastodonClient.Method.POST,
+            parameters = Parameters().apply {
+                append("domain", domain)
+            }
+        )
+    }
+
+    /**
+     * Delete a domain from the allowed domains list.
+     * @param id The ID of the DomainAllow in the database.
+     * @see <a href="https://docs.joinmastodon.org/methods/admin/domain_allows/#delete">Mastodon API documentation: admin/domain_allows/#delete</a>
+     */
+    fun removeAllowedDomain(id: String) {
+        client.performAction(
+            endpoint = "$adminDomainAllowEndpoint/$id",
+            method = MastodonClient.Method.DELETE
+        )
+    }
+}
diff --git a/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainBlockMethods.kt b/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainBlockMethods.kt
index 1dc8f10..aa554a0 100644
--- a/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainBlockMethods.kt
+++ b/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainBlockMethods.kt
@@ -15,7 +15,7 @@ import social.bigbone.api.entity.admin.AdminDomainBlock.Severity
  */
 class AdminDomainBlockMethods(private val client: MastodonClient) {
 
-    private val adminDomainBlocksEndpoint = "api/v1/admin/domain_blocks"
+    private val endpoint = "api/v1/admin/domain_blocks"
 
     /**
      * Show information about all blocked domains.
@@ -27,7 +27,7 @@ class AdminDomainBlockMethods(private val client: MastodonClient) {
     @JvmOverloads
     fun getAllBlockedDomains(range: Range = Range()): MastodonRequest<Pageable<AdminDomainBlock>> {
         return client.getPageableMastodonRequest(
-            endpoint = adminDomainBlocksEndpoint,
+            endpoint = endpoint,
             method = MastodonClient.Method.GET,
             parameters = range.toParameters()
         )
@@ -42,7 +42,7 @@ class AdminDomainBlockMethods(private val client: MastodonClient) {
      */
     fun getBlockedDomain(id: String): MastodonRequest<AdminDomainBlock> {
         return client.getMastodonRequest(
-            endpoint = "$adminDomainBlocksEndpoint/$id",
+            endpoint = "$endpoint/$id",
             method = MastodonClient.Method.GET
         )
     }
@@ -71,7 +71,7 @@ class AdminDomainBlockMethods(private val client: MastodonClient) {
         publicComment: String? = null
     ): MastodonRequest<AdminDomainBlock> {
         return client.getMastodonRequest(
-            endpoint = adminDomainBlocksEndpoint,
+            endpoint = endpoint,
             method = MastodonClient.Method.POST,
             parameters = Parameters().apply {
                 append("domain", domain)
@@ -113,7 +113,7 @@ class AdminDomainBlockMethods(private val client: MastodonClient) {
         publicComment: String? = null
     ): MastodonRequest<AdminDomainBlock> {
         return client.getMastodonRequest(
-            endpoint = "$adminDomainBlocksEndpoint/$id",
+            endpoint = "$endpoint/$id",
             method = MastodonClient.Method.PUT,
             parameters = Parameters().apply {
                 severity?.let { append("severity", severity.apiName) }
@@ -138,7 +138,7 @@ class AdminDomainBlockMethods(private val client: MastodonClient) {
      */
     fun removeBlockedDomain(id: String) {
         client.performAction(
-            endpoint = "$adminDomainBlocksEndpoint/$id",
+            endpoint = "$endpoint/$id",
             method = MastodonClient.Method.DELETE
         )
     }
diff --git a/docs/api-coverage/admin/domain-allows.md b/docs/api-coverage/admin/domain-allows.md
index 431e458..f093c16 100644
--- a/docs/api-coverage/admin/domain-allows.md
+++ b/docs/api-coverage/admin/domain-allows.md
@@ -11,8 +11,8 @@ nav_order: 7
 <a href="https://docs.joinmastodon.org/methods/admin/domain_allows/" target="_blank">https://docs.joinmastodon.org/methods/admin/domain_allows/</a>
 
 | Method                                   | Description                 | Status | Comments | 
-|------------------------------------------|-----------------------------|--------|----------|
-| `GET /api/v1/admin/domain_allows`        | List all allowed domains    | 🔴     |          |
-| `GET /api/v1/admin/domain_allows/:id`    | Get a single allowed domain | 🔴     |          |
-| `POST /api/v1/admin/domain_allows`       | Allow a domain to federate  | 🔴     |          |
-| `DELETE /api/v1/admin/domain_allows/:id` | Delete an allowed domain    | 🔴     |          |
+|------------------------------------------|-----------------------------|----|--|
+| `GET /api/v1/admin/domain_allows`        | List all allowed domains    | <img src="/assets/green16.png"> | Fully supported. |
+| `GET /api/v1/admin/domain_allows/:id`    | Get a single allowed domain | <img src="/assets/green16.png"> | Fully supported. |
+| `POST /api/v1/admin/domain_allows`       | Allow a domain to federate  | <img src="/assets/green16.png"> | Fully supported. |
+| `DELETE /api/v1/admin/domain_allows/:id` | Delete an allowed domain    | <img src="/assets/green16.png"> | Fully supported. |


Test patch:
diff --git a/bigbone/src/test/assets/admin_domain_allow.json b/bigbone/src/test/assets/admin_domain_allow.json
new file mode 100644
index 0000000..94fd8ae
--- /dev/null
+++ b/bigbone/src/test/assets/admin_domain_allow.json
@@ -0,0 +1,5 @@
+{
+  "id": "1",
+  "domain": "mastodon.social",
+  "created_at": "2022-09-14T21:23:02.755Z"
+}
\ No newline at end of file
diff --git a/bigbone/src/test/assets/admin_domain_allow_list.json b/bigbone/src/test/assets/admin_domain_allow_list.json
new file mode 100644
index 0000000..f70c0fa
--- /dev/null
+++ b/bigbone/src/test/assets/admin_domain_allow_list.json
@@ -0,0 +1,12 @@
+[
+  {
+    "id": "2",
+    "domain": "mastodon",
+    "created_at": "2022-09-14T21:24:15.360Z"
+  },
+  {
+    "id": "1",
+    "domain": "mastodon.social",
+    "created_at": "2022-09-14T21:23:02.755Z"
+  }
+]
\ No newline at end of file
diff --git a/bigbone/src/test/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethodsTest.kt b/bigbone/src/test/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethodsTest.kt
new file mode 100644
index 0000000..6deb135
--- /dev/null
+++ b/bigbone/src/test/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethodsTest.kt
@@ -0,0 +1,116 @@
+package social.bigbone.api.method.admin
+
+import io.mockk.slot
+import io.mockk.verify
+import org.amshove.kluent.AnyException
+import org.amshove.kluent.invoking
+import org.amshove.kluent.shouldBeEqualTo
+import org.amshove.kluent.shouldNotThrow
+import org.amshove.kluent.shouldThrow
+import org.amshove.kluent.withMessage
+import org.junit.jupiter.api.Test
+import social.bigbone.MastodonClient
+import social.bigbone.Parameters
+import social.bigbone.api.Range
+import social.bigbone.api.exception.BigBoneRequestException
+import social.bigbone.testtool.MockClient
+
+class AdminDomainAllowMethodsTest {
+
+    private val adminDomainAllowEndpoint = "api/v1/admin/domain_allows"
+
+    @Test
+    fun `Given client returning success, when getting all allowed domains, then return list of allowed domains`() {
+        val client = MockClient.mock("admin_domain_allow_list.json")
+        val adminDomainAllowMethods = AdminDomainAllowMethods(client)
+        val range = Range("1", "10", "1", 10)
+        val allowedDomains = adminDomainAllowMethods.getAllAllowedDomains(range).execute()
+        allowedDomains.part.size shouldBeEqualTo 2
+        with(allowedDomains.part[1]) {
+            id shouldBeEqualTo "1"
+            domain shouldBeEqualTo "mastodon.social"
+        }
+
+        val parametersCapturingSlot = slot<Parameters>()
+        verify {
+            client.get(
+                path = adminDomainAllowEndpoint,
+                query = capture(parametersCapturingSlot)
+            )
+        }
+        with(parametersCapturingSlot.captured) {
+            toQuery() shouldBeEqualTo "max_id=${range.maxId}&min_id=${range.minId}&since_id=${range.sinceId}&limit=${range.limit}"
+        }
+    }
+
+    @Test
+    fun `Given client returning success, when getting specific allowed domain, then return expected data for single domain`() {
+        val client = MockClient.mock("admin_domain_allow.json")
+        val adminDomainAllowMethods = AdminDomainAllowMethods(client)
+        val pathVariable = "1"
+        val adminDomainAllowed = adminDomainAllowMethods.getAllowedDomain(pathVariable).execute()
+        with(adminDomainAllowed) {
+            id shouldBeEqualTo pathVariable
+            domain shouldBeEqualTo "mastodon.social"
+        }
+
+        verify {
+            client.get(
+                path = "$adminDomainAllowEndpoint/$pathVariable"
+            )
+        }
+    }
+
+    @Test
+    fun `Given client returning success, when posting a domain to be allowed to federate, then return expected data about the domain`() {
+        val client = MockClient.mock("admin_domain_allow.json")
+        val adminDomainAllowMethods = AdminDomainAllowMethods(client)
+
+        invoking {
+            adminDomainAllowMethods.allowDomainToFederate(domain = "mastodon.social").execute()
+        } shouldNotThrow AnyException
+
+        val parametersCapturingSlot = slot<Parameters>()
+        verify {
+            client.post(
+                path = adminDomainAllowEndpoint,
+                body = capture(parametersCapturingSlot),
+                addIdempotencyKey = false
+            )
+        }
+        with(parametersCapturingSlot.captured) {
+            toQuery() shouldBeEqualTo "domain=mastodon.social"
+        }
+    }
+
+    @Test
+    fun `Given client returning success, when removing specific domain from being allowd, then check data for single ip`() {
+        val client = MockClient.mock("admin_domain_allow.json")
+        val adminDomainAllowMethods = AdminDomainAllowMethods(client)
+        val id = "1"
+
+        invoking {
+            adminDomainAllowMethods.removeAllowedDomain(id = id)
+        } shouldNotThrow AnyException
+
+        verify {
+            client.performAction(
+                endpoint = "$adminDomainAllowEndpoint/$id",
+                method = MastodonClient.Method.DELETE
+            )
+        }
+    }
+
+    @Test
+    fun `Given a client returning forbidden, when getting specific allowed domain, then propagate error`() {
+        val client = MockClient.failWithResponse(
+            responseJsonAssetPath = "error_403_forbidden.json",
+            responseCode = 403,
+            message = "Forbidden"
+        )
+
+        invoking {
+            AdminDomainAllowMethods(client).getAllowedDomain(id = "1").execute()
+        } shouldThrow BigBoneRequestException::class withMessage "Forbidden"
+    }
+}