Skip to content
This repository has been archived by the owner on Jul 30, 2024. It is now read-only.
/ NuGet.Jobs Public archive

Commit

Permalink
Status - unit tests (#580)
Browse files Browse the repository at this point in the history
  • Loading branch information
Scott Bommarito authored Oct 12, 2018
1 parent c75c1c5 commit c3aeb02
Show file tree
Hide file tree
Showing 69 changed files with 5,740 additions and 436 deletions.
1 change: 0 additions & 1 deletion src/StatusAggregator/Export/EventExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using NuGet.Jobs.Extensions;
Expand Down
1 change: 0 additions & 1 deletion src/StatusAggregator/Export/IEventExporter.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using NuGet.Services.Status;
using NuGet.Services.Status.Table;

Expand Down
2 changes: 1 addition & 1 deletion src/StatusAggregator/Factory/AggregationStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public async Task<bool> CanBeAggregatedByAsync(ParsedIncident input, TAggregatio

// 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)
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;
Expand Down
26 changes: 20 additions & 6 deletions src/StatusAggregator/Job.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Autofac.Core;
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Newtonsoft.Json.Linq;
using NuGet.Jobs;
Expand Down Expand Up @@ -46,6 +47,7 @@ public override void Init(IServiceContainer serviceContainer, IDictionary<string

AddStorage(containerBuilder);
AddFactoriesAndUpdaters(containerBuilder);
AddIncidentRegexParser(containerBuilder);
AddExporters(containerBuilder);
AddEntityCollector(containerBuilder);

Expand Down Expand Up @@ -95,13 +97,13 @@ private static void AddManualStatusChangeHandling(IServiceCollection serviceColl

private static void AddParsing(IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<IIncidentParsingFilter, SeverityFilter>();
serviceCollection.AddTransient<IIncidentParsingFilter, EnvironmentFilter>();
serviceCollection.AddTransient<IIncidentRegexParsingFilter, SeverityRegexParsingFilter>();
serviceCollection.AddTransient<IIncidentRegexParsingFilter, EnvironmentRegexParsingFilter>();

serviceCollection.AddTransient<IIncidentParser, OutdatedSearchServiceInstanceIncidentParser>();
serviceCollection.AddTransient<IIncidentParser, PingdomIncidentParser>();
serviceCollection.AddTransient<IIncidentParser, ValidationDurationIncidentParser>();
serviceCollection.AddTransient<IIncidentParser, TrafficManagerEndpointStatusIncidentParser>();
serviceCollection.AddTransient<IIncidentRegexParsingHandler, OutdatedSearchServiceInstanceIncidentRegexParsingHandler>();
serviceCollection.AddTransient<IIncidentRegexParsingHandler, PingdomIncidentRegexParsingHandler>();
serviceCollection.AddTransient<IIncidentRegexParsingHandler, ValidationDurationIncidentRegexParsingHandler>();
serviceCollection.AddTransient<IIncidentRegexParsingHandler, TrafficManagerEndpointStatusIncidentRegexParsingHandler>();

serviceCollection.AddTransient<IAggregateIncidentParser, AggregateIncidentParser>();
}
Expand Down Expand Up @@ -249,6 +251,18 @@ private static void AddEventUpdater(ContainerBuilder containerBuilder)
.As<IComponentAffectingEntityUpdater<EventEntity>>();
}

private static void AddIncidentRegexParser(ContainerBuilder containerBuilder)
{
containerBuilder
.RegisterAdapter<IIncidentRegexParsingHandler, IIncidentParser>(
(ctx, handler) =>
{
return new IncidentRegexParser(
handler,
ctx.Resolve<ILogger<IncidentRegexParser>>());
});
}

private static void AddEntityCollector(ContainerBuilder containerBuilder)
{
containerBuilder
Expand Down
18 changes: 4 additions & 14 deletions src/StatusAggregator/Messages/IMessageContentBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,13 @@ namespace StatusAggregator.Messages
public interface IMessageContentBuilder
{
/// <summary>
/// Tries to get contents for a message of type <paramref name="type"/> affecting <paramref name="component"/>.
/// Builds contents for a message of type <paramref name="type"/> affecting <paramref name="component"/>.
/// </summary>
/// <param name="contents">The content of the message.</param>
/// <returns>
/// True if contents for the message can be generated.
/// False otherwise.
/// </returns>
string GetContentsForMessageHelper(MessageType type, IComponent component);
string Build(MessageType type, IComponent component);

/// <summary>
/// Tries to get contents for a message of type <paramref name="type"/> affecting <paramref name="component"/> with status <paramref name="status"/>.
/// Builds contents for a message of type <paramref name="type"/> affecting <paramref name="component"/> with status <paramref name="status"/>.
/// </summary>
/// <param name="contents">The content of the message.</param>
/// <returns>
/// True if contents for the message can be generated.
/// False otherwise.
/// </returns>
string GetContentsForMessageHelper(MessageType type, IComponent component, ComponentStatus status);
string Build(MessageType type, IComponent component, ComponentStatus status);
}
}
4 changes: 2 additions & 2 deletions src/StatusAggregator/Messages/IMessageFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ public interface IMessageFactory
/// <summary>
/// Creates a message for <paramref name="eventEntity"/> at <paramref name="time"/> of type <paramref name="type"/> affecting <paramref name="component"/>.
/// </summary>
Task<MessageEntity> CreateMessageAsync(EventEntity eventEntity, DateTime time, MessageType type, IComponent component);
Task CreateMessageAsync(EventEntity eventEntity, DateTime time, MessageType type, IComponent component);

/// <summary>
/// Creates a message for <paramref name="eventEntity"/> at <paramref name="time"/> of type <paramref name="type"/> affecting <paramref name="component"/> with status <paramref name="status"/>.
/// </summary>
Task<MessageEntity> CreateMessageAsync(EventEntity eventEntity, DateTime time, MessageType type, IComponent component, ComponentStatus status);
Task CreateMessageAsync(EventEntity eventEntity, DateTime time, MessageType type, IComponent component, ComponentStatus status);

/// <summary>
/// Updates the message for <paramref name="eventEntity"/> at <paramref name="time"/> of type <paramref name="type"/> affecting <paramref name="component"/>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public IEnumerable<MessageChangeEvent> Get(EventEntity eventEntity, DateTime cur
var startTime = linkedGroup.StartTime;
_logger.LogInformation("Incident group started at {StartTime}.", startTime);
events.Add(new MessageChangeEvent(startTime, path, status, MessageType.Start));
if (linkedGroup.EndTime.HasValue)
if (!linkedGroup.IsActive)
{
var endTime = linkedGroup.EndTime.Value;
_logger.LogInformation("Incident group ended at {EndTime}.", endTime);
Expand Down
22 changes: 11 additions & 11 deletions src/StatusAggregator/Messages/MessageContentBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@ public MessageContentBuilder(ILogger<MessageContentBuilder> logger)
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

public string GetContentsForMessageHelper(
public string Build(
MessageType type,
IComponent component)
{
return GetContentsForMessageHelper(type, component, component.Status);
return Build(type, component, component.Status);
}

public string GetContentsForMessageHelper(
public string Build(
MessageType type,
IComponent component,
ComponentStatus status)
{
return GetContentsForMessageHelper(type, component.Path, status);
return Build(type, component.Path, status);
}

private string GetContentsForMessageHelper(
private string Build(
MessageType type,
string path,
ComponentStatus status)
Expand All @@ -51,23 +51,23 @@ private string GetContentsForMessageHelper(

_logger.LogInformation("Using template {MessageTemplate}.", messageTemplate);

var componentName = GetPrettyName(path);
_logger.LogInformation("Using {ComponentName} for name of component.", componentName);
var nameString = GetName(path);
_logger.LogInformation("Using {ComponentName} for name of component.", nameString);

var actionDescription = GetActionDescriptionFromPath(path);
if (actionDescription == null)
{
throw new ArgumentException("Could not find an action description for path.", nameof(path));
}

var componentStatus = status.ToString().ToLowerInvariant();
var contents = string.Format(messageTemplate, componentName, componentStatus, actionDescription);
var statusString = status.ToString().ToLowerInvariant();
var contents = string.Format(messageTemplate, nameString, statusString, actionDescription);
_logger.LogInformation("Returned {Contents} for contents of message.", contents);
return contents;
}
}

private string GetPrettyName(string path)
private string GetName(string path)
{
var componentNames = ComponentUtility.GetNames(path);
return string.Join(" ", componentNames.Skip(1).Reverse());
Expand All @@ -83,7 +83,7 @@ private string GetActionDescriptionFromPath(string path)
{
return _actionDescriptionForComponentPathMap
.FirstOrDefault(m => m.Matches(path))?
.ActionDescription; ;
.ActionDescription;
}

/// <remarks>
Expand Down
11 changes: 5 additions & 6 deletions src/StatusAggregator/Messages/MessageFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ public MessageFactory(
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

public Task<MessageEntity> CreateMessageAsync(EventEntity eventEntity, DateTime time, MessageType type, IComponent component)
public Task CreateMessageAsync(EventEntity eventEntity, DateTime time, MessageType type, IComponent component)
{
return CreateMessageAsync(eventEntity, time, type, component, component.Status);
}

public async Task<MessageEntity> CreateMessageAsync(EventEntity eventEntity, DateTime time, MessageType type, IComponent component, ComponentStatus status)
public async Task CreateMessageAsync(EventEntity eventEntity, DateTime time, MessageType type, IComponent component, ComponentStatus status)
{
using (_logger.Scope("Creating new message of type {Type} for event {EventRowKey} at {Timestamp} affecting {ComponentPath} with status {ComponentStatus}.",
type, eventEntity.RowKey, time, component.Path, status))
Expand All @@ -42,15 +42,14 @@ public async Task<MessageEntity> CreateMessageAsync(EventEntity eventEntity, Dat
if (existingMessage != null)
{
_logger.LogInformation("Message already exists, will not recreate.");
return existingMessage;
return;
}

var contents = _builder.GetContentsForMessageHelper(type, component, status);
var contents = _builder.Build(type, component, status);
var messageEntity = new MessageEntity(eventEntity, time, contents, type);
_logger.LogInformation("Creating message with time {MessageTimestamp} and contents {MessageContents}.",
messageEntity.Time, messageEntity.Contents);
await _table.InsertAsync(messageEntity);
return messageEntity;
}
}

Expand Down Expand Up @@ -81,7 +80,7 @@ public async Task UpdateMessageAsync(EventEntity eventEntity, DateTime time, Mes
return;
}

var newContents = _builder.GetContentsForMessageHelper(type, component);
var newContents = _builder.Build(type, component);
_logger.LogInformation("Replacing contents of message with time {MessageTimestamp} and contents {OldMessageContents} with {NewMessageContents}.",
existingMessage.Time, existingMessage.Contents, newContents);
existingMessage.Contents = newContents;
Expand Down
33 changes: 0 additions & 33 deletions src/StatusAggregator/Parse/EnvironmentPrefixIncidentParser.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using NuGet.Services.Incidents;

namespace StatusAggregator.Parse
{
/// <summary>
/// Subclass of <see cref="IncidentRegexParsingHandler"/> that expects <see cref="Incident"/>s are prefixed with "[ENVIRONMENT]".
/// </summary>
public abstract class EnvironmentPrefixIncidentRegexParsingHandler : IncidentRegexParsingHandler
{
public EnvironmentPrefixIncidentRegexParsingHandler(
string subtitleRegEx,
IEnumerable<IIncidentRegexParsingFilter> filters)
: base(
PrependEnvironmentRegexGroup(subtitleRegEx),
filters)
{
if (!filters.Any(f => f is EnvironmentRegexParsingFilter))
{
throw new ArgumentException(
$"A {nameof(EnvironmentPrefixIncidentRegexParsingHandler)} must be run with an {nameof(EnvironmentRegexParsingFilter)}!",
nameof(filters));
}
}

private static string PrependEnvironmentRegexGroup(string subtitleRegEx)
{
return $@"\[(?<{EnvironmentRegexParsingFilter.EnvironmentGroupName}>.*)\] {subtitleRegEx}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ namespace StatusAggregator.Parse
/// <summary>
/// Expects that the <see cref="Incident"/> contains a <see cref="Group"/> named <see cref="EnvironmentGroupName"/> with a whitelisted value.
/// </summary>
public class EnvironmentFilter : IIncidentParsingFilter
public class EnvironmentRegexParsingFilter : IIncidentRegexParsingFilter
{
public const string EnvironmentGroupName = "Environment";

private IEnumerable<string> _environments { get; }

private readonly ILogger<EnvironmentFilter> _logger;
private readonly ILogger<EnvironmentRegexParsingFilter> _logger;

public EnvironmentFilter(
public EnvironmentRegexParsingFilter(
StatusAggregatorConfiguration configuration,
ILogger<EnvironmentFilter> logger)
ILogger<EnvironmentRegexParsingFilter> logger)
{
_environments = configuration?.Environments ?? throw new ArgumentNullException(nameof(configuration));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
namespace StatusAggregator.Parse
{
/// <summary>
/// An additional filter that can be applied to a <see cref="IncidentParser"/>
/// An additional filter that can be applied to a <see cref="IncidentRegexParser"/>
/// </summary>
public interface IIncidentParsingFilter
public interface IIncidentRegexParsingFilter
{
/// <summary>
/// Returns whether or not an <see cref="IncidentParser"/> should parse <paramref name="incident"/>.
/// Returns whether or not an <see cref="IncidentRegexParser"/> should parse <paramref name="incident"/>.
/// </summary>
bool ShouldParse(Incident incident, GroupCollection groups);
}
Expand Down
37 changes: 37 additions & 0 deletions src/StatusAggregator/Parse/IIncidentRegexParsingHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Text.RegularExpressions;
using NuGet.Services.Incidents;
using NuGet.Services.Status;

namespace StatusAggregator.Parse
{
public interface IIncidentRegexParsingHandler
{
string RegexPattern { get; }
IReadOnlyCollection<IIncidentRegexParsingFilter> Filters { get; }

/// <summary>
/// Attempts to parse a <see cref="ParsedIncident.AffectedComponentPath"/> from <paramref name="incident"/>.
/// </summary>
/// <param name="affectedComponentPath">
/// The <see cref="ParsedIncident.AffectedComponentPath"/> parsed from <paramref name="incident"/> or <c>null</c> if <paramref name="incident"/> could not be parsed.
/// </param>
/// <returns>
/// <c>true</c> if a <see cref="ParsedIncident.AffectedComponentPath"/> can be parsed from <paramref name="incident"/> and <c>false</c> otherwise.
/// </returns>
bool TryParseAffectedComponentPath(Incident incident, GroupCollection groups, out string affectedComponentPath);

/// <summary>
/// Attempts to parse a <see cref="ParsedIncident.AffectedComponentStatus"/> from <paramref name="incident"/>.
/// </summary>
/// <param name="affectedComponentStatus"></param>
/// The <see cref="ParsedIncident.AffectedComponentStatus"/> parsed from <paramref name="incident"/> or <see cref="default(ComponentStatus)"/> if <paramref name="incident"/> could not be parsed.
/// <returns>
/// <c>true</c> if a <see cref="ParsedIncident.AffectedComponentStatus"/> can be parsed from <paramref name="incident"/> and <c>false</c> otherwise.
/// </returns>
bool TryParseAffectedComponentStatus(Incident incident, GroupCollection groups, out ComponentStatus affectedComponentStatus);
}
}
Loading

0 comments on commit c3aeb02

Please sign in to comment.