-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
As 3155 handle approximate location in telemetry (#101)
* Add queries for approximate lat and long * Update e2e test to use gql client * Allow all time location to query approximate * Add permission test for the approximate location * Add unit test for oneOf * Add signal de-duplication * Add approximate e2e test * Update shared version * Update dockerfile with h3 C dependencies
- Loading branch information
1 parent
f3120fa
commit 9bbb4e5
Showing
28 changed files
with
2,022 additions
and
847 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
package e2e_test | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/DIMO-Network/model-garage/pkg/vss" | ||
"github.com/DIMO-Network/telemetry-api/internal/repositories" | ||
"github.com/DIMO-Network/telemetry-api/internal/service/ch" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"github.com/uber/h3-go/v4" | ||
) | ||
|
||
func TestApproximateLocation(t *testing.T) { | ||
services := GetTestServices(t) | ||
locationTime := time.Date(2024, 11, 20, 22, 28, 17, 0, time.UTC) | ||
// Set up test data in Clickhouse | ||
startLoc := h3.LatLng{Lat: 40.75005062700222, Lng: -74.0094415688571} | ||
endLoc := h3.LatLng{Lat: 40.73899538333504, Lng: -73.99386110247163} | ||
signals := []vss.Signal{ | ||
{ | ||
Source: ch.SourceTranslations["smartcar"], | ||
Timestamp: locationTime.Add(-time.Hour * 24), | ||
Name: vss.FieldCurrentLocationLatitude, | ||
ValueNumber: startLoc.Lat, | ||
TokenID: 39718, | ||
}, | ||
{ | ||
Source: ch.SourceTranslations["smartcar"], | ||
Timestamp: locationTime.Add(-time.Hour * 24), | ||
Name: vss.FieldCurrentLocationLongitude, | ||
ValueNumber: startLoc.Lng, | ||
TokenID: 39718, | ||
}, | ||
{ | ||
Source: ch.SourceTranslations["smartcar"], | ||
Timestamp: locationTime, | ||
Name: vss.FieldCurrentLocationLatitude, | ||
ValueNumber: endLoc.Lat, | ||
TokenID: 39718, | ||
}, | ||
{ | ||
Source: ch.SourceTranslations["smartcar"], | ||
Timestamp: locationTime, | ||
Name: vss.FieldCurrentLocationLongitude, | ||
ValueNumber: endLoc.Lng, | ||
TokenID: 39718, | ||
}, | ||
} | ||
|
||
insertSignal(t, services.CH, signals) | ||
|
||
// Create and set up GraphQL server | ||
telemetryClient := NewGraphQLServer(t, services.Settings) | ||
|
||
// Execute the query | ||
query := ` | ||
query { | ||
signalsLatest(tokenId: 39718) { | ||
lastSeen | ||
currentLocationApproximateLatitude{ | ||
timestamp | ||
value | ||
} | ||
currentLocationApproximateLongitude{ | ||
timestamp | ||
value | ||
} | ||
} | ||
}` | ||
|
||
// Create auth token for vehicle | ||
token := services.Auth.CreateVehicleToken(t, "39718", []int{8}) | ||
|
||
// Execute request | ||
result := ApproxResult{} | ||
err := telemetryClient.Post(query, &result, WithToken(token)) | ||
require.NoError(t, err) | ||
|
||
expectedStartLatLong := repositories.GetApproximateLoc(startLoc.Lat, startLoc.Lng) | ||
expectedEndLatLong := repositories.GetApproximateLoc(endLoc.Lat, endLoc.Lng) | ||
// Assert the results | ||
assert.Equal(t, locationTime.Format(time.RFC3339), result.SignalLatest.LastSeen) | ||
assert.Equal(t, locationTime.Format(time.RFC3339), result.SignalLatest.ApproxLat.Timestamp) | ||
assert.Equal(t, locationTime.Format(time.RFC3339), result.SignalLatest.ApproxLong.Timestamp) | ||
assert.Equal(t, expectedEndLatLong.Lat, result.SignalLatest.ApproxLat.Value) | ||
assert.Equal(t, expectedEndLatLong.Lng, result.SignalLatest.ApproxLong.Value) | ||
|
||
// verify we do not leak the exact location | ||
query = `query { | ||
signalsLatest(tokenId: 39718) { | ||
lastSeen | ||
speed { | ||
timestamp | ||
value | ||
} | ||
currentLocationApproximateLatitude{ | ||
timestamp | ||
value | ||
} | ||
currentLocationApproximateLongitude{ | ||
timestamp | ||
value | ||
} | ||
currentLocationLatitude{ | ||
timestamp | ||
value | ||
} | ||
currentLocationLongitude{ | ||
timestamp | ||
value | ||
} | ||
} | ||
}` | ||
|
||
// Execute request | ||
result = ApproxResult{} | ||
err = telemetryClient.Post(query, &result, WithToken(token)) | ||
require.Error(t, err) | ||
|
||
// Assert the results | ||
assert.Equal(t, locationTime.Format(time.RFC3339), result.SignalLatest.LastSeen) | ||
assert.Equal(t, locationTime.Format(time.RFC3339), result.SignalLatest.ApproxLat.Timestamp) | ||
assert.Equal(t, locationTime.Format(time.RFC3339), result.SignalLatest.ApproxLong.Timestamp) | ||
assert.Equal(t, expectedEndLatLong.Lat, result.SignalLatest.ApproxLat.Value) | ||
assert.Equal(t, expectedEndLatLong.Lng, result.SignalLatest.ApproxLong.Value) | ||
assert.Nil(t, result.SignalLatest.Lat) | ||
assert.Nil(t, result.SignalLatest.Long) | ||
|
||
query = `query { | ||
signals(tokenId:39718, from: "2020-04-15T09:21:19Z", to: "2025-04-27T09:21:19Z", interval:"24h"){ | ||
timestamp | ||
currentLocationApproximateLatitude(agg: FIRST) | ||
currentLocationApproximateLongitude(agg: FIRST) | ||
} | ||
}` | ||
// Execute request | ||
aggResult := ApproxAgg{} | ||
err = telemetryClient.Post(query, &aggResult, WithToken(token)) | ||
require.NoError(t, err) | ||
|
||
require.Len(t, aggResult.Signals, 2) | ||
// Assert the results | ||
assert.Equal(t, locationTime.Add(-time.Hour*24).Truncate(time.Hour*24).Format(time.RFC3339), aggResult.Signals[0].Timestamp) | ||
assert.Equal(t, expectedStartLatLong.Lat, *aggResult.Signals[0].ApproxLat) | ||
assert.Equal(t, expectedStartLatLong.Lng, *aggResult.Signals[0].ApproxLong) | ||
|
||
assert.Equal(t, locationTime.Truncate(time.Hour*24).Format(time.RFC3339), aggResult.Signals[1].Timestamp) | ||
assert.Equal(t, expectedEndLatLong.Lat, *aggResult.Signals[1].ApproxLat) | ||
assert.Equal(t, expectedEndLatLong.Lng, *aggResult.Signals[1].ApproxLong) | ||
|
||
query = `query { | ||
signals(tokenId:39718, from: "2020-04-15T09:21:19Z", to: "2025-04-27T09:21:19Z", interval:"24h"){ | ||
timestamp | ||
currentLocationApproximateLatitude(agg: FIRST) | ||
currentLocationApproximateLongitude(agg: FIRST) | ||
currentLocationLatitude(agg: FIRST) | ||
currentLocationLongitude(agg: FIRST) | ||
} | ||
}` | ||
// Execute request | ||
aggResult = ApproxAgg{} | ||
err = telemetryClient.Post(query, &aggResult, WithToken(token)) | ||
require.Error(t, err) | ||
|
||
// Assert the results | ||
require.Len(t, aggResult.Signals, 2) | ||
assert.Equal(t, locationTime.Add(-time.Hour*24).Truncate(time.Hour*24).Format(time.RFC3339), aggResult.Signals[0].Timestamp) | ||
assert.Equal(t, expectedStartLatLong.Lat, *aggResult.Signals[0].ApproxLat) | ||
assert.Equal(t, expectedStartLatLong.Lng, *aggResult.Signals[0].ApproxLong) | ||
assert.Nil(t, aggResult.Signals[0].Lat) | ||
assert.Nil(t, aggResult.Signals[0].Long) | ||
|
||
assert.Equal(t, locationTime.Truncate(time.Hour*24).Format(time.RFC3339), aggResult.Signals[1].Timestamp) | ||
assert.Equal(t, expectedEndLatLong.Lat, *aggResult.Signals[1].ApproxLat) | ||
assert.Equal(t, expectedEndLatLong.Lng, *aggResult.Signals[1].ApproxLong) | ||
assert.Nil(t, aggResult.Signals[1].Lat) | ||
assert.Nil(t, aggResult.Signals[1].Long) | ||
} | ||
|
||
type ApproxResult struct { | ||
SignalLatest struct { | ||
LastSeen string `json:"lastSeen"` | ||
ApproxLat *SignalWithTime `json:"currentLocationApproximateLatitude"` | ||
ApproxLong *SignalWithTime `json:"currentLocationApproximateLongitude"` | ||
Lat *SignalWithTime `json:"currentLocationLatitude"` | ||
Long *SignalWithTime `json:"currentLocationLongitude"` | ||
} `json:"signalsLatest"` | ||
} | ||
|
||
type ApproxAgg struct { | ||
Signals []struct { | ||
Timestamp string `json:"timestamp"` | ||
ApproxLat *float64 `json:"currentLocationApproximateLatitude"` | ||
ApproxLong *float64 `json:"currentLocationApproximateLongitude"` | ||
Lat *float64 `json:"currentLocationLatitude"` | ||
Long *float64 `json:"currentLocationLongitude"` | ||
} `json:"signals"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package e2e_test | ||
|
||
import ( | ||
"strconv" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestPermission(t *testing.T) { | ||
services := GetTestServices(t) | ||
const unusedTokenID = 999999 | ||
// Create and set up GraphQL server | ||
telemetryClient := NewGraphQLServer(t, services.Settings) | ||
|
||
tests := []struct { | ||
name string | ||
tokenID int | ||
query string | ||
permissions []int | ||
expectedErr string // checking error strings since that is what the server returns | ||
}{ | ||
{ | ||
name: "No permissions", | ||
tokenID: unusedTokenID, | ||
query: `query { | ||
signalsLatest(tokenId: 39718) { | ||
lastSeen | ||
} | ||
}`, | ||
permissions: []int{}, | ||
expectedErr: `[{"message":"unauthorized: token id does not match","path":["signalsLatest"]}]`, | ||
}, | ||
{ | ||
name: "Token permissions", | ||
tokenID: 39718, | ||
query: `query { | ||
signalsLatest(tokenId: 39718) { | ||
lastSeen | ||
} | ||
}`, | ||
permissions: []int{}, | ||
}, | ||
{ | ||
name: "Partial permissions", | ||
tokenID: 39718, | ||
query: `query { | ||
signalsLatest(tokenId: 39718) { | ||
lastSeen | ||
speed { | ||
value | ||
} | ||
} | ||
}`, | ||
permissions: []int{}, | ||
expectedErr: `[{"message":"unauthorized: missing required privilege(s) VEHICLE_NON_LOCATION_DATA","path":["signalsLatest","speed"]}]`, | ||
}, | ||
{ | ||
name: "Non Location permissions", | ||
tokenID: 39718, | ||
query: `query { | ||
signalsLatest(tokenId: 39718) { | ||
lastSeen | ||
speed { | ||
value | ||
} | ||
} | ||
}`, | ||
permissions: []int{1}, | ||
}, | ||
{ | ||
name: "Location permissions", | ||
tokenID: 39718, | ||
query: `query { | ||
signalsLatest(tokenId: 39718) { | ||
lastSeen | ||
currentLocationLatitude { | ||
value | ||
} | ||
} | ||
}`, | ||
permissions: []int{4}, | ||
}, | ||
{ | ||
name: "Approximate Location permissions", | ||
tokenID: 39718, | ||
query: `query { | ||
signalsLatest(tokenId: 39718) { | ||
lastSeen | ||
currentLocationApproximateLatitude { | ||
value | ||
} | ||
} | ||
}`, | ||
permissions: []int{8}, | ||
}, | ||
{ | ||
name: "Approximate Location with ALL_TIME permission", | ||
tokenID: 39718, | ||
query: `query { | ||
signalsLatest(tokenId: 39718) { | ||
lastSeen | ||
currentLocationApproximateLatitude { | ||
value | ||
} | ||
} | ||
}`, | ||
permissions: []int{4}, | ||
}, | ||
|
||
{ | ||
name: "Neither Location nor Approximate Location permissions", | ||
tokenID: 39718, | ||
query: `query { | ||
signalsLatest(tokenId: 39718) { | ||
lastSeen | ||
currentLocationApproximateLatitude { | ||
value | ||
} | ||
} | ||
}`, | ||
permissions: []int{1}, | ||
expectedErr: `[{"message":"unauthorized: requires at least one of the following privileges [VEHICLE_APPROXIMATE_LOCATION VEHICLE_ALL_TIME_LOCATION]" ,"path":["signalsLatest","currentLocationApproximateLatitude"]}]`, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
token := services.Auth.CreateVehicleToken(t, strconv.Itoa(tt.tokenID), tt.permissions) | ||
// Execute request | ||
result := map[string]any{} | ||
err := telemetryClient.Post(tt.query, &result, WithToken(token)) | ||
if tt.expectedErr != "" { | ||
require.Error(t, err) | ||
require.JSONEq(t, tt.expectedErr, err.Error()) | ||
return | ||
} | ||
require.NoError(t, err) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.