コード例 #1
0
            public async Task ReturnsTrueIfAggregationStillActive(bool inputIsActive)
            {
                var aggregatedEntity = new TAggregatedEntity
                {
                    ParentRowKey = AggregationRowKey
                };

                Table.SetupQuery(aggregatedEntity);

                var incident = new Incident()
                {
                    Source = new IncidentSourceData()
                    {
                        CreateDate = new DateTime(2018, 9, 13)
                    }
                };

                if (!inputIsActive)
                {
                    incident.MitigationData = new IncidentStateChangeEventData()
                    {
                        Date = new DateTime(2018, 10, 9)
                    };
                }

                var input = new ParsedIncident(incident, "", ComponentStatus.Up);

                Updater
                .Setup(x => x.UpdateAsync(Aggregation, input.StartTime))
                .Returns(Task.CompletedTask);

                var result = await Strategy.CanBeAggregatedByAsync(input, Aggregation);

                Assert.True(result);
            }
コード例 #2
0
            public async Task CreatesEvent()
            {
                var input = new ParsedIncident(Incident, "somePath", ComponentStatus.Up);

                EventEntity entity = null;

                Table
                .Setup(x => x.InsertOrReplaceAsync(It.IsAny <ITableEntity>()))
                .Returns(Task.CompletedTask)
                .Callback <ITableEntity>(e =>
                {
                    Assert.IsType <EventEntity>(e);
                    entity = e as EventEntity;
                });

                var aggregationPath = "thePath";

                Provider
                .Setup(x => x.Get(input))
                .Returns(aggregationPath);

                var result = await Factory.CreateAsync(input);

                Assert.Equal(entity, result);
                Assert.Equal(aggregationPath, entity.AffectedComponentPath);
                Assert.Equal((int)ComponentStatus.Up, entity.AffectedComponentStatus);
                Assert.Equal(input.StartTime, entity.StartTime);

                Table
                .Verify(
                    x => x.InsertOrReplaceAsync(It.IsAny <ITableEntity>()),
                    Times.Once());
            }
コード例 #3
0
            public async Task ReturnsFalseIfAggregationInactiveAndInputActive()
            {
                Aggregation.EndTime = new DateTime(2018, 10, 9);

                var aggregatedEntity = new TAggregatedEntity
                {
                    ParentRowKey = AggregationRowKey
                };

                var incident = new Incident()
                {
                    Source = new IncidentSourceData()
                    {
                        CreateDate = new DateTime(2018, 9, 13)
                    }
                };

                var input = new ParsedIncident(incident, "", ComponentStatus.Up);

                Table.SetupQuery(aggregatedEntity);

                Updater
                .Setup(x => x.UpdateAsync(Aggregation, input.StartTime))
                .Returns(Task.CompletedTask);

                var result = await Strategy.CanBeAggregatedByAsync(input, Aggregation);

                Assert.False(result);
            }
コード例 #4
0
        public async Task <bool> CanBeAggregatedByAsync(ParsedIncident input, TAggregationEntity aggregationEntity)
        {
            using (_logger.Scope("Determining if entity can be linked to aggregation {AggregationRowKey}", aggregationEntity.RowKey))
            {
                if (!_table.GetChildEntities <TChildEntity, TAggregationEntity>(aggregationEntity).ToList().Any())
                {
                    // A manually created aggregation will have no children. We cannot use an aggregation that was manually created.
                    // It is also possible that some bug or data issue has broken this aggregation. If that is the case, we cannot use it either.
                    _logger.LogInformation("Cannot link entity to aggregation because it is not linked to any children.");
                    return(false);
                }

                // To guarantee that the aggregation reflects the latest information and is actually active, we must update it.
                await _aggregationUpdater.UpdateAsync(aggregationEntity, input.StartTime);

                if (!aggregationEntity.IsActive && input.IsActive)
                {
                    _logger.LogInformation("Cannot link entity to aggregation because it has been deactivated and the incident has not been.");
                    return(false);
                }

                _logger.LogInformation("Entity can be linked to aggregation.");
                return(true);
            }
        }
