Skip to content

Commit 3eadabb

Browse files
committed
Add export instrumented tests
1 parent d5e0692 commit 3eadabb

File tree

11 files changed

+206
-117
lines changed

11 files changed

+206
-117
lines changed

app/src/main/java/org/nitri/opentopo/ors/Directions.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import kotlinx.coroutines.launch
66
import kotlinx.coroutines.withContext
77
import okhttp3.ResponseBody
88
import org.nitri.ors.api.OpenRouteServiceApi
9-
import org.nitri.ors.model.route.RouteRequest
109
import org.nitri.ors.repository.RouteRepository
1110
import retrofit2.Response
1211

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package org.nitri.ors
2+
3+
import android.content.Context
4+
import androidx.test.core.app.ApplicationProvider
5+
import androidx.test.ext.junit.runners.AndroidJUnit4
6+
import kotlinx.coroutines.runBlocking
7+
import org.junit.Assert.assertNotNull
8+
import org.junit.Assert.assertTrue
9+
import org.junit.Test
10+
import org.junit.runner.RunWith
11+
import org.nitri.ors.client.OpenRouteServiceClient
12+
import org.nitri.ors.model.export.ExportRequest
13+
import org.nitri.ors.repository.ExportRepository
14+
15+
@RunWith(AndroidJUnit4::class)
16+
class ExportRepositoryInstrumentedTest {
17+
18+
private fun createRepository(context: Context): ExportRepository {
19+
val apiKey = context.getString(R.string.ors_api_key)
20+
val api = OpenRouteServiceClient.create(apiKey, context)
21+
return ExportRepository(api)
22+
}
23+
24+
@Test
25+
fun testExport_successful() = runBlocking {
26+
val context = ApplicationProvider.getApplicationContext<Context>()
27+
val repository = createRepository(context)
28+
29+
// Bounding box around Heidelberg, Germany
30+
val bbox = listOf(
31+
listOf(8.681495, 49.41461), // minLon, minLat
32+
listOf(8.686507, 49.41943) // maxLon, maxLat
33+
)
34+
35+
val response = repository.export(bbox = bbox, profile ="driving-car")
36+
37+
assertNotNull("Export response should not be null", response)
38+
// Basic sanity checks on structure
39+
assertTrue("Points list should not be empty", response.nodes.isNotEmpty())
40+
assertTrue("Edges list should not be empty", response.edges.isNotEmpty())
41+
}
42+
43+
@Test
44+
fun testExportJson_successful() = runBlocking {
45+
val context = ApplicationProvider.getApplicationContext<Context>()
46+
val repository = createRepository(context)
47+
48+
val bbox = listOf(
49+
listOf(8.681495, 49.41461),
50+
listOf(8.686507, 49.41943)
51+
)
52+
53+
54+
val response = repository.exportJson(bbox = bbox, profile ="driving-car")
55+
56+
assertNotNull("JSON Export response should not be null", response)
57+
assertTrue("Points list should not be empty", response.nodes.isNotEmpty())
58+
}
59+
60+
@Test
61+
fun testExportTopoJson_successful() = runBlocking {
62+
val context = ApplicationProvider.getApplicationContext<Context>()
63+
val repository = createRepository(context)
64+
65+
val bbox = listOf(
66+
listOf(8.681495, 49.41461),
67+
listOf(8.686507, 49.41943)
68+
)
69+
70+
val topo = repository.exportTopoJson(bbox = bbox, profile = "driving-car")
71+
72+
assertNotNull("TopoJSON Export response should not be null", topo)
73+
assertTrue("Type should be present", topo.type.isNotBlank())
74+
}
75+
}

ors-client/src/androidTest/java/org/nitri/ors/RouteRepositoryGeoJsonInstrumentedTest.kt

Lines changed: 0 additions & 34 deletions
This file was deleted.

ors-client/src/androidTest/java/org/nitri/ors/RouteRepositoryGpxInstrumentedTest.kt

Lines changed: 0 additions & 36 deletions
This file was deleted.

ors-client/src/androidTest/java/org/nitri/ors/RouteRepositoryInstrumentedTest.kt

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,29 @@ import android.content.Context
44
import androidx.test.core.app.ApplicationProvider
55
import androidx.test.ext.junit.runners.AndroidJUnit4
66
import kotlinx.coroutines.runBlocking
7+
import okhttp3.ResponseBody
78
import org.junit.Assert.*
89
import org.junit.Test
910
import org.junit.runner.RunWith
1011
import org.nitri.ors.client.OpenRouteServiceClient
12+
import org.nitri.ors.model.route.GeoJsonRouteResponse
13+
import org.nitri.ors.repository.ExportRepository
1114
import org.nitri.ors.repository.RouteRepository
15+
import retrofit2.Response
1216

