Das ■ HONEYOS ist jetzt ein Menue statt eines Links: Schreibtisch, Einstellungen, HoneyBar einklappen, Abmelden. In fremden Apps sind es Links auf den Schreibtisch (?einstellungen=1 / ?abmelden=1), weil die HoneyBar dort nicht schreiben darf — Sitzung und CSRF-Pruefwort ueber- leben die Site-Grenze nicht, und ein Abmelden per Aufruf koennte jede Seite ausloesen. Der Abmeldeknopf in der Menueleiste faellt weg (stand doppelt), und Abmelden erscheint nur, wenn HoneyOS ueberhaupt anmeldet — dafuer gibt /api/apps jetzt oidc mit heraus. Nebenbei: restliche 'Leiste' im Text zu 'HoneyBar' vereinheitlicht.
117 lines
3.4 KiB
Go
117 lines
3.4 KiB
Go
package weather
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
)
|
|
|
|
// Ein erfundener Wetterdienst. Kein Test in diesem Projekt fragt je wirklich
|
|
// bei Open-Meteo an: Das wäre langsam, unzuverlässig und würde einen fremden
|
|
// Dienst für unsere Testläufe benutzen.
|
|
func fakeWetter(t *testing.T, code int, grad float64, aufrufe *int32) *Dienst {
|
|
t.Helper()
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if aufrufe != nil {
|
|
atomic.AddInt32(aufrufe, 1)
|
|
}
|
|
if strings.Contains(r.URL.Path, "search") {
|
|
json.NewEncoder(w).Encode(map[string]any{"results": []map[string]any{
|
|
{"name": "Köln", "country": "Deutschland", "admin1": "Nordrhein-Westfalen",
|
|
"latitude": 50.938, "longitude": 6.96},
|
|
}})
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(map[string]any{"current": map[string]any{
|
|
"temperature_2m": grad, "weather_code": code,
|
|
}})
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
d := Neu()
|
|
d.wetterURL = srv.URL + "/forecast"
|
|
d.sucheURL = srv.URL + "/search"
|
|
return d
|
|
}
|
|
|
|
func TestWetterKommtAn(t *testing.T) {
|
|
d := fakeWetter(t, 61, 12.4, nil)
|
|
w, err := d.Jetzt(context.Background(), "Köln", 50.9, 6.9)
|
|
if err != nil {
|
|
t.Fatalf("unerwartet: %v", err)
|
|
}
|
|
if w.Ort != "Köln" || w.Grad != 12.4 {
|
|
t.Fatalf("falsche Werte: %+v", w)
|
|
}
|
|
if w.Glyph != "regen" || w.Text != "regen" {
|
|
t.Fatalf("Code 61 ist Regen, nicht %q/%q", w.Glyph, w.Text)
|
|
}
|
|
}
|
|
|
|
// Der Zwischenspeicher ist der Grund, warum die HoneyBar auf jeder Seite jeder App
|
|
// stehen darf: Ohne ihn ginge bei jedem Seitenaufruf eine Anfrage nach draußen.
|
|
func TestZweiteFrageGehtNichtNachDraussen(t *testing.T) {
|
|
var aufrufe int32
|
|
d := fakeWetter(t, 0, 21, &aufrufe)
|
|
for i := 0; i < 5; i++ {
|
|
if _, err := d.Jetzt(context.Background(), "Köln", 50.9, 6.9); err != nil {
|
|
t.Fatalf("unerwartet: %v", err)
|
|
}
|
|
}
|
|
if aufrufe != 1 {
|
|
t.Fatalf("%d Anfragen nach draußen, erwartet 1", aufrufe)
|
|
}
|
|
}
|
|
|
|
func TestAndererOrtWirdGetrenntGehalten(t *testing.T) {
|
|
var aufrufe int32
|
|
d := fakeWetter(t, 0, 21, &aufrufe)
|
|
d.Jetzt(context.Background(), "Köln", 50.9, 6.9)
|
|
d.Jetzt(context.Background(), "Wien", 48.2, 16.4)
|
|
if aufrufe != 2 {
|
|
t.Fatalf("zwei Orte, %d Anfragen", aufrufe)
|
|
}
|
|
}
|
|
|
|
func TestWettercodesWerdenZuSymbolen(t *testing.T) {
|
|
fälle := map[int]string{
|
|
0: "sonne", 2: "heiter", 3: "wolke", 45: "nebel",
|
|
61: "regen", 80: "regen", 73: "schnee", 86: "schnee", 95: "gewitter",
|
|
}
|
|
for code, will := range fälle {
|
|
if g, _ := wmo(code); g != will {
|
|
t.Errorf("Code %d → %q, erwartet %q", code, g, will)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOrtssucheGibtKoordinatenZurueck(t *testing.T) {
|
|
d := fakeWetter(t, 0, 20, nil)
|
|
orte, err := d.Suche(context.Background(), "Köln")
|
|
if err != nil {
|
|
t.Fatalf("unerwartet: %v", err)
|
|
}
|
|
if len(orte) != 1 || orte[0].Lat == 0 || orte[0].Lon == 0 {
|
|
t.Fatalf("ohne Koordinaten müsste jede Abfrage neu suchen: %+v", orte)
|
|
}
|
|
// Bundesland statt Land: „Köln, Deutschland" gibt es mehrfach.
|
|
if !strings.Contains(orte[0].Land, "Nordrhein-Westfalen") {
|
|
t.Fatalf("Land zu grob: %q", orte[0].Land)
|
|
}
|
|
}
|
|
|
|
func TestSucheOhneBegriffFragtNichtAn(t *testing.T) {
|
|
var aufrufe int32
|
|
d := fakeWetter(t, 0, 20, &aufrufe)
|
|
if _, err := d.Suche(context.Background(), " "); err == nil {
|
|
t.Fatal("leerer Begriff müsste abgelehnt werden")
|
|
}
|
|
if aufrufe != 0 {
|
|
t.Fatalf("leerer Begriff ging trotzdem nach draußen (%d)", aufrufe)
|
|
}
|
|
}
|