Refactored the application structure to better separate concerns between files and packages
Some checks failed
ci/woodpecker/push/build-image Pipeline failed
Some checks failed
ci/woodpecker/push/build-image Pipeline failed
This commit is contained in:
100
internal/app/service_test.go
Normal file
100
internal/app/service_test.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user