Skip to content

Commit 1ce38e5

Browse files
committed
Added IsNewAndShouldBePosted and IsNewAndShouldBeStored.
Removed static from all Function classes and methods.
1 parent 8f04639 commit 1ce38e5

16 files changed

+297
-45
lines changed

src/AzureFunctionsUpdates.UnitTests/Models/LatestReleasesTests.cs

+47-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
using AzureFunctionsUpdates.Models;
22
using AzureFunctionsUpdates.UnitTests.TestObjectBuilders;
33
using FluentAssertions;
4+
using System;
45
using Xunit;
56

67
namespace AzureFunctionsUpdates.UnitTests.Models
78
{
89
public class LatestReleasesTests
910
{
1011
[Fact]
11-
public void GivenHistoryReleaseIsNullRelease_WhenIsNewReleaseIsCalled_ThenIsNewReleaseShouldBeTrue()
12+
public void GivenHistoryReleaseIsNullRelease_WhenIsNewAndShouldBeStoredIsCalled_ThenResultShouldBeTrue()
1213
{
1314
// Arrange
1415
const string repoName = "repo";
@@ -20,11 +21,11 @@ public void GivenHistoryReleaseIsNullRelease_WhenIsNewReleaseIsCalled_ThenIsNewR
2021
var latestReleases = new LatestReleases(repoConfig, releasesFromGitHub, releasesFromHistory);
2122

2223
// Assert
23-
latestReleases.IsNewRelease.Should().BeTrue("because no release was found in history data.");
24+
latestReleases.IsNewAndShouldBeStored.Should().BeTrue("because no release was found in history data.");
2425
}
2526

2627
[Fact]
27-
public void GivenHistoryReleaseIsReleaseWithMatchingReleaseId_WhenIsNewReleaseIsCalled_ThenIsNewReleaseShouldBeFalse()
28+
public void GivenHistoryReleaseIsReleaseWithMatchingReleaseId_WhenIIsNewAndShouldBeStoredIsCalled_ThenResultShouldBeFalse()
2829
{
2930
// Arrange
3031
const string repoName = "repo";
@@ -37,11 +38,11 @@ public void GivenHistoryReleaseIsReleaseWithMatchingReleaseId_WhenIsNewReleaseIs
3738
var latestReleases = new LatestReleases(repoConfig, releasesFromGitHub, releasesFromHistory);
3839

3940
// Assert
40-
latestReleases.IsNewRelease.Should().BeFalse("because the releaseIds are equal");
41+
latestReleases.IsNewAndShouldBeStored.Should().BeFalse("because the releaseIds are equal");
4142
}
4243

4344
[Fact]
44-
public void GivenHistoryReleaseIsReleaseWithNonMatchingReleaseId_WhenIsNewReleaseIsCalled_ThenIsNewReleaseShouldBeTrue()
45+
public void GivenHistoryReleaseIsReleaseWithNonMatchingReleaseId_WhenIsNewAndShouldBeStoredIsCalled_ThenResultShouldBeTrue()
4546
{
4647
// Arrange
4748
const string repoName = "repo";
@@ -55,7 +56,47 @@ public void GivenHistoryReleaseIsReleaseWithNonMatchingReleaseId_WhenIsNewReleas
5556
var latestReleases = new LatestReleases(repoConfig, releasesFromGitHub, releasesFromHistory);
5657

5758
// Assert
58-
latestReleases.IsNewRelease.Should().BeTrue("because the releaseIds are not equal");
59+
latestReleases.IsNewAndShouldBeStored.Should().BeTrue("because the releaseIds are not equal");
60+
}
61+
62+
[Fact]
63+
public void GivenHistoryReleaseIsNullReleaseAndGitHubReleaseIsWithinTimeWindow_WhenIsNewAndShouldBePostedIsCalled_ThenResultShouldBeTrue()
64+
{
65+
// Arrange
66+
const string repoName = "repo";
67+
const int releaseIdGithub = 1;
68+
var daysTimespan = new TimeSpan(2, 0, 0, 0);
69+
var gitHubReleaseDate = DateTimeOffset.UtcNow.Subtract(daysTimespan);
70+
var repoConfig = RepositoryConfigurationBuilder.BuildOne(repoName);
71+
var releasesFromGitHub = RepositoryReleaseBuilder.BuildListContainingOneWithReleaseIdAndDate(repoName, releaseIdGithub, gitHubReleaseDate);
72+
var releasesFromHistory = RepositoryReleaseBuilder.BuildListContainingOneNullRelease(repoName);
73+
74+
// Act
75+
var latestReleases = new LatestReleases(repoConfig, releasesFromGitHub, releasesFromHistory);
76+
77+
// Assert
78+
latestReleases.IsNewAndShouldBeStored.Should().BeTrue("because the release is not in history yet.");
79+
latestReleases.IsNewAndShouldBePosted.Should().BeTrue($"because the release date is within the time window of {LatestReleases.MaximumNumberOfDaysToPostAboutNewlyFoundRelease} days");
80+
}
81+
82+
[Fact]
83+
public void GivenHistoryReleaseIsNullReleaseAndGitHubReleaseIsOutsideTimeWindow_WhenIsNewAndShouldBePostedIsCalled_ThenResultShouldBeFalse()
84+
{
85+
// Arrange
86+
const string repoName = "repo";
87+
const int releaseIdGithub = 1;
88+
var daysTimespan = new TimeSpan(5, 0, 0, 0);
89+
var gitHubReleaseDate = DateTimeOffset.UtcNow.Subtract(daysTimespan);
90+
var repoConfig = RepositoryConfigurationBuilder.BuildOne(repoName);
91+
var releasesFromGitHub = RepositoryReleaseBuilder.BuildListContainingOneWithReleaseIdAndDate(repoName, releaseIdGithub, gitHubReleaseDate);
92+
var releasesFromHistory = RepositoryReleaseBuilder.BuildListContainingOneNullRelease(repoName);
93+
94+
// Act
95+
var latestReleases = new LatestReleases(repoConfig, releasesFromGitHub, releasesFromHistory);
96+
97+
// Assert
98+
latestReleases.IsNewAndShouldBeStored.Should().BeTrue("because the release is not in history yet.");
99+
latestReleases.IsNewAndShouldBePosted.Should().BeFalse($"because the release date is outside the time window of {LatestReleases.MaximumNumberOfDaysToPostAboutNewlyFoundRelease} days");
59100
}
60101
}
61102
}