コード例 #5
0
        public async Task <IncidentEntity> CreateAsync(ParsedIncident input)
        {
            var groupEntity = await _aggregationProvider.GetAsync(input);

            var affectedPath = _pathProvider.Get(input);

            using (_logger.Scope("Creating incident for parsed incident with path {AffectedComponentPath}.", affectedPath))
            {
                var incidentEntity = new IncidentEntity(
                    input.Id,
                    groupEntity,
                    affectedPath,
                    input.AffectedComponentStatus,
                    input.StartTime,
                    input.EndTime);

                await _table.InsertOrReplaceAsync(incidentEntity);

                if (incidentEntity.AffectedComponentStatus > groupEntity.AffectedComponentStatus)
                {
                    _logger.LogInformation("Incident {IncidentRowKey} has a greater severity than incident group {GroupRowKey} it was just linked to ({NewSeverity} > {OldSeverity}), updating group's severity.",
                                           incidentEntity.RowKey, groupEntity.RowKey, (ComponentStatus)incidentEntity.AffectedComponentStatus, (ComponentStatus)groupEntity.AffectedComponentStatus);
                    groupEntity.AffectedComponentStatus = incidentEntity.AffectedComponentStatus;
                    await _table.ReplaceAsync(groupEntity);
                }

                return(incidentEntity);
            }
        }
コード例 #6
0
            public async Task ReturnsFalseIfNoLinkedEntities()
            {
                var aggregatedEntityLinkedToDifferentAggregation = new TAggregatedEntity
                {
                    ParentRowKey = "wrongRowKey"
                };

                Table.SetupQuery(aggregatedEntityLinkedToDifferentAggregation);

                var incident = new Incident()
                {
                    Source = new IncidentSourceData()
                    {
                        CreateDate = new DateTime(2018, 9, 13)
                    }
                };

                var input = new ParsedIncident(incident, "", ComponentStatus.Up);

                var result = await Strategy.CanBeAggregatedByAsync(input, Aggregation);

                Assert.False(result);

                Updater
                .Verify(
                    x => x.UpdateAsync(It.IsAny <TEntityAggregation>(), It.IsAny <DateTime>()),
                    Times.Never());
            }
コード例 #7
0
        /// <summary>
        /// <see cref="EventEntity"/>s should be created using the path of <paramref name="input"/>'s level 1 ancestor.
        /// </summary>
        public string Get(ParsedIncident input)
        {
            var pathParts = ComponentUtility.GetNames(input.AffectedComponentPath);
            var topLevelComponentPathParts = pathParts.Take(2).ToArray();

            return(ComponentUtility.GetPath(topLevelComponentPathParts));
        }
コード例 #8
0
            public void ReturnsListOfParsedIncidents()
            {
                var failingParser1 = CreateParser(false);
                var failingParser2 = CreateParser(false);

                var parsedIncident1   = new ParsedIncident(Incident, "one", ComponentStatus.Degraded);
                var successfulParser1 = CreateParser(true, parsedIncident1);

                var parsedIncident2   = new ParsedIncident(Incident, "two", ComponentStatus.Down);
                var successfulParser2 = CreateParser(true, parsedIncident2);

                var parsers = new Mock <IIncidentParser>[]
                {
                    failingParser1,
                    successfulParser1,
                    successfulParser2,
                    failingParser2
                };

                var aggregateParser = new AggregateIncidentParser(
                    parsers.Select(p => p.Object),
                    Mock.Of <ILogger <AggregateIncidentParser> >());

                var result = aggregateParser.ParseIncident(Incident);

                foreach (var parser in parsers)
                {
                    parser.Verify();
                }

                Assert.Equal(2, result.Count());
                Assert.Contains(parsedIncident1, result);
                Assert.Contains(parsedIncident2, result);
            }
