예제 #1
0
        public async Task TimeSeriesInsightsQuery_GetEventsLifecycle()
        {
            // Arrange
            TimeSeriesInsightsClient tsiClient    = GetClient();
            DeviceClient             deviceClient = await GetDeviceClient().ConfigureAwait(false);

            // Figure out what the Time Series Id is composed of
            TimeSeriesModelSettings modelSettings = await tsiClient.GetModelSettingsAsync().ConfigureAwait(false);

            // Create a Time Series Id where the number of keys that make up the Time Series Id is fetched from Model Settings
            TimeSeriesId tsiId = await GetUniqueTimeSeriesInstanceIdAsync(tsiClient, modelSettings.TimeSeriesIdProperties.Count)
                                 .ConfigureAwait(false);

            try
            {
                // Send some events to the IoT hub
                await QueryTestsHelper.SendEventsToHubAsync(
                    deviceClient,
                    tsiId,
                    modelSettings.TimeSeriesIdProperties.ToArray(),
                    2)
                .ConfigureAwait(false);

                // Act

                // Get events from last 1 minute
                DateTimeOffset now       = Recording.UtcNow;
                DateTimeOffset endTime   = now.AddMinutes(10);
                DateTimeOffset startTime = now.AddMinutes(-10);

                // This retry logic was added as the TSI instance are not immediately available after creation
                await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() =>
                {
                    AsyncPageable <QueryResultPage> queryEventsPages = tsiClient.QueryEventsAsync(tsiId, startTime, endTime);

                    await foreach (QueryResultPage eventPage in queryEventsPages)
                    {
                        eventPage.Timestamps.Should().HaveCount(2);
                        eventPage.Timestamps.Should().OnlyContain(timeStamp => timeStamp >= startTime).And.OnlyContain(timeStamp => timeStamp <= endTime);
                        eventPage.Properties.Should().NotBeEmpty();
                        eventPage.Properties.First().Should().NotBeNull();
                    }

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);

                // Send more events to the hub
                await QueryTestsHelper.SendEventsToHubAsync(
                    deviceClient,
                    tsiId,
                    modelSettings.TimeSeriesIdProperties.ToArray(),
                    2)
                .ConfigureAwait(false);

                // This retry logic was added as the TSI instance are not immediately available after creation
                await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() =>
                {
                    AsyncPageable <QueryResultPage> queryEventsPages = tsiClient.QueryEventsAsync(tsiId, startTime, endTime);

                    await foreach (QueryResultPage eventPage in queryEventsPages)
                    {
                        eventPage.Timestamps.Should().HaveCount(4);
                        eventPage.Timestamps.Should()
                        .OnlyContain(timeStamp => timeStamp >= startTime)
                        .And
                        .OnlyContain(timeStamp => timeStamp <= endTime);
                        eventPage.Properties.Should().NotBeEmpty();
                        eventPage.Properties.First().Should().NotBeNull();
                    }

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);

                // Send 2 events with a special condition that can be used later to query on
                IDictionary <string, object> messageBase = QueryTestsHelper.BuildMessageBase(modelSettings.TimeSeriesIdProperties.ToArray(), tsiId);
                messageBase[QueryTestsHelper.Temperature] = 1.2;
                messageBase[QueryTestsHelper.Humidity]    = 3.4;
                string messageBody = JsonSerializer.Serialize(messageBase);
                var    message     = new Message(Encoding.ASCII.GetBytes(messageBody))
                {
                    ContentType     = "application/json",
                    ContentEncoding = "utf-8",
                };

                Func <Task> sendEventAct = async() => await deviceClient.SendEventAsync(message).ConfigureAwait(false);

                await sendEventAct.Should().NotThrowAsync();

                // Send it again
                sendEventAct.Should().NotThrow();

                // Query for the two events with a filter

                // Only project Temperature and one of the Id properties
                var queryRequestOptions = new QueryEventsRequestOptions
                {
                    Filter    = "$event.Temperature.Double = 1.2",
                    StoreType = StoreType.WarmStore,
                };
                queryRequestOptions.ProjectedProperties.Add(
                    new EventProperty
                {
                    Name = QueryTestsHelper.Temperature,
                    Type = "Double",
                });
                queryRequestOptions.ProjectedProperties.Add(
                    new EventProperty
                {
                    Name = modelSettings.TimeSeriesIdProperties.First().Name,
                    Type = modelSettings.TimeSeriesIdProperties.First().Type.ToString(),
                });

                await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() =>
                {
                    AsyncPageable <QueryResultPage> queryEventsPages = tsiClient.QueryEventsAsync(tsiId, startTime, endTime, queryRequestOptions);
                    await foreach (QueryResultPage eventPage in queryEventsPages)
                    {
                        eventPage.Timestamps.Should().HaveCount(2);
                        eventPage.Properties.Should().HaveCount(2);
                        eventPage.Properties.First().Should().NotBeNull();
                        eventPage.Properties.First().Name.Should().Be(QueryTestsHelper.Temperature);
                        eventPage.Properties[1].Name.Should().Be(modelSettings.TimeSeriesIdProperties.First().Name);
                    }

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);

                // Query for the two events with a filter, but only take 1
                queryRequestOptions.MaximumNumberOfEvents = 1;
                AsyncPageable <QueryResultPage> queryEventsPagesWithFilter = tsiClient.QueryEventsAsync(tsiId, startTime, endTime, queryRequestOptions);
                await foreach (QueryResultPage eventPage in queryEventsPagesWithFilter)
                {
                    eventPage.Timestamps.Should().HaveCount(1);
                    eventPage.Properties.Should().HaveCount(2);
                    eventPage.Properties.First().Should().NotBeNull();
                    eventPage.Properties.First().Name.Should().Be(QueryTestsHelper.Temperature);
                    eventPage.Properties[1].Name.Should().Be(modelSettings.TimeSeriesIdProperties.First().Name);
                }

                await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() =>
                {
                    // Query for all the events using a timespan
                    AsyncPageable <QueryResultPage> queryEventsPagesWithTimespan = tsiClient.QueryEventsAsync(tsiId, TimeSpan.FromMinutes(20), endTime);
                    await foreach (QueryResultPage eventPage in queryEventsPagesWithTimespan)
                    {
                        eventPage.Timestamps.Should().HaveCount(6);
                        eventPage.Timestamps.Should()
                        .OnlyContain(timeStamp => timeStamp >= startTime)
                        .And
                        .OnlyContain(timeStamp => timeStamp <= endTime);
                        eventPage.Properties.Should().NotBeEmpty();
                        eventPage.Properties.First().Should().NotBeNull();
                    }

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);
            }
            finally
            {
                deviceClient?.Dispose();
            }
        }
