All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
119 lines
3.4 KiB
Go
119 lines
3.4 KiB
Go
// service_test.go validates application service delegation behavior.
|
|
// Layer: internal/app tests for read use-case orchestration.
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
|
)
|
|
|
|
type fakeRepository struct {
|
|
observation *model.WeatherObservation
|
|
forecast *model.WeatherForecastRun
|
|
narrative *model.WeatherForecastRun
|
|
alerts *model.WeatherAlertRun
|
|
conditions *CurrentConditions
|
|
err error
|
|
|
|
currentConditionsWindow int
|
|
}
|
|
|
|
func (r *fakeRepository) LatestObservation(context.Context) (*model.WeatherObservation, error) {
|
|
return r.observation, r.err
|
|
}
|
|
|
|
func (r *fakeRepository) LatestHourlyForecast(context.Context) (*model.WeatherForecastRun, error) {
|
|
return r.forecast, r.err
|
|
}
|
|
|
|
func (r *fakeRepository) LatestNarrativeForecast(context.Context) (*model.WeatherForecastRun, error) {
|
|
return r.narrative, r.err
|
|
}
|
|
|
|
func (r *fakeRepository) LatestAlertRun(context.Context) (*model.WeatherAlertRun, error) {
|
|
return r.alerts, r.err
|
|
}
|
|
|
|
func (r *fakeRepository) CurrentConditions(_ context.Context, observationWindowMinutes int) (*CurrentConditions, error) {
|
|
r.currentConditionsWindow = observationWindowMinutes
|
|
return r.conditions, r.err
|
|
}
|
|
|
|
func TestServiceDelegatesObservation(t *testing.T) {
|
|
repo := &fakeRepository{observation: &model.WeatherObservation{StationID: "KSTL"}}
|
|
svc := NewService(repo)
|
|
|
|
obs, err := svc.LatestObservation(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if obs == nil || obs.StationID != "KSTL" {
|
|
t.Fatalf("unexpected observation: %+v", obs)
|
|
}
|
|
}
|
|
|
|
func TestServiceDelegatesForecast(t *testing.T) {
|
|
repo := &fakeRepository{forecast: &model.WeatherForecastRun{LocationID: "stl"}}
|
|
svc := NewService(repo)
|
|
|
|
run, err := svc.LatestHourlyForecast(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if run == nil || run.LocationID != "stl" {
|
|
t.Fatalf("unexpected forecast: %+v", run)
|
|
}
|
|
}
|
|
|
|
func TestServiceDelegatesNarrativeForecast(t *testing.T) {
|
|
repo := &fakeRepository{narrative: &model.WeatherForecastRun{LocationID: "stl-narrative"}}
|
|
svc := NewService(repo)
|
|
|
|
run, err := svc.LatestNarrativeForecast(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if run == nil || run.LocationID != "stl-narrative" {
|
|
t.Fatalf("unexpected forecast: %+v", run)
|
|
}
|
|
}
|
|
|
|
func TestServiceDelegatesAlerts(t *testing.T) {
|
|
repo := &fakeRepository{alerts: &model.WeatherAlertRun{LocationID: "stl"}}
|
|
svc := NewService(repo)
|
|
|
|
run, err := svc.LatestAlertRun(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if run == nil || run.LocationID != "stl" {
|
|
t.Fatalf("unexpected alert run: %+v", run)
|
|
}
|
|
}
|
|
|
|
func TestServiceUsesDefaultCurrentConditionsWindow(t *testing.T) {
|
|
repo := &fakeRepository{conditions: &CurrentConditions{ConditionCode: model.WMOUnknown}}
|
|
svc := NewService(repo)
|
|
|
|
_, err := svc.CurrentConditions(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if repo.currentConditionsWindow != ObservationWindowMinutesDefault {
|
|
t.Fatalf("expected observation window %d, got %d", ObservationWindowMinutesDefault, repo.currentConditionsWindow)
|
|
}
|
|
}
|
|
|
|
func TestServicePropagatesErrors(t *testing.T) {
|
|
want := errors.New("boom")
|
|
repo := &fakeRepository{err: want}
|
|
svc := NewService(repo)
|
|
|
|
if _, err := svc.LatestObservation(context.Background()); !errors.Is(err, want) {
|
|
t.Fatalf("expected error %v, got %v", want, err)
|
|
}
|
|
}
|