コード例 #9
0
        public async Task <TAggregationEntity> GetAsync(ParsedIncident input)
        {
            TAggregationEntity aggregationEntity = null;

            var possiblePath = _aggregationPathProvider.Get(input);
            // Find an aggregation to link to
            var possibleAggregationsQuery = _table
                                            .CreateQuery <TAggregationEntity>()
                                            .Where(e =>
                                                   // The aggregation must affect the same path
                                                   e.AffectedComponentPath == possiblePath &&
                                                   // The aggregation must begin before or at the same time
                                                   e.StartTime <= input.StartTime);

            // The aggregation must cover the same time period
            if (input.IsActive)
            {
                // An active input can only be linked to an active aggregation
                possibleAggregationsQuery = possibleAggregationsQuery
                                            .Where(e => e.IsActive);
            }
            else
            {
                // An inactive input can be linked to an active aggregation or an inactive aggregation that ends after it
                possibleAggregationsQuery = possibleAggregationsQuery
                                            .Where(e =>
                                                   e.IsActive ||
                                                   e.EndTime >= input.EndTime);
            }

            var possibleAggregations = possibleAggregationsQuery
                                       .ToList();

            _logger.LogInformation("Found {AggregationCount} possible aggregations to link entity to with path {AffectedComponentPath}.", possibleAggregations.Count(), possiblePath);
            foreach (var possibleAggregation in possibleAggregations)
            {
                if (await _strategy.CanBeAggregatedByAsync(input, possibleAggregation))
                {
                    _logger.LogInformation("Linking entity to aggregation.");
                    aggregationEntity = possibleAggregation;
                    break;
                }
            }

            if (aggregationEntity == null)
            {
                _logger.LogInformation("Could not find existing aggregation to link to, creating new aggregation to link entity to.");
                aggregationEntity = await _aggregationFactory.CreateAsync(input);

                _logger.LogInformation("Created new aggregation {AggregationRowKey} to link entity to.", aggregationEntity.RowKey);
            }

            return(aggregationEntity);
        }
コード例 #10
0
            private Mock <IIncidentParser> CreateParser(bool result, ParsedIncident returnedIncident = null)
            {
                var parser = new Mock <IIncidentParser>();

                parser
                .Setup(x => x.TryParseIncident(Incident, out returnedIncident))
                .Returns(result)
                .Verifiable();

                return(parser);
            }
コード例 #11
0
            public async Task CreatesEntityAndDoesNotIncreaseSeverity(ComponentStatus existingStatus)
            {
                var input = new ParsedIncident(Incident, "the path", ComponentStatus.Degraded);

                IncidentEntity entity = null;

                Table
                .Setup(x => x.InsertOrReplaceAsync(It.IsAny <ITableEntity>()))
                .Returns(Task.CompletedTask)
                .Callback <ITableEntity>(e =>
                {
                    Assert.IsType <IncidentEntity>(e);
                    entity = e as IncidentEntity;
                });

                var group = new IncidentGroupEntity
                {
                    RowKey = "parentRowKey",
                    AffectedComponentStatus = (int)existingStatus
                };

                Provider
                .Setup(x => x.GetAsync(input))
                .ReturnsAsync(group);

                var expectedPath = "the provided path";

                PathProvider
                .Setup(x => x.Get(input))
                .Returns(expectedPath);

                var result = await Factory.CreateAsync(input);

                Assert.Equal(entity, result);

                Assert.Equal(input.Id, entity.IncidentApiId);
                Assert.Equal(group.RowKey, entity.ParentRowKey);
                Assert.Equal(expectedPath, entity.AffectedComponentPath);
                Assert.Equal((int)input.AffectedComponentStatus, entity.AffectedComponentStatus);
                Assert.Equal(input.StartTime, entity.StartTime);
                Assert.Equal(input.EndTime, entity.EndTime);
                Assert.Equal((int)existingStatus, group.AffectedComponentStatus);

                Table
                .Verify(
                    x => x.InsertOrReplaceAsync(It.IsAny <IncidentEntity>()),
                    Times.Once());

                Table
                .Verify(
                    x => x.ReplaceAsync(It.IsAny <IncidentGroupEntity>()),
                    Times.Never());
            }
コード例 #12
0
        public async Task <EventEntity> CreateAsync(ParsedIncident input)
        {
            var affectedPath = _pathProvider.Get(input);

            using (_logger.Scope("Creating event for parsed incident with path {AffectedComponentPath}.", affectedPath))
            {
                var entity = new EventEntity(affectedPath, input.StartTime);
                await _table.InsertOrReplaceAsync(entity);

                return(entity);
            }
        }
            public void GetsAffectedComponentPath(string path)
            {
                var incident = new Incident()
                {
                    Source = new IncidentSourceData()
                    {
                        CreateDate = new DateTime(2018, 10, 10)
                    }
                };

                var input = new ParsedIncident(incident, path, ComponentStatus.Up);

                var result = Provider.Get(input);

                Assert.Equal(path, result);
            }
コード例 #14
0
        public async Task <IncidentGroupEntity> CreateAsync(ParsedIncident input)
        {
            var eventEntity = await _aggregationProvider.GetAsync(input);

            var affectedPath = _pathProvider.Get(input);

            using (_logger.Scope("Creating incident for parsed incident with path {AffectedComponentPath}.", affectedPath))
            {
                var incidentGroupEntity = new IncidentGroupEntity(
                    eventEntity,
                    affectedPath,
                    (ComponentStatus)input.AffectedComponentStatus,
                    input.StartTime);

                await _table.InsertOrReplaceAsync(incidentGroupEntity);

                return(incidentGroupEntity);
            }
        }
コード例 #15
0
            public async Task CreatesParsedIncidents()
            {
                var firstIncident = new Incident()
                {
                    CreateDate = Cursor + TimeSpan.FromMinutes(1),
                    Source     = new IncidentSourceData {
                        CreateDate = Cursor + TimeSpan.FromMinutes(5)
                    }
                };

                var secondIncident = new Incident()
                {
                    CreateDate = Cursor + TimeSpan.FromMinutes(2),
                    Source     = new IncidentSourceData {
                        CreateDate = Cursor + TimeSpan.FromMinutes(6)
                    }
                };

                var thirdIncident = new Incident()
                {
                    CreateDate = Cursor + TimeSpan.FromMinutes(3),
                    Source     = new IncidentSourceData {
                        CreateDate = Cursor + TimeSpan.FromMinutes(4)
                    }
                };

                var incidents = new[] { firstIncident, secondIncident, thirdIncident };

                SetupClientQuery(
                    Cursor,
                    incidents);

                var firstFirstParsedIncident   = new ParsedIncident(firstIncident, "", ComponentStatus.Up);
                var firstSecondParsedIncident  = new ParsedIncident(firstIncident, "", ComponentStatus.Up);
                var secondFirstParsedIncident  = new ParsedIncident(secondIncident, "", ComponentStatus.Up);
                var secondSecondParsedIncident = new ParsedIncident(secondIncident, "", ComponentStatus.Up);
                var thirdParsedIncident        = new ParsedIncident(thirdIncident, "", ComponentStatus.Up);

                Parser
                .Setup(x => x.ParseIncident(firstIncident))
                .Returns(new[] { firstFirstParsedIncident, firstSecondParsedIncident });

                Parser
                .Setup(x => x.ParseIncident(secondIncident))
                .Returns(new[] { secondFirstParsedIncident, secondSecondParsedIncident });

                Parser
                .Setup(x => x.ParseIncident(thirdIncident))
                .Returns(new[] { thirdParsedIncident });

                var lastCreateDate = DateTime.MinValue;

                Factory
                .Setup(x => x.CreateAsync(It.IsAny <ParsedIncident>()))
                .ReturnsAsync(new IncidentEntity())
                .Callback <ParsedIncident>(incident =>
                {
                    var nextCreateDate = incident.StartTime;
                    Assert.True(nextCreateDate >= lastCreateDate);
                    lastCreateDate = nextCreateDate;
                });

                var result = await Processor.FetchSince(Cursor);

                Assert.Equal(incidents.Max(i => i.CreateDate), result);

                Factory
                .Verify(
                    x => x.CreateAsync(firstFirstParsedIncident),
                    Times.Once());

                Factory
                .Verify(
                    x => x.CreateAsync(secondFirstParsedIncident),
                    Times.Once());
            }
