Files
marcandClaude Opus 5 fbaf24bdbd fix(api): leere Listen als [] statt null -- Kalender blieb weiss
Ein Konto ohne Kalender bekam auf GET /api/v1/calendars die Antwort
`null`, und die Ansicht brach ab:

    for (const cal of calendars)   ->  "e is not iterable"

Nicht der Kalender ist schuld, sondern eine Eigenheit, die jeden
Listenendpunkt betrifft: In Go ist eine Liste ohne Eintraege oft nil,
und encoding/json schreibt dafuer null statt []. Jede Store-Funktion,
die mit `var out []T` anfaengt, liefert das bei leerem Ergebnis --
Favoriten, Papierkorb, Versionen, Freigaben, Etiketten. Der Kalender war
nur der, der daran zerbricht.

Deshalb die Regel dort, wo alle Antworten durchlaufen: response.JSON
macht aus einer nil-Liste eine leere. Einzelne Objekte und nil bleiben
unberuehrt. In den Stores waere es die falsche Stelle -- es gibt
Dutzende Listenendpunkte, und der naechste faengt wieder mit
`var out []T` an. Eine Regel, an die man sich erinnern muss, ist keine.

Dazu zwei Dinge, die derselbe Fall ans Licht brachte:

  - Der Web-Weg legte, anders als CalDAV, keinen Standardkalender an.
    Ein neues Konto stand vor einer leeren Ansicht und musste erraten,
    dass es selbst einen anlegen soll -- waehrend ein CalDAV-Client, der
    sich vorher verbunden hatte, bereits einen sah, den die Oberflaeche
    nicht zeigte.
  - Die Ansicht faengt eine unerwartete Antwort jetzt selbst ab (?? []).
    Der Server schickt kein null mehr; eine Seite sollte daran trotzdem
    nicht zerbrechen.

Drei Tests in pkg/response: leere Liste wird [], befuellte bleibt
unveraendert, Objekte und nil bleiben, was sie sind.

Zur Korrektur meiner frueheren Erklaerung: Die fehlenden Cache-Header
waren ein echtes Problem, aber nicht die Ursache dieses Fehlers. Der
gemeldete Bundle-Name stimmte diesmal mit dem ausgelieferten ueberein --
daran war zu sehen, dass es kein alter Stand sein konnte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 19:56:54 +02:00

69 lines
1.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package response
import (
"encoding/json"
"net/http/httptest"
"testing"
)
// Eine leere Liste muss als [] herauskommen, nicht als null. Jede
// Oberfläche, die über die Antwort läuft, bricht sonst ab – so ist die
// Kalenderansicht bei einem Konto ohne Kalender weiß geblieben.
func TestJSONLeereListeWirdZuLeeremArray(t *testing.T) {
type eintrag struct {
Name string `json:"name"`
}
var leer []eintrag
w := httptest.NewRecorder()
JSON(w, 200, leer)
if got := w.Body.String(); !contains(got, `"data":[]`) {
t.Fatalf("erwartet \"data\":[], bekam %s", got)
}
}
func TestJSONBefuellteListeBleibtUnveraendert(t *testing.T) {
w := httptest.NewRecorder()
JSON(w, 200, []string{"a", "b"})
var out struct {
Data []string `json:"data"`
}
if err := json.Unmarshal(w.Body.Bytes(), &out); err != nil {
t.Fatal(err)
}
if len(out.Data) != 2 || out.Data[0] != "a" {
t.Fatalf("Inhalt verändert: %v", out.Data)
}
}
// Einzelne Objekte und nil dürfen davon nicht berührt werden – sonst
// verwandelte die Regel jede fehlende Antwort in eine leere Liste.
func TestJSONObjekteBleibenObjekte(t *testing.T) {
w := httptest.NewRecorder()
JSON(w, 200, map[string]int{"n": 1})
if got := w.Body.String(); !contains(got, `"n":1`) {
t.Fatalf("Objekt verändert: %s", got)
}
w = httptest.NewRecorder()
JSON(w, 200, nil)
if got := w.Body.String(); contains(got, "[]") {
t.Fatalf("nil sollte keine Liste werden: %s", got)
}
}
func contains(s, sub string) bool {
return len(s) >= len(sub) && (s == sub || len(sub) == 0 || indexOf(s, sub) >= 0)
}
func indexOf(s, sub string) int {
for i := 0; i+len(sub) <= len(s); i++ {
if s[i:i+len(sub)] == sub {
return i
}
}
return -1
}