| /* | |
| Copyright 2017 Google Inc. | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use this file except in compliance with the License. | |
| You may obtain a copy of the License at | |
| https://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| */ | |
| package main | |
| import ( | |
| "flag" | |
| "fmt" | |
| "log" | |
| "os" | |
| "path/filepath" | |
| "time" | |
| "github.com/maps-booking-v3/api" | |
| "github.com/maps-booking-v3/utils" | |
| mpb "github.com/maps-booking-v3/v3" | |
| ) | |
| const logFile = "http_test_client_log_" | |
| var ( | |
| serverAddr = flag.String("server_addr", "example.com:80", "Your http server's address in the format of host:port") | |
| credentialsFile = flag.String("credentials_file", "", "File containing credentials for your server. Leave blank to bypass authentication. File should have exactly one line of the form 'username:password'.") | |
| testSlots = flag.Int("num_test_slots", 10, "Maximum number of slots to test from availability_feed. Slots will be selected randomly") | |
| allFlows = flag.Bool("all_tests", false, "Whether to test all endpoints syncronously, including ListOrders flow.") | |
| healthFlow = flag.Bool("health_check_test", false, "Whether to test the Health endpoint.") | |
| checkFlow = flag.Bool("check_order_test", false, "Whether to test the CheckOrderFulfillability endpoint.") | |
| orderFlow = flag.Bool("create_order_test", false, "Whether to test the CreateOrder endpoint.") | |
| serviceFeed = flag.String("service_feed", "", "Absolute path to service feed required for all tests except health. Feeds can be in either json or pb3 format") | |
| availabilityFeed = flag.String("availability_feed", "", "Absolute path to availability feed required for all tests except health. Feeds can be in either json or pb3 format") | |
| outputDir = flag.String("output_dir", "", "Absolute path of dir to dump log file.") | |
| caFile = flag.String("ca_file", "", "Absolute path to your server's Certificate Authority root cert. Downloading all roots currently recommended by the Google Internet Authority is a suitable alternative https://pki.google.com/roots.pem. Leave blank to connect using http rather than https.") | |
| fullServerName = flag.String("full_server_name", "", "Fully qualified domain name. Same name used to sign CN. Only necessary if ca_file is specified and the base URL differs from the server address.") | |
| outputToTerminal = flag.Bool("output_to_terminal", false, "Output to terminal rather than a file.") | |
| ) | |
| type counters struct { | |
| TotalSlotsProcessed int | |
| HealthCheckSuccess bool | |
| CheckOrderFulfillabilitySuccess int | |
| CheckOrderFulfillabilityErrors int | |
| CreateOrderSuccess int | |
| CreateOrderErrors int | |
| ListOrdersSuccess bool | |
| } | |
| func createLogFile() (*os.File, error) { | |
| var err error | |
| outPath := *outputDir | |
| if outPath == "" { | |
| outPath, err = os.Getwd() | |
| if err != nil { | |
| return nil, err | |
| } | |
| } | |
| now := time.Now().UTC() | |
| nowString := fmt.Sprintf("%d-%02d-%02d_%02d-%02d-%02d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second()) | |
| outFile := filepath.Join(outPath, fmt.Sprintf("%s%s", logFile, nowString)) | |
| return os.Create(outFile) | |
| } | |
| func logStats(stats counters) { | |
| log.Println("\n************* Begin Stats *************\n") | |
| var totalErrors int | |
| if *healthFlow || *allFlows { | |
| if stats.HealthCheckSuccess { | |
| log.Println("HealthCheck Succeeded") | |
| } else { | |
| totalErrors++ | |
| log.Println("HealthCheck Failed") | |
| } | |
| } | |
| if *checkFlow || *allFlows { | |
| totalErrors += stats.CheckOrderFulfillabilityErrors | |
| log.Printf("CheckOrderFulfillability Errors: %d/%d", stats.CheckOrderFulfillabilityErrors, stats.CheckOrderFulfillabilityErrors+stats.CheckOrderFulfillabilitySuccess) | |
| } | |
| if *orderFlow || *allFlows { | |
| totalErrors += stats.CreateOrderErrors | |
| log.Printf("CreateOrder Errors: %d/%d", stats.CreateOrderErrors, stats.CreateOrderErrors+stats.CreateOrderSuccess) | |
| } | |
| if *allFlows { | |
| if stats.ListOrdersSuccess { | |
| log.Println("ListOrders Succeeded") | |
| } else { | |
| totalErrors++ | |
| log.Println("ListOrders Failed") | |
| } | |
| } | |
| if *checkFlow || *orderFlow || *allFlows { | |
| log.Printf("Total Slots Processed: %d", stats.TotalSlotsProcessed) | |
| } | |
| log.Println("\n\n\n") | |
| if totalErrors == 0 { | |
| log.Println("All Tests Pass!") | |
| } else { | |
| log.Printf("Found %d Errors", totalErrors) | |
| } | |
| log.Println("\n************* End Stats *************\n") | |
| os.Exit(totalErrors) | |
| } | |
| func main() { | |
| flag.Parse() | |
| var stats counters | |
| if !*outputToTerminal { | |
| // Set up logging before continuing with flows | |
| f, err := createLogFile() | |
| if err != nil { | |
| log.Fatalf("Failed to create log file %v", err) | |
| } | |
| defer f.Close() | |
| log.SetOutput(f) | |
| } | |
| conn, err := api.InitHTTPConnection(*serverAddr, *credentialsFile, *caFile, *fullServerName) | |
| if err != nil { | |
| log.Fatalf("Failed to init http connection %v", err) | |
| } | |
| // HealthCheck Flow | |
| if *healthFlow || *allFlows { | |
| stats.HealthCheckSuccess = true | |
| if err := api.HealthCheck(conn); err != nil { | |
| stats.HealthCheckSuccess = false | |
| log.Println(err.Error()) | |
| } | |
| if !*allFlows && !*checkFlow && !*orderFlow { | |
| logStats(stats) | |
| } | |
| } | |
| if *availabilityFeed == "" || *serviceFeed == "" { | |
| log.Fatal("please set both availability_feed and service_feed flags") | |
| } | |
| testInventory, err := utils.MerchantLineItemMapFrom(*serviceFeed, *availabilityFeed, *testSlots) | |
| if err != nil { | |
| log.Fatal(err.Error()) | |
| } | |
| // CheckOrderFulfillability Flow | |
| if *checkFlow || *allFlows { | |
| utils.LogFlow("CheckOrderFulfillability", "Start") | |
| for _, value := range testInventory { | |
| stats.TotalSlotsProcessed += len(value) | |
| } | |
| i := 0 | |
| for merchantID, lineItems := range testInventory { | |
| if err = api.CheckOrderFulfillability(merchantID, lineItems, conn); err != nil { | |
| log.Printf("%s. skipping slots %d-%d/%d", err.Error(), i, i+len(lineItems)-1, stats.TotalSlotsProcessed) | |
| stats.CheckOrderFulfillabilityErrors += len(lineItems) | |
| delete(testInventory, merchantID) | |
| i += len(lineItems) | |
| continue | |
| } | |
| stats.CheckOrderFulfillabilitySuccess += len(lineItems) | |
| i += len(lineItems) | |
| } | |
| utils.LogFlow("CheckOrderFulfillability", "End") | |
| } | |
| // CreateOrder Flow. | |
| var orders []*mpb.Order | |
| if *orderFlow || *allFlows { | |
| utils.LogFlow("CreateOrder", "Start") | |
| if stats.TotalSlotsProcessed == 0 { | |
| for _, value := range testInventory { | |
| stats.TotalSlotsProcessed += len(value) | |
| } | |
| } | |
| i := 0 | |
| for merchantID, lineItems := range testInventory { | |
| order, err := api.CreateOrder(merchantID, lineItems, conn) | |
| if err != nil { | |
| log.Printf("%s. skipping slot %d-%d/%d", err.Error(), i, i+len(lineItems)-1, stats.TotalSlotsProcessed) | |
| stats.CreateOrderErrors += len(lineItems) | |
| delete(testInventory, merchantID) | |
| i += len(lineItems) | |
| continue | |
| } | |
| orders = append(orders, order) | |
| stats.CreateOrderSuccess += len(lineItems) | |
| i += len(lineItems) | |
| } | |
| utils.LogFlow("CreateOrder", "End") | |
| } | |
| // ListOrders Flow | |
| if *allFlows { | |
| utils.LogFlow("ListOrders", "Start") | |
| stats.ListOrdersSuccess = true | |
| if err = api.ListOrders(orders, conn); err != nil { | |
| stats.ListOrdersSuccess = false | |
| log.Println(err.Error()) | |
| } | |
| utils.LogFlow("ListOrders", "End") | |
| } | |
| logStats(stats) | |
| } |