public async Task ShouldReturnEMptyIndividualReportIfNoResolutions() { var workitems = Data.TeamMembers.Select(m => { var workItemId = m.RelatedWorkItemIds.Random(); var workItem = new VSTSWorkItem { Id = Guid.NewGuid(), WorkItemId = workItemId, Fields = new Dictionary <string, string>() }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, WorkItemTypes.Bug); workItem.Fields.Add(VSTSFieldNames.WorkItemCreatedDate, DateTime.UtcNow.AddDays(-5).ToString()); return(workItem); }).ToList(); _classificationContextMock.Setup(c => c.Classify(It.IsAny <VSTSWorkItem>(), It.IsAny <ClassificationScope>())) .Returns(Enumerable.Empty <WorkItemResolution>()); Data.RepositoryMock.Setup(r => r.GetAsync(It.IsAny <Expression <Func <VSTSWorkItem, bool> > >())) .Returns <Expression <Func <VSTSWorkItem, bool> > >(e => Task.FromResult(workitems.Where(e.Compile()))); var report = await Report(); var firstMember = Data.TeamMembers.ElementAt(0); report.IndividualReports.Should().HaveCount(1); report.IndividualReports.Should().OnlyContain(r => r.MemberEmail == firstMember.Email && r.MemberName == firstMember.DisplayName && r.TotalResolved == 0); }
public async Task ShouldCorrectlyCalculateResolvedCount(string workItemType, string resolution, int expectedCount) { var workitems = Data.TeamMembers.Select(m => { var workItemId = m.RelatedWorkItemIds.Random(); var workItem = new VSTSWorkItem { Id = Guid.NewGuid(), WorkItemId = workItemId, Fields = new Dictionary <string, string>() }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, workItemType); workItem.Fields.Add(VSTSFieldNames.WorkItemCreatedDate, DateTime.UtcNow.AddDays(-5).ToString()); return(workItem); }).ToList(); _classificationContextMock.Setup(c => c.Classify(It.IsAny <VSTSWorkItem>(), It.IsAny <ClassificationScope>())) .Returns <VSTSWorkItem, ClassificationScope>((w, _) => { var member = Data.TeamMembers.Single(t => t.RelatedWorkItemIds.Contains(w.WorkItemId)); return(new[] { new WorkItemResolution(w, resolution, "Because", DateTime.UtcNow, member.Email, "bla") }); }); Data.RepositoryMock.Setup(r => r.GetAsync(It.IsAny <Expression <Func <VSTSWorkItem, bool> > >())) .Returns <Expression <Func <VSTSWorkItem, bool> > >(e => Task.FromResult(workitems.Where(e.Compile()))); var report = await Report(); report.TotalResolved.Should().Be(expectedCount); }
public static float GetActiveDuration(this VSTSWorkItem workItem, IEnumerable <TeamMember> team) { if (workItem.Updates == null || !workItem.Updates.Any()) { return(0.0f); } var activeTime = 0.0F; var isActive = false; var assignedToTeam = false; DateTime?lastActivated = null; foreach (var update in workItem.Updates) { var isActivation = !update.State.IsEmpty && update.State.NewValue == WorkItemStates.Active; var isOnHold = !update.State.IsEmpty && update.State.NewValue == WorkItemStates.New; var isResolved = !update.State.IsEmpty && (update.State.NewValue == WorkItemStates.Resolved || update.State.NewValue == WorkItemStates.Closed); var isCodeReview = !update.Tags.IsEmpty && WorkItemTags.ContainsTag(update.Tags.NewValue, WorkItemTags.CodeReview) || update.Relations?.Added != null && update.Relations.Added.Any(i => !string.IsNullOrWhiteSpace(i.Name) && i.Name.Equals("Pull Request", StringComparison.InvariantCultureIgnoreCase)); var isBlocked = !update.Tags.IsEmpty && (WorkItemTags.ContainsTag(update.Tags.NewValue, WorkItemTags.Blocked) || WorkItemTags.ContainsTag(update.Tags.NewValue, WorkItemTags.OnHold)); var isUnBlocked = !isBlocked && !update.Tags.IsEmpty && (WorkItemTags.ContainsTag(update.Tags.OldValue, WorkItemTags.Blocked) && !WorkItemTags.ContainsTag(update.Tags.NewValue, WorkItemTags.Blocked) || WorkItemTags.ContainsTag(update.Tags.OldValue, WorkItemTags.OnHold) && !WorkItemTags.ContainsTag(update.Tags.NewValue, WorkItemTags.OnHold)); if (!assignedToTeam && !string.IsNullOrWhiteSpace(update.AssignedTo.NewValue)) { assignedToTeam = team.Any(m => update.AssignedTo.NewValue.Contains(m.Email)); if (isActive) { lastActivated = update.ChangedDate; } } if (isActive && (isOnHold || isBlocked)) { isActive = false; if (lastActivated != null && assignedToTeam) { activeTime += lastActivated.Value.CountBusinessDaysThrough(update.ChangedDate); } } else if ((isActivation && !isBlocked) || isUnBlocked) { lastActivated = update.ChangedDate; isActive = true; } else if (isResolved || isCodeReview) { if (isActive && lastActivated != null && assignedToTeam) { activeTime += lastActivated.Value.CountBusinessDaysThrough(update.ChangedDate); } break; } } return(activeTime); }
public void ShouldReturnEmptyStringIfFieldIsNotInTheCollection() { var wi = new VSTSWorkItem { Fields = new Dictionary <string, string>() }; wi.WorkItemType.Should().BeEmpty(); }
public void ShouldReturnEmptyStringIfFieldsAreNull() { var wi = new VSTSWorkItem { Fields = null }; wi.Reason.Should().BeEmpty(); }
private WorkItemResolution GetResolvedResolutionFor(VSTSWorkItem workItem, TeamMember member = null, DateTime?revisedDate = null) { var date = revisedDate ?? DateTime.UtcNow.AddDays(-2); var teamMember = member ?? new TeamMember { Id = Guid.NewGuid(), DisplayName = Guid.NewGuid().ToString(), Email = Guid.NewGuid().ToString() }; return(new WorkItemResolution(workItem, "Resolved", "Because", date, teamMember.Email, teamMember.DisplayName)); }
private static float TryGetFromUpdates(VSTSWorkItem wi, string fieldName) { var recoveredValue = wi.Updates .LastOrDefault(u => (u.State?.NewValue == WorkItemStates.Closed || u.State?.NewValue == WorkItemStates.Resolved) && u.Fields.ContainsKey(fieldName))? .Fields[fieldName].OldValue; float.TryParse(recoveredValue, out var result); return(result); }
public IEnumerable <WorkItemResolution> Classify(VSTSWorkItem item, ClassificationScope scope) { var rs = from c in _classifiers let r = c.Classify(new WorkItemResolutionRequest { WorkItem = item, Team = scope.Team, StartDate = scope.StartDate, EndDate = scope.EndDate }) where !r.IsNone && (IsInRange(r, scope) || r.IsError) select r; return(rs.ToList()); }
public VSTSWorkItem GetWorkItemFor(TeamMember user, string type = WorkItemTypes.Bug) { var resolvedWorkItem = new VSTSWorkItem { Id = Guid.NewGuid(), WorkItemId = user.RelatedWorkItemIds.Random(), Fields = new Dictionary <string, string>() }; resolvedWorkItem.Fields.Add(VSTSFieldNames.WorkItemType, type); resolvedWorkItem.Fields.Add(VSTSFieldNames.WorkItemCreatedDate, DateTime.UtcNow.AddDays(-_random.Next(1, 15)).ToString()); return(resolvedWorkItem); }
public WorkItemResolution(VSTSWorkItem workitem, string resolution, string reason, DateTime resolutionDate, string memberEmail, string memberName) { WorkItemId = workitem.WorkItemId; WorkItemTitle = workitem.Title; WorkItemType = workitem.WorkItemType; Resolution = resolution; Reason = reason; ResolutionDate = resolutionDate; MemberEmail = memberEmail; MemberName = memberName; }
private VSTSWorkItem GetWithDates(string changedDate, string resolvedDate, string closedDate, string stateChangeDate) { var wi = new VSTSWorkItem(); wi.Fields = new Dictionary <string, string>(); wi.Fields.Add("System.ChangedDate", changedDate); wi.Fields.Add("Microsoft.VSTS.Common.ResolvedDate", resolvedDate); wi.Fields.Add("Microsoft.VSTS.Common.ClosedDate", closedDate); wi.Fields.Add("Microsoft.VSTS.Common.StateChangeDate", stateChangeDate); return(wi); }
private float GetActiveDuration(VSTSWorkItem workItem) { if (workItem.Updates == null || !workItem.Updates.Any()) { return(0.0f); } var activeTime = 0.0F; var isActive = false; DateTime?lastActivated = null; foreach (var update in workItem.Updates) { var isActivation = !update.State.IsEmpty && update.State.NewValue == WorkItemStates.Active; var isOnHold = !update.State.IsEmpty && update.State.NewValue == WorkItemStates.New; var isResolved = !update.State.IsEmpty && (update.State.NewValue == WorkItemStates.Resolved || update.State.NewValue == WorkItemStates.Closed); var isCodeReview = !update.Tags.IsEmpty && ContainsTag(update.Tags.NewValue, CodeReviewTag); var isBlocked = !update.Tags.IsEmpty && (ContainsTag(update.Tags.NewValue, BlockedTag) || ContainsTag(update.Tags.NewValue, OnHoldTag)); var isUnBlocked = !isBlocked && !update.Tags.IsEmpty && (ContainsTag(update.Tags.OldValue, BlockedTag) && !ContainsTag(update.Tags.NewValue, BlockedTag) || ContainsTag(update.Tags.OldValue, OnHoldTag) && !ContainsTag(update.Tags.NewValue, OnHoldTag)); if (isActive && (isOnHold || isBlocked)) { isActive = false; if (lastActivated != null) { activeTime += lastActivated.Value.CountBusinessDaysThrough(update.ChangedDate); } } else if ((isActivation && !isBlocked) || isUnBlocked) { lastActivated = update.ChangedDate; isActive = true; } else if (isActive && (isResolved || isCodeReview)) { if (lastActivated != null) { activeTime += lastActivated.Value.CountBusinessDaysThrough(update.ChangedDate); } break; } } return(activeTime); }
public void ShouldReturnNoneIfUnexpectedOrEmptyType(string type) { var workItem = new VSTSWorkItem { Fields = new Dictionary <string, string>() }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, type); var result = _classifier.Classify(new WorkItemResolutionRequest { WorkItem = workItem }); result.Should().NotBeNull(); result.IsNone.Should().BeTrue(); }
private static WorkItemResolutionRequest GetRequest(IEnumerable <WorkItemUpdate> updates, string type = WorkItemTypes.Task) { var workItem = new VSTSWorkItem { Fields = new Dictionary <string, string>(), WorkItemId = 0 }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, type); workItem.Updates = updates; return(new WorkItemResolutionRequest { WorkItem = workItem, Team = FakeTeam }); }
public static VSTSWorkItem GetWorkItemWithDate(DateTime?createdDate) { var wi = new VSTSWorkItem { Fields = new Dictionary <string, string>() }; wi.Id = Guid.NewGuid(); wi.WorkItemId = _random.Next(100_000_000); if (createdDate.HasValue) { wi.Fields.Add(VSTSFieldNames.WorkItemCreatedDate, createdDate.Value.ToString("s")); } return(wi); }
public void ShouldReturnNoneIfStateIsNotSet() { var workItem = new VSTSWorkItem { Fields = new Dictionary <string, string>() }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, WorkItemTypes.Bug); workItem.Updates = UpdateBuilder.Create().Update().Build(); var result = _classifier.Classify(new WorkItemResolutionRequest { WorkItem = workItem, Team = ResolvedWorkItemsDataProvider.FakeTeam }); result.Should().NotBeNull(); result.IsNone.Should().BeTrue(); }
public void ShouldReturnNoneIfEmptyHistoryItems() { var workItem = new VSTSWorkItem { Fields = new Dictionary <string, string>() }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, DummyClassifier.SupportedType); workItem.Updates = Enumerable.Empty <WorkItemUpdate>(); var result = _classifier.Classify(new WorkItemResolutionRequest { WorkItem = workItem }); result.Should().NotBeNull(); result.IsNone.Should().BeTrue(); }
private async Task <AggregatedWorkitemsETAReport> ExecuteReportWithResolutions(int?completed = null, int?remaining = null, int?original = null, IEnumerable <WorkItemUpdate> updates = null) { var workitems = Data.TeamMembers .SelectMany(t => t.RelatedWorkItemIds) .Select(id => { var wi = new VSTSWorkItem { WorkItemId = id, Fields = new Dictionary <string, string>() }; wi.Fields[VSTSFieldNames.WorkItemType] = WorkItemTypes.Bug; wi.Fields[VSTSFieldNames.Title] = "Bla"; if (completed.HasValue) { wi.Fields[CompletedWorkField] = completed.ToString(); } if (remaining.HasValue) { wi.Fields[RemainingWorkField] = remaining?.ToString(); } if (original.HasValue) { wi.Fields[OriginalEstimateField] = original?.ToString(); } if (updates != null) { wi.Updates = updates; } return(wi); }).ToList(); Data.RepositoryMock.Setup(r => r.GetAsync(It.IsAny <Expression <Func <VSTSWorkItem, bool> > >())) .Returns <Expression <Func <VSTSWorkItem, bool> > >(e => Task.FromResult(workitems.Where(e.Compile()))); _classificationContextMock.Setup(c => c.Classify(It.IsAny <VSTSWorkItem>(), It.IsAny <ClassificationScope>())) .Returns <VSTSWorkItem, ClassificationScope>((w, _) => { var member = Data.TeamMembers.Single(t => t.RelatedWorkItemIds.Contains(w.WorkItemId)); return(new[] { new WorkItemResolution(w, WorkItemStates.Resolved, "Because", DateTime.UtcNow, member.Email, "bla") }); }); return(await Report()); }
public async Task ShouldFetchWorkitemsForMembersInProfile() { // TODO: Revisit this test var allMembers = Data.TeamMembers .Union(Data.CreateTeamMembers(2)); var workitems = allMembers.Select(m => { var workItemId = Data.TeamMemberIds.Contains(m.Id) ? m.RelatedWorkItemIds.Random() : -_random.Next(0, 50); var workItem = new VSTSWorkItem { Id = Guid.NewGuid(), WorkItemId = workItemId, Fields = new Dictionary <string, string>() }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, WorkItemTypes.Bug); workItem.Fields.Add(VSTSFieldNames.WorkItemCreatedDate, DateTime.UtcNow.AddDays(-5).ToString()); workItem.Updates = UpdateBuilder.Create() .Activated() .Then().Resolved(m) .Build(); return(workItem); }).ToList(); _classificationContextMock.Setup(c => c.Classify(It.IsAny <VSTSWorkItem>(), It.IsAny <ClassificationScope>())) .Returns <VSTSWorkItem, ClassificationScope>((w, _) => { var member = Data.TeamMembers.Single(t => t.RelatedWorkItemIds.Contains(w.WorkItemId)); return(new[] { new WorkItemResolution(w, WorkItemStates.Resolved, "Because", DateTime.UtcNow, member.Email, "bla") }); }); Data.RepositoryMock.Setup(r => r.GetAsync(It.IsAny <Expression <Func <VSTSWorkItem, bool> > >())) .Returns <Expression <Func <VSTSWorkItem, bool> > >(e => Task.FromResult(workitems.Where(e.Compile()))); var report = await Report(); report.Should().NotBeNull(); report.Should().BeOfType <AggregatedWorkitemsETAReport>(); report.IndividualReports.Should().HaveCount(2); report.IndividualReports.Should().OnlyContain(r => Data.TeamMembers.Any(m => m.Email == r.MemberEmail && m.DisplayName == r.MemberName)); report.IndividualReports.Should().OnlyContain(r => r.OriginalEstimated == 0); report.IndividualReports.Should().OnlyContain(r => r.CompletedWithEstimates == 0); report.IndividualReports.Should().OnlyContain(r => r.WithoutETA == 1); report.IndividualReports.Should().OnlyContain(r => r.TotalResolved == 1); }
public void ShouldReturnNoneIfMovedToResolvedFromClosed() { var workItem = new VSTSWorkItem { Fields = new Dictionary <string, string>() }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, WorkItemTypes.Bug); workItem.Updates = UpdateBuilder.Create().Resolved(ResolvedWorkItemsDataProvider.FakeTeam.ElementAt(0), from: "Closed") .Because("Reasons") .Build(); var result = _classifier.Classify(new WorkItemResolutionRequest { WorkItem = workItem, Team = ResolvedWorkItemsDataProvider.FakeTeam }); result.Should().NotBeNull(); result.IsNone.Should().BeTrue(); }
public static float GetEtaValue(this VSTSWorkItem wi, ETAFieldType etaType, Settings settings) { var fieldName = FieldNameFor(wi.WorkItemType, etaType, settings); if (!wi.Fields.ContainsKey(fieldName)) { return(etaType == ETAFieldType.RemainingWork ? TryGetFromUpdates(wi, fieldName) : 0); } var value = wi.Fields[fieldName]; if (string.IsNullOrEmpty(value)) { return(0); } return(float.Parse(value)); }
public void ShouldReturnResolutionForSupportedType() { var workItem = new VSTSWorkItem { Fields = new Dictionary <string, string>() }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, DummyClassifier.SupportedType); workItem.Updates = UpdateBuilder.Create().Resolved() .Build(); var result = _classifier.Classify(new WorkItemResolutionRequest { WorkItem = workItem }); result.Should().NotBeNull(); result.IsNone.Should().BeFalse(); result.Resolution.Should().Be(DummyClassifier.ExpectedResolution); }
public void ShouldReturnNoneIfNoMatchingHistoryItems() { var workItem = new VSTSWorkItem { Fields = new Dictionary <string, string>() }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, WorkItemTypes.Bug); workItem.Updates = UpdateBuilder.Create() .Activated() .Then().New() .Build(); var result = _classifier.Classify(new WorkItemResolutionRequest { WorkItem = workItem }); result.Should().NotBeNull(); result.IsNone.Should().BeTrue(); }
public void ShouldReturnErrorResolutionIfException() { var workItem = new VSTSWorkItem { Fields = new Dictionary <string, string>() }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, ExceptionWorkItemClassifier.SupportedType); workItem.Updates = UpdateBuilder.Create().Resolved() .Build(); var classifier = new ExceptionWorkItemClassifier(); var result = classifier.Classify(new WorkItemResolutionRequest { WorkItem = workItem }); result.Should().NotBeNull(); result.IsError.Should().BeTrue(); result.Reason.Should().Be(ExceptionWorkItemClassifier.ExpectedReason); }
public void ShouldReturnNoneIfResolvedNotByTheTeam() { var workItem = new VSTSWorkItem { Fields = new Dictionary <string, string>() }; workItem.Fields.Add(VSTSFieldNames.WorkItemType, WorkItemTypes.Bug); workItem.Updates = UpdateBuilder.Create() .Resolved(new TeamMember { DisplayName = "Not a member", Email = "*****@*****.**" }) .Because("Reasons") .Build(); var result = _classifier.Classify(new WorkItemResolutionRequest { WorkItem = workItem, Team = ResolvedWorkItemsDataProvider.FakeTeam }); result.Should().NotBeNull(); result.IsNone.Should().BeTrue(); }
public static bool IsAssignedToTeamMember(this VSTSWorkItem item, IEnumerable <TeamMember> team) { var assignedTo = item.Updates.LastOrDefault(u => !u.AssignedTo.IsEmpty)?.AssignedTo.NewValue; return(team.Any(m => !string.IsNullOrWhiteSpace(assignedTo) && assignedTo.Contains(m.Email))); }
public static bool IsInCodeReview(this VSTSWorkItem item) { return(item.State == WorkItemStates.Active && (WorkItemTags.ContainsTag(item.Updates.LastOrDefault(u => !u.Tags.IsEmpty)?.Tags.NewValue, WorkItemTags.CodeReview) || item.Updates.Any(u => u.Relations?.Added != null && u.Relations.Added.Any(i => i.IsPullRequest)))); }
public VSTSWorkItem SaveWorkItem(VSTSWorkItem workItem) { return(SaveWorkItems(new VSTSWorkItem[] { workItem }).FirstOrDefault()); }
private TeamMember GetMemberFromWorkItem(VSTSWorkItem workItem) { return(_team.SingleOrDefault(m => m.RelatedWorkItemIds != null && m.RelatedWorkItemIds.Contains(workItem.WorkItemId))); }