1317
@RunWith(AndroidJUnit4::class)
1418
class RouteRepositoryInstrumentedTest {
1519

20+
private fun createRepository(context: Context): RouteRepository {
21+
val apiKey = context.getString(R.string.ors_api_key)
22+
val api = OpenRouteServiceClient.create(apiKey, context)
23+
return RouteRepository(api)
24+
}
25+
1626
@Test
1727
fun testFetchRoute_successful() = runBlocking {
1828
val context = ApplicationProvider.getApplicationContext<Context>()
19-
val apiKey = context.getString(R.string.ors_api_key)
20-
val client = OpenRouteServiceClient.create(apiKey, context)
21-
val repository = RouteRepository(client)
29+
val repository = createRepository(context)
2230

2331
val start = Pair(8.681495, 49.41461)
2432
val end = Pair(8.687872, 49.420318)
@@ -27,4 +35,33 @@ class RouteRepositoryInstrumentedTest {
2735

2836
assertNotNull("Route should not be null", route)
2937
}
38+
39+
@Test
40+
fun testFetchGpxRoute_successful() = runBlocking {
41+
val context = ApplicationProvider.getApplicationContext<Context>()
42+
val repository = createRepository(context)
43+
44+
val start = Pair(8.681495, 49.41461)
45+
val end = Pair(8.687872, 49.420318)
46+
47+
val response: Response<ResponseBody> = repository.getRouteGpx(start, end,"driving-car")
48+
49+
val gpxXml = response.body()?.string()
50+
51+
assertNotNull("GPX response body should not be null", gpxXml)
52+
assert(gpxXml!!.contains("<gpx")) { "Response does not appear to be valid GPX" }
53+
}
54+
55+
@Test
56+
fun testFetchGeoJsonRoute_successful() = runBlocking {
57+
val context = ApplicationProvider.getApplicationContext<Context>()
58+
val repository = createRepository(context)
59+
60+
val start = Pair(8.681495, 49.41461)
61+
val end = Pair(8.687872, 49.420318)
62+
63+
val route: GeoJsonRouteResponse = repository.getRouteGeoJson(start, end, "driving-car")
64+
65+
assertNotNull("Route should not be null", route)
66+
}
3067
}

ors-client/src/main/java/org/nitri/ors/api/OpenRouteServiceApi.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
package org.nitri.ors.api
22

3-
import okhttp3.RequestBody
43
import okhttp3.ResponseBody
54
import org.nitri.ors.model.export.ExportRequest
65
import org.nitri.ors.model.export.ExportResponse
76
import org.nitri.ors.model.export.TopoJsonExportResponse
87
import org.nitri.ors.model.route.GeoJsonRouteResponse
9-
import org.nitri.ors.model.route.GpxResponse
108
import org.nitri.ors.model.route.RouteRequest
119
import org.nitri.ors.model.route.RouteResponse
1210
import retrofit2.Response
1311
import retrofit2.http.Body
1412
import retrofit2.http.GET
15-
import retrofit2.http.Headers
1613
import retrofit2.http.POST
1714
import retrofit2.http.Path
1815
import retrofit2.http.Query

ors-client/src/main/java/org/nitri/ors/client/OpenRouteServiceClient.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,9 @@ object OpenRouteServiceClient {
2525
})
2626
.addInterceptor { chain ->
2727
val original = chain.request()
28-
val originalUrl = original.url
29-
30-
val newUrl = originalUrl.newBuilder()
31-
.addQueryParameter("api_key", apiKey)
32-
.build()
3328

3429
val newRequest = original.newBuilder()
35-
.url(newUrl)
30+
.addHeader("Authorization", "Bearer $apiKey")
3631
.addHeader("User-Agent", userAgent)
3732
.build()
3833

ors-client/src/main/java/org/nitri/ors/model/export/ExportRequest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable
44

55
@Serializable
66
data class ExportRequest(
7-
val boundingBox: List<Double>, // [minLon, minLat, maxLon, maxLat]
8-
val profile: String,
7+
val bbox: List<List<Double>>, // [minLon, minLat, maxLon, maxLat]
8+
val id: String,
99
val geometry: Boolean? = null
1010
)
Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,56 @@
11
package org.nitri.ors.model.export
22

3+
import kotlinx.serialization.KSerializer
34
import kotlinx.serialization.Serializable
5+
import kotlinx.serialization.SerialName
6+
import kotlinx.serialization.descriptors.PrimitiveKind
7+
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
8+
import kotlinx.serialization.descriptors.SerialDescriptor
9+
import kotlinx.serialization.encoding.Decoder
10+
import kotlinx.serialization.encoding.Encoder
11+
import kotlinx.serialization.json.JsonDecoder
12+
import kotlinx.serialization.json.JsonPrimitive
13+
import kotlinx.serialization.json.doubleOrNull
414

515
@Serializable
616
data class ExportResponse(
7-
val points: List<Point>,
8-
val edges: List<Edge>,
9-
val weights: List<Weight>
17+
val nodes: List<Node> = emptyList(),
18+
val edges: List<GraphEdge> = emptyList()
1019
)
1120

1221
@Serializable
13-
data class Point(
14-
val id: Int,
15-
val coordinates: List<Double> // [lon, lat]
22+
data class Node(
23+
@SerialName("nodeId") val nodeId: Long,
24+
/** [lon, lat] */
25+
val location: List<Double>
1626
)
1727

1828
@Serializable
19-
data class Edge(
20-
val id: Int,
21-
val startPointId: Int,
22-
val endPointId: Int
23-
)
24-
25-
@Serializable
26-
data class Weight(
27-
val edgeId: Int,
29+
data class GraphEdge(
30+
@SerialName("fromId") val fromId: Long,
31+
@SerialName("toId") val toId: Long,
32+
@Serializable(with = StringAsDoubleSerializer::class)
2833
val weight: Double
2934
)
35+
36+
/**
37+
* ORS sometimes returns numeric fields (e.g., "weight") as strings.
38+
* This serializer accepts either a JSON number or a string number.
39+
*/
40+
object StringAsDoubleSerializer : KSerializer<Double> {
41+
override val descriptor: SerialDescriptor =
42+
PrimitiveSerialDescriptor("StringAsDouble", PrimitiveKind.DOUBLE)
43+
44+
override fun deserialize(decoder: Decoder): Double {
45+
return if (decoder is JsonDecoder) {
46+
val prim = decoder.decodeJsonElement() as JsonPrimitive
47+
prim.doubleOrNull ?: prim.content.toDouble()
48+
} else {
49+
decoder.decodeDouble()
50+
}
51+
}
52+
53+
override fun serialize(encoder: Encoder, value: Double) {
54+
encoder.encodeDouble(value)
55+
}
56+
}
Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,41 @@
11
package org.nitri.ors.model.export
22

3+
import kotlinx.serialization.SerialName
34
import kotlinx.serialization.Serializable
4-
import kotlinx.serialization.json.JsonElement
55

66
@Serializable
77
data class TopoJsonExportResponse(
8-
val type: String,
9-
val objects: Map<String, JsonElement> = emptyMap(), // or JsonObject
10-
val arcs: List<List<List<Int>>> = emptyList(),
11-
val transform: Transform
8+
val type: String, // "Topology"
9+
val objects: TopoObjects,
10+
/** Top-level arcs are arrays of [lon, lat] Double coordinates */
11+
val arcs: List<List<List<Double>>>,
12+
val bbox: List<Double> // [minLon, minLat, maxLon, maxLat]
1213
)
1314

1415
@Serializable
15-
data class Transform(
16-
val scale: List<Double>,
17-
val translate: List<Double>
16+
data class TopoObjects(
17+
val network: GeometryCollection
1818
)
19+
20+
@Serializable
21+
data class GeometryCollection(
22+
val type: String, // "GeometryCollection"
23+
val geometries: List<TopoGeometry>
24+
)
25+
26+
@Serializable
27+
data class TopoGeometry(
28+
val type: String, // "LineString" (currently)
29+
/** These are indices into the top-level arcs array (can be negative to indicate reversal) */
30+
val arcs: List<Int>,
31+
val properties: GeometryProps? = null // be lenient; fields can vary
32+
)
33+
34+
@Serializable
35+
data class GeometryProps(
36+
// weight often arrives as a string in this endpoint
37+
val weight: String? = null,
38+
@SerialName("node_from") val nodeFrom: Long? = null,
39+
@SerialName("node_to") val nodeTo: Long? = null
40+
)
41+

0 commit comments

Comments
 (0)