コード例 #16
0
 /// <summary>
 /// <see cref="IncidentEntity"/>s and <see cref="IncidentGroupEntity"/>s should be created using the same path as the <paramref name="input"/>.
 /// </summary>
 public string Get(ParsedIncident input)
 {
     return(input.AffectedComponentPath);
 }
コード例 #17
0
            public async Task CreatesNewEntityIfNoPossibleAggregation()
            {
                var inputPath = "howdy";
                var input     = new ParsedIncident(Incident, inputPath, ComponentStatus.Degraded);

                var providedPath = "hello";

                PathProvider
                .Setup(x => x.Get(input))
                .Returns(providedPath);

                var aggregationWithDifferentPath = new TEntityAggregation
                {
                    AffectedComponentPath = "other path",
                    StartTime             = input.StartTime
                };

                var aggregationAfter = new TEntityAggregation
                {
                    AffectedComponentPath = providedPath,
                    StartTime             = input.StartTime + TimeSpan.FromDays(1)
                };

                var aggregationBefore = new TEntityAggregation
                {
                    AffectedComponentPath = providedPath,
                    StartTime             = input.StartTime - TimeSpan.FromDays(2),
                    EndTime = input.StartTime - TimeSpan.FromDays(1)
                };

                var activeAggregationToDeactivate = new TEntityAggregation
                {
                    AffectedComponentPath = providedPath,
                    StartTime             = input.StartTime
                };

                var inactiveAggregationToDeactivate = new TEntityAggregation
                {
                    AffectedComponentPath = providedPath,
                    StartTime             = input.StartTime,
                    EndTime = input.EndTime
                };

                Table.SetupQuery(
                    aggregationWithDifferentPath,
                    aggregationAfter,
                    aggregationBefore,
                    activeAggregationToDeactivate,
                    inactiveAggregationToDeactivate);

                Strategy
                .Setup(x => x.CanBeAggregatedByAsync(input, activeAggregationToDeactivate))
                .ReturnsAsync(false);

                Strategy
                .Setup(x => x.CanBeAggregatedByAsync(input, inactiveAggregationToDeactivate))
                .ReturnsAsync(false);

                var createdAggregation = new TEntityAggregation();

                AggregationFactory
                .Setup(x => x.CreateAsync(input))
                .ReturnsAsync(createdAggregation);

                var result = await Provider.GetAsync(input);

                Assert.Equal(createdAggregation, result);

                Strategy
                .Verify(
                    x => x.CanBeAggregatedByAsync(It.IsAny <ParsedIncident>(), aggregationWithDifferentPath),
                    Times.Never());

                Strategy
                .Verify(
                    x => x.CanBeAggregatedByAsync(It.IsAny <ParsedIncident>(), aggregationAfter),
                    Times.Never());

                Strategy
                .Verify(
                    x => x.CanBeAggregatedByAsync(It.IsAny <ParsedIncident>(), aggregationBefore),
                    Times.Never());

                Strategy
                .Verify(
                    x => x.CanBeAggregatedByAsync(It.IsAny <ParsedIncident>(), activeAggregationToDeactivate),
                    Times.Once());

                Strategy
                .Verify(
                    x => x.CanBeAggregatedByAsync(It.IsAny <ParsedIncident>(), inactiveAggregationToDeactivate),
                    Times.Once());
            }