commit
3562a8c900
45 changed files with 803 additions and 469 deletions
10
.github/dependabot.yml
vendored
Normal file
10
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gradle
|
||||
directory: "/"
|
||||
target-branch: "develop"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
time: "12:00"
|
||||
open-pull-requests-limit: 15
|
||||
75
.github/workflows/pull-request-jobs.yml
vendored
Normal file
75
.github/workflows/pull-request-jobs.yml
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
name: Verify Pull request is publishable
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
|
||||
env:
|
||||
GITHUB_USERNAME: "fknives"
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
jobs:
|
||||
run-code-analysis:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '11'
|
||||
- name: Run clean
|
||||
run: ./gradlew clean
|
||||
- name: Run detekt
|
||||
run: ./gradlew detekt
|
||||
- name: Upload Detekt Results
|
||||
uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: Detekt Results
|
||||
path: build/reports/detekt.html
|
||||
retention-days: 1
|
||||
- name: Run ktlint
|
||||
run: ./gradlew ktlintCheck
|
||||
- name: Upload ktLint Results
|
||||
uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: ktLint Results
|
||||
path: ./**/build/reports/ktlint/**/*ktlint*Check.txt
|
||||
retention-days: 1
|
||||
- name: Run Lint
|
||||
run: ./gradlew lint
|
||||
- name: Upload Lint Results
|
||||
uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: Lint Results
|
||||
path: ./**/build/reports/*lint-results*.html
|
||||
retention-days: 1
|
||||
|
||||
run-tests:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '11'
|
||||
- name: Run Unit Tests
|
||||
run: ./gradlew unitTests
|
||||
- name: Upload Test Results
|
||||
uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: Unit Test Results
|
||||
path: ./**/build/reports/tests/**/index.html
|
||||
retention-days: 1
|
||||
|
|
@ -6,11 +6,11 @@ plugins {
|
|||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "org.fnives.test.showcase"
|
||||
minSdkVersion 21
|
||||
//noinspection OldTargetApi // todo
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
|
@ -25,16 +25,10 @@ android {
|
|||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
androidTest {
|
||||
|
|
@ -46,19 +40,6 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
testOptions.unitTests.all {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events 'started', 'passed', 'skipped', 'failed'
|
||||
exceptionFormat "full"
|
||||
showStandardStreams true
|
||||
}
|
||||
}
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
// needed for androidTest
|
||||
packagingOptions {
|
||||
exclude 'META-INF/LGPL2.1'
|
||||
|
|
@ -66,14 +47,6 @@ android {
|
|||
exclude 'META-INF/LICENSE.md'
|
||||
exclude 'META-INF/LICENSE-notice.md'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
warningsAsErrors true
|
||||
abortOnError true
|
||||
textReport true
|
||||
ignore 'Overdraw'
|
||||
textOutput "stdout"
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
|
|
@ -83,20 +56,17 @@ afterEvaluate {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation "androidx.core:core-ktx:$androidx_core_version"
|
||||
implementation "androidx.appcompat:appcompat:$androidx_appcompat_version"
|
||||
implementation "com.google.android.material:material:$androidx_material_version"
|
||||
implementation "androidx.constraintlayout:constraintlayout:$androidx_constraintlayout_version"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:$androidx_livedata_version"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$androidx_livedata_version"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$androidx_livedata_version"
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:$androidx_swiperefreshlayout_version"
|
||||
|
||||
// Koin
|
||||
implementation "org.koin:koin-androidx-scope:$koin_version"
|
||||
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
|
||||
implementation "org.koin:koin-androidx-fragment:$koin_version"
|
||||
implementation "io.insert-koin:koin-android:$koin_version"
|
||||
|
||||
implementation "androidx.room:room-runtime:$androidx_room_version"
|
||||
kapt "androidx.room:room-compiler:$androidx_room_version"
|
||||
|
|
@ -113,7 +83,7 @@ dependencies {
|
|||
testImplementation "org.mockito.kotlin:mockito-kotlin:$testing_kotlin_mockito_version"
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
|
||||
testImplementation "com.jraska.livedata:testing-ktx:$testing_livedata_version"
|
||||
testImplementation "org.koin:koin-test:$koin_version"
|
||||
testImplementation "io.insert-koin:koin-test-junit5:$koin_version"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version"
|
||||
|
||||
// robolectric specific
|
||||
|
|
@ -130,7 +100,7 @@ dependencies {
|
|||
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:$testing_junit5_version"
|
||||
|
||||
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
|
||||
androidTestImplementation "org.koin:koin-test:$koin_version"
|
||||
androidTestImplementation "io.insert-koin:koin-test-junit4:$koin_version"
|
||||
androidTestImplementation "junit:junit:$testing_junit4_version"
|
||||
androidTestImplementation "androidx.test:core:$testing_androidx_code_version"
|
||||
androidTestImplementation "androidx.test:runner:$testing_androidx_code_version"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ object AndroidTestServerTypeConfiguration : ServerTypeConfiguration, KoinTest {
|
|||
.build()
|
||||
loadKoinModules(
|
||||
module {
|
||||
single(qualifier = sessionless, override = true) { okHttpClientWithCertificate }
|
||||
single(qualifier = sessionless) { okHttpClientWithCertificate }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
android:name=".TestShowcaseApplication"
|
||||
android:theme="@style/Theme.TestShowCase"
|
||||
tools:ignore="AllowBackup">
|
||||
<activity android:name=".ui.splash.SplashActivity">
|
||||
<activity android:name=".ui.splash.SplashActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
val adapter = FavouriteContentAdapter(viewModel.mapToAdapterListener())
|
||||
binding.recycler.layoutManager = LinearLayoutManager(this)
|
||||
binding.recycler.addItemDecoration(VerticalSpaceItemDecoration(resources.getDimensionPixelOffset(R.dimen.padding)))
|
||||
binding.recycler.addItemDecoration(
|
||||
VerticalSpaceItemDecoration(resources.getDimensionPixelOffset(R.dimen.padding))
|
||||
)
|
||||
binding.recycler.adapter = adapter
|
||||
|
||||
viewModel.content.observe(this) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package org.fnives.test.showcase.favourite
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.take
|
||||
|
|
@ -20,6 +21,7 @@ import org.koin.test.KoinTest
|
|||
import org.koin.test.inject
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
internal class FavouriteContentLocalStorageImplTest : KoinTest {
|
||||
|
||||
|
|
@ -48,21 +50,23 @@ internal class FavouriteContentLocalStorageImplTest : KoinTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_content_id_added_WHEN_removed_to_Favourite_THEN_it_no_longer_can_be_read_out() = runBlocking {
|
||||
val expected = listOf<ContentId>()
|
||||
sut.markAsFavourite(ContentId("b"))
|
||||
fun GIVEN_content_id_added_WHEN_removed_to_Favourite_THEN_it_no_longer_can_be_read_out() =
|
||||
runBlocking {
|
||||
val expected = listOf<ContentId>()
|
||||
sut.markAsFavourite(ContentId("b"))
|
||||
|
||||
sut.deleteAsFavourite(ContentId("b"))
|
||||
val actual = sut.observeFavourites().first()
|
||||
sut.deleteAsFavourite(ContentId("b"))
|
||||
val actual = sut.observeFavourites().first()
|
||||
|
||||
Assert.assertEquals(expected, actual)
|
||||
}
|
||||
Assert.assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_empty_database_WHILE_observing_content_WHEN_favourite_added_THEN_change_is_emitted() =
|
||||
runBlocking<Unit> {
|
||||
val expected = listOf(listOf(), listOf(ContentId("a")))
|
||||
|
||||
val testDispatcher = TestCoroutineDispatcher()
|
||||
val actual = async(testDispatcher) {
|
||||
sut.observeFavourites().take(2).toList()
|
||||
}
|
||||
|
|
@ -79,6 +83,7 @@ internal class FavouriteContentLocalStorageImplTest : KoinTest {
|
|||
val expected = listOf(listOf(ContentId("a")), listOf())
|
||||
sut.markAsFavourite(ContentId("a"))
|
||||
|
||||
val testDispatcher = TestCoroutineDispatcher()
|
||||
val actual = async(testDispatcher) {
|
||||
sut.observeFavourites().take(2).toList()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import org.fnives.test.showcase.core.login.LoginUseCase
|
|||
import org.fnives.test.showcase.testutils.InstantExecutorExtension
|
||||
import org.fnives.test.showcase.testutils.TestMainDispatcher
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.kotlin.mock
|
||||
|
||||
@Disabled("CodeKata")
|
||||
@ExtendWith(InstantExecutorExtension::class, TestMainDispatcher::class)
|
||||
class CodeKataAuthViewModel {
|
||||
|
||||
|
|
@ -25,24 +27,20 @@ class CodeKataAuthViewModel {
|
|||
@DisplayName("GIVEN_initialized_viewModel_WHEN_observed_THEN_loading_false_other_fields_are_empty")
|
||||
@Test
|
||||
fun initialSetup() {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN_password_text_WHEN_onPasswordChanged_is_called_THEN_password_livedata_is_updated")
|
||||
@Test
|
||||
fun whenPasswordChangedLiveDataIsUpdated() {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN_username_text_WHEN_onUsernameChanged_is_called_THEN_username_livedata_is_updated")
|
||||
@Test
|
||||
fun whenUsernameChangedLiveDataIsUpdated() {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN_no_password_or_username_WHEN_login_is_Called_THEN_empty_credentials_are_used_in_usecase")
|
||||
@Test
|
||||
fun noPasswordUsesEmptyStringInLoginUseCase() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,29 @@
|
|||
package org.fnives.test.showcase.ui.splash
|
||||
|
||||
import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase
|
||||
import org.fnives.test.showcase.testutils.InstantExecutorExtension
|
||||
import org.fnives.test.showcase.testutils.TestMainDispatcher
|
||||
import org.fnives.test.showcase.ui.shared.Event
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@Disabled("CodeKata")
|
||||
internal class CodeKataSplashViewModelTest {
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN not logged in user WHEN splash started THEN after half a second navigated to authentication")
|
||||
@Test
|
||||
fun loggedOutUserGoesToAuthentication() {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN logged in user WHEN splash started THEN after half a second navigated to home")
|
||||
@Test
|
||||
fun loggedInUserGoestoHome() {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN not logged in user WHEN splash started THEN before half a second no event is sent")
|
||||
@Test
|
||||
fun withoutEnoughTimeNoNavigationHappens() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
sdk=28
|
||||
shadows=org.fnives.test.showcase.testutils.shadow.ShadowSnackbar
|
||||
instrumentedPackages=androidx.loader.content
|
||||
|
|
|
|||
49
build.gradle
49
build.gradle
|
|
@ -1,61 +1,30 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
ext.kotlin_version = "1.4.32"
|
||||
ext.detekt_version = "1.16.0"
|
||||
ext.kotlin_version = "1.5.30"
|
||||
ext.detekt_version = "1.18.1"
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:4.1.3"
|
||||
classpath 'com.android.tools.build:gradle:7.0.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "org.jlleitschuh.gradle:ktlint-gradle:10.0.0"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
classpath "org.jlleitschuh.gradle:ktlint-gradle:10.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
//apply plugin: "io.gitlab.arturbosch.detekt" version "$detekt_version"
|
||||
plugins {
|
||||
id "io.gitlab.arturbosch.detekt" version "$detekt_version"
|
||||
}
|
||||
detekt {
|
||||
toolVersion = "$detekt_version"
|
||||
|
||||
input = files(
|
||||
"$projectDir/app/src/main/java",
|
||||
"$projectDir/core/src/main/java",
|
||||
"$projectDir/mockserver/src/main/java",
|
||||
"$projectDir/model/src/main/java",
|
||||
"$projectDir/network/src/main/java"
|
||||
)
|
||||
config = files("$projectDir/detekt/detekt.yml")
|
||||
baseline = file("$projectDir/detekt/baseline.xml")
|
||||
reports {
|
||||
txt {
|
||||
enabled = true
|
||||
destination = file("build/reports/detekt.txt")
|
||||
}
|
||||
html {
|
||||
enabled = true
|
||||
destination = file("build/reports/detekt.html")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
|
@ -70,4 +39,8 @@ task androidTests(dependsOn: "app:connectedAndroidTest"){
|
|||
description = 'Run all Android tests'
|
||||
}
|
||||
|
||||
apply from: 'gradlescripts/versions.gradle'
|
||||
apply from: 'gradlescripts/versions.gradle'
|
||||
apply from: 'gradlescripts/detekt.config.gradle'
|
||||
apply from: 'gradlescripts/ktlint.gradle'
|
||||
apply from: 'gradlescripts/lint.gradle'
|
||||
apply from: 'gradlescripts/testoptions.gradle'
|
||||
|
|
@ -8,23 +8,19 @@ java {
|
|||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events 'started', 'passed', 'skipped', 'failed'
|
||||
exceptionFormat "full"
|
||||
showStandardStreams true
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
||||
|
||||
api project(":model")
|
||||
implementation project(":network")
|
||||
|
||||
testImplementation "org.koin:koin-test:$koin_version"
|
||||
testImplementation "io.insert-koin:koin-test-junit5:$koin_version"
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
|
||||
testImplementation "org.mockito.kotlin:mockito-kotlin:$testing_kotlin_mockito_version"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package org.fnives.test.showcase.core.content
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
|
@ -24,6 +25,8 @@ internal class ContentRepository(private val contentRemoteSource: ContentRemoteS
|
|||
}
|
||||
emit(response)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val contents: Flow<Resource<List<Content>>> = mutableContentFlow.flatMapLatest {
|
||||
if (it.item != null) flowOf(Resource.Success(it.item)) else requestFlow
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ class GetAllContentUseCase internal constructor(
|
|||
) {
|
||||
|
||||
fun get(): Flow<Resource<List<FavouriteContent>>> =
|
||||
contentRepository.contents.combine(favouriteContentLocalStorage.observeFavourites(), ::combineContentWithFavourites)
|
||||
contentRepository.contents.combine(
|
||||
favouriteContentLocalStorage.observeFavourites(),
|
||||
::combineContentWithFavourites
|
||||
)
|
||||
|
||||
companion object {
|
||||
private fun combineContentWithFavourites(
|
||||
|
|
@ -24,10 +27,18 @@ class GetAllContentUseCase internal constructor(
|
|||
when (contentResource) {
|
||||
is Resource.Error -> Resource.Error(contentResource.error)
|
||||
is Resource.Loading -> Resource.Loading()
|
||||
is Resource.Success -> Resource.Success(combineContentWithFavourites(contentResource.data, favouriteContents))
|
||||
is Resource.Success ->
|
||||
Resource.Success(
|
||||
combineContentWithFavourites(contentResource.data, favouriteContents)
|
||||
)
|
||||
}
|
||||
|
||||
private fun combineContentWithFavourites(content: List<Content>, favourite: List<ContentId>): List<FavouriteContent> =
|
||||
content.map { FavouriteContent(content = it, isFavourite = favourite.contains(it.id)) }
|
||||
private fun combineContentWithFavourites(
|
||||
content: List<Content>,
|
||||
favourite: List<ContentId>
|
||||
): List<FavouriteContent> =
|
||||
content.map {
|
||||
FavouriteContent(content = it, isFavourite = favourite.contains(it.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ fun createCoreModule(
|
|||
.plus(repositoryModule())
|
||||
|
||||
fun repositoryModule() = module {
|
||||
single(override = true) { ContentRepository(get()) }
|
||||
single { ContentRepository(get()) }
|
||||
}
|
||||
|
||||
fun useCaseModule() = module {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import org.fnives.test.showcase.model.shared.Resource
|
|||
import org.fnives.test.showcase.network.shared.exceptions.NetworkException
|
||||
import org.fnives.test.showcase.network.shared.exceptions.ParsingException
|
||||
|
||||
@Suppress("RethrowCaughtException")
|
||||
internal suspend fun <T> wrapIntoAnswer(callback: suspend () -> T): Answer<T> =
|
||||
try {
|
||||
Answer.Success(callback())
|
||||
|
|
|
|||
|
|
@ -1,77 +1,50 @@
|
|||
package org.fnives.test.showcase.core.content
|
||||
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.fnives.test.showcase.core.shared.UnexpectedException
|
||||
import org.fnives.test.showcase.model.content.Content
|
||||
import org.fnives.test.showcase.model.content.ContentId
|
||||
import org.fnives.test.showcase.model.content.ImageUrl
|
||||
import org.fnives.test.showcase.model.shared.Resource
|
||||
import org.fnives.test.showcase.network.content.ContentRemoteSource
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.doAnswer
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.doSuspendableAnswer
|
||||
import org.mockito.kotlin.doThrow
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@Disabled("CodeKata")
|
||||
class CodeKataContentRepositoryTest {
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN no interaction THEN remote source is not called")
|
||||
@Test
|
||||
fun fetchingIsLazy() {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN content response WHEN content observed THEN loading AND data is returned")
|
||||
@Test
|
||||
fun happyFlow() = runBlockingTest {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN content error WHEN content observed THEN loading AND data is returned")
|
||||
@Test
|
||||
fun errorFlow() = runBlockingTest {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN saved cache WHEN collected THEN cache is returned")
|
||||
@Test
|
||||
fun verifyCaching() = runBlockingTest {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN no response from remote source WHEN content observed THEN loading is returned")
|
||||
@Test
|
||||
fun loadingIsShownBeforeTheRequestIsReturned() = runBlockingTest {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN content response THEN error WHEN fetched THEN returned states are loading data loading error")
|
||||
@Test
|
||||
fun whenFetchingRequestIsCalledAgain() = runBlockingTest {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN content response THEN error WHEN fetched THEN only 4 items are emitted")
|
||||
@Test
|
||||
fun noAdditionalItemsEmitted() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,15 @@ import org.junit.jupiter.api.Assertions
|
|||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.*
|
||||
import org.mockito.kotlin.doAnswer
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.doSuspendableAnswer
|
||||
import org.mockito.kotlin.doThrow
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
internal class ContentRepositoryTest {
|
||||
|
|
@ -43,10 +51,19 @@ internal class ContentRepositoryTest {
|
|||
@Test
|
||||
fun happyFlow() = runBlockingTest {
|
||||
val expected = listOf(
|
||||
Resource.Loading(),
|
||||
Resource.Success(listOf(Content(ContentId("a"), "", "", ImageUrl(""))))
|
||||
Resource.Loading(),
|
||||
Resource.Success(listOf(Content(ContentId("a"), "", "", ImageUrl(""))))
|
||||
)
|
||||
whenever(mockContentRemoteSource.get()).doReturn(
|
||||
listOf(
|
||||
Content(
|
||||
ContentId("a"),
|
||||
"",
|
||||
"",
|
||||
ImageUrl("")
|
||||
)
|
||||
)
|
||||
)
|
||||
whenever(mockContentRemoteSource.get()).doReturn(listOf(Content(ContentId("a"), "", "", ImageUrl(""))))
|
||||
|
||||
val actual = sut.contents.take(2).toList()
|
||||
|
||||
|
|
@ -58,8 +75,8 @@ internal class ContentRepositoryTest {
|
|||
fun errorFlow() = runBlockingTest {
|
||||
val exception = RuntimeException()
|
||||
val expected = listOf(
|
||||
Resource.Loading(),
|
||||
Resource.Error<List<Content>>(UnexpectedException(exception))
|
||||
Resource.Loading(),
|
||||
Resource.Error<List<Content>>(UnexpectedException(exception))
|
||||
)
|
||||
whenever(mockContentRemoteSource.get()).doThrow(exception)
|
||||
|
||||
|
|
@ -101,27 +118,27 @@ internal class ContentRepositoryTest {
|
|||
@DisplayName("GIVEN content response THEN error WHEN fetched THEN returned states are loading data loading error")
|
||||
@Test
|
||||
fun whenFetchingRequestIsCalledAgain() =
|
||||
runBlockingTest(testDispatcher) {
|
||||
val exception = RuntimeException()
|
||||
val expected = listOf(
|
||||
Resource.Loading(),
|
||||
Resource.Success(emptyList()),
|
||||
Resource.Loading(),
|
||||
Resource.Error<List<Content>>(UnexpectedException(exception))
|
||||
)
|
||||
var first = true
|
||||
whenever(mockContentRemoteSource.get()).doAnswer {
|
||||
if (first) emptyList<Content>().also { first = false } else throw exception
|
||||
}
|
||||
|
||||
val actual = async(testDispatcher) { sut.contents.take(4).toList() }
|
||||
testDispatcher.advanceUntilIdle()
|
||||
sut.fetch()
|
||||
testDispatcher.advanceUntilIdle()
|
||||
|
||||
Assertions.assertEquals(expected, actual.await())
|
||||
runBlockingTest(testDispatcher) {
|
||||
val exception = RuntimeException()
|
||||
val expected = listOf(
|
||||
Resource.Loading(),
|
||||
Resource.Success(emptyList()),
|
||||
Resource.Loading(),
|
||||
Resource.Error<List<Content>>(UnexpectedException(exception))
|
||||
)
|
||||
var first = true
|
||||
whenever(mockContentRemoteSource.get()).doAnswer {
|
||||
if (first) emptyList<Content>().also { first = false } else throw exception
|
||||
}
|
||||
|
||||
val actual = async(testDispatcher) { sut.contents.take(4).toList() }
|
||||
testDispatcher.advanceUntilIdle()
|
||||
sut.fetch()
|
||||
testDispatcher.advanceUntilIdle()
|
||||
|
||||
Assertions.assertEquals(expected, actual.await())
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN content response THEN error WHEN fetched THEN only 4 items are emitted")
|
||||
@Test
|
||||
fun noAdditionalItemsEmitted() {
|
||||
|
|
@ -129,10 +146,10 @@ internal class ContentRepositoryTest {
|
|||
runBlockingTest(testDispatcher) {
|
||||
val exception = RuntimeException()
|
||||
val expected = listOf(
|
||||
Resource.Loading(),
|
||||
Resource.Success(emptyList()),
|
||||
Resource.Loading(),
|
||||
Resource.Error<List<Content>>(UnexpectedException(exception))
|
||||
Resource.Loading(),
|
||||
Resource.Success(emptyList()),
|
||||
Resource.Loading(),
|
||||
Resource.Error<List<Content>>(UnexpectedException(exception))
|
||||
)
|
||||
var first = true
|
||||
whenever(mockContentRemoteSource.get()).doAnswer {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package org.fnives.test.showcase.core.content
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.take
|
||||
|
|
@ -20,6 +21,7 @@ import org.mockito.kotlin.mock
|
|||
import org.mockito.kotlin.whenever
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal class GetAllContentUseCaseTest {
|
||||
|
||||
private lateinit var sut: GetAllContentUseCase
|
||||
|
|
@ -36,71 +38,78 @@ internal class GetAllContentUseCaseTest {
|
|||
mockContentRepository = mock()
|
||||
favouriteContentIdFlow = MutableStateFlow(emptyList())
|
||||
contentFlow = MutableStateFlow(Resource.Loading())
|
||||
whenever(mockFavouriteContentLocalStorage.observeFavourites()).doReturn(favouriteContentIdFlow)
|
||||
whenever(mockFavouriteContentLocalStorage.observeFavourites()).doReturn(
|
||||
favouriteContentIdFlow
|
||||
)
|
||||
whenever(mockContentRepository.contents).doReturn(contentFlow)
|
||||
sut = GetAllContentUseCase(mockContentRepository, mockFavouriteContentLocalStorage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_loading_AND_empty_favourite_WHEN_observed_THEN_loading_is_shown() = runBlockingTest(testDispatcher) {
|
||||
favouriteContentIdFlow.value = emptyList()
|
||||
contentFlow.value = Resource.Loading()
|
||||
val expected = Resource.Loading<List<FavouriteContent>>()
|
||||
fun GIVEN_loading_AND_empty_favourite_WHEN_observed_THEN_loading_is_shown() =
|
||||
runBlockingTest(testDispatcher) {
|
||||
favouriteContentIdFlow.value = emptyList()
|
||||
contentFlow.value = Resource.Loading()
|
||||
val expected = Resource.Loading<List<FavouriteContent>>()
|
||||
|
||||
val actual = sut.get().take(1).toList()
|
||||
val actual = sut.get().take(1).toList()
|
||||
|
||||
Assertions.assertEquals(listOf(expected), actual)
|
||||
}
|
||||
Assertions.assertEquals(listOf(expected), actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_loading_AND_listOfFavourite_WHEN_observed_THEN_loading_is_shown() = runBlockingTest(testDispatcher) {
|
||||
favouriteContentIdFlow.value = listOf(ContentId("a"))
|
||||
contentFlow.value = Resource.Loading()
|
||||
val expected = Resource.Loading<List<FavouriteContent>>()
|
||||
fun GIVEN_loading_AND_listOfFavourite_WHEN_observed_THEN_loading_is_shown() =
|
||||
runBlockingTest(testDispatcher) {
|
||||
favouriteContentIdFlow.value = listOf(ContentId("a"))
|
||||
contentFlow.value = Resource.Loading()
|
||||
val expected = Resource.Loading<List<FavouriteContent>>()
|
||||
|
||||
val actual = sut.get().take(1).toList()
|
||||
val actual = sut.get().take(1).toList()
|
||||
|
||||
Assertions.assertEquals(listOf(expected), actual)
|
||||
}
|
||||
Assertions.assertEquals(listOf(expected), actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_error_AND_empty_favourite_WHEN_observed_THEN_error_is_shown() = runBlockingTest(testDispatcher) {
|
||||
favouriteContentIdFlow.value = emptyList()
|
||||
val exception = Throwable()
|
||||
contentFlow.value = Resource.Error(exception)
|
||||
val expected = Resource.Error<List<FavouriteContent>>(exception)
|
||||
fun GIVEN_error_AND_empty_favourite_WHEN_observed_THEN_error_is_shown() =
|
||||
runBlockingTest(testDispatcher) {
|
||||
favouriteContentIdFlow.value = emptyList()
|
||||
val exception = Throwable()
|
||||
contentFlow.value = Resource.Error(exception)
|
||||
val expected = Resource.Error<List<FavouriteContent>>(exception)
|
||||
|
||||
val actual = sut.get().take(1).toList()
|
||||
val actual = sut.get().take(1).toList()
|
||||
|
||||
Assertions.assertEquals(listOf(expected), actual)
|
||||
}
|
||||
Assertions.assertEquals(listOf(expected), actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_error_AND_listOfFavourite_WHEN_observed_THEN_error_is_shown() = runBlockingTest(testDispatcher) {
|
||||
favouriteContentIdFlow.value = listOf(ContentId("b"))
|
||||
val exception = Throwable()
|
||||
contentFlow.value = Resource.Error(exception)
|
||||
val expected = Resource.Error<List<FavouriteContent>>(exception)
|
||||
fun GIVEN_error_AND_listOfFavourite_WHEN_observed_THEN_error_is_shown() =
|
||||
runBlockingTest(testDispatcher) {
|
||||
favouriteContentIdFlow.value = listOf(ContentId("b"))
|
||||
val exception = Throwable()
|
||||
contentFlow.value = Resource.Error(exception)
|
||||
val expected = Resource.Error<List<FavouriteContent>>(exception)
|
||||
|
||||
val actual = sut.get().take(1).toList()
|
||||
val actual = sut.get().take(1).toList()
|
||||
|
||||
Assertions.assertEquals(listOf(expected), actual)
|
||||
}
|
||||
Assertions.assertEquals(listOf(expected), actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_listOfContent_AND_empty_favourite_WHEN_observed_THEN_favourites_are_returned() = runBlockingTest(testDispatcher) {
|
||||
favouriteContentIdFlow.value = emptyList()
|
||||
val content = Content(ContentId("a"), "b", "c", ImageUrl("d"))
|
||||
contentFlow.value = Resource.Success(listOf(content))
|
||||
val items = listOf(
|
||||
FavouriteContent(content, false)
|
||||
)
|
||||
val expected = Resource.Success(items)
|
||||
fun GIVEN_listOfContent_AND_empty_favourite_WHEN_observed_THEN_favourites_are_returned() =
|
||||
runBlockingTest(testDispatcher) {
|
||||
favouriteContentIdFlow.value = emptyList()
|
||||
val content = Content(ContentId("a"), "b", "c", ImageUrl("d"))
|
||||
contentFlow.value = Resource.Success(listOf(content))
|
||||
val items = listOf(
|
||||
FavouriteContent(content, false)
|
||||
)
|
||||
val expected = Resource.Success(items)
|
||||
|
||||
val actual = sut.get().take(1).toList()
|
||||
val actual = sut.get().take(1).toList()
|
||||
|
||||
Assertions.assertEquals(listOf(expected), actual)
|
||||
}
|
||||
Assertions.assertEquals(listOf(expected), actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_listOfContent_AND_other_favourite_id_WHEN_observed_THEN_favourites_are_returned() =
|
||||
|
|
|
|||
|
|
@ -1,20 +1,11 @@
|
|||
package org.fnives.test.showcase.core.login
|
||||
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.fnives.test.showcase.core.shared.UnexpectedException
|
||||
import org.fnives.test.showcase.core.storage.UserDataLocalStorage
|
||||
import org.fnives.test.showcase.model.auth.LoginCredentials
|
||||
import org.fnives.test.showcase.model.auth.LoginStatus
|
||||
import org.fnives.test.showcase.model.session.Session
|
||||
import org.fnives.test.showcase.model.shared.Answer
|
||||
import org.fnives.test.showcase.network.auth.LoginRemoteSource
|
||||
import org.fnives.test.showcase.network.auth.model.LoginStatusResponses
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.*
|
||||
|
||||
@Disabled("CodeKata")
|
||||
class CodeKataSecondLoginUseCaseTest {
|
||||
|
||||
@BeforeEach
|
||||
|
|
@ -51,4 +42,4 @@ class CodeKataSecondLoginUseCaseTest {
|
|||
fun invalidResponseResultsInErrorReturned() {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,14 @@ import org.junit.jupiter.api.Assertions
|
|||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.*
|
||||
import java.util.stream.Stream
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.doThrow
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||
import org.mockito.kotlin.verifyZeroInteractions
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
internal class LoginUseCaseTest {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
package org.fnives.test.showcase.core.session
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.*
|
||||
import org.junit.jupiter.api.Disabled
|
||||
|
||||
class CodeKataFirstSessionExpirationAdapterTest {
|
||||
|
||||
}
|
||||
@Disabled("CodeKata")
|
||||
class CodeKataFirstSessionExpirationAdapterTest
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
#mock-maker-inline
|
||||
mock-maker-inline
|
||||
|
|
@ -1,56 +1,79 @@
|
|||
build:
|
||||
maxIssues: 15
|
||||
maxIssues: 0
|
||||
excludeCorrectable: false
|
||||
weights:
|
||||
# complexity: 2
|
||||
# LongParameterList: 1
|
||||
# style: 1
|
||||
# comments: 1
|
||||
# complexity: 2
|
||||
# LongParameterList: 1
|
||||
# style: 1
|
||||
# comments: 1
|
||||
|
||||
config:
|
||||
validation: true
|
||||
# when writing own rules with new properties, exclude the property path e.g.: "my_rule_set,.*>.*>[my_property]"
|
||||
excludes: ""
|
||||
warningsAsErrors: false
|
||||
# when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
|
||||
excludes: ''
|
||||
|
||||
processors:
|
||||
active: true
|
||||
exclude:
|
||||
# - 'DetektProgressListener'
|
||||
- 'DetektProgressListener'
|
||||
# - 'KtFileCountProcessor'
|
||||
# - 'PackageCountProcessor'
|
||||
# - 'ClassCountProcessor'
|
||||
# - 'FunctionCountProcessor'
|
||||
# - 'PropertyCountProcessor'
|
||||
# - 'ClassCountProcessor'
|
||||
# - 'PackageCountProcessor'
|
||||
# - 'KtFileCountProcessor'
|
||||
# - 'ProjectComplexityProcessor'
|
||||
# - 'ProjectCognitiveComplexityProcessor'
|
||||
# - 'ProjectLLOCProcessor'
|
||||
# - 'ProjectCLOCProcessor'
|
||||
# - 'ProjectLOCProcessor'
|
||||
# - 'ProjectSLOCProcessor'
|
||||
# - 'LicenseHeaderLoaderExtension'
|
||||
|
||||
console-reports:
|
||||
active: true
|
||||
exclude:
|
||||
# - 'ProjectStatisticsReport'
|
||||
# - 'ComplexityReport'
|
||||
# - 'NotificationReport'
|
||||
# - 'FindingsReport'
|
||||
- 'FileBasedFindingsReport'
|
||||
# - 'BuildFailureReport'
|
||||
- 'ProjectStatisticsReport'
|
||||
- 'ComplexityReport'
|
||||
- 'NotificationReport'
|
||||
# - 'FindingsReport'
|
||||
- 'FileBasedFindingsReport'
|
||||
|
||||
output-reports:
|
||||
active: true
|
||||
exclude:
|
||||
# - 'TxtOutputReport'
|
||||
# - 'XmlOutputReport'
|
||||
# - 'HtmlOutputReport'
|
||||
|
||||
comments:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
AbsentOrWrongFileLicense:
|
||||
active: false
|
||||
licenseTemplateFile: 'license.template'
|
||||
licenseTemplateIsRegex: false
|
||||
CommentOverPrivateFunction:
|
||||
active: false
|
||||
CommentOverPrivateProperty:
|
||||
active: false
|
||||
DeprecatedBlockTag:
|
||||
active: false
|
||||
EndOfSentenceFormat:
|
||||
active: false
|
||||
endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!:]$)
|
||||
endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
|
||||
UndocumentedPublicClass:
|
||||
active: false
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
searchInNestedClass: true
|
||||
searchInInnerClass: true
|
||||
searchInInnerObject: true
|
||||
searchInInnerInterface: true
|
||||
UndocumentedPublicFunction:
|
||||
active: false
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
UndocumentedPublicProperty:
|
||||
active: false
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
|
||||
complexity:
|
||||
active: true
|
||||
|
|
@ -61,57 +84,86 @@ complexity:
|
|||
active: false
|
||||
threshold: 10
|
||||
includeStaticDeclarations: false
|
||||
includePrivateDeclarations: false
|
||||
ComplexMethod:
|
||||
active: true
|
||||
threshold: 25
|
||||
threshold: 15
|
||||
ignoreSingleWhenExpression: false
|
||||
ignoreSimpleWhenEntries: false
|
||||
ignoreNestingFunctions: false
|
||||
nestingFunctions: run,let,apply,with,also,use,forEach,isNotNull,ifNull
|
||||
nestingFunctions:
|
||||
- 'also'
|
||||
- 'apply'
|
||||
- 'forEach'
|
||||
- 'isNotNull'
|
||||
- 'ifNull'
|
||||
- 'let'
|
||||
- 'run'
|
||||
- 'use'
|
||||
- 'with'
|
||||
LabeledExpression:
|
||||
active: false
|
||||
ignoredLabels: ""
|
||||
ignoredLabels: []
|
||||
LargeClass:
|
||||
active: true
|
||||
threshold: 600
|
||||
LongMethod:
|
||||
active: true
|
||||
threshold: 200
|
||||
threshold: 60
|
||||
ignoreAnnotated: []
|
||||
LongParameterList:
|
||||
active: true
|
||||
functionThreshold: 6
|
||||
constructorThreshold: 6
|
||||
constructorThreshold: 7
|
||||
ignoreDefaultParameters: false
|
||||
ignoreDataClasses: true
|
||||
ignoreAnnotated: []
|
||||
MethodOverloading:
|
||||
active: false
|
||||
threshold: 6
|
||||
NamedArguments:
|
||||
active: false
|
||||
threshold: 3
|
||||
NestedBlockDepth:
|
||||
active: true
|
||||
threshold: 100
|
||||
threshold: 4
|
||||
ReplaceSafeCallChainWithRun:
|
||||
active: false
|
||||
StringLiteralDuplication:
|
||||
active: false
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
threshold: 3
|
||||
ignoreAnnotation: true
|
||||
excludeStringsWithLessThan5Characters: true
|
||||
ignoreStringsRegex: '$^'
|
||||
TooManyFunctions:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
thresholdInFiles: 25
|
||||
thresholdInClasses: 25
|
||||
thresholdInInterfaces: 25
|
||||
thresholdInObjects: 25
|
||||
thresholdInEnums: 25
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
thresholdInFiles: 11
|
||||
thresholdInClasses: 11
|
||||
thresholdInInterfaces: 11
|
||||
thresholdInObjects: 11
|
||||
thresholdInEnums: 11
|
||||
ignoreDeprecated: false
|
||||
ignorePrivate: false
|
||||
ignoreOverridden: false
|
||||
|
||||
coroutines:
|
||||
active: true
|
||||
GlobalCoroutineUsage:
|
||||
active: false
|
||||
RedundantSuspendModifier:
|
||||
active: false
|
||||
SleepInsteadOfDelay:
|
||||
active: false
|
||||
SuspendFunWithFlowReturnType:
|
||||
active: false
|
||||
|
||||
empty-blocks:
|
||||
active: true
|
||||
EmptyCatchBlock:
|
||||
active: true
|
||||
allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
|
||||
allowedExceptionNameRegex: '_|(ignore|expected).*'
|
||||
EmptyClassBlock:
|
||||
active: true
|
||||
EmptyDefaultConstructor:
|
||||
|
|
@ -135,6 +187,8 @@ empty-blocks:
|
|||
active: true
|
||||
EmptySecondaryConstructor:
|
||||
active: true
|
||||
EmptyTryBlock:
|
||||
active: true
|
||||
EmptyWhenBlock:
|
||||
active: true
|
||||
EmptyWhileBlock:
|
||||
|
|
@ -143,53 +197,73 @@ empty-blocks:
|
|||
exceptions:
|
||||
active: true
|
||||
ExceptionRaisedInUnexpectedLocation:
|
||||
active: false
|
||||
methodNames: 'toString,hashCode,equals,finalize'
|
||||
active: true
|
||||
methodNames:
|
||||
- 'equals'
|
||||
- 'finalize'
|
||||
- 'hashCode'
|
||||
- 'toString'
|
||||
InstanceOfCheckForException:
|
||||
active: false
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
NotImplementedDeclaration:
|
||||
active: false
|
||||
ObjectExtendsThrowable:
|
||||
active: false
|
||||
PrintStackTrace:
|
||||
active: false
|
||||
active: true
|
||||
RethrowCaughtException:
|
||||
active: false
|
||||
active: true
|
||||
ReturnFromFinally:
|
||||
active: false
|
||||
active: true
|
||||
ignoreLabeled: false
|
||||
SwallowedException:
|
||||
active: false
|
||||
ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException'
|
||||
allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
|
||||
active: true
|
||||
ignoredExceptionTypes:
|
||||
- 'InterruptedException'
|
||||
- 'MalformedURLException'
|
||||
- 'NumberFormatException'
|
||||
- 'ParseException'
|
||||
allowedExceptionNameRegex: '_|(ignore|expected).*'
|
||||
ThrowingExceptionFromFinally:
|
||||
active: false
|
||||
active: true
|
||||
ThrowingExceptionInMain:
|
||||
active: false
|
||||
ThrowingExceptionsWithoutMessageOrCause:
|
||||
active: true
|
||||
exceptions: 'IllegalArgumentException,IllegalStateException,IOException'
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
exceptions:
|
||||
- 'ArrayIndexOutOfBoundsException'
|
||||
- 'Exception'
|
||||
- 'IllegalArgumentException'
|
||||
- 'IllegalMonitorStateException'
|
||||
- 'IllegalStateException'
|
||||
- 'IndexOutOfBoundsException'
|
||||
- 'NullPointerException'
|
||||
- 'RuntimeException'
|
||||
- 'Throwable'
|
||||
ThrowingNewInstanceOfSameException:
|
||||
active: true
|
||||
TooGenericExceptionCaught:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
exceptionNames:
|
||||
- ArrayIndexOutOfBoundsException
|
||||
- Error
|
||||
- Exception
|
||||
- IllegalMonitorStateException
|
||||
- NullPointerException
|
||||
- IndexOutOfBoundsException
|
||||
- RuntimeException
|
||||
- Throwable
|
||||
allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
|
||||
- 'ArrayIndexOutOfBoundsException'
|
||||
- 'Error'
|
||||
- 'Exception'
|
||||
- 'IllegalMonitorStateException'
|
||||
- 'IndexOutOfBoundsException'
|
||||
- 'NullPointerException'
|
||||
- 'RuntimeException'
|
||||
- 'Throwable'
|
||||
allowedExceptionNameRegex: '_|(ignore|expected).*'
|
||||
TooGenericExceptionThrown:
|
||||
active: true
|
||||
exceptionNames:
|
||||
- Error
|
||||
- Exception
|
||||
- Throwable
|
||||
- RuntimeException
|
||||
- 'Error'
|
||||
- 'Exception'
|
||||
- 'RuntimeException'
|
||||
- 'Throwable'
|
||||
|
||||
formatting:
|
||||
active: true
|
||||
|
|
@ -198,28 +272,42 @@ formatting:
|
|||
AnnotationOnSeparateLine:
|
||||
active: false
|
||||
autoCorrect: true
|
||||
AnnotationSpacing:
|
||||
active: false
|
||||
autoCorrect: true
|
||||
ArgumentListWrapping:
|
||||
active: false
|
||||
autoCorrect: true
|
||||
indentSize: 4
|
||||
maxLineLength: 120
|
||||
ChainWrapping:
|
||||
active: true
|
||||
autoCorrect: true
|
||||
CommentSpacing:
|
||||
active: true
|
||||
autoCorrect: true
|
||||
EnumEntryNameCase:
|
||||
active: false
|
||||
autoCorrect: true
|
||||
Filename:
|
||||
active: true
|
||||
FinalNewline:
|
||||
active: true
|
||||
autoCorrect: true
|
||||
insertFinalNewLine: true
|
||||
ImportOrdering:
|
||||
active: true
|
||||
active: false
|
||||
autoCorrect: true
|
||||
layout: '*,java.**,javax.**,kotlin.**,^'
|
||||
Indentation:
|
||||
active: true
|
||||
active: false
|
||||
autoCorrect: true
|
||||
indentSize: 4
|
||||
continuationIndentSize: 4
|
||||
MaximumLineLength:
|
||||
active: true
|
||||
maxLineLength: 250
|
||||
maxLineLength: 120
|
||||
ignoreBackTickedIdentifier: false
|
||||
ModifierOrdering:
|
||||
active: true
|
||||
autoCorrect: true
|
||||
|
|
@ -235,6 +323,9 @@ formatting:
|
|||
NoEmptyClassBody:
|
||||
active: true
|
||||
autoCorrect: true
|
||||
NoEmptyFirstLineInMethodBlock:
|
||||
active: false
|
||||
autoCorrect: true
|
||||
NoLineBreakAfterElse:
|
||||
active: true
|
||||
autoCorrect: true
|
||||
|
|
@ -265,6 +356,10 @@ formatting:
|
|||
active: true
|
||||
autoCorrect: true
|
||||
indentSize: 4
|
||||
maxLineLength: 120
|
||||
SpacingAroundAngleBrackets:
|
||||
active: false
|
||||
autoCorrect: true
|
||||
SpacingAroundColon:
|
||||
active: true
|
||||
autoCorrect: true
|
||||
|
|
@ -277,6 +372,9 @@ formatting:
|
|||
SpacingAroundDot:
|
||||
active: true
|
||||
autoCorrect: true
|
||||
SpacingAroundDoubleColon:
|
||||
active: false
|
||||
autoCorrect: true
|
||||
SpacingAroundKeyword:
|
||||
active: true
|
||||
autoCorrect: true
|
||||
|
|
@ -289,85 +387,108 @@ formatting:
|
|||
SpacingAroundRangeOperator:
|
||||
active: true
|
||||
autoCorrect: true
|
||||
SpacingAroundUnaryOperator:
|
||||
active: false
|
||||
autoCorrect: true
|
||||
SpacingBetweenDeclarationsWithAnnotations:
|
||||
active: false
|
||||
autoCorrect: true
|
||||
SpacingBetweenDeclarationsWithComments:
|
||||
active: false
|
||||
autoCorrect: true
|
||||
StringTemplate:
|
||||
active: true
|
||||
autoCorrect: true
|
||||
|
||||
naming:
|
||||
active: true
|
||||
BooleanPropertyNaming:
|
||||
active: false
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
allowedPattern: '^(is|has|are)'
|
||||
ClassNaming:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
classPattern: '[A-Z$][a-zA-Z0-9$]*'
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
classPattern: '[A-Z][a-zA-Z0-9]*'
|
||||
ConstructorParameterNaming:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
parameterPattern: '[a-z][A-Za-z0-9]*'
|
||||
privateParameterPattern: '[a-z][A-Za-z0-9]*'
|
||||
excludeClassPattern: '$^'
|
||||
ignoreOverridden: true
|
||||
EnumNaming:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*'
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
|
||||
ForbiddenClassName:
|
||||
active: false
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
forbiddenName: ''
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
forbiddenName: []
|
||||
FunctionMaxLength:
|
||||
active: false
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
maximumFunctionNameLength: 30
|
||||
FunctionMinLength:
|
||||
active: false
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
minimumFunctionNameLength: 3
|
||||
FunctionNaming:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$'
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)'
|
||||
excludeClassPattern: '$^'
|
||||
ignoreOverridden: true
|
||||
ignoreAnnotated:
|
||||
- 'Composable'
|
||||
FunctionParameterNaming:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
parameterPattern: '[a-z][A-Za-z0-9]*'
|
||||
excludeClassPattern: '$^'
|
||||
ignoreOverridden: true
|
||||
InvalidPackageDeclaration:
|
||||
active: false
|
||||
excludes: ['**/*.kts']
|
||||
rootPackage: ''
|
||||
MatchingDeclarationName:
|
||||
active: true
|
||||
mustBeFirst: true
|
||||
MemberNameEqualsClassName:
|
||||
active: true
|
||||
ignoreOverridden: true
|
||||
NoNameShadowing:
|
||||
active: false
|
||||
NonBooleanPropertyPrefixedWithIs:
|
||||
active: false
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
ObjectPropertyNaming:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
constantPattern: '[A-Za-z][_A-Za-z0-9]*'
|
||||
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
|
||||
privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
|
||||
PackageNaming:
|
||||
active: false
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$'
|
||||
active: true
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
|
||||
TopLevelPropertyNaming:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
constantPattern: '[A-Z][_A-Z0-9]*'
|
||||
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
|
||||
privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
|
||||
VariableMaxLength:
|
||||
active: false
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
maximumVariableNameLength: 64
|
||||
VariableMinLength:
|
||||
active: false
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
minimumVariableNameLength: 1
|
||||
VariableNaming:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
variablePattern: '[a-z][A-Za-z0-9]*'
|
||||
privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
|
||||
excludeClassPattern: '$^'
|
||||
|
|
@ -379,29 +500,52 @@ performance:
|
|||
active: true
|
||||
ForEachOnRange:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
SpreadOperator:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
UnnecessaryTemporaryInstantiation:
|
||||
active: true
|
||||
|
||||
potential-bugs:
|
||||
active: true
|
||||
AvoidReferentialEquality:
|
||||
active: false
|
||||
forbiddenTypePatterns:
|
||||
- 'kotlin.String'
|
||||
CastToNullableType:
|
||||
active: false
|
||||
Deprecation:
|
||||
active: false
|
||||
DontDowncastCollectionTypes:
|
||||
active: false
|
||||
DoubleMutabilityForCollection:
|
||||
active: false
|
||||
DuplicateCaseInWhenExpression:
|
||||
active: true
|
||||
EqualsAlwaysReturnsTrueOrFalse:
|
||||
active: true
|
||||
EqualsWithHashCodeExist:
|
||||
active: true
|
||||
ExitOutsideMain:
|
||||
active: false
|
||||
ExplicitGarbageCollectionCall:
|
||||
active: true
|
||||
HasPlatformType:
|
||||
active: false
|
||||
ImplicitDefaultLocale:
|
||||
IgnoredReturnValue:
|
||||
active: false
|
||||
restrictToAnnotatedMethods: true
|
||||
returnValueAnnotations:
|
||||
- '*.CheckResult'
|
||||
- '*.CheckReturnValue'
|
||||
ignoreReturnValueAnnotations:
|
||||
- '*.CanIgnoreReturnValue'
|
||||
ImplicitDefaultLocale:
|
||||
active: true
|
||||
ImplicitUnitReturnType:
|
||||
active: false
|
||||
allowExplicitReturnType: true
|
||||
InvalidRange:
|
||||
active: true
|
||||
IteratorHasNextCallsNextMethod:
|
||||
|
|
@ -410,20 +554,33 @@ potential-bugs:
|
|||
active: true
|
||||
LateinitUsage:
|
||||
active: false
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludeAnnotatedProperties: ""
|
||||
ignoreOnClassesPattern: ""
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
excludeAnnotatedProperties: []
|
||||
ignoreOnClassesPattern: ''
|
||||
MapGetWithNotNullAssertionOperator:
|
||||
active: false
|
||||
MissingWhenCase:
|
||||
active: true
|
||||
allowElseExpression: true
|
||||
NullableToStringCall:
|
||||
active: false
|
||||
RedundantElseInWhen:
|
||||
active: true
|
||||
UnconditionalJumpStatementInLoop:
|
||||
active: false
|
||||
UnnecessaryNotNullOperator:
|
||||
active: true
|
||||
UnnecessarySafeCall:
|
||||
active: true
|
||||
UnreachableCatchBlock:
|
||||
active: false
|
||||
UnreachableCode:
|
||||
active: true
|
||||
UnsafeCallOnNullableType:
|
||||
active: true
|
||||
UnsafeCast:
|
||||
active: true
|
||||
UnusedUnaryOperator:
|
||||
active: false
|
||||
UselessPostfixExpression:
|
||||
active: false
|
||||
|
|
@ -432,30 +589,51 @@ potential-bugs:
|
|||
|
||||
style:
|
||||
active: true
|
||||
ClassOrdering:
|
||||
active: false
|
||||
CollapsibleIfStatements:
|
||||
active: true
|
||||
active: false
|
||||
DataClassContainsFunctions:
|
||||
active: true
|
||||
active: false
|
||||
conversionFunctionPrefix: 'to'
|
||||
DataClassShouldBeImmutable:
|
||||
active: false
|
||||
DestructuringDeclarationWithTooManyEntries:
|
||||
active: false
|
||||
maxDestructuringEntries: 3
|
||||
EqualsNullCall:
|
||||
active: true
|
||||
EqualsOnSignatureLine:
|
||||
active: false
|
||||
ExplicitCollectionElementAccessMethod:
|
||||
active: false
|
||||
ExplicitItLambdaParameter:
|
||||
active: false
|
||||
ExpressionBodySyntax:
|
||||
active: true
|
||||
active: false
|
||||
includeLineWrapping: false
|
||||
ForbiddenComment:
|
||||
active: true
|
||||
values: 'TODO:,FIXME:,STOPSHIP:'
|
||||
allowedPatterns: ""
|
||||
values:
|
||||
- 'FIXME:'
|
||||
- 'STOPSHIP:'
|
||||
- 'TODO:'
|
||||
allowedPatterns: ''
|
||||
ForbiddenImport:
|
||||
active: false
|
||||
imports: ''
|
||||
forbiddenPatterns: ""
|
||||
imports: []
|
||||
forbiddenPatterns: ''
|
||||
ForbiddenMethodCall:
|
||||
active: false
|
||||
methods:
|
||||
- 'kotlin.io.print'
|
||||
- 'kotlin.io.println'
|
||||
ForbiddenPublicDataClass:
|
||||
active: true
|
||||
excludes: ['**']
|
||||
ignorePackages:
|
||||
- '*.internal'
|
||||
- '*.internal.*'
|
||||
ForbiddenVoid:
|
||||
active: false
|
||||
ignoreOverridden: false
|
||||
|
|
@ -463,30 +641,44 @@ style:
|
|||
FunctionOnlyReturningConstant:
|
||||
active: true
|
||||
ignoreOverridableFunction: true
|
||||
excludedFunctions: 'describeContents'
|
||||
excludeAnnotatedFunction: "dagger.Provides"
|
||||
ignoreActualFunction: true
|
||||
excludedFunctions: ''
|
||||
excludeAnnotatedFunction:
|
||||
- 'dagger.Provides'
|
||||
LibraryCodeMustSpecifyReturnType:
|
||||
active: true
|
||||
excludes: ['**']
|
||||
LibraryEntitiesShouldNotBePublic:
|
||||
active: true
|
||||
excludes: ['**']
|
||||
LoopWithTooManyJumpStatements:
|
||||
active: true
|
||||
maxJumpCount: 1
|
||||
MagicNumber:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
ignoreNumbers: '-1,0,1,2'
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
ignoreNumbers:
|
||||
- '-1'
|
||||
- '0'
|
||||
- '1'
|
||||
- '2'
|
||||
ignoreHashCodeFunction: true
|
||||
ignorePropertyDeclaration: false
|
||||
ignoreLocalVariableDeclaration: false
|
||||
ignoreConstantDeclaration: true
|
||||
ignoreCompanionObjectPropertyDeclaration: true
|
||||
ignoreAnnotation: false
|
||||
ignoreNamedArgument: true
|
||||
ignoreEnums: false
|
||||
ignoreRanges: false
|
||||
ignoreExtensionFunctions: true
|
||||
MandatoryBracesIfStatements:
|
||||
active: true
|
||||
active: false
|
||||
MandatoryBracesLoops:
|
||||
active: false
|
||||
MaxLineLength:
|
||||
active: true
|
||||
maxLineLength: 160 #default: 120
|
||||
maxLineLength: 120
|
||||
excludePackageStatements: true
|
||||
excludeImportStatements: true
|
||||
excludeCommentStatements: false
|
||||
|
|
@ -494,12 +686,16 @@ style:
|
|||
active: true
|
||||
ModifierOrder:
|
||||
active: true
|
||||
NestedClassesVisibility:
|
||||
MultilineLambdaItParameter:
|
||||
active: false
|
||||
NestedClassesVisibility:
|
||||
active: true
|
||||
NewLineAtEndOfFile:
|
||||
active: true
|
||||
NoTabs:
|
||||
active: false
|
||||
ObjectLiteralToLambda:
|
||||
active: false
|
||||
OptionalAbstractKeyword:
|
||||
active: true
|
||||
OptionalUnit:
|
||||
|
|
@ -507,29 +703,32 @@ style:
|
|||
OptionalWhenBraces:
|
||||
active: false
|
||||
PreferToOverPairSyntax:
|
||||
active: true
|
||||
active: false
|
||||
ProtectedMemberInFinalClass:
|
||||
active: true
|
||||
RedundantExplicitType:
|
||||
active: true
|
||||
active: false
|
||||
RedundantHigherOrderMapUsage:
|
||||
active: false
|
||||
RedundantVisibilityModifierRule:
|
||||
active: false
|
||||
ReturnCount:
|
||||
active: true
|
||||
max: 5
|
||||
excludedFunctions: "equals"
|
||||
excludedFunctions: 'equals'
|
||||
excludeLabeled: false
|
||||
excludeReturnFromLambda: true
|
||||
excludeGuardClauses: false
|
||||
SafeCast:
|
||||
active: true
|
||||
SerialVersionUIDInSerializableClass:
|
||||
active: false
|
||||
active: true
|
||||
SpacingBetweenPackageAndImports:
|
||||
active: false
|
||||
ThrowsCount:
|
||||
active: true
|
||||
max: 10
|
||||
max: 2
|
||||
excludeGuardClauses: false
|
||||
TrailingWhitespace:
|
||||
active: false
|
||||
UnderscoresInNumericLiterals:
|
||||
|
|
@ -537,43 +736,62 @@ style:
|
|||
acceptableDecimalLength: 5
|
||||
UnnecessaryAbstractClass:
|
||||
active: true
|
||||
excludeAnnotatedClasses: "dagger.Module"
|
||||
excludeAnnotatedClasses:
|
||||
- 'dagger.Module'
|
||||
UnnecessaryAnnotationUseSiteTarget:
|
||||
active: false
|
||||
UnnecessaryApply:
|
||||
active: true
|
||||
UnnecessaryFilter:
|
||||
active: false
|
||||
UnnecessaryInheritance:
|
||||
active: true
|
||||
UnnecessaryLet:
|
||||
active: true
|
||||
active: false
|
||||
UnnecessaryParentheses:
|
||||
active: true
|
||||
active: false
|
||||
UntilInsteadOfRangeTo:
|
||||
active: false
|
||||
UnusedImports:
|
||||
active: true
|
||||
active: false
|
||||
UnusedPrivateClass:
|
||||
active: true
|
||||
UnusedPrivateMember:
|
||||
active: true
|
||||
allowedNames: "(_|ignored|expected|serialVersionUID)"
|
||||
allowedNames: '(_|ignored|expected|serialVersionUID)'
|
||||
UseArrayLiteralsInAnnotations:
|
||||
active: false
|
||||
UseCheckNotNull:
|
||||
active: false
|
||||
UseCheckOrError:
|
||||
active: false
|
||||
UseDataClass:
|
||||
active: false
|
||||
excludeAnnotatedClasses: ""
|
||||
excludeAnnotatedClasses: []
|
||||
allowVars: false
|
||||
UseEmptyCounterpart:
|
||||
active: false
|
||||
UseIfEmptyOrIfBlank:
|
||||
active: false
|
||||
UseIfInsteadOfWhen:
|
||||
active: false
|
||||
UseIsNullOrEmpty:
|
||||
active: false
|
||||
UseOrEmpty:
|
||||
active: false
|
||||
UseRequire:
|
||||
active: false
|
||||
UseRequireNotNull:
|
||||
active: false
|
||||
UselessCallOnNotNull:
|
||||
active: true
|
||||
UtilityClassWithPublicConstructor:
|
||||
active: true
|
||||
VarCouldBeVal:
|
||||
active: false
|
||||
active: true
|
||||
WildcardImport:
|
||||
active: true
|
||||
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
|
||||
excludeImports: 'java.util.*,kotlinx.android.synthetic.*'
|
||||
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||
excludeImports:
|
||||
- 'java.util.*'
|
||||
- 'kotlinx.android.synthetic.*'
|
||||
|
|
|
|||
|
|
@ -17,5 +17,6 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
android.jetifier.ignorelist=bcprov-jdk15on
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
|
||||
|
|
|
|||
24
gradlescripts/detekt.config.gradle
Normal file
24
gradlescripts/detekt.config.gradle
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
detekt {
|
||||
toolVersion = "$detekt_version"
|
||||
|
||||
source = files(
|
||||
"$projectDir/app/src/main/java",
|
||||
"$projectDir/core/src/main/java",
|
||||
"$projectDir/mockserver/src/main/java",
|
||||
"$projectDir/model/src/main/java",
|
||||
"$projectDir/network/src/main/java"
|
||||
)
|
||||
config = files("$projectDir/detekt/detekt.yml")
|
||||
baseline = file("$projectDir/detekt/baseline.xml")
|
||||
reports {
|
||||
txt {
|
||||
enabled = true
|
||||
destination = file("build/reports/detekt.txt")
|
||||
}
|
||||
html {
|
||||
enabled = true
|
||||
destination = file("build/reports/detekt.html")
|
||||
}
|
||||
}
|
||||
}
|
||||
3
gradlescripts/ktlint.gradle
Normal file
3
gradlescripts/ktlint.gradle
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
subprojects {
|
||||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||
}
|
||||
34
gradlescripts/lint.gradle
Normal file
34
gradlescripts/lint.gradle
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
subprojects { module ->
|
||||
plugins.withType(JavaPlugin).whenPluginAdded {
|
||||
configure(module) {
|
||||
apply plugin: "com.android.lint"
|
||||
lintOptions {
|
||||
warningsAsErrors true
|
||||
abortOnError true
|
||||
textReport true
|
||||
ignore 'Overdraw'
|
||||
textOutput "stdout"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugins.withId("com.android.application") {
|
||||
module.android.lintOptions {
|
||||
warningsAsErrors true
|
||||
abortOnError true
|
||||
textReport true
|
||||
ignore 'Overdraw'
|
||||
textOutput "stdout"
|
||||
}
|
||||
}
|
||||
|
||||
plugins.withId("com.android.library") {
|
||||
module.android.lintOptions {
|
||||
warningsAsErrors true
|
||||
abortOnError true
|
||||
textReport true
|
||||
ignore 'Overdraw'
|
||||
textOutput "stdout"
|
||||
}
|
||||
}
|
||||
}
|
||||
44
gradlescripts/testoptions.gradle
Normal file
44
gradlescripts/testoptions.gradle
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
subprojects { module ->
|
||||
plugins.withType(JavaPlugin).whenPluginAdded {
|
||||
module.test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events 'started', 'passed', 'skipped', 'failed'
|
||||
exceptionFormat "full"
|
||||
showStandardStreams true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugins.withId("com.android.application") {
|
||||
module.android.testOptions.unitTests.all {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events 'started', 'passed', 'skipped', 'failed'
|
||||
exceptionFormat "full"
|
||||
showStandardStreams true
|
||||
}
|
||||
}
|
||||
module.android.testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugins.withId("com.android.library") {
|
||||
module.android.testOptions.unitTests.all {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events 'started', 'passed', 'skipped', 'failed'
|
||||
exceptionFormat "full"
|
||||
showStandardStreams true
|
||||
}
|
||||
}
|
||||
module.android.testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +1,28 @@
|
|||
project.ext {
|
||||
androidx_core_version = "1.3.2"
|
||||
androidx_appcompat_version = "1.2.0"
|
||||
androidx_material_version = "1.3.0"
|
||||
androidx_constraintlayout_version = "2.0.4"
|
||||
androidx_core_version = "1.6.0"
|
||||
androidx_appcompat_version = "1.3.1"
|
||||
androidx_material_version = "1.4.0"
|
||||
androidx_constraintlayout_version = "2.1.0"
|
||||
androidx_livedata_version = "2.3.1"
|
||||
androidx_swiperefreshlayout_version = "1.1.0"
|
||||
androidx_room_version = "2.2.6"
|
||||
androidx_room_version = "2.3.0"
|
||||
|
||||
coroutines_version = "1.4.3"
|
||||
koin_version = "2.2.2"
|
||||
koin_version = "3.1.2"
|
||||
coil_version = "1.1.1"
|
||||
retrofit_version = "2.9.0"
|
||||
okhttp_version = "4.9.1"
|
||||
moshi_version = "1.11.0"
|
||||
moshi_version = "1.12.0"
|
||||
|
||||
testing_androidx_code_version = "1.3.0"
|
||||
testing_androidx_junit_version = "1.1.2"
|
||||
testing_androidx_code_version = "1.4.0"
|
||||
testing_androidx_junit_version = "1.1.3"
|
||||
testing_androidx_arch_core_version = "2.1.0"
|
||||
testing_livedata_version = "1.1.2"
|
||||
testing_livedata_version = "1.2.0"
|
||||
testing_kotlin_mockito_version = "3.1.0"
|
||||
testing_junit5_version = "5.7.0"
|
||||
testing_json_assert_version = "1.5.0"
|
||||
testing_junit4_version = "4.13.2"
|
||||
testing_robolectric_version = "4.5.1"
|
||||
testing_espresso_version = "3.3.0"
|
||||
testing_robolectric_version = "4.6.1"
|
||||
testing_espresso_version = "3.4.0"
|
||||
testing_okhttp3_idling_resource_version = "1.0.0"
|
||||
}
|
||||
|
|
@ -9,8 +9,6 @@ java {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
api "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
|
||||
api project(":model")
|
||||
|
||||
api "com.squareup.okhttp3:mockwebserver:$okhttp_version"
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class MockServerScenarioSetup internal constructor(
|
|||
|
||||
companion object {
|
||||
const val PORT: Int = 7335
|
||||
val HTTP_BASE_URL get() = "http://${InetAddress.getLocalHost().hostName}"
|
||||
val HTTP_BASE_URL get() = "http://${InetAddress.getLocalHost().canonicalHostName}"
|
||||
val HTTPS_BASE_URL get() = "https://localhost"
|
||||
|
||||
private fun MockWebServer.useHttps(): HandshakeCertificates {
|
||||
|
|
|
|||
|
|
@ -6,14 +6,4 @@ plugins {
|
|||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = ["-Xinline-classes"]
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
package org.fnives.test.showcase.model.content
|
||||
|
||||
inline class ContentId(val id: String)
|
||||
@JvmInline
|
||||
value class ContentId(val id: String)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
package org.fnives.test.showcase.model.content
|
||||
|
||||
inline class ImageUrl(val url: String)
|
||||
@JvmInline
|
||||
value class ImageUrl(val url: String)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
package org.fnives.test.showcase.model.network
|
||||
|
||||
inline class BaseUrl(val baseUrl: String)
|
||||
@JvmInline
|
||||
value class BaseUrl(val baseUrl: String)
|
||||
|
|
|
|||
|
|
@ -9,17 +9,7 @@ java {
|
|||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events 'started', 'passed', 'skipped', 'failed'
|
||||
exceptionFormat "full"
|
||||
showStandardStreams true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
||||
|
||||
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
||||
|
|
@ -27,15 +17,15 @@ dependencies {
|
|||
implementation "com.squareup.moshi:moshi:$moshi_version"
|
||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version"
|
||||
api "org.koin:koin-core:$koin_version"
|
||||
api "io.insert-koin:koin-core:$koin_version"
|
||||
|
||||
api project(":model")
|
||||
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
|
||||
testImplementation "org.mockito.kotlin:mockito-kotlin:$testing_kotlin_mockito_version"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine:5.7.0"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version"
|
||||
testImplementation project(':mockserver')
|
||||
testImplementation "org.koin:koin-test:$koin_version"
|
||||
testImplementation "io.insert-koin:koin-test-junit5:$koin_version"
|
||||
testImplementation "org.skyscreamer:jsonassert:$testing_json_assert_version"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version"
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ private fun contentModule() = module {
|
|||
|
||||
private fun sessionlessNetworkingModule(baseUrl: BaseUrl, enableLogging: Boolean) = module {
|
||||
factory { MoshiConverterFactory.create() }
|
||||
single(qualifier = sessionless, override = true) {
|
||||
single(qualifier = sessionless) {
|
||||
OkHttpClient.Builder()
|
||||
.addInterceptor(PlatformInterceptor())
|
||||
.setupLogging(enableLogging)
|
||||
|
|
|
|||
|
|
@ -14,11 +14,15 @@ internal class SessionAuthenticator(
|
|||
private val networkSessionExpirationListener: NetworkSessionExpirationListener
|
||||
) : Authenticator {
|
||||
|
||||
@Suppress("SwallowedException")
|
||||
override fun authenticate(route: Route?, response: Response): Request? {
|
||||
if (authenticationHeaderUtils.hasToken(response.request)) {
|
||||
return runBlocking {
|
||||
try {
|
||||
val newSession = loginRemoteSource.refresh(networkSessionLocalStorage.session?.refreshToken.orEmpty())
|
||||
val refreshToken = networkSessionLocalStorage.session
|
||||
?.refreshToken
|
||||
.orEmpty()
|
||||
val newSession = loginRemoteSource.refresh(refreshToken)
|
||||
networkSessionLocalStorage.session = newSession
|
||||
return@runBlocking authenticationHeaderUtils.attachToken(response.request)
|
||||
} catch (throwable: Throwable) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import java.io.EOFException
|
|||
|
||||
internal object ExceptionWrapper {
|
||||
|
||||
@Suppress("RethrowCaughtException")
|
||||
@Throws(NetworkException::class, ParsingException::class)
|
||||
suspend fun <T> wrap(request: suspend () -> T) = try {
|
||||
request()
|
||||
|
|
|
|||
|
|
@ -1,79 +1,59 @@
|
|||
package org.fnives.test.showcase.network.auth
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.fnives.test.showcase.model.auth.LoginCredentials
|
||||
import org.fnives.test.showcase.model.network.BaseUrl
|
||||
import org.fnives.test.showcase.model.session.Session
|
||||
import org.fnives.test.showcase.network.auth.model.LoginStatusResponses
|
||||
import org.fnives.test.showcase.network.di.createNetworkModules
|
||||
import org.fnives.test.showcase.network.session.NetworkSessionLocalStorage
|
||||
import org.fnives.test.showcase.network.shared.exceptions.NetworkException
|
||||
import org.fnives.test.showcase.network.shared.exceptions.ParsingException
|
||||
import org.junit.jupiter.api.*
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.context.stopKoin
|
||||
import org.koin.test.KoinTest
|
||||
import org.koin.test.inject
|
||||
import org.mockito.kotlin.mock
|
||||
import org.skyscreamer.jsonassert.JSONAssert
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
|
||||
@Disabled("CodeKata")
|
||||
class CodeKataLoginRemoteSourceTest {
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN successful response WHEN request is fired THEN login status success is returned")
|
||||
@Test
|
||||
fun successResponseIsParsedProperly() = runBlocking {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN successful response WHEN request is fired THEN the request is setup properly")
|
||||
@Test
|
||||
fun requestProperlySetup() = runBlocking {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN bad request response WHEN request is fired THEN login status invalid credentials is returned")
|
||||
@Test
|
||||
fun badRequestMeansInvalidCredentials() = runBlocking {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN_internal_error_response_WHEN_request_is_fired_THEN_network_exception_is_thrown")
|
||||
@Test
|
||||
fun genericErrorMeansNetworkError() {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN invalid json response WHEN request is fired THEN network exception is thrown")
|
||||
@Test
|
||||
fun invalidJsonMeansParsingException() {
|
||||
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN malformed json response WHEN request is fired THEN network exception is thrown")
|
||||
@Test
|
||||
fun malformedJsonMeansParsingException() {
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal fun Any.readResourceFile(filePath: String): String = try {
|
||||
BufferedReader(InputStreamReader(this.javaClass.classLoader.getResourceAsStream(filePath)!!))
|
||||
.readLines().joinToString("\n")
|
||||
.readLines().joinToString("\n")
|
||||
} catch (nullPointerException: NullPointerException) {
|
||||
throw IllegalArgumentException("$filePath file not found!", nullPointerException)
|
||||
}
|
||||
|
|
@ -86,6 +66,5 @@ class CodeKataLoginRemoteSourceTest {
|
|||
} while (true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,11 @@ import org.fnives.test.showcase.network.session.NetworkSessionLocalStorage
|
|||
import org.fnives.test.showcase.network.shared.MockServerScenarioSetupExtensions
|
||||
import org.fnives.test.showcase.network.shared.exceptions.NetworkException
|
||||
import org.fnives.test.showcase.network.shared.exceptions.ParsingException
|
||||
import org.junit.jupiter.api.*
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.RegisterExtension
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.context.stopKoin
|
||||
|
|
@ -26,6 +30,7 @@ import org.skyscreamer.jsonassert.JSONCompareMode
|
|||
class LoginRemoteSourceTest : KoinTest {
|
||||
|
||||
private val sut by inject<LoginRemoteSource>()
|
||||
|
||||
@RegisterExtension
|
||||
@JvmField
|
||||
val mockServerScenarioSetupExtensions = MockServerScenarioSetupExtensions()
|
||||
|
|
@ -76,7 +81,11 @@ class LoginRemoteSourceTest : KoinTest {
|
|||
Assertions.assertEquals(null, request.getHeader("Authorization"))
|
||||
Assertions.assertEquals("/login", request.path)
|
||||
val loginRequest = createExpectedLoginRequestJson("a", "b")
|
||||
JSONAssert.assertEquals(loginRequest, request.body.readUtf8(), JSONCompareMode.NON_EXTENSIBLE)
|
||||
JSONAssert.assertEquals(
|
||||
loginRequest,
|
||||
request.body.readUtf8(),
|
||||
JSONCompareMode.NON_EXTENSIBLE
|
||||
)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN bad request response WHEN request is fired THEN login status invalid credentials is returned")
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
package org.fnives.test.showcase.network.content
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.fnives.test.showcase.model.network.BaseUrl
|
||||
import org.fnives.test.showcase.model.session.Session
|
||||
import org.fnives.test.showcase.network.auth.CodeKataLoginRemoteSourceTest.Companion.readResourceFile
|
||||
import org.fnives.test.showcase.network.di.createNetworkModules
|
||||
import org.fnives.test.showcase.network.session.NetworkSessionExpirationListener
|
||||
import org.fnives.test.showcase.network.session.NetworkSessionLocalStorage
|
||||
import org.fnives.test.showcase.network.shared.exceptions.NetworkException
|
||||
import org.junit.jupiter.api.*
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.context.stopKoin
|
||||
import org.koin.test.KoinTest
|
||||
import org.koin.test.inject
|
||||
import org.mockito.kotlin.*
|
||||
import org.mockito.kotlin.mock
|
||||
|
||||
@Disabled("CodeKata")
|
||||
class CodeKataSessionExpirationTest : KoinTest {
|
||||
|
||||
private val sut by inject<ContentRemoteSource>()
|
||||
|
|
@ -32,12 +33,12 @@ class CodeKataSessionExpirationTest : KoinTest {
|
|||
mockNetworkSessionExpirationListener = mock()
|
||||
startKoin {
|
||||
modules(
|
||||
createNetworkModules(
|
||||
baseUrl = BaseUrl(mockWebServer.url("mockserver/").toString()),
|
||||
enableLogging = true,
|
||||
networkSessionExpirationListenerProvider = { mockNetworkSessionExpirationListener },
|
||||
networkSessionLocalStorageProvider = { mockNetworkSessionLocalStorage }
|
||||
).toList()
|
||||
createNetworkModules(
|
||||
baseUrl = BaseUrl(mockWebServer.url("mockserver/").toString()),
|
||||
enableLogging = true,
|
||||
networkSessionExpirationListenerProvider = { mockNetworkSessionExpirationListener },
|
||||
networkSessionLocalStorageProvider = { mockNetworkSessionLocalStorage }
|
||||
).toList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -56,6 +57,5 @@ class CodeKataSessionExpirationTest : KoinTest {
|
|||
@DisplayName("GIVEN 401 THEN failing refresh WHEN content requested THE error is returned and callback is Called")
|
||||
@Test
|
||||
fun failingRefreshResultsInSessionExpiration() = runBlocking {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ import org.fnives.test.showcase.network.session.NetworkSessionExpirationListener
|
|||
import org.fnives.test.showcase.network.session.NetworkSessionLocalStorage
|
||||
import org.fnives.test.showcase.network.shared.MockServerScenarioSetupExtensions
|
||||
import org.fnives.test.showcase.network.shared.exceptions.NetworkException
|
||||
import org.junit.jupiter.api.*
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.RegisterExtension
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.context.stopKoin
|
||||
|
|
@ -64,35 +68,39 @@ class SessionExpirationTest : KoinTest {
|
|||
@DisplayName("GIVEN_401_THEN_refresh_token_ok_response_WHEN_content_requested_THE_tokens_are_refreshed_and_request_retried_with_new_tokens")
|
||||
@Test
|
||||
fun successRefreshResultsInRequestRetry() = runBlocking {
|
||||
var sessionToReturnByMock: Session? = ContentData.loginSuccessResponse
|
||||
mockServerScenarioSetup.setScenario(
|
||||
ContentScenario.Unauthorized(false)
|
||||
.then(ContentScenario.Success(true)),
|
||||
false
|
||||
)
|
||||
mockServerScenarioSetup.setScenario(RefreshTokenScenario.Success, false)
|
||||
whenever(mockNetworkSessionLocalStorage.session).doAnswer { sessionToReturnByMock }
|
||||
doAnswer { sessionToReturnByMock = it.arguments[0] as Session? }
|
||||
.whenever(mockNetworkSessionLocalStorage).session = anyOrNull()
|
||||
var sessionToReturnByMock: Session? = ContentData.loginSuccessResponse
|
||||
mockServerScenarioSetup.setScenario(
|
||||
ContentScenario.Unauthorized(false)
|
||||
.then(ContentScenario.Success(true)),
|
||||
false
|
||||
)
|
||||
mockServerScenarioSetup.setScenario(RefreshTokenScenario.Success, false)
|
||||
whenever(mockNetworkSessionLocalStorage.session).doAnswer { sessionToReturnByMock }
|
||||
doAnswer { sessionToReturnByMock = it.arguments[0] as Session? }
|
||||
.whenever(mockNetworkSessionLocalStorage).session = anyOrNull()
|
||||
|
||||
sut.get()
|
||||
sut.get()
|
||||
|
||||
mockServerScenarioSetup.takeRequest()
|
||||
val refreshRequest = mockServerScenarioSetup.takeRequest()
|
||||
val retryAfterTokenRefreshRequest = mockServerScenarioSetup.takeRequest()
|
||||
mockServerScenarioSetup.takeRequest()
|
||||
val refreshRequest = mockServerScenarioSetup.takeRequest()
|
||||
val retryAfterTokenRefreshRequest = mockServerScenarioSetup.takeRequest()
|
||||
|
||||
Assertions.assertEquals("PUT", refreshRequest.method)
|
||||
Assertions.assertEquals("/login/${ContentData.loginSuccessResponse.refreshToken}", refreshRequest.path)
|
||||
Assertions.assertEquals(null, refreshRequest.getHeader("Authorization"))
|
||||
Assertions.assertEquals("Android", refreshRequest.getHeader("Platform"))
|
||||
Assertions.assertEquals("", refreshRequest.body.readUtf8())
|
||||
Assertions.assertEquals(
|
||||
ContentData.refreshSuccessResponse.accessToken,
|
||||
retryAfterTokenRefreshRequest.getHeader("Authorization")
|
||||
)
|
||||
verify(mockNetworkSessionLocalStorage, times(1)).session = ContentData.refreshSuccessResponse
|
||||
verifyZeroInteractions(mockNetworkSessionExpirationListener)
|
||||
}
|
||||
Assertions.assertEquals("PUT", refreshRequest.method)
|
||||
Assertions.assertEquals(
|
||||
"/login/${ContentData.loginSuccessResponse.refreshToken}",
|
||||
refreshRequest.path
|
||||
)
|
||||
Assertions.assertEquals(null, refreshRequest.getHeader("Authorization"))
|
||||
Assertions.assertEquals("Android", refreshRequest.getHeader("Platform"))
|
||||
Assertions.assertEquals("", refreshRequest.body.readUtf8())
|
||||
Assertions.assertEquals(
|
||||
ContentData.refreshSuccessResponse.accessToken,
|
||||
retryAfterTokenRefreshRequest.getHeader("Authorization")
|
||||
)
|
||||
verify(mockNetworkSessionLocalStorage, times(1)).session =
|
||||
ContentData.refreshSuccessResponse
|
||||
verifyZeroInteractions(mockNetworkSessionExpirationListener)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN 401 THEN failing refresh WHEN content requested THE error is returned and callback is Called")
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue