Skip to content

Commit

Permalink
Test: Add unit tests for TimeTableViewModel (#515)
Browse files Browse the repository at this point in the history
### TL;DR
Enhanced the trip planning test infrastructure with improved fake builders and added comprehensive tests for journey caching.

### What changed?
- Created a builder pattern for `FakeTripResponseBuilder` with configurable parameters
- Added support for generating multiple journeys with realistic time sequences
- Implemented tests for journey caching behavior in `TimeTableViewModel`
- Added visibility annotations for testing internal components
- Enhanced logging for journey time debugging

### How to test?
1. Run the TimeTableViewModelTest suite
2. Verify the new journey caching tests:
   - Test for basic journey updates
   - Test for started journey threshold handling
   - Test for journey sorting behavior
3. Validate the fake builder functionality with different journey configurations

### Why make this change?
To improve test coverage and maintainability of the trip planning feature, particularly around journey caching logic. The enhanced fake builders provide more realistic test scenarios and make it easier to test complex journey combinations.
  • Loading branch information
ksharma-xyz authored Jan 8, 2025
1 parent e1beff2 commit 571f489
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 63 deletions.
2 changes: 2 additions & 0 deletions core/test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ kotlin {
commonTest {
dependencies {
implementation(projects.core.analytics)
implementation(projects.core.dateTime)
implementation(projects.core.log)
implementation(projects.sandook)
implementation(projects.feature.tripPlanner.ui)
Expand All @@ -53,6 +54,7 @@ kotlin {
implementation(libs.test.kotlinxCoroutineTest)
implementation(libs.test.turbine)
implementation(libs.kotlinx.collections.immutable)
implementation(libs.kotlinx.datetime)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
package xyz.ksharma.core.test.fakes

import xyz.ksharma.core.test.fakes.FakeTripResponseBuilder.buildDestinationStopSequence
import xyz.ksharma.core.test.fakes.FakeTripResponseBuilder.buildOriginStopSequence
import xyz.ksharma.core.test.fakes.FakeTripResponseBuilder.buildTransportation
import xyz.ksharma.krail.trip.planner.network.api.model.StopFinderResponse
import xyz.ksharma.krail.trip.planner.network.api.model.StopType
import xyz.ksharma.krail.trip.planner.network.api.model.TripResponse
import xyz.ksharma.krail.trip.planner.network.api.model.TripResponse.StopSequence
import xyz.ksharma.krail.trip.planner.network.api.service.DepArr
import xyz.ksharma.krail.trip.planner.network.api.service.TripPlanningService

Expand All @@ -21,26 +17,7 @@ class FakeTripPlanningService : TripPlanningService {
date: String?,
time: String?,
): TripResponse {
return if (isSuccess)

// Return a fake TripResponse
TripResponse(
journeys = listOf(
TripResponse.Journey(
legs = listOf(
TripResponse.Leg(
origin = buildOriginStopSequence(),
destination = buildDestinationStopSequence(),
stopSequence = listOf(
buildOriginStopSequence(),
),
transportation = buildTransportation(),
duration = 100,
),
),
),
)
)
return if (isSuccess) FakeTripResponseBuilder.buildTripResponse()
else throw IllegalStateException("Failed to fetch trip")
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,62 @@
package xyz.ksharma.core.test.fakes

import kotlinx.datetime.Clock
import xyz.ksharma.krail.trip.planner.network.api.model.StopType
import xyz.ksharma.krail.trip.planner.network.api.model.TripResponse
import xyz.ksharma.krail.trip.planner.network.api.model.TripResponse.StopSequence
import kotlin.time.Duration.Companion.minutes

object FakeTripResponseBuilder {
private var originStopSequence: StopSequence = buildStopSequence()
private var destinationStopSequence: StopSequence = buildStopSequence()
private var stopSequence: List<StopSequence> = listOf(buildStopSequence())
private var transportation: TripResponse.Transportation =
buildTransportation()
private var duration: Long = 200

fun buildOriginStopSequence() = StopSequence(
arrivalTimePlanned = "2024-09-24T19:00:00Z",
arrivalTimeEstimated = "2024-09-24T19:10:00Z",
departureTimePlanned = "2024-09-24T19:10:00Z",
departureTimeEstimated = "2024-09-24T19:10:00Z",
name = "Origin Stop",
disassembledName = "Origin Name",
id = "Origin_stop_id",
type = StopType.STOP.type,
)
fun setOriginStopSequence(originStopSequence: StopSequence) =
apply { this.originStopSequence = originStopSequence }

fun setDestinationStopSequence(destinationStopSequence: StopSequence) =
apply { this.destinationStopSequence = destinationStopSequence }

fun setStopSequence(stopSequence: List<StopSequence>) =
apply { this.stopSequence = stopSequence }

fun buildDestinationStopSequence() = StopSequence(
arrivalTimePlanned = "2024-09-24T20:00:00Z",
arrivalTimeEstimated = "2024-09-24T20:10:00Z",
departureTimePlanned = "2024-09-24T20:10:00Z",
departureTimeEstimated = "2024-09-24T20:10:00Z",
name = "Destination Stop",
disassembledName = "Destination Name",
id = "Destination_stop_id",
fun setTransportation(transportation: TripResponse.Transportation) =
apply { this.transportation = transportation }

fun setDuration(duration: Long) = apply { this.duration = duration }

fun build(): TripResponse.Leg {
return TripResponse.Leg(
origin = originStopSequence,
destination = destinationStopSequence,
stopSequence = stopSequence,
transportation = transportation,
duration = duration
)
}

private fun buildStopSequence(
arrivalTimePlanned: String = "2024-09-24T19:00:00Z",
arrivalTimeEstimated: String = "2024-09-24T19:00:00Z",
departureTimePlanned: String = "2024-09-24T19:10:00Z",
departureTimeEstimated: String = "2024-09-24T19:10:00Z",
name: String = "Stop",
id: String = "stop_id",
) = StopSequence(
arrivalTimePlanned = arrivalTimePlanned,
arrivalTimeEstimated = arrivalTimeEstimated,
departureTimePlanned = departureTimePlanned,
departureTimeEstimated = departureTimeEstimated,
name = name,
disassembledName = name,
id = id,
type = StopType.STOP.type,
)

fun buildTransportation() = TripResponse.Transportation(
private fun buildTransportation(transportationId: String = "Transportation Id") = TripResponse.Transportation(
disassembledName = "Transportation Name",
product = TripResponse.Product(
productClass = 1,
Expand All @@ -39,7 +67,77 @@ object FakeTripResponseBuilder {
id = "Destination Operator Id",
),
name = "Transportation Name",
id = "Transportation Id",
id = transportationId,
description = "Transportation Description",
)

private fun buildJourneyLeg(legIndex: Int, stops: Int, journeyIndex: Int = 0) =
TripResponse.Leg(
origin = buildStopSequence(
name = "Origin Stop $legIndex",
arrivalTimeEstimated = Clock.System.now().plus(5.minutes * journeyIndex).toString(),
arrivalTimePlanned = Clock.System.now().plus(5.minutes * journeyIndex).toString(),
departureTimeEstimated = Clock.System.now().plus(5.minutes * journeyIndex).toString(),
departureTimePlanned = Clock.System.now().plus(5.minutes * journeyIndex).toString(),
),
destination = buildStopSequence(
name = "Destination Stop $legIndex",
arrivalTimeEstimated = Clock.System.now().plus(10.minutes * journeyIndex).toString(),
arrivalTimePlanned = Clock.System.now().plus(10.minutes * journeyIndex).toString(),
departureTimeEstimated = Clock.System.now().plus(10.minutes * journeyIndex).toString(),
departureTimePlanned = Clock.System.now().plus(10.minutes * journeyIndex).toString(),
),
stopSequence = List(stops) { index ->
buildStopSequence(
id = "stop_id_${index + 1}",
name = "Stop ${index + 1}",
arrivalTimeEstimated = Clock.System.now().plus(5.minutes + journeyIndex.minutes)
.toString(),
arrivalTimePlanned = Clock.System.now().plus(5.minutes + journeyIndex.minutes)
.toString(),
departureTimeEstimated = Clock.System.now().plus(5.minutes + journeyIndex.minutes)
.toString(),
departureTimePlanned = Clock.System.now().plus(5.minutes + journeyIndex.minutes)
.toString(),
)
},
transportation = buildTransportation(
transportationId = "Transportation Id $journeyIndex",
),
duration = 120,
)

private fun buildJourneyList(
numberOfJourney: Int = 1,
numberOfLegs: Int = 1,
reverseTimeOrder: Boolean = false,
): List<TripResponse.Journey> {
val journeyList = List(numberOfJourney) { buildJourney(numberOfLegs = numberOfLegs, journeyIndex = it) }
return if (reverseTimeOrder) journeyList.reversed() else journeyList
}

private fun buildJourney(
numberOfLegs: Int = 1,
stops: Int = 1,
journeyIndex: Int = 0,
): TripResponse.Journey {
return TripResponse.Journey(
legs = List(numberOfLegs) { index ->
buildJourneyLeg(legIndex = index, stops = stops, journeyIndex = journeyIndex)
},
)
}

fun buildTripResponse(
numberOfJourney: Int = 1, numberOfLegs: Int = 1,
reverseTimeOrder: Boolean = false,
): TripResponse {
return TripResponse(
journeys = buildJourneyList(
numberOfJourney = numberOfJourney,
numberOfLegs = numberOfLegs,
reverseTimeOrder = reverseTimeOrder,
),
)
}
}
Loading

0 comments on commit 571f489

Please sign in to comment.