func TestTrackTrackCargo(t *testing.T) { var ( cargos = repository.NewInMemcargo() handlingEvents = repository.NewInMemHandlingEvent() service = NewService(cargos, handlingEvents) ) c := cargo.New("TEST", cargo.RouteSpecification{ Origin: "SESTO", Destination: "FIHEL", ArrivalDeadline: time.Date(2005, 12, 4, 0, 0, 0, 0, time.UTC), }) cargos.Store(c) ctx := context.Background() logger := log.NewLogfmtLogger(ioutil.Discard) h := MakeHandler(ctx, service, logger) req, _ := http.NewRequest("GET", "http://example.com/tracking/v1/cargos/TEST", nil) rec := httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Errorf("rec.Code = %d; want = %d", rec.Code, http.StatusOK) } if content := rec.Header().Get("Content-Type"); content != "application/json; charset=utf-8" { t.Errorf("Content-Type = %q; want = %q", content, "application/json; charset=utf-8") } var response trackCargoResponse if err := json.NewDecoder(rec.Body).Decode(&response); err != nil { t.Error(err) } if response.Err != nil { t.Errorf("response.Err = %q", response.Err) } var eta time.Time want := Cargo{ TrackingID: "TEST", Origin: "SESTO", Destination: "FIHEL", ArrivalDeadline: time.Date(2005, 12, 4, 0, 0, 0, 0, time.UTC), ETA: eta.In(time.UTC), StatusText: "Not received", NextExpectedActivity: "There are currently no expected activities for this cargo.", Events: nil, } if !reflect.DeepEqual(want, *response.Cargo) { t.Errorf("response.Cargo = %#v; want = %#v", response.Cargo, want) } }
func (s *S) TestInspectUnloadedCargo(c *C) { var ( cargoRepository = repository.NewInMemcargo() handlingEventRepository = repository.NewInMemHandlingEvent() cargoEventHandler = &stubEventHandler{make([]interface{}, 0)} inspectionService = &service{ cargoRepository: cargoRepository, handlingEventRepository: handlingEventRepository, cargoEventHandler: cargoEventHandler, } ) trackingID := cargo.TrackingID("ABC123") unloadedCargo := cargo.New(trackingID, cargo.RouteSpecification{ Origin: location.SESTO, Destination: location.CNHKG, }) var voyageNumber voyage.Number = "001A" unloadedCargo.AssignToRoute(cargo.Itinerary{Legs: []cargo.Leg{ {VoyageNumber: voyageNumber, LoadLocation: location.SESTO, UnloadLocation: location.AUMEL}, {VoyageNumber: voyageNumber, LoadLocation: location.AUMEL, UnloadLocation: location.CNHKG}, }}) cargoRepository.Store(unloadedCargo) storeEvent(handlingEventRepository, trackingID, voyageNumber, cargo.Receive, location.SESTO) storeEvent(handlingEventRepository, trackingID, voyageNumber, cargo.Load, location.SESTO) storeEvent(handlingEventRepository, trackingID, voyageNumber, cargo.Unload, location.AUMEL) storeEvent(handlingEventRepository, trackingID, voyageNumber, cargo.Load, location.AUMEL) storeEvent(handlingEventRepository, trackingID, voyageNumber, cargo.Unload, location.CNHKG) c.Check(len(cargoEventHandler.handledEvents), Equals, 0) inspectionService.InspectCargo(trackingID) c.Check(len(cargoEventHandler.handledEvents), Equals, 1) }
func TestTrackUnknownCargo(t *testing.T) { var ( cargos = repository.NewInMemcargo() handlingEvents = repository.NewInMemHandlingEvent() service = NewService(cargos, handlingEvents) ) ctx := context.Background() logger := log.NewLogfmtLogger(ioutil.Discard) h := MakeHandler(ctx, service, logger) req, _ := http.NewRequest("GET", "http://example.com/tracking/v1/cargos/not_found", nil) rec := httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != http.StatusNotFound { t.Errorf("rec.Code = %d; want = %d", rec.Code, http.StatusNotFound) } wantContent := "application/json; charset=utf-8" if got := rec.Header().Get("Content-Type"); got != wantContent { t.Errorf("Content-Type = %q; want = %q", got, wantContent) } var response map[string]interface{} if err := json.NewDecoder(rec.Body).Decode(&response); err != nil { t.Error(err) } err, ok := response["error"] if !ok { t.Error("missing error") } if err != "unknown cargo" { t.Errorf(`"error": %q; want = %q`, err, "unknown cargo") } }
func main() { var ( addr = envString("PORT", defaultPort) rsurl = envString("ROUTINGSERVICE_URL", defaultRoutingServiceURL) dburl = envString("MONGODB_URL", defaultMongoDBURL) dbname = envString("DB_NAME", defaultDBName) httpAddr = flag.String("http.addr", ":"+addr, "HTTP listen address") routingServiceURL = flag.String("service.routing", rsurl, "routing service URL") mongoDBURL = flag.String("db.url", dburl, "MongoDB URL") databaseName = flag.String("db.name", dbname, "MongoDB database name") inmem = flag.Bool("inmem", false, "use in-memory repositories") ctx = context.Background() ) flag.Parse() var logger log.Logger logger = log.NewLogfmtLogger(os.Stderr) logger = &serializedLogger{Logger: logger} logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC) // Setup repositories var ( cargos cargo.Repository locations location.Repository voyages voyage.Repository handlingEvents cargo.HandlingEventRepository ) if *inmem { cargos = repository.NewInMemcargo() locations = repository.NewInMemLocation() voyages = repository.NewInMemVoyage() handlingEvents = repository.NewInMemHandlingEvent() } else { session, err := mgo.Dial(*mongoDBURL) if err != nil { panic(err) } defer session.Close() session.SetMode(mgo.Monotonic, true) cargos, _ = repository.NewMongoCargo(*databaseName, session) locations, _ = repository.NewMongoLocation(*databaseName, session) voyages, _ = repository.NewMongoVoyage(*databaseName, session) handlingEvents = repository.NewMongoHandlingEvent(*databaseName, session) } // Configure some questionable dependencies. var ( handlingEventFactory = cargo.HandlingEventFactory{ CargoRepository: cargos, VoyageRepository: voyages, LocationRepository: locations, } handlingEventHandler = handling.NewEventHandler( inspection.NewService(cargos, handlingEvents, nil), ) ) // Facilitate testing by adding some cargos. storeTestData(cargos) fieldKeys := []string{"method"} var rs routing.Service rs = routing.NewProxyingMiddleware(*routingServiceURL, ctx)(rs) var bs booking.Service bs = booking.NewService(cargos, locations, handlingEvents, rs) bs = booking.NewLoggingService(log.NewContext(logger).With("component", "booking"), bs) bs = booking.NewInstrumentingService( kitprometheus.NewCounter(stdprometheus.CounterOpts{ Namespace: "api", Subsystem: "booking_service", Name: "request_count", Help: "Number of requests received.", }, fieldKeys), metrics.NewTimeHistogram(time.Microsecond, kitprometheus.NewSummary(stdprometheus.SummaryOpts{ Namespace: "api", Subsystem: "booking_service", Name: "request_latency_microseconds", Help: "Total duration of requests in microseconds.", }, fieldKeys)), bs) var ts tracking.Service ts = tracking.NewService(cargos, handlingEvents) ts = tracking.NewLoggingService(log.NewContext(logger).With("component", "tracking"), ts) ts = tracking.NewInstrumentingService( kitprometheus.NewCounter(stdprometheus.CounterOpts{ Namespace: "api", Subsystem: "tracking_service", Name: "request_count", Help: "Number of requests received.", }, fieldKeys), metrics.NewTimeHistogram(time.Microsecond, kitprometheus.NewSummary(stdprometheus.SummaryOpts{ Namespace: "api", Subsystem: "tracking_service", Name: "request_latency_microseconds", Help: "Total duration of requests in microseconds.", }, fieldKeys)), ts) var hs handling.Service hs = handling.NewService(handlingEvents, handlingEventFactory, handlingEventHandler) hs = handling.NewLoggingService(log.NewContext(logger).With("component", "handling"), hs) hs = handling.NewInstrumentingService( kitprometheus.NewCounter(stdprometheus.CounterOpts{ Namespace: "api", Subsystem: "handling_service", Name: "request_count", Help: "Number of requests received.", }, fieldKeys), metrics.NewTimeHistogram(time.Microsecond, kitprometheus.NewSummary(stdprometheus.SummaryOpts{ Namespace: "api", Subsystem: "handling_service", Name: "request_latency_microseconds", Help: "Total duration of requests in microseconds.", }, fieldKeys)), hs) httpLogger := log.NewContext(logger).With("component", "http") mux := http.NewServeMux() mux.Handle("/booking/v1/", booking.MakeHandler(ctx, bs, httpLogger)) mux.Handle("/tracking/v1/", tracking.MakeHandler(ctx, ts, httpLogger)) mux.Handle("/handling/v1/", handling.MakeHandler(ctx, hs, httpLogger)) http.Handle("/", accessControl(mux)) http.Handle("/metrics", stdprometheus.Handler()) errs := make(chan error, 2) go func() { logger.Log("transport", "http", "address", *httpAddr, "msg", "listening") errs <- http.ListenAndServe(*httpAddr, nil) }() go func() { c := make(chan os.Signal) signal.Notify(c, syscall.SIGINT) errs <- fmt.Errorf("%s", <-c) }() logger.Log("terminated", <-errs) }