init push
This commit is contained in:
675
internal/transport/http/handlers/flight_handler_test.go
Normal file
675
internal/transport/http/handlers/flight_handler_test.go
Normal file
@@ -0,0 +1,675 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
afterflightinspection "wucher/internal/domain/after_flight_inspection"
|
||||
"wucher/internal/domain/flight"
|
||||
flightdata "wucher/internal/domain/flight_data"
|
||||
flightinspection "wucher/internal/domain/flight_inspection"
|
||||
"wucher/internal/domain/mission"
|
||||
reserveac "wucher/internal/domain/reserve_ac"
|
||||
takeoverdomain "wucher/internal/domain/takeover"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
)
|
||||
|
||||
type flightServiceMock struct {
|
||||
createFn func(context.Context, *flight.Flight) error
|
||||
getByIDFn func(context.Context, []byte) (*flight.Flight, error)
|
||||
listByUserIDFn func(context.Context, []byte, int, int) ([]flight.Flight, int64, error)
|
||||
listFn func(context.Context, string, string, string, int, int) ([]flight.Flight, int64, error)
|
||||
}
|
||||
|
||||
type missionServiceMock struct {
|
||||
listByFlightIDFn func(context.Context, []byte) ([]mission.Mission, error)
|
||||
listByFlightIDsFn func(context.Context, [][]byte) ([]mission.Mission, error)
|
||||
listFn func(context.Context, string, string, []byte, int, int) ([]mission.Mission, int64, error)
|
||||
listDTFn func(context.Context, mission.ListFilter, int, int) ([]mission.Mission, int64, int64, error)
|
||||
deleteByIDFn func(context.Context, []byte, []byte) error
|
||||
}
|
||||
|
||||
type afterFlightInspectionServiceMock struct {
|
||||
getByFlightInspectionIDFn func(context.Context, []byte) (*afterflightinspection.AfterFlightInspection, error)
|
||||
}
|
||||
|
||||
type takeoverServiceMock struct {
|
||||
getByIDFn func(context.Context, []byte) (*takeoverdomain.TakeoverAc, error)
|
||||
}
|
||||
|
||||
func (m *flightServiceMock) Create(ctx context.Context, row *flight.Flight) error {
|
||||
if m.createFn != nil {
|
||||
return m.createFn(ctx, row)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *flightServiceMock) Update(context.Context, *flight.Flight) error { return nil }
|
||||
func (m *flightServiceMock) Delete(context.Context, []byte, []byte) error { return nil }
|
||||
func (m *flightServiceMock) GetByID(ctx context.Context, id []byte) (*flight.Flight, error) {
|
||||
if m.getByIDFn != nil {
|
||||
return m.getByIDFn(ctx, id)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (m *flightServiceMock) ListByUserID(ctx context.Context, userID []byte, limit, offset int) ([]flight.Flight, int64, error) {
|
||||
if m.listByUserIDFn != nil {
|
||||
return m.listByUserIDFn(ctx, userID, limit, offset)
|
||||
}
|
||||
return nil, 0, nil
|
||||
}
|
||||
func (m *flightServiceMock) ListByCreatedBy(context.Context, []byte, string, string, string, int, int) ([]flight.Flight, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
func (m *flightServiceMock) GetByReserveAcID(context.Context, []byte) (*flight.Flight, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *flightServiceMock) ListByReserveAcID(context.Context, []byte) ([]flight.Flight, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *flightServiceMock) GetByTakeoverAcID(context.Context, []byte) (*flight.Flight, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *flightServiceMock) ListByTakeoverAcIDs(context.Context, [][]byte) ([]flight.Flight, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *flightServiceMock) GetByDutyRosterID(context.Context, []byte) (*flight.Flight, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *flightServiceMock) ListByDutyRosterIDs(context.Context, [][]byte) ([]flight.Flight, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *flightServiceMock) List(ctx context.Context, filter string, date string, sort string, limit, offset int) ([]flight.Flight, int64, error) {
|
||||
if m.listFn != nil {
|
||||
return m.listFn(ctx, filter, date, sort, limit, offset)
|
||||
}
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
func (m *missionServiceMock) Create(context.Context, *mission.Mission) error { return nil }
|
||||
func (m *missionServiceMock) CreateType(context.Context, *mission.MissionCategory) error {
|
||||
return nil
|
||||
}
|
||||
func (m *missionServiceMock) UpdateByID(context.Context, []byte, string, []byte, string, []byte) error {
|
||||
return nil
|
||||
}
|
||||
func (m *missionServiceMock) DeleteByFlightID(context.Context, []byte, []byte) error { return nil }
|
||||
func (m *missionServiceMock) AttachFile(context.Context, []byte, []byte) error { return nil }
|
||||
func (m *missionServiceMock) DetachFile(context.Context, []byte, []byte) error { return nil }
|
||||
func (m *missionServiceMock) DeleteByID(ctx context.Context, missionID []byte, deletedBy []byte) error {
|
||||
if m.deleteByIDFn != nil {
|
||||
return m.deleteByIDFn(ctx, missionID, deletedBy)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *missionServiceMock) ListByFlightID(ctx context.Context, flightID []byte) ([]mission.Mission, error) {
|
||||
if m.listByFlightIDFn != nil {
|
||||
return m.listByFlightIDFn(ctx, flightID)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (m *missionServiceMock) ListByFlightIDs(ctx context.Context, flightIDs [][]byte) ([]mission.Mission, error) {
|
||||
if m.listByFlightIDsFn != nil {
|
||||
return m.listByFlightIDsFn(ctx, flightIDs)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (m *missionServiceMock) GetByID(context.Context, []byte) (*mission.Mission, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *missionServiceMock) GetByFlightID(context.Context, []byte) (*mission.Mission, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *missionServiceMock) List(ctx context.Context, filter string, sort string, flightID []byte, flightDataStatus string, limit, offset int) ([]mission.Mission, int64, error) {
|
||||
if m.listFn != nil {
|
||||
return m.listFn(ctx, filter, sort, flightID, limit, offset)
|
||||
}
|
||||
return nil, 0, nil
|
||||
}
|
||||
func (m *missionServiceMock) ListDatatable(ctx context.Context, filter mission.ListFilter, limit, offset int) ([]mission.Mission, int64, int64, error) {
|
||||
if m.listDTFn != nil {
|
||||
return m.listDTFn(ctx, filter, limit, offset)
|
||||
}
|
||||
return nil, 0, 0, nil
|
||||
}
|
||||
func (m *missionServiceMock) ListTypes(context.Context) ([]mission.MissionCategory, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *afterFlightInspectionServiceMock) Upsert(context.Context, []byte, *afterflightinspection.UpsertRequest) (*afterflightinspection.AfterFlightInspection, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *afterFlightInspectionServiceMock) GetByFlightInspectionID(ctx context.Context, id []byte) (*afterflightinspection.AfterFlightInspection, error) {
|
||||
if m.getByFlightInspectionIDFn != nil {
|
||||
return m.getByFlightInspectionIDFn(ctx, id)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (m *afterFlightInspectionServiceMock) DeleteByFlightInspectionID(context.Context, []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *takeoverServiceMock) Create(context.Context, *takeoverdomain.TakeoverAc) error { return nil }
|
||||
func (m *takeoverServiceMock) Update(context.Context, *takeoverdomain.TakeoverAc) error { return nil }
|
||||
func (m *takeoverServiceMock) Delete(context.Context, []byte, []byte) error { return nil }
|
||||
func (m *takeoverServiceMock) AttachFile(context.Context, []byte, []byte) error { return nil }
|
||||
func (m *takeoverServiceMock) GetByID(ctx context.Context, id []byte) (*takeoverdomain.TakeoverAc, error) {
|
||||
if m.getByIDFn != nil {
|
||||
return m.getByIDFn(ctx, id)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (m *takeoverServiceMock) List(context.Context, int, int) ([]takeoverdomain.TakeoverAc, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
func newFlightApp(h *FlightHandler, withActor bool) *fiber.App {
|
||||
app := fiber.New()
|
||||
app.Use(func(c *fiber.Ctx) error {
|
||||
if withActor {
|
||||
c.Locals("user_id", uuidv7.MustBytes())
|
||||
}
|
||||
return c.Next()
|
||||
})
|
||||
app.Post("/flights", h.Create)
|
||||
app.Get("/flights/me", h.GetByAuthUser)
|
||||
app.Get("/flights", h.List)
|
||||
return app
|
||||
}
|
||||
|
||||
func doReqFlight(t *testing.T, app *fiber.App, method, path string, body []byte) (int, string) {
|
||||
t.Helper()
|
||||
var rdr io.Reader
|
||||
if body != nil {
|
||||
rdr = bytes.NewReader(body)
|
||||
}
|
||||
req, err := http.NewRequest(method, path, rdr)
|
||||
if err != nil {
|
||||
t.Fatalf("new req: %v", err)
|
||||
}
|
||||
if body != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("app.Test: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
return resp.StatusCode, string(b)
|
||||
}
|
||||
|
||||
func TestFlightHandler(t *testing.T) {
|
||||
t.Run("new and with reserve ac", func(t *testing.T) {
|
||||
h := NewFlightHandler(&flightServiceMock{})
|
||||
if h == nil || h.validate == nil {
|
||||
t.Fatal("expected handler initialized")
|
||||
}
|
||||
if h.WithReserveAcService(nil) != h {
|
||||
t.Fatal("expected same pointer")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create invalid json", func(t *testing.T) {
|
||||
h := NewFlightHandler(&flightServiceMock{})
|
||||
code, _ := doReqFlight(t, newFlightApp(h, true), http.MethodPost, "/flights", []byte(`{"data":`))
|
||||
if code != http.StatusBadRequest {
|
||||
t.Fatalf("expected 400 got %d", code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create validation", func(t *testing.T) {
|
||||
h := NewFlightHandler(&flightServiceMock{})
|
||||
code, _ := doReqFlight(t, newFlightApp(h, true), http.MethodPost, "/flights", []byte(`{}`))
|
||||
if code != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("expected 422 got %d", code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create date invalid", func(t *testing.T) {
|
||||
h := NewFlightHandler(&flightServiceMock{})
|
||||
body := []byte(`{"data":{"type":"flight","attributes":{"date":"x"}}}`)
|
||||
code, _ := doReqFlight(t, newFlightApp(h, true), http.MethodPost, "/flights", body)
|
||||
if code != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("expected 422 got %d", code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create optional uuid invalid", func(t *testing.T) {
|
||||
h := NewFlightHandler(&flightServiceMock{})
|
||||
body := []byte(`{"data":{"type":"flight","attributes":{"date":"2026-04-14","duty_roster_id":"bad"}}}`)
|
||||
code, _ := doReqFlight(t, newFlightApp(h, true), http.MethodPost, "/flights", body)
|
||||
if code != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("expected 422 got %d", code)
|
||||
}
|
||||
body = []byte(`{"data":{"type":"flight","attributes":{"date":"2026-04-14","reserve_ac_id":"bad"}}}`)
|
||||
code, _ = doReqFlight(t, newFlightApp(h, true), http.MethodPost, "/flights", body)
|
||||
if code != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("expected 422 got %d", code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create service error", func(t *testing.T) {
|
||||
h := NewFlightHandler(&flightServiceMock{
|
||||
createFn: func(context.Context, *flight.Flight) error { return errors.New("boom") },
|
||||
})
|
||||
body := []byte(`{"data":{"type":"flight","attributes":{"date":"2026-04-14"}}}`)
|
||||
code, _ := doReqFlight(t, newFlightApp(h, true), http.MethodPost, "/flights", body)
|
||||
if code != http.StatusBadRequest {
|
||||
t.Fatalf("expected 400 got %d", code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create success with refetch and fallback", func(t *testing.T) {
|
||||
var createdID []byte
|
||||
h := NewFlightHandler(&flightServiceMock{
|
||||
createFn: func(_ context.Context, row *flight.Flight) error {
|
||||
row.ID = uuidv7.MustBytes()
|
||||
createdID = append([]byte(nil), row.ID...)
|
||||
return nil
|
||||
},
|
||||
getByIDFn: func(_ context.Context, id []byte) (*flight.Flight, error) {
|
||||
if string(id) != string(createdID) {
|
||||
t.Fatal("unexpected id")
|
||||
}
|
||||
return &flight.Flight{
|
||||
ID: id,
|
||||
Date: time.Date(2026, 4, 14, 0, 0, 0, 0, time.UTC),
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
}, nil
|
||||
},
|
||||
})
|
||||
body := []byte(`{"data":{"type":"flight","attributes":{"date":"2026-04-14","duty_roster_id":"` + mustUUIDStringHospital(t, uuidv7.MustBytes()) + `","reserve_ac_id":"` + mustUUIDStringHospital(t, uuidv7.MustBytes()) + `"}}}`)
|
||||
code, _ := doReqFlight(t, newFlightApp(h, true), http.MethodPost, "/flights", body)
|
||||
if code != http.StatusCreated {
|
||||
t.Fatalf("expected 201 got %d", code)
|
||||
}
|
||||
|
||||
h2 := NewFlightHandler(&flightServiceMock{
|
||||
createFn: func(_ context.Context, row *flight.Flight) error {
|
||||
row.ID = uuidv7.MustBytes()
|
||||
row.CreatedAt = time.Now().UTC()
|
||||
row.UpdatedAt = row.CreatedAt
|
||||
return nil
|
||||
},
|
||||
getByIDFn: func(context.Context, []byte) (*flight.Flight, error) { return nil, errors.New("x") },
|
||||
})
|
||||
code, _ = doReqFlight(t, newFlightApp(h2, true), http.MethodPost, "/flights", []byte(`{"data":{"type":"flight","attributes":{"date":"2026-04-14"}}}`))
|
||||
if code != http.StatusCreated {
|
||||
t.Fatalf("expected 201 got %d", code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("get by auth user branches", func(t *testing.T) {
|
||||
h := NewFlightHandler(&flightServiceMock{})
|
||||
code, _ := doReqFlight(t, newFlightApp(h, false), http.MethodGet, "/flights/me", nil)
|
||||
if code != http.StatusUnauthorized {
|
||||
t.Fatalf("expected 401 got %d", code)
|
||||
}
|
||||
|
||||
hErr := NewFlightHandler(&flightServiceMock{
|
||||
listByUserIDFn: func(context.Context, []byte, int, int) ([]flight.Flight, int64, error) {
|
||||
return nil, 0, errors.New("err")
|
||||
},
|
||||
})
|
||||
code, _ = doReqFlight(t, newFlightApp(hErr, true), http.MethodGet, "/flights/me?page[number]=0&page[size]=500", nil)
|
||||
if code != http.StatusBadRequest {
|
||||
t.Fatalf("expected 400 got %d", code)
|
||||
}
|
||||
|
||||
row := flight.Flight{
|
||||
ID: uuidv7.MustBytes(),
|
||||
Date: time.Date(2026, 4, 14, 0, 0, 0, 0, time.UTC),
|
||||
DutyRosterID: uuidv7.MustBytes(),
|
||||
ReserveAcID: uuidv7.MustBytes(),
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
}
|
||||
|
||||
hOK := NewFlightHandler(&flightServiceMock{
|
||||
listByUserIDFn: func(_ context.Context, _ []byte, limit, offset int) ([]flight.Flight, int64, error) {
|
||||
if limit != 100 || offset != 0 {
|
||||
t.Fatalf("unexpected paging %d %d", limit, offset)
|
||||
}
|
||||
return []flight.Flight{row}, 1, nil
|
||||
},
|
||||
})
|
||||
code, body := doReqFlight(t, newFlightApp(hOK, true), http.MethodGet, "/flights/me?page[number]=0&page[size]=500", nil)
|
||||
if code != http.StatusOK || !strings.Contains(body, `"flight"`) {
|
||||
t.Fatalf("expected 200 flight body, got %d %s", code, body)
|
||||
}
|
||||
|
||||
hPg := NewFlightHandler(&flightServiceMock{
|
||||
listByUserIDFn: func(_ context.Context, _ []byte, limit, offset int) ([]flight.Flight, int64, error) {
|
||||
if limit <= 0 || offset != 0 {
|
||||
t.Fatalf("unexpected paging")
|
||||
}
|
||||
return nil, 0, nil
|
||||
},
|
||||
})
|
||||
code, _ = doReqFlight(t, newFlightApp(hPg, true), http.MethodGet, "/flights/me?page=1&limit=5", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
code, _ = doReqFlight(t, newFlightApp(hPg, true), http.MethodGet, "/flights/me?page=1&size=7", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
code, _ = doReqFlight(t, newFlightApp(hPg, true), http.MethodGet, "/flights/me?page=-1&page[size]=-1", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
code, _ = doReqFlight(t, newFlightApp(hPg, true), http.MethodGet, "/flights/me?page=1&page[size]=-1&limit=-1&size=-1", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("list branches", func(t *testing.T) {
|
||||
hErr := NewFlightHandler(&flightServiceMock{
|
||||
listFn: func(context.Context, string, string, string, int, int) ([]flight.Flight, int64, error) {
|
||||
return nil, 0, errors.New("no")
|
||||
},
|
||||
})
|
||||
code, _ := doReqFlight(t, newFlightApp(hErr, true), http.MethodGet, "/flights?search=x", nil)
|
||||
if code != http.StatusBadRequest {
|
||||
t.Fatalf("expected 400 got %d", code)
|
||||
}
|
||||
|
||||
hOK := NewFlightHandler(&flightServiceMock{
|
||||
listFn: func(_ context.Context, filter, date, sort string, limit, offset int) ([]flight.Flight, int64, error) {
|
||||
if filter != "abc" || sort != "-date" || limit != 0 || offset != 0 {
|
||||
t.Fatalf("unexpected args")
|
||||
}
|
||||
return []flight.Flight{{ID: uuidv7.MustBytes(), Date: time.Now().UTC(), CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC()}}, 1, nil
|
||||
},
|
||||
}).WithMissionService(&missionServiceMock{
|
||||
listByFlightIDFn: func(_ context.Context, _ []byte) ([]mission.Mission, error) {
|
||||
return []mission.Mission{{ID: uuidv7.MustBytes(), Type: "CAT", CreatedAt: time.Now().UTC()}}, nil
|
||||
},
|
||||
})
|
||||
code, body := doReqFlight(t, newFlightApp(hOK, true), http.MethodGet, "/flights?filter[search]=abc&sort=-date&page[number]=0&page[size]=0", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
if !strings.Contains(body, `"mission_id"`) {
|
||||
t.Fatalf("expected mission_id in response, got %s", body)
|
||||
}
|
||||
|
||||
hProgress := NewFlightHandler(&flightServiceMock{
|
||||
listFn: func(_ context.Context, filter, date, sort string, limit, offset int) ([]flight.Flight, int64, error) {
|
||||
if filter != "" || date != "" || sort != "" || limit != 0 || offset != 0 {
|
||||
t.Fatalf("unexpected args")
|
||||
}
|
||||
return []flight.Flight{{
|
||||
ID: uuidv7.MustBytes(),
|
||||
Date: time.Now().UTC(),
|
||||
TakeoverAcID: uuidv7.MustBytes(),
|
||||
Takeover: &takeoverdomain.TakeoverAc{
|
||||
ReserveAcID: uuidv7.MustBytes(),
|
||||
ReserveAc: &reserveac.ReserveAc{Status: "onprogress"},
|
||||
},
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
}}, 1, nil
|
||||
},
|
||||
})
|
||||
code, body = doReqFlight(t, newFlightApp(hProgress, true), http.MethodGet, "/flights", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
if !strings.Contains(body, `"status":"in_progress"`) {
|
||||
t.Fatalf("expected on_progress status, got %s", body)
|
||||
}
|
||||
if !strings.Contains(body, `"complete":false`) {
|
||||
t.Fatalf("expected complete=false in response, got %s", body)
|
||||
}
|
||||
|
||||
inspectionID := uuidv7.MustBytes()
|
||||
afterID := uuidv7.MustBytes()
|
||||
hAfter := NewFlightHandler(&flightServiceMock{
|
||||
listFn: func(_ context.Context, filter, date, sort string, limit, offset int) ([]flight.Flight, int64, error) {
|
||||
if filter != "" || date != "" || sort != "" || limit != 0 || offset != 0 {
|
||||
t.Fatalf("unexpected args")
|
||||
}
|
||||
return []flight.Flight{{
|
||||
ID: uuidv7.MustBytes(),
|
||||
Date: time.Now().UTC(),
|
||||
TakeoverAcID: uuidv7.MustBytes(),
|
||||
Takeover: &takeoverdomain.TakeoverAc{
|
||||
ReserveAcID: uuidv7.MustBytes(),
|
||||
ReserveAc: &reserveac.ReserveAc{
|
||||
Inspection: &flightinspection.FlightInspection{ID: inspectionID},
|
||||
},
|
||||
},
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
}}, 1, nil
|
||||
},
|
||||
}).WithMissionService(&missionServiceMock{
|
||||
listByFlightIDFn: func(_ context.Context, _ []byte) ([]mission.Mission, error) {
|
||||
return []mission.Mission{{ID: uuidv7.MustBytes(), Type: "CAT", CreatedAt: time.Now().UTC()}}, nil
|
||||
},
|
||||
}).WithAfterFlightInspectionService(&afterFlightInspectionServiceMock{
|
||||
getByFlightInspectionIDFn: func(_ context.Context, id []byte) (*afterflightinspection.AfterFlightInspection, error) {
|
||||
if string(id) != string(inspectionID) {
|
||||
t.Fatalf("unexpected inspection id")
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
return &afterflightinspection.AfterFlightInspection{
|
||||
ID: afterID,
|
||||
FlightInspectionID: inspectionID,
|
||||
CreatedAt: now,
|
||||
CreatedBy: uuidv7.MustBytes(),
|
||||
UpdatedAt: now,
|
||||
UpdatedBy: uuidv7.MustBytes(),
|
||||
}, nil
|
||||
},
|
||||
}).WithTakeoverService(&takeoverServiceMock{
|
||||
getByIDFn: func(_ context.Context, id []byte) (*takeoverdomain.TakeoverAc, error) {
|
||||
if len(id) != 16 {
|
||||
t.Fatalf("unexpected takeover id")
|
||||
}
|
||||
return &takeoverdomain.TakeoverAc{
|
||||
ID: id,
|
||||
BaseID: uuidv7.MustBytes(),
|
||||
ReserveAc: &reserveac.ReserveAc{
|
||||
AircraftID: uuidv7.MustBytes(),
|
||||
Inspection: &flightinspection.FlightInspection{ID: inspectionID},
|
||||
},
|
||||
RosterCrews: []takeoverdomain.TakeoverRosterCrew{{ID: uuidv7.MustBytes()}},
|
||||
OtherPeople: []takeoverdomain.TakeoverOtherPerson{{ID: uuidv7.MustBytes()}},
|
||||
}, nil
|
||||
},
|
||||
})
|
||||
code, body = doReqFlight(t, newFlightApp(hAfter, true), http.MethodGet, "/flights", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
if !strings.Contains(body, `"phase":"after_flight_inspection"`) {
|
||||
t.Fatalf("expected after_flight_inspection step, got %s", body)
|
||||
}
|
||||
if !strings.Contains(body, `"step":3`) {
|
||||
t.Fatalf("expected step 3 in response, got %s", body)
|
||||
}
|
||||
if !strings.Contains(body, `"complete":true`) {
|
||||
t.Fatalf("expected complete=true in response, got %s", body)
|
||||
}
|
||||
|
||||
code, _ = doReqFlight(t, newFlightApp(hOK, true), http.MethodGet, "/flights?page=1&limit=5&search=abc&sort=-date", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
code, _ = doReqFlight(t, newFlightApp(hOK, true), http.MethodGet, "/flights?page=1&size=7&search=abc&sort=-date", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
code, _ = doReqFlight(t, newFlightApp(hOK, true), http.MethodGet, "/flights?page=-1&page[size]=-1&search=abc&sort=-date", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
code, _ = doReqFlight(t, newFlightApp(hOK, true), http.MethodGet, "/flights?page=1&page[size]=-1&limit=-1&size=-1&search=abc&sort=-date", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
code, _ = doReqFlight(t, newFlightApp(hOK, true), http.MethodGet, "/flights?page=1&page[size]=200&search=abc&sort=-date", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("expected 200 got %d", code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("helpers", func(t *testing.T) {
|
||||
if stringPtrOrEmpty(nil) != "" {
|
||||
t.Fatal("expected empty")
|
||||
}
|
||||
v := " x "
|
||||
if stringPtrOrEmpty(&v) != "x" {
|
||||
t.Fatal("expected trimmed")
|
||||
}
|
||||
if timeStringOrEmptyRefTime(nil) != "" || timeStringOrEmptyRefTime(&time.Time{}) != "" {
|
||||
t.Fatal("expected empty")
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
if timeStringOrEmptyRefTime(&now) == "" {
|
||||
t.Fatal("expected time")
|
||||
}
|
||||
raw := "bad"
|
||||
if _, errObj := parseOptionalUUIDFlight(&raw, "/data/attributes/duty_roster_id"); errObj == nil {
|
||||
t.Fatal("expected err")
|
||||
}
|
||||
raw = " "
|
||||
if id, errObj := parseOptionalUUIDFlight(&raw, "/data/attributes/duty_roster_id"); errObj != nil || id != nil {
|
||||
t.Fatal("expected nil")
|
||||
}
|
||||
raw = mustUUIDStringHospital(t, uuidv7.MustBytes())
|
||||
if id, errObj := parseOptionalUUIDFlight(&raw, "/data/attributes/duty_roster_id"); errObj != nil || len(id) != 16 {
|
||||
t.Fatal("expected id")
|
||||
}
|
||||
if s := flightMissionStep(nil); s.Status != "unassigned" {
|
||||
t.Fatal("expected unassigned")
|
||||
}
|
||||
if s := flightDutyRosterStep(nil); s.Status != "unassigned" {
|
||||
t.Fatal("expected unassigned")
|
||||
}
|
||||
if s := flightReserveAcStep(nil); s.Status != "unassigned" {
|
||||
t.Fatal("expected unassigned")
|
||||
}
|
||||
if s := flightReserveAcStep(&flight.Flight{Takeover: &takeoverdomain.TakeoverAc{ReserveAcID: uuidv7.MustBytes(), ReserveAc: &reserveac.ReserveAc{Status: "pending"}}}); s.Status != "pending" {
|
||||
t.Fatal("expected pending")
|
||||
}
|
||||
if s := flightReserveAcStep(&flight.Flight{Takeover: &takeoverdomain.TakeoverAc{ReserveAcID: uuidv7.MustBytes(), ReserveAc: &reserveac.ReserveAc{Status: "onprogress"}}}); s.Status != "in_progress" {
|
||||
t.Fatal("expected on progress")
|
||||
}
|
||||
if s := flightReserveAcStep(&flight.Flight{Takeover: &takeoverdomain.TakeoverAc{ReserveAcID: uuidv7.MustBytes(), ReserveAc: &reserveac.ReserveAc{Status: "approved", UpdatedAt: time.Now().UTC(), UpdatedBy: uuidv7.MustBytes()}}}); s.Status != "completed" {
|
||||
t.Fatal("expected completed")
|
||||
}
|
||||
if s := flightMissionStep(&flight.Flight{}); s.Status != "unassigned" {
|
||||
t.Fatal("expected mission unassigned")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestFlightHandlerList_MissionStepShowsInProgressUntilFlightDataComplete(t *testing.T) {
|
||||
flightID := uuidv7.MustBytes()
|
||||
missionID := uuidv7.MustBytes()
|
||||
|
||||
h := NewFlightHandler(&flightServiceMock{
|
||||
listFn: func(context.Context, string, string, string, int, int) ([]flight.Flight, int64, error) {
|
||||
return []flight.Flight{{
|
||||
ID: flightID,
|
||||
Date: time.Date(2026, time.June, 26, 0, 0, 0, 0, time.UTC),
|
||||
CreatedAt: time.Date(2026, time.June, 24, 4, 31, 37, 0, time.UTC),
|
||||
CreatedBy: []byte("creator-id-12345"),
|
||||
}}, 1, nil
|
||||
},
|
||||
}).WithMissionService(&missionServiceMock{
|
||||
listByFlightIDFn: func(context.Context, []byte) ([]mission.Mission, error) {
|
||||
return []mission.Mission{{
|
||||
ID: missionID,
|
||||
FlightID: flightID,
|
||||
Type: "CAT",
|
||||
CreatedAt: time.Date(2026, time.June, 24, 5, 23, 3, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2026, time.June, 24, 5, 23, 3, 0, time.UTC),
|
||||
}}, nil
|
||||
},
|
||||
}).WithFlightDataService(&flightDataServiceMock{
|
||||
getByMisFn: func(context.Context, []byte) (*flightdata.FlightData, error) {
|
||||
return &flightdata.FlightData{Status: flightdata.StatusInProgress}, nil
|
||||
},
|
||||
})
|
||||
|
||||
app := fiber.New()
|
||||
app.Use(func(c *fiber.Ctx) error {
|
||||
c.Locals("user_id", uuidv7.MustBytes())
|
||||
return c.Next()
|
||||
})
|
||||
app.Get("/flights", h.List)
|
||||
|
||||
code, body := doReqFlight(t, app, http.MethodGet, "/flights?date=2026-06-26&limit=2000", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("unexpected status: %d body=%s", code, body)
|
||||
}
|
||||
if !strings.Contains(body, `"phase":"mission"`) {
|
||||
t.Fatalf("expected mission step in body: %s", body)
|
||||
}
|
||||
if !strings.Contains(body, `"status":"in_progress"`) {
|
||||
t.Fatalf("expected mission step in_progress: %s", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlightHandlerList_MissionStepShowsCompletedWhenFlightDataComplete(t *testing.T) {
|
||||
flightID := uuidv7.MustBytes()
|
||||
missionID := uuidv7.MustBytes()
|
||||
|
||||
h := NewFlightHandler(&flightServiceMock{
|
||||
listFn: func(context.Context, string, string, string, int, int) ([]flight.Flight, int64, error) {
|
||||
return []flight.Flight{{
|
||||
ID: flightID,
|
||||
Date: time.Date(2026, time.June, 26, 0, 0, 0, 0, time.UTC),
|
||||
CreatedAt: time.Date(2026, time.June, 24, 4, 31, 37, 0, time.UTC),
|
||||
CreatedBy: []byte("creator-id-12345"),
|
||||
}}, 1, nil
|
||||
},
|
||||
}).WithMissionService(&missionServiceMock{
|
||||
listByFlightIDFn: func(context.Context, []byte) ([]mission.Mission, error) {
|
||||
return []mission.Mission{{
|
||||
ID: missionID,
|
||||
FlightID: flightID,
|
||||
Type: "CAT",
|
||||
CreatedAt: time.Date(2026, time.June, 24, 5, 23, 3, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2026, time.June, 24, 6, 41, 45, 0, time.UTC),
|
||||
UpdatedBy: []byte("test test"),
|
||||
}}, nil
|
||||
},
|
||||
}).WithFlightDataService(&flightDataServiceMock{
|
||||
getByMisFn: func(context.Context, []byte) (*flightdata.FlightData, error) {
|
||||
return &flightdata.FlightData{Status: flightdata.StatusCompleted}, nil
|
||||
},
|
||||
})
|
||||
|
||||
app := fiber.New()
|
||||
app.Use(func(c *fiber.Ctx) error {
|
||||
c.Locals("user_id", uuidv7.MustBytes())
|
||||
return c.Next()
|
||||
})
|
||||
app.Get("/flights", h.List)
|
||||
|
||||
code, body := doReqFlight(t, app, http.MethodGet, "/flights?date=2026-06-26&limit=2000", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("unexpected status: %d body=%s", code, body)
|
||||
}
|
||||
if !strings.Contains(body, `"phase":"mission"`) {
|
||||
t.Fatalf("expected mission step in body: %s", body)
|
||||
}
|
||||
if !strings.Contains(body, `"status":"completed"`) {
|
||||
t.Fatalf("expected mission step completed: %s", body)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user