예제 #2
0
        public async Task TimeSeriesInsightsQuery_GetSeriesLifecycle()
        {
            // Arrange
            TimeSeriesInsightsClient tsiClient    = GetClient();
            DeviceClient             deviceClient = await GetDeviceClient().ConfigureAwait(false);

            // Figure out what the Time Series Id is composed of
            TimeSeriesModelSettings modelSettings = await tsiClient.ModelSettings.GetAsync().ConfigureAwait(false);

            // Create a Time Series Id where the number of keys that make up the Time Series Id is fetched from Model Settings
            TimeSeriesId tsiId = await GetUniqueTimeSeriesInstanceIdAsync(tsiClient, modelSettings.TimeSeriesIdProperties.Count)
                                 .ConfigureAwait(false);

            try
            {
                // Send some events to the IoT hub
                await QueryTestsHelper.SendEventsToHubAsync(
                    deviceClient,
                    tsiId,
                    modelSettings.TimeSeriesIdProperties.ToArray(),
                    10)
                .ConfigureAwait(false);

                // Act

                // Query for temperature events with two calculateions. First with the temperature value as is, and the second
                // with the temperature value multiplied by 2.
                DateTimeOffset now       = Recording.UtcNow;
                DateTimeOffset endTime   = now.AddMinutes(10);
                DateTimeOffset startTime = now.AddMinutes(-10);

                var temperatureNumericVariable = new NumericVariable(
                    new TimeSeriesExpression($"$event.{QueryTestsHelper.Temperature}"),
                    new TimeSeriesExpression("avg($value)"));
                var temperatureNumericVariableTimesTwo = new NumericVariable(
                    new TimeSeriesExpression($"$event.{QueryTestsHelper.Temperature} * 2"),
                    new TimeSeriesExpression("avg($value)"));
                var temperatureTimesTwoVariableName = $"{QueryTestsHelper.Temperature}TimesTwo";

                var querySeriesRequestOptions = new QuerySeriesRequestOptions();
                querySeriesRequestOptions.InlineVariables[QueryTestsHelper.Temperature]    = temperatureNumericVariable;
                querySeriesRequestOptions.InlineVariables[temperatureTimesTwoVariableName] = temperatureNumericVariableTimesTwo;

                // This retry logic was added as the TSI instance are not immediately available after creation
                await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() =>
                {
                    AsyncPageable <QueryResultPage> querySeriesEventsPages = tsiClient.Query.GetSeriesAsync(
                        tsiId,
                        startTime,
                        endTime,
                        querySeriesRequestOptions);

                    await foreach (QueryResultPage seriesEventsPage in querySeriesEventsPages)
                    {
                        seriesEventsPage.Timestamps.Should().HaveCount(10);
                        seriesEventsPage.Timestamps.Should().OnlyContain(timeStamp => timeStamp >= startTime)
                        .And
                        .OnlyContain(timeStamp => timeStamp <= endTime);
                        seriesEventsPage.Properties.Count.Should().Be(3); // EventCount, Temperature and TemperatureTimesTwo
                        seriesEventsPage.Properties.Should().Contain((property) => property.Name == QueryTestsHelper.Temperature)
                        .And
                        .Contain((property) => property.Name == temperatureTimesTwoVariableName);

                        // Assert that the values for the Temperature property is equal to the values for the other property, multiplied by 2
                        var temperatureValues = seriesEventsPage
                                                .Properties
                                                .First((property) => property.Name == QueryTestsHelper.Temperature)
                                                .Values.Cast <double>().ToList();

                        var temperatureTimesTwoValues = seriesEventsPage
                                                        .Properties
                                                        .First((property) => property.Name == temperatureTimesTwoVariableName)
                                                        .Values.Cast <double>().ToList();
                        temperatureTimesTwoValues.Should().Equal(temperatureValues.Select((property) => property * 2).ToList());
                    }

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);

                // Query for all the series events using a timespan
                AsyncPageable <QueryResultPage> querySeriesEventsPagesWithTimespan = tsiClient.Query.GetSeriesAsync(tsiId, TimeSpan.FromMinutes(10), null, querySeriesRequestOptions);
                await foreach (QueryResultPage seriesEventsPage in querySeriesEventsPagesWithTimespan)
                {
                    seriesEventsPage.Timestamps.Should().HaveCount(10);
                    seriesEventsPage.Properties.Count.Should().Be(3); // EventCount, Temperature and TemperatureTimesTwo
                }

                // Query for temperature and humidity
                var humidityNumericVariable = new NumericVariable(
                    new TimeSeriesExpression("$event.Humidity"),
                    new TimeSeriesExpression("avg($value)"));
                querySeriesRequestOptions.InlineVariables[QueryTestsHelper.Humidity] = humidityNumericVariable;
                querySeriesRequestOptions.ProjectedVariables.Add(QueryTestsHelper.Temperature);
                querySeriesRequestOptions.ProjectedVariables.Add(QueryTestsHelper.Humidity);
                await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() =>
                {
                    AsyncPageable <QueryResultPage> querySeriesEventsPages = tsiClient.Query.GetSeriesAsync(tsiId, startTime, endTime, querySeriesRequestOptions);

                    await foreach (QueryResultPage seriesEventsPage in querySeriesEventsPages)
                    {
                        seriesEventsPage.Timestamps.Should().HaveCount(10);
                        seriesEventsPage.Timestamps.Should().OnlyContain(timeStamp => timeStamp >= startTime)
                        .And
                        .OnlyContain(timeStamp => timeStamp <= endTime);
                        seriesEventsPage.Properties.Count.Should().Be(2); // Temperature and Humidity
                        seriesEventsPage.Properties.Should().Contain((property) => property.Name == QueryTestsHelper.Temperature)
                        .And
                        .Contain((property) => property.Name == QueryTestsHelper.Humidity);
                    }

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);

                // Send 2 events with a special condition that can be used later to query on
                IDictionary <string, object> messageBase = QueryTestsHelper.BuildMessageBase(modelSettings.TimeSeriesIdProperties.ToArray(), tsiId);
                messageBase[QueryTestsHelper.Temperature] = 1.2;
                messageBase[QueryTestsHelper.Humidity]    = 3.4;
                string messageBody = JsonSerializer.Serialize(messageBase);
                var    message     = new Message(Encoding.ASCII.GetBytes(messageBody))
                {
                    ContentType     = "application/json",
                    ContentEncoding = "utf-8",
                };

                Func <Task> sendEventAct = async() => await deviceClient.SendEventAsync(message).ConfigureAwait(false);

                sendEventAct.Should().NotThrow();

                // Send it again
                sendEventAct.Should().NotThrow();

                // Query for the two events with a filter
                querySeriesRequestOptions.Filter = "$event.Temperature.Double = 1.2";
                await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() =>
                {
                    AsyncPageable <QueryResultPage> querySeriesEventsPages = tsiClient.Query.GetSeriesAsync(tsiId, startTime, endTime, querySeriesRequestOptions);
                    await foreach (QueryResultPage seriesEventsPage in querySeriesEventsPages)
                    {
                        seriesEventsPage.Timestamps.Should().HaveCount(2);
                        seriesEventsPage.Properties.Should().HaveCount(2)
                        .And
                        .Contain((property) => property.Name == QueryTestsHelper.Temperature)
                        .And
                        .Contain((property) => property.Name == QueryTestsHelper.Humidity);

                        var temperatureValues = seriesEventsPage
                                                .Properties
                                                .First((property) => property.Name == QueryTestsHelper.Temperature)
                                                .Values.Cast <double>().ToList();
                        temperatureValues.Should().AllBeEquivalentTo(1.2);
                    }

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);

                // Query for the two events with a filter, but only take 1
                querySeriesRequestOptions.MaximumNumberOfEvents = 1;
                AsyncPageable <QueryResultPage> querySeriesEventsPagesWithFilter = tsiClient.Query.GetSeriesAsync(tsiId, startTime, endTime, querySeriesRequestOptions);
                await foreach (QueryResultPage seriesEventsPage in querySeriesEventsPagesWithFilter)
                {
                    seriesEventsPage.Timestamps.Should().HaveCount(1);
                    seriesEventsPage.Properties.Should().HaveCount(2)
                    .And
                    .Contain((property) => property.Name == QueryTestsHelper.Temperature)
                    .And
                    .Contain((property) => property.Name == QueryTestsHelper.Humidity);

                    var temperatureValues = seriesEventsPage
                                            .Properties
                                            .First((property) => property.Name == QueryTestsHelper.Temperature)
                                            .Values.Cast <double>().ToList();
                    temperatureValues.Should().AllBeEquivalentTo(1.2);
                }
            }
            finally
            {
                deviceClient?.Dispose();
            }
        }
        public async Task TimeSeriesInsightsQuery_AggregateSeriesWithNumericVariable()
        {
            // Arrange
            TimeSeriesInsightsClient tsiClient    = GetClient();
            DeviceClient             deviceClient = await GetDeviceClient().ConfigureAwait(false);

            // Figure out what the Time Series Id is composed of
            TimeSeriesModelSettings modelSettings = await tsiClient.ModelSettings.GetAsync().ConfigureAwait(false);

            // Create a Time Series Id where the number of keys that make up the Time Series Id is fetched from Model Settings
            TimeSeriesId tsiId = await GetUniqueTimeSeriesInstanceIdAsync(tsiClient, modelSettings.TimeSeriesIdProperties.Count)
                                 .ConfigureAwait(false);

            try
            {
                // Send some events to the IoT hub with with a second between each event
                await QueryTestsHelper.SendEventsToHubAsync(
                    deviceClient,
                    tsiId,
                    modelSettings.TimeSeriesIdProperties.ToArray(),
                    30)
                .ConfigureAwait(false);

                DateTimeOffset now       = Recording.UtcNow;
                DateTimeOffset endTime   = now.AddMinutes(10);
                DateTimeOffset startTime = now.AddMinutes(-10);

                var temperatureNumericVariable = new NumericVariable(
                    new TimeSeriesExpression($"$event.{QueryTestsHelper.Temperature}"),
                    new TimeSeriesExpression("avg($value)"));

                var queryAggregateSeriesRequestOptions = new QueryAggregateSeriesRequestOptions();
                queryAggregateSeriesRequestOptions.InlineVariables[QueryTestsHelper.Temperature] = temperatureNumericVariable;
                queryAggregateSeriesRequestOptions.ProjectedVariables.Add(QueryTestsHelper.Temperature);

                // This retry logic was added as the TSI instance are not immediately available after creation
                await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() =>
                {
                    QueryAnalyzer queryAggregateSeriesPages = tsiClient.Queries.CreateAggregateSeriesQueryAnalyzer(
                        tsiId,
                        startTime,
                        endTime,
                        TimeSpan.FromSeconds(5),
                        queryAggregateSeriesRequestOptions);

                    var nonNullFound = false;
                    await foreach (Page <TimeSeriesPoint> aggregateSeriesPage in queryAggregateSeriesPages.GetResultsAsync().AsPages())
                    {
                        foreach (TimeSeriesPoint point in aggregateSeriesPage.Values)
                        {
                            point.GetUniquePropertyNames().Should().HaveCount(1).And.Contain((property) => property == QueryTestsHelper.Temperature);
                            var value = (double?)point.GetValue(QueryTestsHelper.Temperature);
                            if (value != null)
                            {
                                nonNullFound = true;
                            }
                        }

                        nonNullFound.Should().BeTrue();
                    }

                    queryAggregateSeriesPages.Progress.Should().Be(100);

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);

                // Add an interpolated variable
                var linearInterpolationNumericVariable = new NumericVariable(
                    new TimeSeriesExpression($"$event.{QueryTestsHelper.Temperature}"),
                    new TimeSeriesExpression("left($value)"));

                linearInterpolationNumericVariable.Interpolation = new InterpolationOperation
                {
                    Kind     = InterpolationKind.Linear,
                    Boundary = new InterpolationBoundary()
                    {
                        Span = TimeSpan.FromSeconds(1),
                    },
                };

                const string linearInterpolation = "linearInterpolation";
                queryAggregateSeriesRequestOptions.InlineVariables[linearInterpolation] = linearInterpolationNumericVariable;
                queryAggregateSeriesRequestOptions.ProjectedVariables.Add(linearInterpolation);

                await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() =>
                {
                    QueryAnalyzer queryAggregateSeriesPages = tsiClient.Queries.CreateAggregateSeriesQueryAnalyzer(
                        tsiId,
                        startTime,
                        endTime,
                        TimeSpan.FromSeconds(5),
                        queryAggregateSeriesRequestOptions);

                    await foreach (Page <TimeSeriesPoint> aggregateSeriesPage in queryAggregateSeriesPages.GetResultsAsync().AsPages())
                    {
                        aggregateSeriesPage.Values.Should().HaveCountGreaterThan(0);
                        foreach (var point in aggregateSeriesPage.Values)
                        {
                            point.GetUniquePropertyNames().Should().HaveCount(2)
                            .And
                            .Contain((property) => property == QueryTestsHelper.Temperature)
                            .And
                            .Contain((property) => property == linearInterpolation);
                        }
                    }

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);

                // Send 2 events with a special condition that can be used later to query on
                IDictionary <string, object> messageBase = QueryTestsHelper.BuildMessageBase(modelSettings.TimeSeriesIdProperties.ToArray(), tsiId);
                messageBase[QueryTestsHelper.Temperature] = 1.2;
                messageBase[QueryTestsHelper.Humidity]    = 3.4;
                string messageBody = JsonSerializer.Serialize(messageBase);
                var    message     = new Message(Encoding.ASCII.GetBytes(messageBody))
                {
                    ContentType     = "application/json",
                    ContentEncoding = "utf-8",
                };

                Func <Task> sendEventAct = async() => await deviceClient.SendEventAsync(message).ConfigureAwait(false);

                sendEventAct.Should().NotThrow();

                // Send it again
                sendEventAct.Should().NotThrow();

                // Query for the two events with a filter
                queryAggregateSeriesRequestOptions.Filter = "$event.Temperature.Double = 1.2";
                await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() =>
                {
                    QueryAnalyzer queryAggregateSeriesPages = tsiClient.Queries.CreateAggregateSeriesQueryAnalyzer(
                        tsiId,
                        startTime,
                        endTime,
                        TimeSpan.FromSeconds(5),
                        queryAggregateSeriesRequestOptions);

                    var valueFound = false;
                    await foreach (TimeSeriesPoint point in queryAggregateSeriesPages.GetResultsAsync())
                    {
                        point.GetUniquePropertyNames().Should().HaveCount(2)
                        .And
                        .Contain((property) => property == QueryTestsHelper.Temperature)
                        .And
                        .Contain((property) => property == linearInterpolation);

                        var value = (double?)point.GetValue(QueryTestsHelper.Temperature);
                        if (value == 1.2)
                        {
                            valueFound = true;
                        }
                    }

                    valueFound.Should().BeTrue();

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);
            }
            finally
            {
                deviceClient?.Dispose();
            }
        }
        public async Task TimeSeriesInsightsQuery_GetEventsLifecycle()
        {
            // Arrange
            TimeSeriesInsightsClient        tsiClient = GetClient();
            TimeSeriesInsightsModelSettings timeSeriesModelSettings = tsiClient.GetModelSettingsClient();
            TimeSeriesInsightsInstances     instancesClient         = tsiClient.GetInstancesClient();
            TimeSeriesInsightsQueries       queriesClient           = tsiClient.GetQueriesClient();
            DeviceClient deviceClient = await GetDeviceClient().ConfigureAwait(false);

            // Figure out what the Time Series Id is composed of
            TimeSeriesModelSettings modelSettings = await timeSeriesModelSettings.GetAsync().ConfigureAwait(false);

            // Create a Time Series Id where the number of keys that make up the Time Series Id is fetched from Model Settings
            TimeSeriesId tsiId = await GetUniqueTimeSeriesInstanceIdAsync(instancesClient, modelSettings.TimeSeriesIdProperties.Count)
                                 .ConfigureAwait(false);

            try
            {
                var initialEventsCount = 50;

                // Send some events to the IoT hub
                await QueryTestsHelper.SendEventsToHubAsync(
                    deviceClient,
                    tsiId,
                    modelSettings.TimeSeriesIdProperties.ToArray(),
                    initialEventsCount)
                .ConfigureAwait(false);

                // Act

                // Get events from last 10 minute
                DateTimeOffset now       = Recording.UtcNow;
                DateTimeOffset endTime   = now.AddMinutes(10);
                DateTimeOffset startTime = now.AddMinutes(-10);

                // This retry logic was added as the TSI instance are not immediately available after creation
                await TestRetryHelper.RetryAsync <AsyncPageable <TimeSeriesPoint> >(async() =>
                {
                    TimeSeriesQueryAnalyzer queryEventsPages = queriesClient.CreateEventsQuery(tsiId, startTime, endTime);
                    var count = 0;
                    await foreach (TimeSeriesPoint timeSeriesPoint in queryEventsPages.GetResultsAsync())
                    {
                        count++;
                        timeSeriesPoint.Timestamp.Should().BeAfter(startTime).And.BeBefore(endTime);

                        var temperatureValue = timeSeriesPoint.GetNullableDouble(QueryTestsHelper.Temperature);
                        temperatureValue.Should().NotBeNull();

                        var humidityValue = (double?)timeSeriesPoint.GetValue(QueryTestsHelper.Humidity);
                        humidityValue.Should().NotBeNull();
                    }

                    count.Should().Be(initialEventsCount);

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);

                // Send 2 events with a special condition that can be used later to query on
                IDictionary <string, object> messageBase = QueryTestsHelper.BuildMessageBase(
                    modelSettings.TimeSeriesIdProperties.ToArray(),
                    tsiId);
                messageBase[QueryTestsHelper.Temperature] = 1.2;
                messageBase[QueryTestsHelper.Humidity]    = 3.4;
                string messageBody = JsonSerializer.Serialize(messageBase);
                var    message     = new Message(Encoding.ASCII.GetBytes(messageBody))
                {
                    ContentType     = "application/json",
                    ContentEncoding = "utf-8",
                };

                Func <Task> sendEventAct = async() => await deviceClient.SendEventAsync(message).ConfigureAwait(false);

                await sendEventAct.Should().NotThrowAsync();

                // Send it again
                sendEventAct.Should().NotThrow();

                // Query for the two events with a filter

                // Only project Temperature and one of the Id properties
                var queryRequestOptions = new QueryEventsRequestOptions
                {
                    Filter = new TimeSeriesExpression("$event.Temperature.Double = 1.2"),
                    Store  = StoreType.WarmStore,
                };
                queryRequestOptions.ProjectedProperties.Add(
                    new TimeSeriesInsightsEventProperty
                {
                    Name = QueryTestsHelper.Temperature,
                    PropertyValueType = "Double",
                });
                queryRequestOptions.ProjectedProperties.Add(
                    new TimeSeriesInsightsEventProperty
                {
                    Name = modelSettings.TimeSeriesIdProperties.First().Name,
                    PropertyValueType = modelSettings.TimeSeriesIdProperties.First().PropertyType.ToString(),
                });

                await TestRetryHelper.RetryAsync <AsyncPageable <TimeSeriesPoint> >(async() =>
                {
                    TimeSeriesQueryAnalyzer queryEventsPages = queriesClient.CreateEventsQuery(tsiId, startTime, endTime, queryRequestOptions);
                    await foreach (Page <TimeSeriesPoint> page in queryEventsPages.GetResultsAsync().AsPages())
                    {
                        page.Values.Should().HaveCount(2);
                        foreach (TimeSeriesPoint point in page.Values)
                        {
                            var value = (double?)point.GetValue(QueryTestsHelper.Temperature);
                            value.Should().Be(1.2);
                        }
                    }

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);

                // Query for the two events with a filter, but only take 1
                queryRequestOptions.MaxNumberOfEvents = 1;
                TimeSeriesQueryAnalyzer queryEventsPagesWithFilter = queriesClient.CreateEventsQuery(tsiId, startTime, endTime, queryRequestOptions);
                await foreach (Page <TimeSeriesPoint> page in queryEventsPagesWithFilter.GetResultsAsync().AsPages())
                {
                    page.Values.Should().HaveCount(1);
                }

                await TestRetryHelper.RetryAsync <AsyncPageable <TimeSeriesPoint> >(async() =>
                {
                    // Query for all the events using a timespan
                    TimeSeriesQueryAnalyzer queryEventsPagesWithTimespan = queriesClient
                                                                           .CreateEventsQuery(tsiId, TimeSpan.FromMinutes(20), endTime);
                    await foreach (Page <TimeSeriesPoint> page in queryEventsPagesWithTimespan.GetResultsAsync().AsPages())
                    {
                        page.Values.Should().HaveCount(52);
                        foreach (TimeSeriesPoint point in page.Values)
                        {
                            point.Timestamp.Should().BeAfter(startTime).And.BeBefore(endTime);
                            var temperatureValue = (double?)point.GetValue(QueryTestsHelper.Temperature);
                            temperatureValue.Should().NotBeNull();
                            var humidityValue = (double?)point.GetValue(QueryTestsHelper.Humidity);
                            humidityValue.Should().NotBeNull();
                        }
                    }

                    return(null);
                }, MaxNumberOfRetries, s_retryDelay);
            }
            finally
            {
                deviceClient?.Dispose();
            }
        }