Skip to content

Commit

Permalink
Merge Feature/43 feature local test services (#49)
Browse files Browse the repository at this point in the history
* Move core-data files into package directory

* replace placeholder names

* Add unit test steps to android ci workflows

* Create separate api workflows

* remove gcloud artifactory note for main-PRs

* Fix main api release workflow referencing android build

* Add artifactory note

* Create script file to build and start api in background before running all client tests

* fix duplicate package path

* Add ktor-server-host-test dependency for api testing

* Move ServerTest to root package directory

* Remove empty server test

* Add ktor client mock

* Add client mock to :core:remote

* Move AdMobController in wrong package

* Create BuildInfo :core:common object

* Create app lifecycle controller

* Add trivial docs

* provide binding for default app lifecycle controller in data module

* Add Create lifecycle event and create implement default

* remove app from lifecycle controller name

* apps in general are just created and destroyed; windows/activities have more complex lifecycles

* Update lifecycle docs

* Experiment with multiplatform component hierarchy

* remove app from callback names

* convert function to value

* Add kotlinx-io dependencies

* Add kotlinx-io dependencies to :core:local api

* Add :core:common to :app-desktop dependencies

* remove unused appModule import

* provide test directory if build is in debug mode

* Add coreLocalModule doc

* Inject Path instances in auth/profile services

* move test service setup to ProfileModule.kt

* remove function injection for modules

* Koin allows overriding modules by default; this method is more cumbersome

* fix missing local module

* replace single/factories with more concise singleOf/factoryOf

* rename SimpleAuthService for consistency

* remove unused function imports

* remove module info files

* fix singleton interactors

* add todo note

* Add LifecycleController docs

* Add koin testing to :core:data/test sources

* Move koin component notes to package readme

* Extract component classes to dedicated files

* Create ComponentsTest file

* Write KoinComponentsTest with expected behavior

* Modify components to pass test

* add containing scope assertions

* Remove hilt singleton concept from koin components

* Move dependency notes to :core:common readme

* Move InjectionController into di pacakge

* Move SingletonInjectionControllerTest

* Remove ModuleInfo file

* Fix InjectionController package

* Move other :core:common files into package directory

* Add Staging to BuildInfo.BuildType

* Move BuildType to dedicated file

* Replace environment with BuildType

* Restore Environment separate from build type

* Re-replace buildtype with environment

* Update environment import

* remove Staging from build type

* remove unused import

* Move Hilt/proposed-Koin components comparison to di package

* Remove module info file

* remove build section from readme

* Move di components to :core:common module

* change backing stateflow to sharedflow

* make DefaultLifecycleController blocking on events

* Add setup comment

* Add koin component extension

* replace createScope with new extension

* move koin components test to :core:common test source

* Add clarification to nestedScope

* remove unused koin instance

* add dependency docs

* Separate nesting stages for finer tests

* remove closed scope test

* Expand component-focused tests

* Replace external utility function with AppComponent

* remember dependencies objects

* close scope on window exit

* remove unused function

* provide app dependencies through Koin

* Upgrade koin

* Add koin-compose-* dependencies

* start koin once outside application

* Revert "Add koin-compose-* dependencies"

This reverts commit dd28924.

* Revert "Upgrade koin"

This reverts commit 7779db2.

* remove print statement

* Add lifecycleController and trigger events

* remove unused import

* Verify scopes behave as expected

* Fix view scoped within view model

* add experimental component hierarchy

* Fix test class requirements

* rename LifecycleController.lifecycleState to events

* clean imports

* configure Json for entire app

* Inject file encoding/decoding dependencies

* read file during AuthService initialization

* remove module parameters

* remove coreModule module parameters

* remove featureModule module parameters

* Move TestProfileService to dedicated file FakeProfileService

* Make entities serializable

* Extract test auth service to dedicated file

* rename TestAuthService.kt to FakeAuthService

* Install coroutine exception handler in data coroutine scope

* Add name docs

* Move Name to package directory

* Add units readme for future planning

* Add planned compound units

* Replace Company with more general business

* Add fake auth/profile service tests

* Remove references to project Path definition

* trigger profile service saving; assert contents

* initialize profileFile Path

* Write simple file writing test for FakeAuthService

* Make UserEntry serializable

* Add file println statements and clean up file after test

* wait for tasks to complete before assertions

* rename and expose readFile as loadFile

* Write fake auth service loading/login case

* fix assertions

* separate login from profile koin dependencies

* Add file information logs

* move lifecycle controller out of windowcomponent scope

* don't terminate program after application scope ends

* ignore testing directory

* save and restore profiles on program end/start

* Fix attempting to store Uuids as keys

* Move path extension function to :core:data

* Write test for loading profile data

* seal profile interface and make serializable

* remove outdated test expectations

* wait for work to finish

* Make business hierarchy serializable

* Make phone number serializable

* make disabilities serializable

* add profiles test string

* restrict success result to non-null profiles

* Separate different actions/expectations

* restore expectation that a user can be authorized but have no profile

* don't rewrite loading state with new user

* allow null result values for ProfileService.getProfile

* don't return failure for absent profile
  • Loading branch information
TSampley authored Sep 19, 2024
1 parent 59b5945 commit a5f4f0c
Show file tree
Hide file tree
Showing 92 changed files with 1,603 additions and 531 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/main-api-release-flow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: "CD / Android Release Builds"
on:
push:
branches:
- main
paths: # Only run checks on changes api to code or api dependencies
- "api/**/*"
- "core/entities/**/*"

jobs:
build:
name: "Setup and Build Project"
runs-on: [ubuntu-22.04]
if: github.event.pull_request.draft == false # ignore draft pull requests
# env:
#
# outputs:
#
steps:
- name: "Checkout Project"
uses: actions/checkout@v4
- name: "Setup Java 17"
uses: actions/setup-java@v4
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: "Setup Gradle"
uses: gradle/actions/setup-gradle@v3

- name: "Build: Assemble and Test API"
run: |
./gradlew :api:build
# - run: ./gradlew generate coverage report, upload test/coverage reports

- name: "Bundle: Generate Fat-JAR"
run: ./gradlew :api:buildFatJar

# - name: "Upload: Send Release to GCloud Artifact Repository"
50 changes: 50 additions & 0 deletions .github/workflows/main-pr-api-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: "CI / API Builds"
#
on:
pull_request:
types:
- opened # initially opened
- reopened # closed then opened again
- synchronize # any changes pushed
branches:
- main
paths: # Only run checks on changes api to code or api dependencies
- "api/**/*"
- "core/entities/**/*"

# TODO: Include version input

jobs:
build:
name: "Setup and Build Project"
runs-on: [ubuntu-22.04]
if: github.event.pull_request.draft == false # ignore draft pull requests
# env:
#
# outputs:
#
steps:
- name: "Checkout Project"
uses: actions/checkout@v4
- name: "Setup Java 17"
uses: actions/setup-java@v4
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: "Setup Gradle"
uses: gradle/actions/setup-gradle@v3

- name: "Build: Assemble and Test API"
run: |
./gradlew :api:build
# - run: ./gradlew generate coverage report, upload test/coverage reports

- name: "Bundle: Generate Fat-JAR"
run: ./gradlew :api:buildFatJar

- name: "Upload: Send JARs to GitHub"
uses: actions/upload-artifact@v4
with:
name: app-android-debug
path: api/build/libs/XYZ-API-0.1.0.jar
8 changes: 7 additions & 1 deletion .github/workflows/main-pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,14 @@ jobs:
- name: "Setup Gradle"
uses: gradle/actions/setup-gradle@v3

- name: "Test: Run Unit Tests"
run: |
./gradlew jvmTest
- name: "Build: Assemble and Test Debug Binary"
run: ./gradlew :app-android:buildDebug
run: |
./gradlew :app-android:buildDebug
# ./gradlew connectedAndroidTest # requires emulator or device

# - run: ./gradlew generate coverage report, upload test/coverage reports

Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/main-release-flow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ jobs:
uses: gradle/actions/setup-gradle@v3

- name: "Build: Assemble and Test entire project"
run: ./gradlew :app-android:buildRelease
run: |
./gradlew :app-android:buildRelease
./gradlew testReleaseUnitTest
# - run: ./gradlew generate coverage report, upload test/coverage reports

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ xcuserdata
/app-android/debug/app-android-debug.aab
/app-android/release/app-android-release.aab
/xyz-debug-*.json

## Local Test Data
/testing
5 changes: 3 additions & 2 deletions api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,17 @@ kotlin {

testImplementation(libs.kotlin.test)
testImplementation(libs.kotlinx.coroutinesTest)
testImplementation(libs.ktor.server.test.host)
}
}

