Merge pull request 'fix(notifications): always serialize streams so clearing the last subscription saves' (#25) from fix/notifications-empty-subscriptions into main
Reviewed-on: #25 Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
This commit is contained in:
@@ -60,8 +60,15 @@ data class NotificationStreamsDto(
|
||||
* `GET/PUT /auth/me/notifications/subscriptions` — the user's opted-in stream ids.
|
||||
* PUT replaces the full set; unknown ids are dropped server-side and the stored set
|
||||
* echoed back.
|
||||
*
|
||||
* [streams] intentionally has NO default: this DTO doubles as the PUT body, and the
|
||||
* backend validator requires the `streams` field (`body('streams').isArray()`).
|
||||
* kotlinx omits a property equal to its default (encodeDefaults=false), so a default
|
||||
* of `emptyList()` would drop the field when the user clears their LAST subscription,
|
||||
* sending `{}` → 400 "Validation failed" (the "can't turn off the last one" bug). With
|
||||
* no default the empty list always serializes as `{"streams":[]}`. Do not re-add a default.
|
||||
*/
|
||||
@Serializable
|
||||
data class NotificationSubscriptionsDto(
|
||||
val streams: List<String> = emptyList(),
|
||||
val streams: List<String>,
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
package com.runicgateway.app.data.api.dto
|
||||
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
@@ -58,6 +59,15 @@ class NotificationsDtoTest {
|
||||
assertEquals(listOf("news.post", "champ.start"), dto.streams)
|
||||
}
|
||||
|
||||
@Test fun emptySubscriptionsStillSerializeStreamsField() {
|
||||
// Regression: clearing the LAST subscription sends an empty set. The backend
|
||||
// validator requires `streams`, so it must be present as `[]`, not omitted.
|
||||
// Uses the production Json config (no encodeDefaults) to prove the field is
|
||||
// always emitted because the DTO field has no default.
|
||||
val body = json.encodeToString(NotificationSubscriptionsDto(emptyList()))
|
||||
assertEquals("""{"streams":[]}""", body)
|
||||
}
|
||||
|
||||
@Test fun settingsPushBlockDecodes() {
|
||||
val dto = json.decodeFromString<SettingsDto>(
|
||||
"""{"site_title":"Shard","brand":{"name":"Shard"},"push":{"ntfyUrl":"https://ntfy.shard.tld"}}""",
|
||||
|
||||
Reference in New Issue
Block a user