src/AzureFunctionsUpdates.UnitTests/Orchestrations/ReleaseUpdateOrchestrationTests.cs

+32-5
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ namespace AzureFunctionsUpdates.UnitTests.Orchestrations
1212
public class ReleaseUpdateOrchestrationTests
1313
{
1414
[Fact]
15-
public void GivenNoReleasesAreAvailableInHistoryAndNewGithubReleasesAreRetrieved_WhenOrchestrationIsRun_ThenSaveAndPostShouldBeCalled()
15+
public void GivenNoReleasesAreAvailableInHistoryAndNewGithubReleasesAreRetrieved_WhenOrchestrationIsRunForTwoRepos_ThenSaveAndPostShouldBeCalled()
1616
{
1717
// Arrange
1818
var mockContext = OrchestrationContextBuilder.BuildWithoutHistoryAndWithGitHubRelease();
1919
var logger = new Mock<ILogger>();
20+
var releaseUpdateOrchestration = new ReleaseUpdateOrchestration();
2021

2122
// Act
22-
ReleaseUpdateOrchestration.Run(mockContext.Object, logger.Object);
23+
releaseUpdateOrchestration.Run(mockContext.Object, logger.Object);
2324

2425
// Assert
2526
mockContext.Verify(c => c.CallActivityWithRetryAsync(
@@ -33,15 +34,40 @@ public void GivenNoReleasesAreAvailableInHistoryAndNewGithubReleasesAreRetrieved
3334
It.IsAny<RepositoryRelease>()), Times.Exactly(2));
3435
}
3536

37+
[Fact]
38+
public void GivenNoReleasesAreAvailableInHistoryAndNewGithubReleaseReturnsNullRelease_WhenOrchestrationIsRunForTwoRepos_ThenSaveAndPostShouldBeCalledForTheReleaseWhichWasReturnedFromGitHub()
39+
{
40+
// Arrange
41+
var mockContext = OrchestrationContextBuilder.BuildWithoutHistoryAndGitHubReturnsNullRelease();
42+
var logger = new Mock<ILogger>();
43+
var releaseUpdateOrchestration = new ReleaseUpdateOrchestration();
44+
45+
// Act
46+
releaseUpdateOrchestration.Run(mockContext.Object, logger.Object);
47+
48+
// Assert
49+
mockContext.Verify(c => c.CallActivityWithRetryAsync(
50+
nameof(SaveLatestRelease),
51+
It.IsAny<RetryOptions>(),
52+
It.IsAny<RepositoryRelease>()), Times.Exactly(1));
53+
54+
mockContext.Verify(c => c.CallActivityWithRetryAsync(
55+
nameof(PostUpdate),
56+
It.IsAny<RetryOptions>(),
57+
It.IsAny<RepositoryRelease>()), Times.Exactly(1));
58+
}
59+
60+
3661
[Fact]
3762
public void GivenReleasesAreAvailableInHistoryAndNewGithubReleasesAreTheSame_WhenOrchestrationIsRun_ThenSaveAndPostShouldNotBeCalled()
3863
{
3964
// Arrange
4065
var mockContext = OrchestrationContextBuilder.BuildWithHistoryAndWithGitHubWithEqualReleases();
4166
var logger = new Mock<ILogger>();
67+
var releaseUpdateOrchestration = new ReleaseUpdateOrchestration();
4268

4369
// Act
44-
ReleaseUpdateOrchestration.Run(mockContext.Object, logger.Object);
70+
releaseUpdateOrchestration.Run(mockContext.Object, logger.Object);
4571

4672
// Assert
4773
mockContext.Verify(c => c.CallActivityWithRetryAsync(
@@ -56,14 +82,15 @@ public void GivenReleasesAreAvailableInHistoryAndNewGithubReleasesAreTheSame_Whe
5682
}
5783

5884
[Fact]
59-
public void GivenReleasesAreAvailableInHistoryAndOneGithubReleaseIsEuqualAndOneIsDifferent_WhenOrchestrationIsRun_ThenSaveAndPostShouldBeCalledOnce()
85+
public void GivenReleasesAreAvailableInHistoryAndOneGithubReleaseIsEqualAndOneIsDifferent_WhenOrchestrationIsRun_ThenSaveAndPostShouldBeCalledOnce()
6086
{
6187
// Arrange
6288
var mockContext = OrchestrationContextBuilder.BuildWithHistoryAndWithGitHubWithOneEqualAndOneDifferentRelease();
6389
var logger = new Mock<ILogger>();
90+
var releaseUpdateOrchestration = new ReleaseUpdateOrchestration();
6491

6592
// Act
66-
ReleaseUpdateOrchestration.Run(mockContext.Object, logger.Object);
93+
releaseUpdateOrchestration.Run(mockContext.Object, logger.Object);
6794

6895
// Assert
6996
mockContext.Verify(c => c.CallActivityWithRetryAsync(

src/AzureFunctionsUpdates.UnitTests/TestObjectBuilders/OrchestrationContextBuilder.cs

+67-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public static Mock<DurableOrchestrationContextBase> BuildWithoutHistoryAndWithGi
1818
// Setup GetRepositoryConfigurations
1919
mockContext
2020
.Setup(c => c.CallActivityWithRetryAsync<IReadOnlyList<RepositoryConfiguration>>(
21-
nameof(GetRepositoryConfigurations),
21+
nameof(GetConfigurations),
2222
It.IsAny<RetryOptions>(),
2323
null))
2424
.ReturnsAsync(repoConfigurations);
@@ -70,6 +70,70 @@ public static Mock<DurableOrchestrationContextBase> BuildWithoutHistoryAndWithGi
7070
return mockContext;
7171
}
7272

73+
public static Mock<DurableOrchestrationContextBase> BuildWithoutHistoryAndGitHubReturnsNullRelease()
74+
{
75+
const string repository1Name = "repo-1";
76+
const string repository2Name = "repo-2";
77+
var mockContext = new Mock<DurableOrchestrationContextBase>();
78+
var repoConfigurations = RepositoryConfigurationBuilder.BuildTwo(repository1Name, repository2Name);
79+
80+
// Setup GetRepositoryConfigurations
81+
mockContext
82+
.Setup(c => c.CallActivityWithRetryAsync<IReadOnlyList<RepositoryConfiguration>>(
83+
nameof(GetConfigurations),
84+
It.IsAny<RetryOptions>(),
85+
null))
86+
.ReturnsAsync(repoConfigurations);
87+
88+
// Setup GetLatestReleaseFromGitHub
89+
mockContext
90+
.Setup(c => c.CallActivityWithRetryAsync<RepositoryRelease>(
91+
nameof(GetLatestReleaseFromGitHub),
92+
It.IsAny<RetryOptions>(),
93+
repoConfigurations[0]))
94+
.ReturnsAsync(RepositoryReleaseBuilder.BuildOne(repository1Name));
95+
96+
97+
// Returns NullRelease because no release info is retrieved from GitHub
98+
mockContext
99+
.Setup(c => c.CallActivityWithRetryAsync<RepositoryRelease>(
100+
nameof(GetLatestReleaseFromGitHub),
101+
It.IsAny<RetryOptions>(),
102+
repoConfigurations[1]))
103+
.ReturnsAsync(RepositoryReleaseBuilder.BuildNullRelease(repository1Name));
104+
105+
// Setup GetLatestReleaseFromHistory
106+
mockContext
107+
.Setup(c => c.CallActivityWithRetryAsync<RepositoryRelease>(
108+
nameof(GetLatestReleaseFromHistory),
109+
It.IsAny<RetryOptions>(),
110+
repoConfigurations[0]))
111+
.ReturnsAsync(RepositoryReleaseBuilder.BuildNullRelease(repository1Name));
112+
113+
mockContext
114+
.Setup(c => c.CallActivityWithRetryAsync<RepositoryRelease>(
115+
nameof(GetLatestReleaseFromHistory),
116+
It.IsAny<RetryOptions>(),
117+
repoConfigurations[1]))
118+
.ReturnsAsync(RepositoryReleaseBuilder.BuildNullRelease(repository2Name));
119+
120+
// Setup SaveLatestRelease
121+
mockContext
122+
.Setup(c => c.CallActivityWithRetryAsync(
123+
nameof(SaveLatestRelease),
124+
It.IsAny<RetryOptions>(),
125+
It.IsAny<RepositoryRelease>()));
126+
127+
// Setup PostUpdate
128+
mockContext
129+
.Setup(c => c.CallActivityWithRetryAsync(
130+
nameof(PostUpdate),
131+
It.IsAny<RetryOptions>(),
132+
It.IsAny<RepositoryRelease>()));
133+
134+
return mockContext;
135+
}
136+
73137
public static Mock<DurableOrchestrationContextBase> BuildWithHistoryAndWithGitHubWithEqualReleases()
74138
{
75139
const string repository1Name = "repo-1";
@@ -82,7 +146,7 @@ public static Mock<DurableOrchestrationContextBase> BuildWithHistoryAndWithGitHu
82146
// Setup GetRepositoryConfigurations
83147
mockContext
84148
.Setup(c => c.CallActivityWithRetryAsync<IReadOnlyList<RepositoryConfiguration>>(
85-
nameof(GetRepositoryConfigurations),
149+
nameof(GetConfigurations),
86150
It.IsAny<RetryOptions>(),
87151
null))
88152
.ReturnsAsync(repoConfigurations);
@@ -148,7 +212,7 @@ public static Mock<DurableOrchestrationContextBase> BuildWithHistoryAndWithGitHu
148212
// Setup GetRepositoryConfigurations
149213
mockContext
150214
.Setup(c => c.CallActivityWithRetryAsync<IReadOnlyList<RepositoryConfiguration>>(
151-
nameof(GetRepositoryConfigurations),
215+
nameof(GetConfigurations),
152216
It.IsAny<RetryOptions>(),
153217
null))
154218
.ReturnsAsync(repoConfigurations);

src/AzureFunctionsUpdates.UnitTests/TestObjectBuilders/RepositoryReleaseBuilder.cs

+18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using AutoFixture;
22
using AzureFunctionsUpdates.Models;
3+
using System;
34
using System.Collections.Generic;
45

56
namespace AzureFunctionsUpdates.UnitTests.TestObjectBuilders
@@ -23,6 +24,15 @@ public static RepositoryRelease BuildOneWithReleaseId(string repositoryName, int
2324
.Create();
2425
}
2526

27+
public static RepositoryRelease BuildOneWithReleaseIdAndDate(string repositoryName, int id, DateTimeOffset releaseDate)
28+
{
29+
return _fixture.Build<RepositoryRelease>()
30+
.With(r => r.RepositoryName, repositoryName)
31+
.With(r => r.ReleaseId, id)
32+
.With(r => r.ReleaseCreatedAt, releaseDate)
33+
.Create();
34+
}
35+
2636
public static RepositoryRelease BuildNullRelease(string repositoryName)
2737
{
2838
return new NullRelease(repositoryName);
@@ -36,6 +46,14 @@ public static IReadOnlyList<RepositoryRelease> BuildListContainingOneWithRelease
3646
};
3747
}
3848

49+
public static IReadOnlyList<RepositoryRelease> BuildListContainingOneWithReleaseIdAndDate(string repositoryName, int id, DateTimeOffset releaseDate)
50+
{
51+
return new List<RepositoryRelease>
52+
{
53+
BuildOneWithReleaseIdAndDate(repositoryName, id, releaseDate)
54+
};
55+
}
56+
3957
public static IReadOnlyList<RepositoryRelease> BuildListContainingOneNullRelease(string repositoryName)
4058
{
4159
return new List<RepositoryRelease>

src/AzureFunctionsUpdates/Activities/GetRepositoryConfigurations.cs renamed to src/AzureFunctionsUpdates/Activities/GetConfigurations.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@
99

1010
namespace AzureFunctionsUpdates.Activities
1111
{
12-
public static class GetRepositoryConfigurations
12+
public class GetConfigurations
1313
{
14-
[FunctionName(nameof(GetRepositoryConfigurations))]
14+
[FunctionName(nameof(GetConfigurations))]
1515
[StorageAccount(Configuration.ConnectionName)]
16-
public static async Task<IReadOnlyList<RepositoryConfiguration>> Run(
16+
public async Task<IReadOnlyList<RepositoryConfiguration>> Run(
1717
[ActivityTrigger] string unusedInput,
1818
[Table(Configuration.RepositoryConfigurations.TableName)] CloudTable table,
1919
ILogger logger)
2020
{
21-
logger.LogInformation($"Started {nameof(GetRepositoryConfigurations)}.");
21+
logger.LogInformation($"Started {nameof(GetConfigurations)}.");
2222

2323
var repositoryConfigurations = new List<RepositoryConfiguration>();
2424
var query = QueryBuilder<RepositoryConfiguration>.CreateQueryForPartitionKey(Configuration.RepositoryConfigurations.PartitionKey);

src/AzureFunctionsUpdates/Activities/GetLatestReleaseFromGitHub.cs

+15-5
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,29 @@
66

77
namespace AzureFunctionsUpdates.Activities
88
{
9-
public static class GetLatestReleaseFromGitHub
9+
public class GetLatestReleaseFromGitHub
1010
{
11-
private static readonly GitHubClient Client = new GitHubClient(new ProductHeaderValue("AzureFunctionUpdates2019"));
11+
private readonly GitHubClient client = new GitHubClient(new ProductHeaderValue("AzureFunctionUpdates2019"));
1212

1313
[FunctionName(nameof(GetLatestReleaseFromGitHub))]
14-
public static async Task<RepositoryRelease> Run(
14+
public async Task<RepositoryRelease> Run(
1515
[ActivityTrigger] RepositoryConfiguration repoConfiguration,
1616
ILogger logger)
1717
{
1818
logger.LogInformation($"Started {nameof(GetLatestReleaseFromGitHub)} for { repoConfiguration.RepositoryOwner } { repoConfiguration.RepositoryName }.");
1919

20-
var latestRelease = await Client.Repository.Release.GetLatest(repoConfiguration.RepositoryOwner, repoConfiguration.RepositoryName);
21-
var repoRelease = MapToRepoRelease(repoConfiguration, latestRelease);
20+
RepositoryRelease repoRelease = new NullRelease(repoConfiguration.RepositoryName);
21+
try
22+
{
23+
var latestRelease = await client.Repository.Release.GetLatest(repoConfiguration.RepositoryOwner, repoConfiguration.RepositoryName);
24+
repoRelease = MapToRepoRelease(repoConfiguration, latestRelease);
25+
}
26+
catch (NotFoundException)
27+
{
28+
// We're ignoring 404s for Releases since there are repositories without releases.
29+
// But we want to keep monitoring them and notify once they do have a releases.
30+
logger.LogWarning($"No release information found for repository: {repoConfiguration.RepositoryName}.");
31+
}
2232

2333
return repoRelease;
2434
}

src/AzureFunctionsUpdates/Activities/GetLatestReleaseFromHistory.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99

1010
namespace AzureFunctionsUpdates.Activities
1111
{
12-
public static class GetLatestReleaseFromHistory
12+
public class GetLatestReleaseFromHistory
1313
{
1414
[FunctionName(nameof(GetLatestReleaseFromHistory))]
1515
[StorageAccount(Configuration.ConnectionName)]
16-
public static async Task<RepositoryRelease> Run(
16+
public async Task<RepositoryRelease> Run(
1717
[ActivityTrigger] RepositoryConfiguration repoConfiguration,
1818
[Table(Configuration.Releases.TableName)] CloudTable table,
1919
ILogger logger)

src/AzureFunctionsUpdates/Activities/PostUpdate.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77

88
namespace AzureFunctionsUpdates.Activities
99
{
10-
public static class PostUpdate
10+
public class PostUpdate
1111
{
12-
private static string consumerApiKey = Environment.GetEnvironmentVariable("Twitter_Consumer_Api_Key");
13-
private static string consumerApiSecret = Environment.GetEnvironmentVariable("Twitter_Consumer_Api_Secret");
14-
private static string accessToken = Environment.GetEnvironmentVariable("Twitter_Access_Token");
15-
private static string accessTokenSecret = Environment.GetEnvironmentVariable("Twitter_Access_Token_Secret");
12+
private readonly string consumerApiKey = Environment.GetEnvironmentVariable("Twitter_Consumer_Api_Key");
13+
private readonly string consumerApiSecret = Environment.GetEnvironmentVariable("Twitter_Consumer_Api_Secret");
14+
private readonly string accessToken = Environment.GetEnvironmentVariable("Twitter_Access_Token");
15+
private readonly string accessTokenSecret = Environment.GetEnvironmentVariable("Twitter_Access_Token_Secret");
1616

1717
[FunctionName(nameof(PostUpdate))]
18-
public static void Run(
18+
public void Run(
1919
[ActivityTrigger] RepositoryRelease newRelease,
2020
ILogger logger)
2121
{

0 commit comments

Comments
 (0)