// 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 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) 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 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) } }