Skip to content

Commit 4b90823

Browse files
authored
Simplify tests (#111)
Signed-off-by: Arnau Mora <[email protected]>
1 parent a035846 commit 4b90823

27 files changed

+680
-1120
lines changed

.idea/runConfigurations/Run_Coverage.xml

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/Run_tests.xml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
package database
22

3-
enum class EntityTypes {
4-
AREA, ZONE, SECTOR, PATH, BLOCKING
3+
import database.entity.Area
4+
import database.entity.BaseEntity
5+
import database.entity.Blocking
6+
import database.entity.Path
7+
import database.entity.Sector
8+
import database.entity.Zone
9+
10+
sealed class EntityTypes<Entity: BaseEntity>(
11+
/**
12+
* The name used for identifying the entity in the url.
13+
* For example, areas use `area`, since the url for accessing them is `/area/{id}`.
14+
*/
15+
val urlName: String
16+
) {
17+
data object AREA: EntityTypes<Area>("area")
18+
data object ZONE: EntityTypes<Zone>("zone")
19+
data object SECTOR: EntityTypes<Sector>("sector")
20+
data object PATH: EntityTypes<Path>("path")
21+
data object BLOCKING: EntityTypes<Blocking>("blocking")
22+
23+
val name = this::class.simpleName
524
}

src/main/kotlin/distribution/FCMNotifier.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ internal object FCMNotifier : Notifier {
4444
* Sends a notification through FCM to all devices subscribed to the given topic.
4545
* This method is only visible for internal calls or testing purposes.
4646
*/
47-
override fun notify(topic: String, type: EntityTypes, id: Int) {
47+
override fun notify(topic: String, type: EntityTypes<*>, id: Int) {
4848
// Run only if the app is initialized
4949
if (this::app.isInitialized) {
5050
val message = Message.builder()

src/main/kotlin/distribution/Notifier.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ interface Notifier {
2626
* @param type The type of entity that was created, updated, or deleted.
2727
* @param id The ID of the entity that was created, updated, or deleted.
2828
*/
29-
fun notify(topic: String, type: EntityTypes, id: Int)
29+
fun notify(topic: String, type: EntityTypes<*>, id: Int)
3030

3131
/**
3232
* Notifies all devices that a new entity has been created.
@@ -36,7 +36,7 @@ interface Notifier {
3636
* @param type The type of entity that was created.
3737
* @param id The ID of the entity that was created.
3838
*/
39-
fun notifyCreated(type: EntityTypes, id: Int) = notify(TOPIC_CREATED, type, id)
39+
fun notifyCreated(type: EntityTypes<*>, id: Int) = notify(TOPIC_CREATED, type, id)
4040

4141
/**
4242
* Notifies all devices that an entity has been updated.
@@ -46,7 +46,7 @@ interface Notifier {
4646
* @param type The type of entity that was created.
4747
* @param id The ID of the entity that was created.
4848
*/
49-
fun notifyUpdated(type: EntityTypes, id: Int) = notify(TOPIC_UPDATED, type, id)
49+
fun notifyUpdated(type: EntityTypes<*>, id: Int) = notify(TOPIC_UPDATED, type, id)
5050

5151
/**
5252
* Notifies all devices that an entity has been deleted.
@@ -56,5 +56,5 @@ interface Notifier {
5656
* @param type The type of entity that was created.
5757
* @param id The ID of the entity that was created.
5858
*/
59-
fun notifyDeleted(type: EntityTypes, id: Int) = notify(TOPIC_DELETED, type, id)
59+
fun notifyDeleted(type: EntityTypes<*>, id: Int) = notify(TOPIC_DELETED, type, id)
6060
}

src/main/kotlin/server/endpoints/patch/PatchSectorEndpoint.kt

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import database.entity.Sector
77
import database.entity.Zone
88
import database.entity.info.LastUpdate
99
import distribution.Notifier
10+
import io.ktor.http.HttpHeaders
1011
import io.ktor.http.HttpStatusCode
1112
import io.ktor.server.application.ApplicationCall
1213
import io.ktor.server.application.call
@@ -51,6 +52,10 @@ object PatchSectorEndpoint : SecureEndpointBase("/sector/{sectorId}") {
5152
var imageFile: File? = null
5253
var gpxFile: File? = null
5354

55+
var deleteGpx = false
56+
57+
var invalidFile = false
58+
5459
receiveMultipart(
5560
forEachFormItem = { partData ->
5661
when (partData.name) {
@@ -83,19 +88,39 @@ object PatchSectorEndpoint : SecureEndpointBase("/sector/{sectorId}") {
8388
imageFile = partData.save(Storage.ImagesDir, UUID.fromString(uuid))
8489
}
8590
"gpx" -> {
86-
val uuid = ServerDatabase.instance.query { sector.gpx?.nameWithoutExtension }
87-
gpxFile = partData.save(Storage.TracksDir, uuid?.let(UUID::fromString) ?: UUID.randomUUID())
91+
val contentType = partData.headers[HttpHeaders.ContentType]
92+
val contentSize = partData.headers[HttpHeaders.ContentLength]?.toIntOrNull()
93+
94+
// Accept only content type application/gpx (includes application/gpx+xml)
95+
if (contentType?.startsWith("application/gpx") != true) {
96+
invalidFile = true
97+
} else {
98+
if (contentSize?.let { it <= 0 } == true) {
99+
// If the size is 0, delete the gpx file
100+
deleteGpx = true
101+
} else {
102+
val uuid = ServerDatabase.instance.query { sector.gpx?.nameWithoutExtension }
103+
gpxFile = partData.save(
104+
rootDir = Storage.TracksDir,
105+
uuid = uuid?.let(UUID::fromString) ?: UUID.randomUUID()
106+
)
107+
}
108+
}
88109
}
89110
}
90111
}
91112
)
92113

114+
if (invalidFile) return respondFailure(Errors.InvalidFileType)
115+
93116
if (areAllNull(displayName, imageFile, gpxFile, kidsApt, point, sunTime, walkingTime, weight, zone) &&
94-
areAllFalse(removePoint, removeWalkingTime)
117+
areAllFalse(removePoint, removeWalkingTime, deleteGpx)
95118
) {
96119
return respondSuccess(httpStatusCode = HttpStatusCode.NoContent)
97120
}
98121

122+
if (deleteGpx) sector.gpx?.delete()
123+
99124
val json = ServerDatabase.instance.query {
100125
displayName?.let { sector.displayName = it }
101126
kidsApt?.let { sector.kidsApt = it }
@@ -108,6 +133,8 @@ object PatchSectorEndpoint : SecureEndpointBase("/sector/{sectorId}") {
108133
if (removePoint) sector.point = null
109134
if (removeWalkingTime) sector.walkingTime = null
110135

136+
if (deleteGpx) sector.gpx = null
137+
111138
sector.timestamp = Instant.now()
112139

113140
sector.toJson()

src/main/kotlin/server/error/Errors.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ object Errors {
1616
val MissingData = Error(10, "The request misses some required data.", HttpStatusCode.BadRequest)
1717
val InvalidData = Error(11, "The request has some data of the wrong type.", HttpStatusCode.BadRequest)
1818
val TooManyImages = Error(12, "Too many images added. Maximum: ${Paths.MAX_IMAGES}", HttpStatusCode.BadRequest)
19+
val InvalidFileType = Error(13, "The request has a file of the wrong type.", HttpStatusCode.BadRequest)
1920

2021
val Conflict = Error(20, "Multiple parameters in the request conflict.", HttpStatusCode.Conflict)
2122

src/main/kotlin/utils/JsonArrayUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import utils.serialization.JsonSerializer
1313
*
1414
* @return The parsed JSON array represented as a JSONArray object.
1515
*/
16-
val String.jsonArray: JSONArray get() = JSONArray(this)
16+
val String.jsonArray: JSONArray get() = if (this == "\u0000") JSONArray() else JSONArray(this)
1717

1818
/**
1919
* Converts an Iterable of JsonSerializable objects to a JSONArray.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package data
2+
3+
import database.EntityTypes
4+
import kotlin.test.Test
5+
import kotlin.test.assertEquals
6+
7+
class TestEntityTypes {
8+
@Test
9+
fun testName() {
10+
assertEquals("AREA", EntityTypes.AREA.name)
11+
assertEquals("ZONE", EntityTypes.ZONE.name)
12+
assertEquals("SECTOR", EntityTypes.SECTOR.name)
13+
assertEquals("PATH", EntityTypes.PATH.name)
14+
assertEquals("BLOCKING", EntityTypes.BLOCKING.name)
15+
}
16+
}

src/test/kotlin/server/base/ApplicationTestBase.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ abstract class ApplicationTestBase {
3535
object DeviceNotifier : Notifier {
3636
class Notification(
3737
val topic: String,
38-
val type: EntityTypes,
38+
val type: EntityTypes<*>,
3939
val id: Int
4040
)
4141

4242
var notificationStack = emptyList<Notification>()
4343

4444
override fun initialize() { /* Nothing to do */ }
4545

46-
override fun notify(topic: String, type: EntityTypes, id: Int) {
46+
override fun notify(topic: String, type: EntityTypes<*>, id: Int) {
4747
Logger.info("Received notification on topic $topic for entity $type with id $id.")
4848
notificationStack = notificationStack.toMutableList().plusElement(
4949
Notification(topic, type, id)
@@ -58,7 +58,7 @@ abstract class ApplicationTestBase {
5858
* @param type The type of the entity that was notified.
5959
* @param id The id of the entity that was notified.
6060
*/
61-
fun assertNotificationSent(topic: String, type: EntityTypes, id: Int) {
61+
fun assertNotificationSent(topic: String, type: EntityTypes<*>, id: Int) {
6262
var noti: DeviceNotifier.Notification? = null
6363
try {
6464
DeviceNotifier.notificationStack.find { notification ->
@@ -79,7 +79,7 @@ abstract class ApplicationTestBase {
7979
/**
8080
* Asserts that no notification was sent.
8181
*/
82-
fun assertNotificationNotSent(topic: String, type: EntityTypes) {
82+
fun assertNotificationNotSent(topic: String, type: EntityTypes<*>) {
8383
var noti: DeviceNotifier.Notification? = null
8484
try {
8585
DeviceNotifier.notificationStack.find { notification ->

0 commit comments

Comments
 (0)