application {
mainClass = "org.pointyware.replace-me.api.ServerKt"
mainClass = "org.pointyware.xyz.api.ServerKt"
}

ktor {
fatJar {
archiveFileName = "replace-me-API-${version}.jar"
archiveFileName = "XYZ-API-${version}.jar"
}
}

Expand Down
35 changes: 0 additions & 35 deletions api/src/test/kotlin/ServerTest.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.remember
import org.koin.mp.KoinPlatform.getKoin
import org.pointyware.xyz.shared.XyzApp
import org.pointyware.xyz.shared.di.getDependencies
import org.pointyware.xyz.shared.di.AppDependencies

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val koin = getKoin()
val appDependencies = koin.get<AppDependencies>()

setContent {
val appDependencies = remember { getDependencies() }
XyzApp(
dependencies = appDependencies,
isDarkTheme = isSystemInDarkTheme()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license.
*/

package org.pointyware.painteddogs.android.ads
package org.pointyware.xyz.android.ads

import android.content.Context
import com.google.android.gms.ads.MobileAds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
package org.pointyware.xyz.android.di

import org.koin.dsl.module
import org.pointyware.painteddogs.android.ads.AdMobController
import org.pointyware.xyz.android.ads.AdMobController
import org.pointyware.painteddogs.android.di.AndroidLoadingViewResources
import org.pointyware.painteddogs.android.di.AndroidResources
import org.pointyware.xyz.core.ui.ads.AdsController
Expand Down
1 change: 1 addition & 0 deletions app-desktop/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ java {
}

dependencies {
implementation(projects.core.common)
implementation(projects.core.data)
implementation(projects.core.entities)
implementation(projects.core.interactors)
Expand Down
65 changes: 44 additions & 21 deletions app-desktop/src/main/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,63 @@
package org.pointyware.xyz.desktop

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.remember
import androidx.compose.ui.window.Tray
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import org.jetbrains.compose.resources.painterResource
import org.koin.core.component.get
import org.pointyware.xyz.core.common.di.ApplicationComponent
import org.pointyware.xyz.core.data.LifecycleController
import org.pointyware.xyz.desktop.di.desktopModule
import org.pointyware.xyz.shared.XyzApp
import org.pointyware.xyz.shared.di.getDependencies
import org.pointyware.xyz.shared.di.AppDependencies
import org.pointyware.xyz.shared.di.setupKoin

fun main() = application {
/**
* Main entry point for the desktop application.
*/
fun main() {

// Configure Koin Dependency Injection (actually Service Locator)
setupKoin(platformModule = desktopModule())

val appDependencies = remember { getDependencies() }

val state = rememberWindowState()
Window(
title = "My Application",
state = state,
onCloseRequest = this::exitApplication
) {
XyzApp(
dependencies = appDependencies,
isDarkTheme = isSystemInDarkTheme()
)
}
// Get the dependencies
val appComponent = ApplicationComponent()
val appDependencies = appComponent.get<AppDependencies>()

val lifecycleController = appComponent.scope.get<LifecycleController>()
lifecycleController.onStart()
lifecycleController.onResume() // TODO: observe when window is shown/navigated to/focused

application(exitProcessOnExit = false) {

val state = rememberWindowState()
// TODO: observe window state changes for configuration scope

Tray(
icon = painterResource(Res.drawable.tray_icon),
menu = {
Menu("File") {
Window(
title = "My Application",
state = state,
onCloseRequest = {
appComponent.finish()
exitApplication()
}
) {
XyzApp(
dependencies = appDependencies,
isDarkTheme = isSystemInDarkTheme()
)
}
)

Tray(
icon = painterResource(Res.drawable.tray_icon),
menu = {
Menu("File") {
}
}
)
}

lifecycleController.onPause() // TODO: observe when window is hidden/navigated away from/unfocused
lifecycleController.onStop()
}
1 change: 0 additions & 1 deletion app-desktop/src/main/kotlin/di/Module.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package org.pointyware.xyz.desktop.di
import org.koin.dsl.module
import org.pointyware.xyz.core.ui.components.LoadingViewResources
import org.pointyware.xyz.core.ui.design.Resources
import org.pointyware.xyz.shared.di.appModule
import org.pointyware.xyz.shared.entities.SharedFileResources
import org.pointyware.xyz.shared.entities.SharedStringResources
import org.pointyware.xyz.shared.ui.SharedDrawableResources
Expand Down
1 change: 1 addition & 0 deletions app-shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ android {
compileSdk = 34
defaultConfig {
minSdk = 21
targetSdk = 34
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

Expand Down
15 changes: 15 additions & 0 deletions app-shared/src/androidDebug/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:allowBackup="false"
android:supportsRtl="true">
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-7815756395963999~1126625934" />
</application>
</manifest>
2 changes: 0 additions & 2 deletions app-shared/src/commonMain/kotlin/di/AppDependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ interface AppDependencies {
fun getRideDependencies(): RideDependencies
}

fun getDependencies(): AppDependencies = KoinAppDependencies()

class KoinAppDependencies: AppDependencies, KoinComponent {

override fun getNavigationDependencies(): NavigationDependencies = get()
Expand Down
Loading

0 comments on commit a5f4f0c

Please sign in to comment.