From 7b6cd0ab283009d0b990e34c2937d5f725e3b134 Mon Sep 17 00:00:00 2001 From: geisterfurz007 Date: Sun, 22 Oct 2023 13:32:20 +0200 Subject: [PATCH] feat: android unit tests --- composer.lock | 52 ++--- src/SDK/Language/Android.php | 20 ++ templates/android/build.gradle.twig | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- templates/android/library/build.gradle.twig | 17 +- .../src/main/java/io/appwrite/Client.kt.twig | 33 +-- .../main/java/io/appwrite/Response.kt.twig | 7 + .../java/io/appwrite/models/Model.kt.twig | 2 +- .../src/test/java/io/appwrite/IDTest.kt.twig | 12 ++ .../java/io/appwrite/PermissionTest.kt.twig | 21 ++ .../test/java/io/appwrite/QueryTest.kt.twig | 189 ++++++++++++++++++ .../test/java/io/appwrite/RoleTest.kt.twig | 66 ++++++ .../io/appwrite/cookies/CookiesTest.kt.twig | 17 ++ .../java/io/appwrite/models/ModelTest.kt.twig | 28 +++ .../io/appwrite/services/ServiceTest.kt.twig | 77 +++++++ .../dart/test/services/service_test.dart.twig | 3 +- 16 files changed, 501 insertions(+), 47 deletions(-) create mode 100644 templates/android/library/src/main/java/io/appwrite/Response.kt.twig create mode 100644 templates/android/library/src/test/java/io/appwrite/IDTest.kt.twig create mode 100644 templates/android/library/src/test/java/io/appwrite/PermissionTest.kt.twig create mode 100644 templates/android/library/src/test/java/io/appwrite/QueryTest.kt.twig create mode 100644 templates/android/library/src/test/java/io/appwrite/RoleTest.kt.twig create mode 100644 templates/android/library/src/test/java/io/appwrite/cookies/CookiesTest.kt.twig create mode 100644 templates/android/library/src/test/java/io/appwrite/models/ModelTest.kt.twig create mode 100644 templates/android/library/src/test/java/io/appwrite/services/ServiceTest.kt.twig diff --git a/composer.lock b/composer.lock index a542762bd0..8f0e093703 100644 --- a/composer.lock +++ b/composer.lock @@ -370,16 +370,16 @@ "packages-dev": [ { "name": "brianium/paratest", - "version": "v6.10.0", + "version": "v6.10.1", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "c2243b20bcd99c3f651018d1447144372f39b4fa" + "reference": "d6f32a91302b74458e8ef5d132bb2215a5edb34b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/c2243b20bcd99c3f651018d1447144372f39b4fa", - "reference": "c2243b20bcd99c3f651018d1447144372f39b4fa", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/d6f32a91302b74458e8ef5d132bb2215a5edb34b", + "reference": "d6f32a91302b74458e8ef5d132bb2215a5edb34b", "shasum": "" }, "require": { @@ -446,7 +446,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v6.10.0" + "source": "https://github.com/paratestphp/paratest/tree/v6.10.1" }, "funding": [ { @@ -458,7 +458,7 @@ "type": "paypal" } ], - "time": "2023-05-25T13:47:58+00:00" + "time": "2023-10-04T13:33:07+00:00" }, { "name": "doctrine/instantiator", @@ -878,16 +878,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.27", + "version": "9.2.29", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1" + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b0a88255cb70d52653d80c890bd7f38740ea50d1", - "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", "shasum": "" }, "require": { @@ -944,7 +944,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.27" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" }, "funding": [ { @@ -952,7 +952,7 @@ "type": "github" } ], - "time": "2023-07-26T13:44:30+00:00" + "time": "2023-09-19T04:57:46+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1197,16 +1197,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.11", + "version": "9.6.13", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "810500e92855eba8a7a5319ae913be2da6f957b0" + "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/810500e92855eba8a7a5319ae913be2da6f957b0", - "reference": "810500e92855eba8a7a5319ae913be2da6f957b0", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f3d767f7f9e191eab4189abe41ab37797e30b1be", + "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be", "shasum": "" }, "require": { @@ -1221,7 +1221,7 @@ "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-code-coverage": "^9.2.28", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", @@ -1280,7 +1280,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.11" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.13" }, "funding": [ { @@ -1296,7 +1296,7 @@ "type": "tidelift" } ], - "time": "2023-08-19T07:10:56+00:00" + "time": "2023-09-19T05:39:22+00:00" }, { "name": "psr/container", @@ -2839,16 +2839,16 @@ }, { "name": "symfony/string", - "version": "v6.3.2", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "53d1a83225002635bca3482fcbf963001313fb68" + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/53d1a83225002635bca3482fcbf963001313fb68", - "reference": "53d1a83225002635bca3482fcbf963001313fb68", + "url": "https://api.github.com/repos/symfony/string/zipball/13d76d0fb049051ed12a04bef4f9de8715bea339", + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339", "shasum": "" }, "require": { @@ -2905,7 +2905,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.2" + "source": "https://github.com/symfony/string/tree/v6.3.5" }, "funding": [ { @@ -2921,7 +2921,7 @@ "type": "tidelift" } ], - "time": "2023-07-05T08:41:27+00:00" + "time": "2023-09-18T10:38:32+00:00" }, { "name": "theseer/tokenizer", @@ -2986,5 +2986,5 @@ "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/src/SDK/Language/Android.php b/src/SDK/Language/Android.php index 5d3e90bb06..e7a92d9fcb 100644 --- a/src/SDK/Language/Android.php +++ b/src/SDK/Language/Android.php @@ -175,6 +175,11 @@ public function getFiles(): array 'destination' => '/library/src/main/java/{{ sdk.namespace | caseSlash }}/KeepAliveService.kt', 'template' => '/android/library/src/main/java/io/appwrite/KeepAliveService.kt.twig', ], + [ + 'scope' => 'default', + 'destination' => '/library/src/main/java/{{ sdk.namespace | caseSlash }}/Response.kt', + 'template' => '/android/library/src/main/java/io/appwrite/Response.kt.twig', + ], [ 'scope' => 'default', 'destination' => '/library/src/main/java/{{ sdk.namespace | caseSlash }}/views/CallbackActivity.kt', @@ -225,6 +230,16 @@ public function getFiles(): array 'destination' => '/library/src/main/AndroidManifest.xml', 'template' => '/android/library/src/main/AndroidManifest.xml.twig', ], + [ + 'scope' => 'service', + 'destination' => '/library/src/test/java/{{ sdk.namespace | caseSlash }}/services/{{ service.name | caseUcfirst }}ServiceTest.kt', + 'template' => '/android/library/src/test/java/io/appwrite/services/ServiceTest.kt.twig', + ], + [ + 'scope' => 'default', + 'destination' => '/library/src/test/java/{{ sdk.namespace | caseSlash }}/cookies/CookiesTest.kt', + 'template' => '/android/library/src/test/java/io/appwrite/cookies/CookiesTest.kt.twig', + ], [ 'scope' => 'default', 'destination' => '/library/build.gradle', @@ -387,6 +402,11 @@ public function getFiles(): array 'destination' => 'library/src/main/java/io/appwrite/models/{{ definition.name | caseUcfirst }}.kt', 'template' => '/android/library/src/main/java/io/appwrite/models/Model.kt.twig', ], + [ + 'scope' => 'definition', + 'destination' => 'library/src/test/java/io/appwrite/models/{{ definition.name | caseUcfirst }}Test.kt', + 'template' => '/android/library/src/test/java/io/appwrite/models/ModelTest.kt.twig', + ], ]; } } diff --git a/templates/android/build.gradle.twig b/templates/android/build.gradle.twig index f2080e685f..41a2ff5409 100644 --- a/templates/android/build.gradle.twig +++ b/templates/android/build.gradle.twig @@ -10,7 +10,7 @@ buildscript { mavenCentral() } dependencies { - classpath "com.android.tools.build:gradle:4.2.2" + classpath "com.android.tools.build:gradle:7.4.2" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'io.github.gradle-nexus:publish-plugin:1.1.0' diff --git a/templates/android/gradle/wrapper/gradle-wrapper.properties b/templates/android/gradle/wrapper/gradle-wrapper.properties index 85e684fc97..c87e29eb68 100644 --- a/templates/android/gradle/wrapper/gradle-wrapper.properties +++ b/templates/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Jun 01 15:55:54 IST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/templates/android/library/build.gradle.twig b/templates/android/library/build.gradle.twig index 9d809012f3..4a1922f0bf 100644 --- a/templates/android/library/build.gradle.twig +++ b/templates/android/library/build.gradle.twig @@ -34,6 +34,10 @@ android { consumerProguardFiles("consumer-rules.pro") } + testOptions { + unitTests.includeAndroidResources = true + } + buildTypes { release { minifyEnabled false @@ -70,11 +74,18 @@ dependencies { implementation("androidx.activity:activity-ktx:1.6.1") implementation("androidx.browser:browser:1.4.0") - testImplementation 'junit:junit:4.13.2' + testImplementation "org.jetbrains.kotlin:kotlin-test" + testImplementation "io.mockk:mockk:1.13.7" testImplementation "androidx.test.ext:junit-ktx:1.1.5" testImplementation "androidx.test:core-ktx:1.5.0" - testImplementation "org.robolectric:robolectric:4.5.1" - testApi("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1") + testImplementation "org.robolectric:robolectric:4.10.3" + testApi("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") + + androidTestImplementation 'androidx.test:runner:1.5.2' +} + +tasks.withType(Test).configureEach { + useJUnit() } apply from: "${rootProject.projectDir}/scripts/publish-module.gradle" \ No newline at end of file diff --git a/templates/android/library/src/main/java/io/appwrite/Client.kt.twig b/templates/android/library/src/main/java/io/appwrite/Client.kt.twig index 210ef4fbe3..400fe8719b 100644 --- a/templates/android/library/src/main/java/io/appwrite/Client.kt.twig +++ b/templates/android/library/src/main/java/io/appwrite/Client.kt.twig @@ -37,6 +37,7 @@ import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager import kotlin.coroutines.CoroutineContext import kotlin.coroutines.resume +import okhttp3.Response as Okhttp3Response class Client @JvmOverloads constructor( context: Context, @@ -266,7 +267,10 @@ class Client @JvmOverloads constructor( .get() .build() - return awaitResponse(request, responseType, converter) + val response = awaitResponse(request, responseType) + + @Suppress("UNCHECKED_CAST") + return converter?.invoke(response.data) ?: response.data as T } val body = if (MultipartBody.FORM.toString() == headers["content-type"]) { @@ -303,7 +307,10 @@ class Client @JvmOverloads constructor( .method(method, body) .build() - return awaitResponse(request, responseType, converter) + val response = awaitResponse(request, responseType) + + @Suppress("UNCHECKED_CAST") + return converter?.invoke(response.data) ?: response.data as T } /** @@ -441,11 +448,10 @@ class Client @JvmOverloads constructor( * @return [T] */ @Throws({{ spec.title | caseUcfirst }}Exception::class) - private suspend fun awaitResponse( + internal suspend fun awaitResponse( request: Request, responseType: Class, - converter: ((Any) -> T)? = null - ) = suspendCancellableCoroutine { + ) = suspendCancellableCoroutine> { http.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { if (it.isCancelled) { @@ -454,8 +460,7 @@ class Client @JvmOverloads constructor( it.cancel(e) } - @Suppress("UNCHECKED_CAST") - override fun onResponse(call: Call, response: Response) { + override fun onResponse(call: Call, response: Okhttp3Response) { if (!response.isSuccessful) { val body = response.body!! .charStream() @@ -481,19 +486,19 @@ class Client @JvmOverloads constructor( } when { responseType == Boolean::class.java -> { - it.resume(true as T) + it.resume(Response(true)) return } responseType == ByteArray::class.java -> { - it.resume(response.body!! + val data = response.body!! .byteStream() .buffered() - .use(BufferedInputStream::readBytes) as T - ) + .use(BufferedInputStream::readBytes) + it.resume(Response(data)) return } response.body == null -> { - it.resume(true as T) + it.resume(Response(true)) return } } @@ -502,7 +507,7 @@ class Client @JvmOverloads constructor( .buffered() .use(BufferedReader::readText) if (body.isEmpty()) { - it.resume(true as T) + it.resume(Response(true)) return } val map = gson.fromJson( @@ -510,7 +515,7 @@ class Client @JvmOverloads constructor( object : TypeToken(){}.type ) it.resume( - converter?.invoke(map) ?: map as T + Response(map) ) } }) diff --git a/templates/android/library/src/main/java/io/appwrite/Response.kt.twig b/templates/android/library/src/main/java/io/appwrite/Response.kt.twig new file mode 100644 index 0000000000..b261a95205 --- /dev/null +++ b/templates/android/library/src/main/java/io/appwrite/Response.kt.twig @@ -0,0 +1,7 @@ +package {{ sdk.namespace | caseDot }} + +import {{ sdk.namespace | caseDot }}.extensions.toJson + +class Response(val data: T) { + override fun toString(): String = if (data is Map<*, *>) toJson() else data.toString() +} diff --git a/templates/android/library/src/main/java/io/appwrite/models/Model.kt.twig b/templates/android/library/src/main/java/io/appwrite/models/Model.kt.twig index 6b049555cb..9ea5280b69 100644 --- a/templates/android/library/src/main/java/io/appwrite/models/Model.kt.twig +++ b/templates/android/library/src/main/java/io/appwrite/models/Model.kt.twig @@ -1,7 +1,7 @@ package {{ sdk.namespace | caseDot }}.models import com.google.gson.annotations.SerializedName -import io.appwrite.extensions.jsonCast +import {{ sdk.namespace | caseDot }}.extensions.jsonCast /** * {{ definition.description }} diff --git a/templates/android/library/src/test/java/io/appwrite/IDTest.kt.twig b/templates/android/library/src/test/java/io/appwrite/IDTest.kt.twig new file mode 100644 index 0000000000..82fa5c8c3c --- /dev/null +++ b/templates/android/library/src/test/java/io/appwrite/IDTest.kt.twig @@ -0,0 +1,12 @@ +package {{ sdk.namespace | caseDot }} + +import org.junit.Test +import kotlin.test.assertEquals + +class IDTest { + @Test + fun testUnique() = assertEquals("unique()", ID.unique()) + + @Test + fun testCustom() = assertEquals("custom", ID.custom("custom")) +} diff --git a/templates/android/library/src/test/java/io/appwrite/PermissionTest.kt.twig b/templates/android/library/src/test/java/io/appwrite/PermissionTest.kt.twig new file mode 100644 index 0000000000..b4a5417c20 --- /dev/null +++ b/templates/android/library/src/test/java/io/appwrite/PermissionTest.kt.twig @@ -0,0 +1,21 @@ +package {{ sdk.namespace | caseDot }} + +import org.junit.Test +import kotlin.test.assertEquals + +class PermissionTest { + @Test + fun readReturnsRead() = assertEquals("read(\"any\")", Permission.read(Role.any())) + + @Test + fun writeReturnsWrite() = assertEquals("write(\"any\")", Permission.write(Role.any())) + + @Test + fun createReturnsCreate() = assertEquals("create(\"any\")", Permission.create(Role.any())) + + @Test + fun updateReturnsUpdate() = assertEquals("update(\"any\")", Permission.update(Role.any())) + + @Test + fun deleteReturnsDelete() = assertEquals("delete(\"any\")", Permission.delete(Role.any())) +} diff --git a/templates/android/library/src/test/java/io/appwrite/QueryTest.kt.twig b/templates/android/library/src/test/java/io/appwrite/QueryTest.kt.twig new file mode 100644 index 0000000000..c9043dfd19 --- /dev/null +++ b/templates/android/library/src/test/java/io/appwrite/QueryTest.kt.twig @@ -0,0 +1,189 @@ +package {{ sdk.namespace | caseDot }} + +import org.junit.Test +import kotlin.test.assertEquals + +internal class BasicFilterQueryTest( + val description: String, + val value: Any, + val expectedValues: String +) + +class QueryTest { + private val tests = listOf( + BasicFilterQueryTest( + "with a string", + "s", + "[\"s\"]" + ), + BasicFilterQueryTest( + "with an integer", + 1, + "[1]" + ), + BasicFilterQueryTest( + "with a double", + 1.2, + "[1.2]" + ), + BasicFilterQueryTest( + "with a whole number double", + 1.0, + "[1.0]" + ), + BasicFilterQueryTest( + "with a bool", + false, + "[false]" + ), + BasicFilterQueryTest( + "with a list", + listOf("a", "b", "c"), + "[\"a\",\"b\",\"c\"]" + ), + ) + + @Test + fun basicFilterEqual() { + for (t in tests) { + assertEquals( + "equal(\"attr\", ${t.expectedValues})", + Query.equal("attr", t.value), + t.description + ) + } + } + + @Test + fun basicFilterNotEqual() { + for (t in tests) { + assertEquals( + "notEqual(\"attr\", ${t.expectedValues})", + Query.notEqual("attr", t.value), + t.description + ) + } + } + + @Test + fun basicFilterLessThan() { + for (t in tests) { + assertEquals( + "lessThan(\"attr\", ${t.expectedValues})", + Query.lessThan("attr", t.value), + t.description + ) + } + } + + @Test + fun basicFilterLessThanEqual() { + for (t in tests) { + assertEquals( + "lessThanEqual(\"attr\", ${t.expectedValues})", + Query.lessThanEqual("attr", t.value), + t.description + ) + } + } + + @Test + fun basicFilterGreaterThan() { + for (t in tests) { + assertEquals( + "greaterThan(\"attr\", ${t.expectedValues})", + Query.greaterThan("attr", t.value), + t.description + ) + } + } + + @Test + fun basicFilterGreaterThanEqual() { + for (t in tests) { + assertEquals( + "greaterThanEqual(\"attr\", ${t.expectedValues})", + Query.greaterThanEqual("attr", t.value), + t.description + ) + } + } + + @Test + fun searchReturnsSearch() = assertEquals( + "search(\"attr\", [\"keyword1 keyword2\"])", + Query.search("attr", "keyword1 keyword2") + ) + + @Test + fun isNullReturnsIsNull() = assertEquals( + "isNull(\"attr\")", + Query.isNull("attr") + ) + + @Test + fun isNotNullReturnsIsNotNull() = assertEquals( + "isNotNull(\"attr\")", + Query.isNotNull("attr") + ) + + @Test + fun betweenWithIntegers() = assertEquals( + "between(\"attr\", [1,2])", + Query.between("attr", 1, 2) + ) + + @Test + fun betweenWithDoubles() = assertEquals( + "between(\"attr\", [1.0,2.0])", + Query.between("attr", 1.0, 2.0) + ) + + @Test + fun betweenWithStrings() = assertEquals( + "between(\"attr\", [\"a\",\"z\"])", + Query.between("attr", "a", "z") + ) + + @Test + fun selectReturnsSelect() = assertEquals( + "select([\"attr1\",\"attr2\"])", + Query.select(listOf("attr1", "attr2")) + ) + + @Test + fun orderAscReturnsOrderAsc() = assertEquals( + "orderAsc(\"attr\")", + Query.orderAsc("attr") + ) + + @Test + fun orderDescReturnsOrderDesc() = assertEquals( + "orderDesc(\"attr\")", + Query.orderDesc("attr") + ) + + @Test + fun cursorBeforeReturnsCursorBefore() = assertEquals( + "cursorBefore(\"attr\")", + Query.cursorBefore("attr") + ) + + @Test + fun cursorAfterReturnsCursorAfter() = assertEquals( + "cursorAfter(\"attr\")", + Query.cursorAfter("attr") + ) + + @Test + fun limitReturnsLimit() = assertEquals( + "limit(1)", + Query.limit(1) + ) + + @Test + fun offsetReturnsOffset() = assertEquals( + "offset(1)", + Query.offset(1) + ) +} diff --git a/templates/android/library/src/test/java/io/appwrite/RoleTest.kt.twig b/templates/android/library/src/test/java/io/appwrite/RoleTest.kt.twig new file mode 100644 index 0000000000..1ab1125b2c --- /dev/null +++ b/templates/android/library/src/test/java/io/appwrite/RoleTest.kt.twig @@ -0,0 +1,66 @@ +package {{ sdk.namespace | caseDot }} + +import org.junit.Test +import kotlin.test.assertEquals + +class RoleTest { + @Test + fun anyReturnsAny() = assertEquals( + "any", + Role.any() + ) + + @Test + fun userWithoutStatus() = assertEquals( + "user:custom", + Role.user("custom") + ) + + @Test + fun userWithStatus() = assertEquals( + "user:custom/verified", + Role.user("custom", "verified") + ) + + @Test + fun usersWithoutStatus() = assertEquals( + "users", + Role.users() + ) + + @Test + fun usersWithStatus() = assertEquals( + "users/verified", + Role.users("verified") + ) + + @Test + fun guestsReturnsGuests() = assertEquals( + "guests", + Role.guests() + ) + + @Test + fun teamWithoutRole() = assertEquals( + "team:custom", + Role.team("custom") + ) + + @Test + fun teamWithRole() = assertEquals( + "team:custom/owner", + Role.team("custom", "owner") + ) + + @Test + fun memberReturnsMember() = assertEquals( + "member:custom", + Role.member("custom") + ) + + @Test + fun labelReturnsLabel() = assertEquals( + "label:admin", + Role.label("admin") + ) +} diff --git a/templates/android/library/src/test/java/io/appwrite/cookies/CookiesTest.kt.twig b/templates/android/library/src/test/java/io/appwrite/cookies/CookiesTest.kt.twig new file mode 100644 index 0000000000..10ab2422aa --- /dev/null +++ b/templates/android/library/src/test/java/io/appwrite/cookies/CookiesTest.kt.twig @@ -0,0 +1,17 @@ +package {{ sdk.namespace | caseDot }}.cookies + +import java.net.HttpCookie +import kotlin.test.assertEquals +import org.junit.Test + +class CookiesTest { + @Test + fun cookiesToString() { + val httpCookie = HttpCookie("name", "value").apply { + domain = "localhost" + } + val internalCookie = InternalCookie(httpCookie) + + assertEquals("name=value; domain=localhost", internalCookie.toHttpCookie().toSetCookieString()) + } +} diff --git a/templates/android/library/src/test/java/io/appwrite/models/ModelTest.kt.twig b/templates/android/library/src/test/java/io/appwrite/models/ModelTest.kt.twig new file mode 100644 index 0000000000..ba91778d3b --- /dev/null +++ b/templates/android/library/src/test/java/io/appwrite/models/ModelTest.kt.twig @@ -0,0 +1,28 @@ +package {{ sdk.namespace | caseDot }}.models + +import org.junit.Test +import kotlin.test.assertEquals + +class {{definition.name | caseUcfirst}}Test { + + @Test + fun method() { + val model = {{ definition.name | caseUcfirst | overrideIdentifier }}{% if definition.name | hasGenericType(spec) %}{% endif %}( +{% for property in definition.properties %} + {{ property.name | escapeKeyword | removeDollarSign }} = {% if property.type == 'array' %}listOf(){% elseif property.type == 'object' and (property.sub_schema == 'prefs' or property.sub_schema == 'preferences') %}Preferences(data = mapOf()){% elseif property.type == 'object' %}mapOf(){% elseif property.type == 'string' %}"{{property['x-example'] | escapeDollarSign}}"{% elseif property.type == 'boolean' %}true{% elseif property.type == 'number' %}{{property['x-example'] | number_format(1)}}{% else %}{{property['x-example']}}{% endif %}, +{% endfor %} +{% if definition.additionalProperties %} + data = mapOf(), +{% endif %} + ) + val map = model.toMap() + val result = {{ definition.name | caseUcfirst | overrideIdentifier }}.from(map{% if definition.name | hasGenericType(spec) %}, Any::class.java{% endif %}) + +{% for property in definition.properties %} + assertEquals( + {% if property.type == 'array' %}listOf(){% elseif property.type == 'object' and (property.sub_schema == 'prefs' or property.sub_schema == 'preferences') %}mapOf("data" to mapOf()){% elseif property.type == 'object' %}mapOf(){% elseif property.type == 'string' %}"{{property['x-example'] | escapeDollarSign}}"{% elseif property.type == 'boolean' %}true{% elseif property.type == 'number' %}{{property['x-example'] | number_format(1)}}{% else %}{{property['x-example']}}{% endif %}, + result.{{ property.name | escapeKeyword | removeDollarSign }}{% if property.type == 'object' and (property.sub_schema == 'prefs' or property.sub_schema == 'preferences') %}.data{% endif %} + ) +{% endfor %} + } +} diff --git a/templates/android/library/src/test/java/io/appwrite/services/ServiceTest.kt.twig b/templates/android/library/src/test/java/io/appwrite/services/ServiceTest.kt.twig new file mode 100644 index 0000000000..191f797c93 --- /dev/null +++ b/templates/android/library/src/test/java/io/appwrite/services/ServiceTest.kt.twig @@ -0,0 +1,77 @@ +package {{ sdk.namespace | caseDot }}.services + +import androidx.activity.ComponentActivity +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import {{ sdk.namespace | caseDot }}.Client +import {{ sdk.namespace | caseDot }}.Response +import {{ sdk.namespace | caseDot }}.WebAuthComponent +import {{ sdk.namespace | caseDot }}.models.InputFile +import io.mockk.coEvery +import io.mockk.mockkObject +import io.mockk.spyk +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertIs + +{%~ if method.type == 'webAuth' ~%} +@RunWith(AndroidJUnit4::class) +{%~ endif ~%} +class {{service.name | caseUcfirst}}ServiceTest { + private lateinit var client: Client + private lateinit var {{service.name | caseCamel}}: {{service.name | caseUcfirst}} + + @Before + fun setup() { + client = spyk(Client(ApplicationProvider.getApplicationContext())) + {{service.name | caseCamel}} = {{service.name | caseUcfirst}}(client) + } + +{% for method in service.methods %} + @Test + fun method{{method.name | caseUcfirst}}() { + {%- if method.type == 'webAuth' -%} + {%- elseif method.type == 'location' -%} + val data = byteArrayOf() + {%- else -%} + {%~ if method.responseModel and method.responseModel != 'any' ~%} + val data = mapOf( + {%- for definition in spec.definitions ~%}{%~ if definition.name == method.responseModel -%}{%~ for property in definition.properties | filter((param) => param.required) ~%} + "{{property.name | escapeDollarSign}}" to {% if property.type == 'object' %}mapOf(){% elseif property.type == 'array' %}listOf(){% elseif property.type == 'string' %}"{{property.example | escapeDollarSign}}"{% elseif property.type == 'boolean' %}true{% else %}{{property.example}}{% endif %},{%~ endfor ~%}{% set break = true %}{%- else -%}{% set continue = true %}{%- endif -%}{%~ endfor -%} + + ) + {%~ else ~%} + val data = ""; + {%- endif -%} + {% endif %} + + {%~ if method.type == 'webAuth' ~%} + mockkObject(WebAuthComponent.Companion) + coEvery { WebAuthComponent.Companion.authenticate(any(), any(), any(), any()) } answers { + arg<((Result) -> Unit)?>(3)?.invoke(Result.success("http://localhost/oauth?key=example&secret=example")) + } + {%~ else ~%} + coEvery { + client.awaitResponse(any(), any()) + } returns Response(data) + {%~ endif ~%} + + runTest { + val response = {{service.name | caseCamel}}.{{method.name | caseCamel}}({%~ for parameter in method.parameters.all | filter((param) => param.required) ~%} + {{parameter.name | escapeKeyword | caseCamel}} = {% if parameter.type == 'object' %}mapOf(){% elseif parameter.type == 'array' %}listOf(){% elseif parameter.type == 'file' %}InputFile.fromBytes(byteArrayOf(), mimeType = "image/png"){% elseif parameter.type == 'boolean' %}true{% elseif parameter.type == 'string' %}"{% if parameter.example is not empty %}{{parameter.example | escapeDollarSign}}{% endif %}"{% elseif parameter.type == 'integer' and parameter['x-example'] is empty %}1{% elseif parameter.type == 'number' and parameter['x-example'] is empty %}1.0{% else %}{{parameter.example}}{%~ endif ~%},{%~ endfor ~%} + {%~ if method.type == "webAuth" %} + activity = ComponentActivity(), + {%~ endif %} + ) + {%- if method.type == 'location' ~%} + assertIs(response) + {%~ endif ~%} + {%~ if method.responseModel and method.responseModel != 'any' ~%} + assertIs<{{ sdk.namespace | caseDot }}.models.{{method.responseModel | caseUcfirst | overrideIdentifier}}{% if method.responseModel | hasGenericType(spec) %}<*>{% endif %}>(response) + {%~ endif ~%} + } + } +{% endfor %} +} diff --git a/templates/dart/test/services/service_test.dart.twig b/templates/dart/test/services/service_test.dart.twig index b5997121a5..a311142583 100644 --- a/templates/dart/test/services/service_test.dart.twig +++ b/templates/dart/test/services/service_test.dart.twig @@ -101,7 +101,8 @@ void main() { {%- if method.type == 'location' ~%} expect(response, isA()); - {%~ endif ~%}{%~ if method.responseModel and method.responseModel != 'any' ~%} + {%~ endif ~%} + {%~ if method.responseModel and method.responseModel != 'any' ~%} expect(response, isA()); {%~ endif ~%} });