From 087975abd15d41da71fcb5ba3002fa9928dbb097 Mon Sep 17 00:00:00 2001 From: Steven Wolfe Date: Thu, 10 Jan 2019 11:28:29 -0700 Subject: [PATCH] Roaming geofence tests --- tests/fence_roaming_test.go | 277 ++++++++++++++++++++++++++++++++++++ tests/fence_test.go | 8 +- 2 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 tests/fence_roaming_test.go diff --git a/tests/fence_roaming_test.go b/tests/fence_roaming_test.go new file mode 100644 index 00000000..8a6e68ba --- /dev/null +++ b/tests/fence_roaming_test.go @@ -0,0 +1,277 @@ +package tests + +import ( + "errors" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + + "github.com/gomodule/redigo/redis" + "github.com/tidwall/pretty" + "github.com/tidwall/sjson" +) + +func fence_roaming_webhook_test(mc *mockServer) error { + car1, car2, expected := roamingTestData() + finalErr := make(chan error) + + // Create a connection for subscribing to geofence notifications + sc, err := redis.Dial("tcp", fmt.Sprintf(":%d", mc.port)) + if err != nil { + return err + } + defer sc.Close() + + actual := []string{} + // Create the test http server that will capture all messages + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if err := func() error { + // Read the request body + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return err + } + + // If the new message doesn't match whats expected an error + // should be returned + actual = append(actual, cleanMessage(body)) + pos := len(actual) - 1 + if len(expected) < pos+1 { + return fmt.Errorf("More messages than expected were received : '%s'", actual[pos]) + } + if actual[pos] != expected[pos] { + return fmt.Errorf("Expected '%s' but got '%s'", expected[pos], + actual[pos]) + } + if len(actual) == len(expected) { + finalErr <- nil + } + return nil + }(); err != nil { + finalErr <- err + } + fmt.Fprintln(w, "OK!") + })) + defer ts.Close() + + _, err = sc.Do("SETHOOK", "carshook", ts.URL, "NEARBY", "cars", "FENCE", "ROAM", "cars", "*", 1000) + if err != nil { + return err + } + + // Create the base connection for setting up points and geofences + bc, err := redis.Dial("tcp", fmt.Sprintf(":%d", mc.port)) + if err != nil { + return err + } + defer bc.Close() + + // Fire all car movement commands on the base client + for i := range car1 { + if _, err := bc.Do("SET", "cars", "car1", "POINT", car1[i][1], + car1[i][0]); err != nil { + return err + } + if _, err := bc.Do("SET", "cars", "car2", "POINT", car2[i][1], + car2[i][0]); err != nil { + return err + } + } + + return <-finalErr +} + +func fence_roaming_live_test(mc *mockServer) error { + car1, car2, expected := roamingTestData() + finalErr := make(chan error) + + go func() { + // Create a connection for subscribing to geofence notifications + sc, err := redis.Dial("tcp", fmt.Sprintf(":%d", mc.port)) + if err != nil { + finalErr <- err + return + } + defer sc.Close() + + // Set up a live geofence stream + if _, err := sc.Do("NEARBY", "cars", "FENCE", "ROAM", "cars", "*", 1000); err != nil { + finalErr <- err + return + } + + actual := []string{} + for sc.Err() == nil { + if err := func() error { + bodyi, err := sc.Receive() + if err != nil { + return err + } + body, ok := bodyi.([]byte) + if !ok { + return errors.New("Non byte-slice received") + } + + // If the new message doesn't match whats expected an error + // should be returned + actual = append(actual, cleanMessage(body)) + pos := len(actual) - 1 + if len(expected) < pos+1 { + return fmt.Errorf("More messages than expected were received : '%s'", actual[pos]) + } + if actual[pos] != expected[pos] { + return fmt.Errorf("Expected '%s' but got '%s'", expected[pos], + actual[pos]) + } + if len(actual) == len(expected) { + finalErr <- nil + } + return nil + }(); err != nil { + finalErr <- err + } + } + }() + + // Create the base connection for setting up points and geofences + bc, err := redis.Dial("tcp", fmt.Sprintf(":%d", mc.port)) + if err != nil { + return err + } + defer bc.Close() + + // Fire all car movement commands on the base client + for i := range car1 { + if _, err := bc.Do("SET", "cars", "car1", "POINT", car1[i][1], + car1[i][0]); err != nil { + return err + } + if _, err := bc.Do("SET", "cars", "car2", "POINT", car2[i][1], + car2[i][0]); err != nil { + return err + } + } + + return <-finalErr +} + +func fence_roaming_channel_test(mc *mockServer) error { + car1, car2, expected := roamingTestData() + finalErr := make(chan error) + + go func() { + // Create a connection for subscribing to geofence notifications + sc, err := redis.Dial("tcp", fmt.Sprintf(":%d", mc.port)) + if err != nil { + finalErr <- err + return + } + defer sc.Close() + + if _, err := sc.Do("SETCHAN", "carschan", "NEARBY", "cars", "FENCE", "ROAM", "cars", "*", 1000); err != nil { + finalErr <- err + return + } + + // Subscribe the subscription client to the * pattern + psc := redis.PubSubConn{Conn: sc} + if err := psc.PSubscribe("carschan"); err != nil { + finalErr <- err + return + } + + actual := []string{} + for sc.Err() == nil { + if err := func() error { + var body []byte + switch v := psc.Receive().(type) { + case redis.Message: + body = v.Data + case error: + return err + } + if len(body) == 0 { + return nil + } + + // If the new message doesn't match whats expected an error + // should be returned + actual = append(actual, cleanMessage(body)) + pos := len(actual) - 1 + if len(expected) < pos+1 { + return fmt.Errorf("More messages than expected were received : '%s'", actual[pos]) + } + if actual[pos] != expected[pos] { + return fmt.Errorf("Expected '%s' but got '%s'", expected[pos], + actual[pos]) + } + if len(actual) == len(expected) { + finalErr <- nil + } + return nil + }(); err != nil { + finalErr <- err + } + } + }() + + // Create the base connection for setting up points and geofences + bc, err := redis.Dial("tcp", fmt.Sprintf(":%d", mc.port)) + if err != nil { + return err + } + defer bc.Close() + + // Fire all car movement commands on the base client + for i := range car1 { + if _, err := bc.Do("SET", "cars", "car1", "POINT", car1[i][1], + car1[i][0]); err != nil { + return err + } + if _, err := bc.Do("SET", "cars", "car2", "POINT", car2[i][1], + car2[i][0]); err != nil { + return err + } + } + + return <-finalErr +} + +func cleanMessage(body []byte) string { + // Remove fields that are non-deterministic or use case specific + msg, _ := sjson.Delete(string(body), "group") + msg, _ = sjson.Delete(msg, "time") + msg, _ = sjson.Delete(msg, "hook") + msg = string(pretty.Ugly([]byte(msg))) + return msg +} + +func roamingTestData() (car1 [][]float64, car2 [][]float64, output []string) { + car1 = [][]float64{ + {-111.93669319152832, 33.414750027566235}, + {-111.93051338195801, 33.414750027566235}, + {-111.92416191101074, 33.414750027566235}, + {-111.91789627075195, 33.414750027566235}, + {-111.9111156463623, 33.414750027566235}, + {-111.90510749816895, 33.414750027566235}, + {-111.89746856689453, 33.414750027566235}, + } + car2 = [][]float64{ + {-111.89746856689453, 33.414750027566235}, + {-111.90519332885742, 33.414750027566235}, + {-111.91154479980467, 33.414750027566235}, + {-111.91781044006346, 33.414750027566235}, + {-111.92416191101074, 33.414750027566235}, + {-111.93059921264648, 33.414750027566235}, + {-111.93660736083984, 33.414750027566235}, + } + output = []string{ + `{"command":"set","detect":"roam","key":"cars","id":"car1","object":{"type":"Point","coordinates":[-111.91789627075195,33.414750027566235]},"nearby":{"key":"cars","id":"car2","object":{"type":"Point","coordinates":[-111.91154479980467,33.414750027566235]},"meters":589.512}}`, + `{"command":"set","detect":"roam","key":"cars","id":"car2","object":{"type":"Point","coordinates":[-111.91781044006346,33.414750027566235]},"nearby":{"key":"cars","id":"car1","object":{"type":"Point","coordinates":[-111.91789627075195,33.414750027566235]},"meters":7.966}}`, + `{"command":"set","detect":"roam","key":"cars","id":"car1","object":{"type":"Point","coordinates":[-111.9111156463623,33.414750027566235]},"nearby":{"key":"cars","id":"car2","object":{"type":"Point","coordinates":[-111.91781044006346,33.414750027566235]},"meters":621.377}}`, + `{"command":"set","detect":"roam","key":"cars","id":"car2","object":{"type":"Point","coordinates":[-111.92416191101074,33.414750027566235]},"faraway":{"key":"cars","id":"car1","object":{"type":"Point","coordinates":[-111.9111156463623,33.414750027566235]},"meters":1210.89}}`, + `{"command":"set","detect":"roam","key":"cars","id":"car1","object":{"type":"Point","coordinates":[-111.90510749816895,33.414750027566235]},"faraway":{"key":"cars","id":"car2","object":{"type":"Point","coordinates":[-111.92416191101074,33.414750027566235]},"meters":1768.536}}`, + } + return +} diff --git a/tests/fence_test.go b/tests/fence_test.go index dab258bb..971a0c0c 100644 --- a/tests/fence_test.go +++ b/tests/fence_test.go @@ -18,9 +18,15 @@ import ( ) func subTestFence(t *testing.T, mc *mockServer) { + // Standard runStep(t, mc, "basic", fence_basic_test) runStep(t, mc, "channel message order", fence_channel_message_order_test) runStep(t, mc, "detect inside,outside", fence_detect_inside_test) + + // Roaming + runStep(t, mc, "roaming live", fence_roaming_live_test) + runStep(t, mc, "roaming channel", fence_roaming_channel_test) + runStep(t, mc, "roaming webhook", fence_roaming_webhook_test) } type fenceReader struct { @@ -205,7 +211,7 @@ func fence_channel_message_order_test(mc *mockServer) error { finalErr <- nil }() - // Create the base connection for setting up points and geofences geofences + // Create the base connection for setting up points and geofences bc, err := redis.Dial("tcp", fmt.Sprintf(":%d", mc.port)) if err != nil { return err