init push
This commit is contained in:
341
internal/transport/http/handlers/mission_handler_test.go
Normal file
341
internal/transport/http/handlers/mission_handler_test.go
Normal file
@@ -0,0 +1,341 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
flightdata "wucher/internal/domain/flight_data"
|
||||
"wucher/internal/domain/mission"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
)
|
||||
|
||||
func doReqMission(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 TestMissionHandlerDeleteByID(t *testing.T) {
|
||||
missionID := uuidv7.MustBytes()
|
||||
deletedBy := uuidv7.MustBytes()
|
||||
called := false
|
||||
|
||||
h := NewMissionHandler(&missionServiceMock{
|
||||
deleteByIDFn: func(_ context.Context, gotMissionID []byte, gotDeletedBy []byte) error {
|
||||
called = true
|
||||
if string(gotMissionID) != string(missionID) {
|
||||
t.Fatalf("unexpected mission id: %x", gotMissionID)
|
||||
}
|
||||
if string(gotDeletedBy) != string(deletedBy) {
|
||||
t.Fatalf("unexpected deleted by: %x", gotDeletedBy)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
app := fiber.New()
|
||||
app.Use(func(c *fiber.Ctx) error {
|
||||
c.Locals("user_id", deletedBy)
|
||||
return c.Next()
|
||||
})
|
||||
app.Delete("/api/v1/mission/delete-by-id/:mission_id", h.DeleteByID)
|
||||
|
||||
missionIDStr, err := uuidv7.BytesToString(missionID)
|
||||
if err != nil {
|
||||
t.Fatalf("mission id string: %v", err)
|
||||
}
|
||||
code, body := doReqMission(t, app, http.MethodDelete, "/api/v1/mission/delete-by-id/"+missionIDStr, nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("unexpected status: %d body=%s", code, body)
|
||||
}
|
||||
if !strings.Contains(body, `"type":"mission_delete"`) || !strings.Contains(body, `"deleted":true`) {
|
||||
t.Fatalf("unexpected body: %s", body)
|
||||
}
|
||||
if !called {
|
||||
t.Fatal("expected service DeleteByID to be called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMissionHandlerDeleteByIDValidation(t *testing.T) {
|
||||
h := NewMissionHandler(&missionServiceMock{})
|
||||
app := fiber.New()
|
||||
app.Delete("/api/v1/mission/delete-by-id/:mission_id", h.DeleteByID)
|
||||
|
||||
code, body := doReqMission(t, app, http.MethodDelete, "/api/v1/mission/delete-by-id/bad", nil)
|
||||
if code != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("unexpected status: %d body=%s", code, body)
|
||||
}
|
||||
if !strings.Contains(body, mission.ErrorMessageInvalidMissionID) {
|
||||
t.Fatalf("unexpected validation body: %s", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMissionHandlerList_ReturnsKeyedFlightDataForms(t *testing.T) {
|
||||
missionID := uuidv7.MustBytes()
|
||||
flightID := uuidv7.MustBytes()
|
||||
flightDataID := uuidv7.MustBytes()
|
||||
|
||||
h := NewMissionHandler(&missionServiceMock{
|
||||
listFn: func(context.Context, string, string, []byte, int, int) ([]mission.Mission, int64, error) {
|
||||
return []mission.Mission{{
|
||||
ID: missionID,
|
||||
FlightID: flightID,
|
||||
Type: "CAT",
|
||||
CreatedAt: time.Date(2026, time.June, 8, 8, 0, 0, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2026, time.June, 8, 8, 30, 0, 0, time.UTC),
|
||||
}}, 1, nil
|
||||
},
|
||||
}).WithFlightDataService(&flightDataServiceMock{
|
||||
listFn: func(_ context.Context, _ string, gotMissionID []byte, _ []byte, _ int, _ int) ([]flightdata.FlightData, int64, error) {
|
||||
if string(gotMissionID) != string(missionID) {
|
||||
t.Fatalf("unexpected mission id: %x", gotMissionID)
|
||||
}
|
||||
return []flightdata.FlightData{{
|
||||
ID: flightDataID,
|
||||
MissionID: gotMissionID,
|
||||
Mission: &mission.Mission{Type: "CAT"},
|
||||
Status: flightdata.StatusCompleted,
|
||||
CreatedAt: time.Date(2026, time.June, 8, 8, 28, 20, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2026, time.June, 8, 8, 28, 14, 0, time.UTC),
|
||||
}}, 1, nil
|
||||
},
|
||||
})
|
||||
|
||||
app := fiber.New()
|
||||
app.Get("/api/v1/mission/get-all", h.List)
|
||||
|
||||
flightIDStr, err := uuidv7.BytesToString(flightID)
|
||||
if err != nil {
|
||||
t.Fatalf("flight id string: %v", err)
|
||||
}
|
||||
code, body := doReqMission(t, app, http.MethodGet, "/api/v1/mission/get-all?flight_id="+flightIDStr, nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("unexpected status: %d body=%s", code, body)
|
||||
}
|
||||
|
||||
var doc map[string]any
|
||||
if err := json.Unmarshal([]byte(body), &doc); err != nil {
|
||||
t.Fatalf("unmarshal response: %v body=%s", err, body)
|
||||
}
|
||||
|
||||
data, ok := doc["data"].([]any)
|
||||
if !ok || len(data) != 1 {
|
||||
t.Fatalf("unexpected data payload: %#v", doc["data"])
|
||||
}
|
||||
resource, ok := data[0].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected resource payload: %#v", data[0])
|
||||
}
|
||||
attributes, ok := resource["attributes"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected attributes payload: %#v", resource["attributes"])
|
||||
}
|
||||
if _, ok := attributes["flight_data"]; ok {
|
||||
t.Fatalf("expected top-level flight_data to be removed: %s", body)
|
||||
}
|
||||
if got := attributes["status"]; got != "completed" {
|
||||
t.Fatalf("unexpected status: %#v", got)
|
||||
}
|
||||
forms, ok := attributes["forms"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected forms payload: %#v", attributes["forms"])
|
||||
}
|
||||
items, ok := forms["flight_data"].([]any)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected flight_data form payload: %#v", forms["flight_data"])
|
||||
}
|
||||
if len(items) != 1 {
|
||||
t.Fatalf("expected 1 flight_data item, got %d", len(items))
|
||||
}
|
||||
formItem, ok := items[0].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected flight_data item payload: %#v", items[0])
|
||||
}
|
||||
if got := formItem["type"]; got != "flight_data" {
|
||||
t.Fatalf("unexpected flight_data type: %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMissionHandlerList_StaysInProgressUntilAllFlightDataComplete(t *testing.T) {
|
||||
missionID := uuidv7.MustBytes()
|
||||
flightID := uuidv7.MustBytes()
|
||||
flightDataID := uuidv7.MustBytes()
|
||||
|
||||
h := NewMissionHandler(&missionServiceMock{
|
||||
listFn: func(context.Context, string, string, []byte, int, int) ([]mission.Mission, int64, error) {
|
||||
return []mission.Mission{{
|
||||
ID: missionID,
|
||||
FlightID: flightID,
|
||||
Type: "CAT",
|
||||
CreatedAt: time.Date(2026, time.June, 8, 8, 0, 0, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2026, time.June, 8, 8, 30, 0, 0, time.UTC),
|
||||
}}, 1, nil
|
||||
},
|
||||
}).WithFlightDataService(&flightDataServiceMock{
|
||||
listFn: func(_ context.Context, _ string, gotMissionID []byte, _ []byte, _ int, _ int) ([]flightdata.FlightData, int64, error) {
|
||||
if string(gotMissionID) != string(missionID) {
|
||||
t.Fatalf("unexpected mission id: %x", gotMissionID)
|
||||
}
|
||||
return []flightdata.FlightData{{
|
||||
ID: flightDataID,
|
||||
MissionID: gotMissionID,
|
||||
Mission: &mission.Mission{Type: "CAT"},
|
||||
Status: flightdata.StatusInProgress,
|
||||
CreatedAt: time.Date(2026, time.June, 8, 8, 28, 20, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2026, time.June, 8, 8, 28, 14, 0, time.UTC),
|
||||
}}, 1, nil
|
||||
},
|
||||
})
|
||||
|
||||
app := fiber.New()
|
||||
app.Get("/api/v1/mission/get-all", h.List)
|
||||
|
||||
flightIDStr, err := uuidv7.BytesToString(flightID)
|
||||
if err != nil {
|
||||
t.Fatalf("flight id string: %v", err)
|
||||
}
|
||||
code, body := doReqMission(t, app, http.MethodGet, "/api/v1/mission/get-all?flight_id="+flightIDStr, nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("unexpected status: %d body=%s", code, body)
|
||||
}
|
||||
|
||||
var doc map[string]any
|
||||
if err := json.Unmarshal([]byte(body), &doc); err != nil {
|
||||
t.Fatalf("unmarshal response: %v body=%s", err, body)
|
||||
}
|
||||
|
||||
data, ok := doc["data"].([]any)
|
||||
if !ok || len(data) != 1 {
|
||||
t.Fatalf("unexpected data payload: %#v", doc["data"])
|
||||
}
|
||||
resource, ok := data[0].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected resource payload: %#v", data[0])
|
||||
}
|
||||
attributes, ok := resource["attributes"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected attributes payload: %#v", resource["attributes"])
|
||||
}
|
||||
if got := attributes["status"]; got != "in_progress" {
|
||||
t.Fatalf("unexpected status: %#v", got)
|
||||
}
|
||||
forms, ok := attributes["forms"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected forms payload: %#v", attributes["forms"])
|
||||
}
|
||||
items, ok := forms["flight_data"].([]any)
|
||||
if !ok || len(items) != 1 {
|
||||
t.Fatalf("unexpected flight_data form payload: %#v", forms["flight_data"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestMissionHandlerListDatatable_FiltersAndMeta(t *testing.T) {
|
||||
missionID := uuidv7.MustBytes()
|
||||
flightID := uuidv7.MustBytes()
|
||||
pilotID := uuidv7.MustBytes()
|
||||
helicopterID := uuidv7.MustBytes()
|
||||
|
||||
h := NewMissionHandler(&missionServiceMock{
|
||||
listDTFn: func(_ context.Context, got mission.ListFilter, limit, offset int) ([]mission.Mission, int64, int64, error) {
|
||||
if got.Search != "needle" {
|
||||
t.Fatalf("unexpected search: %q", got.Search)
|
||||
}
|
||||
if got.MissionType != "CAT" {
|
||||
t.Fatalf("unexpected mission type: %q", got.MissionType)
|
||||
}
|
||||
if got.StartDate == nil || got.StartDate.Format("2006-01-02") != "2026-06-01" {
|
||||
t.Fatalf("unexpected start date: %#v", got.StartDate)
|
||||
}
|
||||
if got.EndDate == nil || got.EndDate.Format("2006-01-02") != "2026-06-30" {
|
||||
t.Fatalf("unexpected end date: %#v", got.EndDate)
|
||||
}
|
||||
if string(got.PilotID) != string(pilotID) {
|
||||
t.Fatalf("unexpected pilot id: %x", got.PilotID)
|
||||
}
|
||||
if string(got.HelicopterID) != string(helicopterID) {
|
||||
t.Fatalf("unexpected helicopter id: %x", got.HelicopterID)
|
||||
}
|
||||
if limit != 20 || offset != 0 {
|
||||
t.Fatalf("unexpected pagination: limit=%d offset=%d", limit, offset)
|
||||
}
|
||||
return []mission.Mission{{
|
||||
ID: missionID,
|
||||
FlightID: flightID,
|
||||
Type: "CAT",
|
||||
CreatedAt: time.Date(2026, time.June, 8, 8, 0, 0, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2026, time.June, 8, 8, 30, 0, 0, time.UTC),
|
||||
}}, 10, 2, nil
|
||||
},
|
||||
}).WithFlightDataService(&flightDataServiceMock{
|
||||
listFn: func(_ context.Context, _ string, gotMissionID []byte, _ []byte, _ int, _ int) ([]flightdata.FlightData, int64, error) {
|
||||
if string(gotMissionID) != string(missionID) {
|
||||
t.Fatalf("unexpected mission id: %x", gotMissionID)
|
||||
}
|
||||
return nil, 0, nil
|
||||
},
|
||||
})
|
||||
|
||||
app := fiber.New()
|
||||
app.Get("/api/v1/mission/get-all/dt", h.ListDatatable)
|
||||
|
||||
flightIDStr, err := uuidv7.BytesToString(flightID)
|
||||
if err != nil {
|
||||
t.Fatalf("flight id string: %v", err)
|
||||
}
|
||||
pilotIDStr, err := uuidv7.BytesToString(pilotID)
|
||||
if err != nil {
|
||||
t.Fatalf("pilot id string: %v", err)
|
||||
}
|
||||
helicopterIDStr, err := uuidv7.BytesToString(helicopterID)
|
||||
if err != nil {
|
||||
t.Fatalf("helicopter id string: %v", err)
|
||||
}
|
||||
code, body := doReqMission(t, app, http.MethodGet, "/api/v1/mission/get-all/dt?search=needle&date_from=2026-06-01&date_to=2026-06-30&mission_type=CAT&pilot_id="+pilotIDStr+"&helicopter_id="+helicopterIDStr+"&flight_id="+flightIDStr+"&draw=7&start=0&length=20", nil)
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("unexpected status: %d body=%s", code, body)
|
||||
}
|
||||
|
||||
var doc map[string]any
|
||||
if err := json.Unmarshal([]byte(body), &doc); err != nil {
|
||||
t.Fatalf("unmarshal response: %v body=%s", err, body)
|
||||
}
|
||||
|
||||
meta, ok := doc["meta"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected meta payload: %#v", doc["meta"])
|
||||
}
|
||||
if meta["draw"] != float64(7) {
|
||||
t.Fatalf("unexpected draw: %#v", meta["draw"])
|
||||
}
|
||||
if meta["records_total"] != float64(10) || meta["records_filtered"] != float64(2) {
|
||||
t.Fatalf("unexpected meta counts: %#v", meta)
|
||||
}
|
||||
data, ok := doc["data"].([]any)
|
||||
if !ok || len(data) != 1 {
|
||||
t.Fatalf("unexpected data payload: %#v", doc["data"])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user