package api import ( "strings" "testing" ) func completeFlight(path string) flightRecord { return flightRecord{ OperationDate: "2026-10-22", StartTime: "14:05", EndTime: "14:17", Drone: "d1", AreaRoute: "Field north of the hangar", MaxAltitudeAGL: 80, PilotName: "pilot@example.com", Category: "open", Purpose: "commercial", LoggingPath: path, } } func TestNormalizeLoggingPath(t *testing.T) { cases := map[string]string{ "automatic": loggingFlyApp, " automatic ": loggingFlyApp, "automatic_flyapp": loggingFlyApp, "automatic_flyagentapp": loggingFlyAgentApp, "manual": loggingManual, "": "", } for in, want := range cases { if got := normalizeLoggingPath(in); got != want { t.Errorf("normalizeLoggingPath(%q) = %q, want %q", in, got, want) } } } func TestEffectiveLoggingPathDefaults(t *testing.T) { autolog := &droneRecord{AutologsFlights: true} if got := effectiveLoggingPath(flightRecord{}, autolog); got != loggingFlyApp { t.Errorf("autologging drone, blank path = %q, want %q", got, loggingFlyApp) } if got := effectiveLoggingPath(flightRecord{}, &droneRecord{}); got != loggingManual { t.Errorf("plain drone, blank path = %q, want %q", got, loggingManual) } if got := effectiveLoggingPath(flightRecord{LoggingPath: "automatic"}, nil); got != loggingFlyApp { t.Errorf("legacy automatic = %q, want %q", got, loggingFlyApp) } } func TestValidateFlightByPath(t *testing.T) { // The Fly App path is backed by the FDR, so a sparse entry is accepted. sparse := flightRecord{OperationDate: "2026-10-22", Drone: "d1", LoggingPath: loggingFlyApp} if msg, ok := validateFlight(sparse, nil); !ok { t.Errorf("sparse automatic_flyapp entry rejected: %s", msg) } // Manual and Fly Agent entries have no FDR behind them: ยง 5 fields are required. for _, path := range []string{loggingManual, loggingFlyAgentApp} { sparse.LoggingPath = path msg, ok := validateFlight(sparse, nil) if ok || !strings.Contains(msg, path) { t.Errorf("sparse %s entry: ok=%v msg=%q, want a rejection naming the path", path, ok, msg) } if msg, ok := validateFlight(completeFlight(path), nil); !ok { t.Errorf("complete %s entry rejected: %s", path, msg) } } if _, ok := validateFlight(completeFlight("companion"), nil); ok { t.Error("unknown logging path accepted") } } func TestComplianceRedFlagsByPath(t *testing.T) { autolog := &droneRecord{AutologsFlights: true} hasFDRFlag := func(c compliance) bool { for _, f := range c.RedFlags { if strings.Contains(f, "no FDR log") { return true } } return false } if c := computeCompliance(completeFlight(loggingFlyApp), autolog); !hasFDRFlag(c) { t.Error("automatic_flyapp without an FDR url should be flagged") } if c := computeCompliance(completeFlight(loggingFlyAgentApp), autolog); hasFDRFlag(c) { t.Error("automatic_flyagentapp never has an FDR; it must not be flagged for one") } agent := completeFlight(loggingFlyAgentApp) agent.AreaRoute = "" c := computeCompliance(agent, nil) if c.LoggingPath != loggingFlyAgentApp || len(c.RedFlags) == 0 { t.Errorf("automatic_flyagentapp missing area: path=%q flags=%v, want a missing-field flag", c.LoggingPath, c.RedFlags) } }