- new weather api added

This commit is contained in:
2024-09-30 10:02:14 +01:00
parent 0aff414b1c
commit 24da85d20d
28 changed files with 21502 additions and 121 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
package com.appttude.h_mal.atlas_weather.utils
const val baseUrl = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/"
enum class Stubs(
val id: String
) {
@@ -7,5 +8,6 @@ enum class Stubs(
Imperial("valid_response_imperial"),
WrongLocation("wrong_location_response"),
InvalidKey("invalid_api_key_response"),
Sydney("valid_response_metric_sydney")
Sydney("valid_response_metric_sydney"),
New("new_response")
}

View File

@@ -24,10 +24,7 @@ class SettingsScreen : BaseTestRobot() {
RecyclerViewActions.actionOnItem<ViewHolder>(
ViewMatchers.hasDescendant(withText(R.string.weather_units)),
click()))
val label = when (unitType) {
UnitType.METRIC -> "Metric"
UnitType.IMPERIAL -> "Imperial"
}
val label = unitType.getLabel()
onView(withText(label))
.inRoot(isDialog())

View File

@@ -7,7 +7,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
import com.appttude.h_mal.atlas_weather.data.location.MockLocationProvider
import com.appttude.h_mal.atlas_weather.data.network.NetworkModule
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
import com.appttude.h_mal.atlas_weather.data.network.NewWeatherApi
import com.appttude.h_mal.atlas_weather.data.network.interceptors.MockingNetworkInterceptor
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
@@ -32,13 +32,13 @@ class TestAppClass : AppClass() {
IdlingRegistry.getInstance().register(idlingResources)
}
override fun createNetworkModule(): WeatherApi {
return NetworkModule().invoke<WeatherApi>(
override fun createNetworkModule(): NewWeatherApi {
return NetworkModule().invoke<NewWeatherApi>(
mockingNetworkInterceptor,
NetworkConnectionInterceptor(this),
QueryParamsInterceptor(),
loggingInterceptor
) as WeatherApi
) as NewWeatherApi
}
override fun createLocationModule(): LocationProvider {

View File

@@ -13,6 +13,7 @@ import com.appttude.h_mal.atlas_weather.BaseTestRobot
import com.appttude.h_mal.atlas_weather.R
import com.appttude.h_mal.atlas_weather.helpers.EspressoHelper.waitForView
import com.appttude.h_mal.atlas_weather.model.types.UnitType
import com.appttude.h_mal.atlas_weather.model.types.UnitType.Companion.getLabel
fun settingsScreen(func: SettingsScreen.() -> Unit) = SettingsScreen().apply { func() }
@@ -24,10 +25,7 @@ class SettingsScreen : BaseTestRobot() {
RecyclerViewActions.actionOnItem<ViewHolder>(
ViewMatchers.hasDescendant(withText(R.string.weather_units)),
click()))
val label = when (unitType) {
UnitType.METRIC -> "Metric"
UnitType.IMPERIAL -> "Imperial"
}
val label = unitType.getLabel()
onView(withText(label))
.inRoot(isDialog())

View File

@@ -5,17 +5,16 @@ import com.appttude.h_mal.atlas_weather.BaseTest
import com.appttude.h_mal.atlas_weather.model.types.UnitType
import com.appttude.h_mal.atlas_weather.ui.MainActivity
import com.appttude.h_mal.atlas_weather.utils.Stubs
import com.appttude.h_mal.atlas_weather.utils.baseUrl
import com.appttude.h_mal.monoWeather.robot.furtherInfoScreen
import com.appttude.h_mal.monoWeather.robot.settingsScreen
import com.appttude.h_mal.monoWeather.robot.weatherScreen
import okio.IOException
import org.junit.Test
import tools.fastlane.screengrab.Screengrab
class HomePageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
override fun beforeLaunch() {
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)
stubEndpoint(baseUrl, Stubs.New)
clearPrefs()
}

View File

@@ -3,7 +3,7 @@ package com.appttude.h_mal.atlas_weather.application
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
import com.appttude.h_mal.atlas_weather.data.network.NetworkModule
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
import com.appttude.h_mal.atlas_weather.data.network.NewWeatherApi
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
import com.appttude.h_mal.atlas_weather.data.network.networkUtils.loggingInterceptor
@@ -11,12 +11,12 @@ import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
open class AppClass : BaseAppClass() {
override fun createNetworkModule(): WeatherApi {
return NetworkModule().invoke<WeatherApi>(
override fun createNetworkModule(): NewWeatherApi {
return NetworkModule().invoke<NewWeatherApi>(
NetworkConnectionInterceptor(this),
QueryParamsInterceptor(),
loggingInterceptor
) as WeatherApi
) as NewWeatherApi
}
override fun createLocationModule(): LocationProvider = LocationProviderImpl(this)

View File

@@ -3,7 +3,7 @@ package com.appttude.h_mal.atlas_weather.application
import android.app.Application
import com.appttude.h_mal.atlas_weather.data.WeatherSource
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
import com.appttude.h_mal.atlas_weather.data.network.Api
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepositoryImpl
@@ -12,7 +12,6 @@ import com.appttude.h_mal.atlas_weather.helper.ServicesHelper
import com.google.gson.Gson
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware
import org.kodein.di.KodeinContainer
import org.kodein.di.android.x.androidXModule
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
@@ -41,7 +40,7 @@ abstract class BaseAppClass : Application(), KodeinAware {
bind() from singleton { WeatherSource(instance(), instance()) }
}
abstract fun createNetworkModule(): WeatherApi
abstract fun createNetworkModule(): Api
abstract fun createLocationModule(): LocationProvider
abstract fun createRoomDatabase(): AppDatabase

View File

@@ -7,6 +7,7 @@ import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
import com.appttude.h_mal.atlas_weather.model.types.LocationType
import com.appttude.h_mal.atlas_weather.model.types.UnitType
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
import com.appttude.h_mal.atlas_weather.utils.getSymbol
import java.io.IOException
class WeatherSource(
@@ -38,7 +39,7 @@ class WeatherSource(
// get data from database
val weatherEntity = repository.loadSingleCurrentWeatherFromRoom(CURRENT_LOCATION)
// check unit type - if same do nothing
val units = if (repository.getUnitType() == UnitType.METRIC) "°C" else "°F"
val units = repository.getUnitType().getSymbol()
if (weatherEntity.weather.temperatureUnit == units) return weatherEntity.weather
// load data for forced
return fetchWeather(
@@ -55,11 +56,13 @@ class WeatherSource(
// Get weather from api
val weather = repository
.getWeatherFromApi(latLon.first.toString(), latLon.second.toString())
val lat = weather.latitude ?: latLon.first
val long = weather.longitude ?: latLon.second
val currentLocation =
locationProvider.getLocationNameFromLatLong(weather.lat, weather.lon, locationType)
val unit = repository.getUnitType()
val fullWeather = FullWeather(weather).apply {
temperatureUnit = if (unit == UnitType.METRIC) "°C" else "°F"
locationProvider.getLocationNameFromLatLong(lat, long, locationType)
val unit = repository.getUnitType().getSymbol()
val fullWeather = weather.mapData().apply {
temperatureUnit = unit
locationString = currentLocation
}
val entityItem = EntityItem(locationName, fullWeather)

View File

@@ -1,6 +1,6 @@
package com.appttude.h_mal.atlas_weather.data.network
class NetworkModule : BaseNetworkModule() {
override fun baseUrl(): String = "https://api.openweathermap.org/data/2.5/"
override fun baseUrl(): String = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/"
}

View File

@@ -0,0 +1,22 @@
package com.appttude.h_mal.atlas_weather.data.network
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query
interface NewWeatherApi : Api {
// Todo: change the location
// Todo: add endpoint for lat/long
@GET("{location}")
suspend fun getFromApi(
@Query("contentType") exclude: String = "json",
@Query("unitGroup") units: String = "uk",
@Path("location") location: String
): Response<WeatherApiResponse>
}

View File

@@ -16,7 +16,7 @@ class QueryParamsInterceptor : Interceptor {
val original = chain.request()
val url = original.url.newBuilder()
.addQueryParameter("appid", id)
.addQueryParameter("key", id)
.build()
// Request customization: add request headers

View File

@@ -1,11 +1,13 @@
package com.appttude.h_mal.atlas_weather.data.network.networkUtils
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkInterceptor
import com.google.gson.GsonBuilder
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.lang.reflect.Modifier
import java.util.concurrent.TimeUnit
val loggingInterceptor = HttpLoggingInterceptor().apply {
@@ -34,6 +36,13 @@ fun buildOkHttpClient(
return builder.build()
}
fun createGsonConverterFactory(): GsonConverterFactory {
val gson = GsonBuilder()
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
.create()
return GsonConverterFactory.create(gson)
}
fun <T> createRetrofit(
baseUrl: String,
okHttpClient: OkHttpClient,

View File

@@ -1,13 +1,13 @@
package com.appttude.h_mal.atlas_weather.data.repository
import androidx.lifecycle.LiveData
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
import com.appttude.h_mal.atlas_weather.model.types.UnitType
interface Repository {
suspend fun getWeatherFromApi(lat: String, long: String): WeatherResponse
suspend fun getWeatherFromApi(lat: String, long: String): WeatherApiResponse
suspend fun saveCurrentWeatherToRoom(entityItem: EntityItem)
suspend fun saveWeatherListToRoom(list: List<EntityItem>)
fun loadRoomWeatherLiveData(): LiveData<List<EntityItem>>

View File

@@ -1,8 +1,8 @@
package com.appttude.h_mal.atlas_weather.data.repository
import com.appttude.h_mal.atlas_weather.data.network.NewWeatherApi
import com.appttude.h_mal.atlas_weather.data.network.ResponseUnwrap
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
import com.appttude.h_mal.atlas_weather.data.prefs.LOCATION_CONST
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
@@ -12,7 +12,7 @@ import com.appttude.h_mal.atlas_weather.utils.FALLBACK_TIME
class RepositoryImpl(
private val api: WeatherApi,
private val api: NewWeatherApi,
private val db: AppDatabase,
private val prefs: PreferenceProvider
) : Repository, ResponseUnwrap() {
@@ -20,8 +20,8 @@ class RepositoryImpl(
override suspend fun getWeatherFromApi(
lat: String,
long: String
): WeatherResponse {
return responseUnwrap { api.getFromApi(lat, long, units = prefs.getUnitsType().name.lowercase()) }
): WeatherApiResponse {
return responseUnwrap { api.getFromApi(location = lat + long) }
}
override suspend fun saveCurrentWeatherToRoom(entityItem: EntityItem) {

View File

@@ -11,14 +11,13 @@ import com.appttude.h_mal.atlas_weather.data.repository.Repository
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepository
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
import com.appttude.h_mal.atlas_weather.model.types.UnitType
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
import com.appttude.h_mal.atlas_weather.model.widget.InnerWidgetCellData
import com.appttude.h_mal.atlas_weather.model.widget.InnerWidgetData
import com.appttude.h_mal.atlas_weather.model.widget.WidgetData
import com.appttude.h_mal.atlas_weather.model.widget.WidgetError
import com.appttude.h_mal.atlas_weather.model.widget.WidgetState
import com.appttude.h_mal.atlas_weather.model.widget.WidgetWeatherCollection
import com.appttude.h_mal.atlas_weather.utils.getSymbol
import com.appttude.h_mal.atlas_weather.utils.toSmallDayName
import com.squareup.picasso.Picasso
import com.squareup.picasso.Target
@@ -45,9 +44,11 @@ class ServicesHelper(
// Get weather from api
val weather = repository
.getWeatherFromApi(latLong.first.toString(), latLong.second.toString())
val lat = weather.latitude ?: latLong.first
val long = weather.longitude ?: latLong.second
val currentLocation =
locationProvider.getLocationNameFromLatLong(weather.lat, weather.lon)
val fullWeather = FullWeather(weather).apply {
locationProvider.getLocationNameFromLatLong(lat, long)
val fullWeather = weather.mapData().apply {
temperatureUnit = "°C"
locationString = currentLocation
}
@@ -105,8 +106,11 @@ class ServicesHelper(
return WidgetState.HasError(error)
}
val lat = weather.latitude ?: latLong.first
val long = weather.longitude ?: latLong.second
val currentLocation = try {
locationProvider.getLocationNameFromLatLong(weather.lat, weather.lon)
locationProvider.getLocationNameFromLatLong(lat, long)
} catch (e: IOException) {
val data = getWidgetWeatherCollection()
data?.let {
@@ -120,8 +124,8 @@ class ServicesHelper(
return WidgetState.HasError(error)
}
val fullWeather = FullWeather(weather).apply {
temperatureUnit = if (repository.getUnitType() == UnitType.METRIC) "°C" else "°F"
val fullWeather = weather.mapData().apply {
temperatureUnit = repository.getUnitType().getSymbol()
locationString = currentLocation
}
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)

View File

@@ -0,0 +1,5 @@
package com.appttude.h_mal.atlas_weather.model
interface DataMapper <T: Any> {
fun mapData(): T
}

View File

@@ -0,0 +1,28 @@
package com.appttude.h_mal.atlas_weather.model
enum class IconMapper(val label: String) {
snow("13d"),
snow_showers_day("13d"),
snow_showers_night("13n"),
thunder_rain("11d"),
thunder_showers_day("11d"),
thunder_showers_night("11n"),
rain("10d"),
showers_day("10d"),
showers_night("10n"),
fog("50d"),
wind("50d"),
cloudy("04d"),
partly_cloudy_day("03d"),
partly_cloudy_night("03n"),
clear_day("01d"),
clear_night("01n");
companion object{
fun findIconCode(iconId: String?): String? {
val label = iconId?.replace("-", "_")
val enumName = IconMapper.entries.find { it.name == label }
return enumName?.label
}
}
}

View File

@@ -10,7 +10,7 @@ data class WeatherDisplay(
val averageTemp: Double?,
var unit: String?,
var location: String?,
val iconURL: String?,
var iconURL: String?,
val description: String?,
val hourly: List<Hour>?,
val forecast: List<Forecast>?,

View File

@@ -8,11 +8,15 @@ enum class UnitType {
companion object {
fun getByName(name: String?): UnitType? {
return values().firstOrNull {
return entries.firstOrNull {
it.name.lowercase(Locale.ROOT) == name?.lowercase(
Locale.ROOT
)
}
}
fun UnitType.getLabel() = name.lowercase().replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
}
}
}

View File

@@ -1,6 +1,8 @@
package com.appttude.h_mal.atlas_weather.model.weather
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.Current
import com.appttude.h_mal.atlas_weather.data.network.response.weather.CurrentConditions
import com.appttude.h_mal.atlas_weather.model.IconMapper
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
data class Current(
@@ -42,4 +44,24 @@ data class Current(
dailyItem.humidity,
dailyItem.windSpeed
)
constructor(currentConditions: CurrentConditions?): this(
dt = currentConditions?.datetimeEpoch,
sunrise = currentConditions?.sunriseEpoch,
sunset = currentConditions?.sunsetEpoch,
temp = currentConditions?.temp,
visibility = currentConditions?.visibility?.toInt(),
uvi = currentConditions?.uvindex?.toDouble(),
pressure = currentConditions?.pressure?.toInt(),
clouds = currentConditions?.cloudcover?.toInt(),
feelsLike = currentConditions?.feelslike,
windDeg = currentConditions?.winddir?.toInt(),
dewPoint = currentConditions?.dew,
icon = generateIconUrlString(IconMapper.findIconCode(currentConditions?.icon)),
description = currentConditions?.conditions,
main = currentConditions?.conditions,
id = currentConditions?.datetimeEpoch,
humidity = currentConditions?.humidity?.toInt(),
windSpeed = currentConditions?.windspeed
)
}

View File

@@ -1,6 +1,8 @@
package com.appttude.h_mal.atlas_weather.model.weather
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.DailyItem
import com.appttude.h_mal.atlas_weather.data.network.response.weather.Days
import com.appttude.h_mal.atlas_weather.model.IconMapper
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
@@ -50,5 +52,30 @@ data class DailyWeather(
dailyItem.rain
)
constructor(days: Days) : this(
days.datetimeEpoch,
days.sunriseEpoch,
days.sunsetEpoch,
days.tempmin,
days.tempmax,
days.temp?.toDouble(),
days.feelslike,
days.pressure?.toInt(),
days.humidity?.toInt(),
days.dew,
days.windspeed,
days.winddir?.toInt(),
generateIconUrlString(
IconMapper.findIconCode(days.icon)
),
days.description,
days.conditions,
days.datetimeEpoch,
days.cloudcover?.toInt(),
days.precipprob?.toDouble(),
days.uvindex?.toDouble(),
days.precip?.toDouble()
)
}

View File

@@ -2,8 +2,10 @@ package com.appttude.h_mal.atlas_weather.model.weather
import android.os.Parcel
import android.os.Parcelable
import com.appttude.h_mal.atlas_weather.model.IconMapper
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.Hour as ForecastHour
import com.appttude.h_mal.atlas_weather.data.network.response.weather.Hours as WeatherHour
data class Hour(
@@ -24,6 +26,12 @@ data class Hour(
hour.temp,
generateIconUrlString(hour.weather?.getOrNull(0)?.icon)
)
constructor(weatherHour: WeatherHour) : this(
weatherHour.datetimeEpoch,
weatherHour.temp,
generateIconUrlString(IconMapper.findIconCode(weatherHour.icon))
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeValue(dt)

View File

@@ -1,5 +1,7 @@
package com.appttude.h_mal.atlas_weather.utils
import com.appttude.h_mal.atlas_weather.model.types.UnitType
fun generateIconUrlString(icon: String?): String? {
return icon?.let {
@@ -9,4 +11,6 @@ fun generateIconUrlString(icon: String?): String? {
.append("@2x.png")
.toString()
}
}
}
fun UnitType.getSymbol(): String = if (this == UnitType.METRIC) "°C" else "°F"

View File

@@ -1,7 +1,7 @@
package com.appttude.h_mal.atlas_weather.data
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
@@ -18,6 +18,7 @@ import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import java.io.IOException
import kotlin.properties.Delegates
import kotlin.test.assertEquals
class WeatherSourceTest : BaseTest() {
@@ -31,41 +32,43 @@ class WeatherSourceTest : BaseTest() {
@MockK
lateinit var locationProvider: LocationProviderImpl
private lateinit var weatherResponse: WeatherResponse
private lateinit var weatherResponse: WeatherApiResponse
private var lat by Delegates.notNull<Double>()
private var long by Delegates.notNull<Double>()
private lateinit var latlon: Pair<Double, Double>
private lateinit var fullWeather: FullWeather
@Before
fun setUp() {
MockKAnnotations.init(this)
weatherResponse = getTestData("weather_sample.json", WeatherResponse::class.java)
weatherResponse = getTestData("new_response.json", WeatherApiResponse::class.java)
lat = weatherResponse.latitude!!
long = weatherResponse.longitude!!
latlon = Pair(lat, long)
fullWeather = weatherResponse.mapData().apply {
temperatureUnit = "°C"
locationString = CURRENT_LOCATION
}
}
@Test
fun fetchDataForSingleLocation_validLocation_validReturn() {
// Arrange
val latlon = Pair(
weatherResponse.lat,
weatherResponse.lon
)
val fullWeather = FullWeather(weatherResponse).apply {
temperatureUnit = "°C"
locationString = CURRENT_LOCATION
}
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
// Act
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
coEvery { locationProvider.getLatLongFromLocationName(CURRENT_LOCATION) } returns latlon
coEvery {
repository.getWeatherFromApi(
weatherResponse.lat.toString(),
weatherResponse.lon.toString()
lat.toString(),
long.toString()
)
}.returns(weatherResponse)
coEvery {
locationProvider.getLocationNameFromLatLong(
weatherResponse.lat,
weatherResponse.lon,
lat,
long,
LocationType.City
)
}.returns(CURRENT_LOCATION)
@@ -82,17 +85,13 @@ class WeatherSourceTest : BaseTest() {
@Test(expected = IOException::class)
fun fetchDataForSingleLocation_failedWeatherApi_invalidReturn() {
// Arrange
val latlon = Pair(
weatherResponse.lat,
weatherResponse.lon
)
// Act
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
coEvery {
repository.getWeatherFromApi(
weatherResponse.lat.toString(),
weatherResponse.lon.toString()
lat.toString(),
long.toString()
)
} throws IOException("Unable fetch data")
@@ -103,23 +102,19 @@ class WeatherSourceTest : BaseTest() {
@Test(expected = IOException::class)
fun fetchDataForSingleLocation_failedLocation_invalidReturn() {
// Arrange
val latlon = Pair(
weatherResponse.lat,
weatherResponse.lon
)
// Act
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
coEvery {
repository.getWeatherFromApi(
weatherResponse.lat.toString(),
weatherResponse.lon.toString()
lat.toString(),
long.toString()
)
} returns weatherResponse
coEvery {
locationProvider.getLocationNameFromLatLong(
weatherResponse.lat,
weatherResponse.lon
lat,
long
)
}.throws(IOException())
@@ -130,14 +125,6 @@ class WeatherSourceTest : BaseTest() {
@Test
fun searchAboveFallbackTime_validLocation_validReturn() {
// Arrange
val latlon = Pair(
weatherResponse.lat,
weatherResponse.lon
)
val fullWeather = FullWeather(weatherResponse).apply {
temperatureUnit = "°C"
locationString = CURRENT_LOCATION
}
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
// Act
@@ -153,14 +140,6 @@ class WeatherSourceTest : BaseTest() {
@Test
fun forceFetchDataForSingleLocation_validLocation_validReturn() {
// Arrange
val latlon = Pair(
weatherResponse.lat,
weatherResponse.lon
)
val fullWeather = FullWeather(weatherResponse).apply {
temperatureUnit = "°C"
locationString = CURRENT_LOCATION
}
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
// Act
@@ -169,14 +148,14 @@ class WeatherSourceTest : BaseTest() {
coEvery { locationProvider.getLatLongFromLocationName(CURRENT_LOCATION) } returns latlon
coEvery {
repository.getWeatherFromApi(
weatherResponse.lat.toString(),
weatherResponse.lon.toString()
weatherResponse.latitude.toString(),
weatherResponse.longitude.toString()
)
}.returns(weatherResponse)
coEvery {
locationProvider.getLocationNameFromLatLong(
weatherResponse.lat,
weatherResponse.lon,
lat,
long,
LocationType.City
)
}.returns(CURRENT_LOCATION)

View File

@@ -1,7 +1,7 @@
package com.appttude.h_mal.atlas_weather.data.repository
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
import com.appttude.h_mal.atlas_weather.data.network.NewWeatherApi
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
import com.appttude.h_mal.atlas_weather.data.prefs.LOCATION_CONST
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
@@ -29,8 +29,7 @@ class RepositoryImplTest : BaseTest() {
lateinit var repository: RepositoryImpl
@MockK
lateinit var api: WeatherApi
@MockK lateinit var api: NewWeatherApi
@MockK
lateinit var db: AppDatabase
@@ -86,29 +85,29 @@ class RepositoryImplTest : BaseTest() {
@Test
fun getWeatherFromApi_validLatLong_validSearch() {
//Arrange
val mockResponse = createSuccessfulRetrofitMock<WeatherResponse>()
val mockResponse = createSuccessfulRetrofitMock<WeatherApiResponse>()
//Act
//create a successful retrofit response
every { prefs.getUnitsType() } returns (UnitType.METRIC)
coEvery { api.getFromApi("", "") }.returns(mockResponse)
coEvery { api.getFromApi(location = "") }.returns(mockResponse)
// Assert
runBlocking {
val result = repository.getWeatherFromApi("", "")
assertIs<WeatherResponse>(result)
assertIs<WeatherApiResponse>(result)
}
}
@Test
fun getWeatherFromApi_validLatLong_invalidResponse() {
//Arrange
val mockResponse = createErrorRetrofitMock<WeatherResponse>()
val mockResponse = createErrorRetrofitMock<WeatherApiResponse>()
//Act
//create a successful retrofit response
every { prefs.getUnitsType() } returns (UnitType.METRIC)
coEvery { api.getFromApi(any(), any()) } returns (mockResponse)
coEvery { api.getFromApi(location = any()) } returns (mockResponse)
// Assert
val ioExceptionReturned = assertFailsWith<IOException> {

View File

@@ -1,7 +1,7 @@
package com.appttude.h_mal.atlas_weather.helper
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
import com.appttude.h_mal.atlas_weather.data.repository.Repository
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepository
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
@@ -17,6 +17,7 @@ import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import java.io.IOException
import kotlin.properties.Delegates
class ServicesHelperTest : BaseTest() {
@@ -31,40 +32,45 @@ class ServicesHelperTest : BaseTest() {
@MockK
lateinit var locationProvider: LocationProviderImpl
lateinit var weatherResponse: WeatherResponse
lateinit var weatherResponse: WeatherApiResponse
private var lat by Delegates.notNull<Double>()
private var long by Delegates.notNull<Double>()
private lateinit var latlon: Pair<Double, Double>
private lateinit var fullWeather: FullWeather
@Before
fun setUp() {
MockKAnnotations.init(this)
helper = ServicesHelper(repository, settingsRepository, locationProvider)
weatherResponse = getTestData("weather_sample.json", WeatherResponse::class.java)
weatherResponse = getTestData("new_response.json", WeatherApiResponse::class.java)
lat = weatherResponse.latitude!!
long = weatherResponse.longitude!!
latlon = Pair(lat, long)
fullWeather = weatherResponse.mapData().apply {
temperatureUnit = "°C"
locationString = CURRENT_LOCATION
}
}
@Test
fun testWidgetDataAsync_successfulResponse() = runBlocking {
// Arrange
val entityItem = EntityItem(CURRENT_LOCATION, FullWeather(weatherResponse).apply {
temperatureUnit = "°C"
locationString = CURRENT_LOCATION
})
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
// Act
coEvery { locationProvider.getCurrentLatLong() } returns Pair(
weatherResponse.lat,
weatherResponse.lon
)
coEvery { locationProvider.getCurrentLatLong() } returns Pair(lat, long)
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
coEvery {
repository.getWeatherFromApi(
weatherResponse.lat.toString(),
weatherResponse.lon.toString()
lat.toString(),
long.toString()
)
}.returns(weatherResponse)
coEvery {
locationProvider.getLocationNameFromLatLong(
weatherResponse.lat,
weatherResponse.lon
lat,
long
)
}.returns(CURRENT_LOCATION)
every { repository.saveLastSavedAt(CURRENT_LOCATION) } returns Unit

File diff suppressed because it is too large Load Diff