Skip to content

Commit 8736d28

Browse files
Add validation states to migration state logic (#428)
* First pass at checking for FAILED_VALIDATION * Oh hey there's an autoformatter! * Update src/Octoshift/RepositoryMigrationStatus.cs Co-authored-by: Dylan Smith <[email protected]> * Update ado2gh and add tests * Add unit tests for RepositoryMigrationStatus * Autoformat whitespace * Standardize on RepositoryMigrationStatus methods * Use state parsing methods in ado2gh MigrateRepoCommand Co-authored-by: Dylan Smith <[email protected]>
1 parent 1fad5ba commit 8736d28

File tree

8 files changed

+176
-24
lines changed

8 files changed

+176
-24
lines changed

src/Octoshift/RepositoryMigrationStatus.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,11 @@ public static class RepositoryMigrationStatus
66
public const string InProgress = "IN_PROGRESS";
77
public const string Failed = "FAILED";
88
public const string Succeeded = "SUCCEEDED";
9+
public const string PendingValidation = "PENDING_VALIDATION";
10+
public const string FailedValidation = "FAILED_VALIDATION";
11+
12+
public static bool IsSucceeded(string migrationState) => migrationState?.Trim().ToUpper() is Succeeded;
13+
public static bool IsPending(string migrationState) => migrationState?.Trim().ToUpper() is Queued or InProgress or PendingValidation;
14+
public static bool IsFailed(string migrationState) => !(IsPending(migrationState) || IsSucceeded(migrationState));
915
}
1016
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using FluentAssertions;
2+
using Xunit;
3+
4+
namespace OctoshiftCLI.Tests
5+
{
6+
public class RepositoryMigrationStatusTests
7+
{
8+
[Fact]
9+
public void IsSucceeded_Returns_True_For_Succeeded_Status()
10+
{
11+
RepositoryMigrationStatus.IsSucceeded(RepositoryMigrationStatus.Succeeded).Should().BeTrue();
12+
}
13+
14+
[Fact]
15+
public void IsSucceeded_Returns_False_Otherwise()
16+
{
17+
RepositoryMigrationStatus.IsSucceeded(RepositoryMigrationStatus.InProgress).Should().BeFalse();
18+
RepositoryMigrationStatus.IsSucceeded(RepositoryMigrationStatus.Failed).Should().BeFalse();
19+
RepositoryMigrationStatus.IsSucceeded("NOT_A_REAL_STATUS").Should().BeFalse();
20+
}
21+
22+
[Fact]
23+
public void IsPending_Returns_True_For_Pending_Statuses()
24+
{
25+
RepositoryMigrationStatus.IsPending(RepositoryMigrationStatus.Queued).Should().BeTrue();
26+
RepositoryMigrationStatus.IsPending(RepositoryMigrationStatus.PendingValidation).Should().BeTrue();
27+
RepositoryMigrationStatus.IsPending(RepositoryMigrationStatus.InProgress).Should().BeTrue();
28+
}
29+
30+
[Fact]
31+
public void IsPending_Returns_False_Otherwise()
32+
{
33+
RepositoryMigrationStatus.IsPending(RepositoryMigrationStatus.Succeeded).Should().BeFalse();
34+
RepositoryMigrationStatus.IsPending(RepositoryMigrationStatus.Failed).Should().BeFalse();
35+
RepositoryMigrationStatus.IsPending("NOT_A_REAL_STATUS").Should().BeFalse();
36+
}
37+
38+
[Fact]
39+
public void IsFailed_Returns_True_For_Failed_Or_Invalid_Statuses()
40+
{
41+
RepositoryMigrationStatus.IsFailed(RepositoryMigrationStatus.Failed).Should().BeTrue();
42+
RepositoryMigrationStatus.IsFailed(RepositoryMigrationStatus.FailedValidation).Should().BeTrue();
43+
RepositoryMigrationStatus.IsFailed("NOT_A_REAL_STATUS").Should().BeTrue();
44+
}
45+
46+
[Fact]
47+
public void IsFailed_Returns_False_For_Pending_Or_Succeeded_Statuses()
48+
{
49+
RepositoryMigrationStatus.IsFailed(RepositoryMigrationStatus.Queued).Should().BeFalse();
50+
RepositoryMigrationStatus.IsFailed(RepositoryMigrationStatus.InProgress).Should().BeFalse();
51+
RepositoryMigrationStatus.IsFailed(RepositoryMigrationStatus.Succeeded).Should().BeFalse();
52+
}
53+
}
54+
}

src/OctoshiftCLI.Tests/ado2gh/Commands/WaitForMigrationCommandTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,53 @@ await FluentActions
126126
_mockGithubApi.VerifyNoOtherCalls();
127127
}
128128

129+
[Fact]
130+
public async Task With_Migration_ID_That_Fails_Validation()
131+
{
132+
// Arrange
133+
const string failureReason = "FAILURE_REASON";
134+
135+
_mockGithubApi.SetupSequence(x => x.GetMigration(MIGRATION_ID).Result)
136+
.Returns((State: RepositoryMigrationStatus.PendingValidation, RepositoryName: TARGET_REPO, FailureReason: null))
137+
.Returns((State: RepositoryMigrationStatus.PendingValidation, RepositoryName: TARGET_REPO, FailureReason: null))
138+
.Returns((State: RepositoryMigrationStatus.FailedValidation, RepositoryName: TARGET_REPO, FailureReason: failureReason));
139+
140+
_mockGithubApiFactory.Setup(m => m.Create(It.IsAny<string>(), It.IsAny<string>())).Returns(_mockGithubApi.Object);
141+
142+
var actualLogOutput = new List<string>();
143+
_mockOctoLogger.Setup(m => m.LogInformation(It.IsAny<string>())).Callback<string>(s => actualLogOutput.Add(s));
144+
_mockOctoLogger.Setup(m => m.LogError(It.IsAny<string>())).Callback<string>(s => actualLogOutput.Add(s));
145+
146+
var expectedLogOutput = new List<string>
147+
{
148+
$"Waiting for {TARGET_REPO} migration (ID: {MIGRATION_ID}) to finish...",
149+
$"Migration {MIGRATION_ID} for {TARGET_REPO} is {RepositoryMigrationStatus.PendingValidation}",
150+
$"Waiting {WAIT_INTERVAL} seconds...",
151+
$"Migration {MIGRATION_ID} for {TARGET_REPO} is {RepositoryMigrationStatus.PendingValidation}",
152+
$"Waiting {WAIT_INTERVAL} seconds...",
153+
$"Migration {MIGRATION_ID} failed for {TARGET_REPO}"
154+
};
155+
156+
// Act
157+
await FluentActions
158+
.Invoking(async () => await _command.Invoke(MIGRATION_ID))
159+
.Should()
160+
.ThrowAsync<OctoshiftCliException>()
161+
.WithMessage(failureReason);
162+
163+
// Assert
164+
165+
_mockOctoLogger.Verify(m => m.LogInformation(It.IsAny<string>()), Times.Exactly(5));
166+
_mockOctoLogger.Verify(m => m.LogError(It.IsAny<string>()), Times.Once);
167+
168+
_mockGithubApi.Verify(m => m.GetMigration(MIGRATION_ID), Times.Exactly(3));
169+
170+
actualLogOutput.Should().Equal(expectedLogOutput);
171+
172+
_mockOctoLogger.VerifyNoOtherCalls();
173+
_mockGithubApi.VerifyNoOtherCalls();
174+
}
175+
129176
[Fact]
130177
public async Task It_Uses_The_Github_Pat_When_Provided()
131178
{

src/OctoshiftCLI.Tests/gei/Commands/WaitForMigrationCommandTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,53 @@ await FluentActions
126126
_mockGithubApi.VerifyNoOtherCalls();
127127
}
128128

129+
[Fact]
130+
public async Task With_Migration_ID_That_Fails_Validation()
131+
{
132+
// Arrange
133+
const string failureReason = "FAILURE_REASON";
134+
135+
_mockGithubApi.SetupSequence(x => x.GetMigration(MIGRATION_ID).Result)
136+
.Returns((State: RepositoryMigrationStatus.PendingValidation, RepositoryName: TARGET_REPO, FailureReason: null))
137+
.Returns((State: RepositoryMigrationStatus.PendingValidation, RepositoryName: TARGET_REPO, FailureReason: null))
138+
.Returns((State: RepositoryMigrationStatus.FailedValidation, RepositoryName: TARGET_REPO, FailureReason: failureReason));
139+
140+
_mockTargetGithubApiFactory.Setup(m => m.Create(It.IsAny<string>(), It.IsAny<string>())).Returns(_mockGithubApi.Object);
141+
142+
var actualLogOutput = new List<string>();
143+
_mockOctoLogger.Setup(m => m.LogInformation(It.IsAny<string>())).Callback<string>(s => actualLogOutput.Add(s));
144+
_mockOctoLogger.Setup(m => m.LogError(It.IsAny<string>())).Callback<string>(s => actualLogOutput.Add(s));
145+
146+
var expectedLogOutput = new List<string>
147+
{
148+
$"Waiting for {TARGET_REPO} migration (ID: {MIGRATION_ID}) to finish...",
149+
$"Migration {MIGRATION_ID} for {TARGET_REPO} is {RepositoryMigrationStatus.PendingValidation}",
150+
$"Waiting {WAIT_INTERVAL} seconds...",
151+
$"Migration {MIGRATION_ID} for {TARGET_REPO} is {RepositoryMigrationStatus.PendingValidation}",
152+
$"Waiting {WAIT_INTERVAL} seconds...",
153+
$"Migration {MIGRATION_ID} failed for {TARGET_REPO}"
154+
};
155+
156+
// Act
157+
await FluentActions
158+
.Invoking(async () => await _command.Invoke(MIGRATION_ID))
159+
.Should()
160+
.ThrowAsync<OctoshiftCliException>()
161+
.WithMessage(failureReason);
162+
163+
// Assert
164+
165+
_mockOctoLogger.Verify(m => m.LogInformation(It.IsAny<string>()), Times.Exactly(5));
166+
_mockOctoLogger.Verify(m => m.LogError(It.IsAny<string>()), Times.Once);
167+
168+
_mockGithubApi.Verify(m => m.GetMigration(MIGRATION_ID), Times.Exactly(3));
169+
170+
actualLogOutput.Should().Equal(expectedLogOutput);
171+
172+
_mockOctoLogger.VerifyNoOtherCalls();
173+
_mockGithubApi.VerifyNoOtherCalls();
174+
}
175+
129176
[Fact]
130177
public async Task It_Uses_Github_Target_Pat_When_Provided()
131178
{

src/ado2gh/Commands/MigrateRepoCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,14 @@ public async Task Invoke(string adoOrg, string adoTeamProject, string adoRepo, s
132132

133133
var (migrationState, _, failureReason) = await githubApi.GetMigration(migrationId);
134134

135-
while (migrationState.Trim().ToUpper() is "IN_PROGRESS" or "QUEUED")
135+
while (RepositoryMigrationStatus.IsPending(migrationState))
136136
{
137137
_log.LogInformation($"Migration in progress (ID: {migrationId}). State: {migrationState}. Waiting 10 seconds...");
138138
await Task.Delay(10000);
139139
(migrationState, _, failureReason) = await githubApi.GetMigration(migrationId);
140140
}
141141

142-
if (migrationState.Trim().ToUpper() == "FAILED")
142+
if (RepositoryMigrationStatus.IsFailed(migrationState))
143143
{
144144
_log.LogError($"Migration Failed. Migration ID: {migrationId}");
145145
throw new OctoshiftCliException(failureReason);

src/ado2gh/Commands/WaitForMigrationCommand.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,18 @@ public async Task Invoke(string migrationId, string githubPat = null, bool verbo
5757

5858
while (true)
5959
{
60-
switch (state)
60+
if (RepositoryMigrationStatus.IsSucceeded(state))
6161
{
62-
case RepositoryMigrationStatus.Failed:
63-
_log.LogError($"Migration {migrationId} failed for {repositoryName}");
64-
throw new OctoshiftCliException(failureReason);
65-
case RepositoryMigrationStatus.Succeeded:
66-
_log.LogSuccess($"Migration {migrationId} succeeded for {repositoryName}");
67-
return;
68-
default: // IN_PROGRESS, QUEUED
69-
_log.LogInformation($"Migration {migrationId} for {repositoryName} is {state}");
70-
break;
62+
_log.LogSuccess($"Migration {migrationId} succeeded for {repositoryName}");
63+
return;
64+
}
65+
else if (RepositoryMigrationStatus.IsFailed(state))
66+
{
67+
_log.LogError($"Migration {migrationId} failed for {repositoryName}");
68+
throw new OctoshiftCliException(failureReason);
7169
}
7270

71+
_log.LogInformation($"Migration {migrationId} for {repositoryName} is {state}");
7372
_log.LogInformation($"Waiting {WaitIntervalInSeconds} seconds...");
7473
await Task.Delay(WaitIntervalInSeconds * 1000);
7574

src/gei/Commands/MigrateRepoCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,14 +221,14 @@ public async Task Invoke(MigrateRepoCommandArgs args)
221221

222222
var (migrationState, _, failureReason) = await githubApi.GetMigration(migrationId);
223223

224-
while (migrationState.Trim().ToUpper() is "IN_PROGRESS" or "QUEUED")
224+
while (RepositoryMigrationStatus.IsPending(migrationState))
225225
{
226226
_log.LogInformation($"Migration in progress (ID: {migrationId}). State: {migrationState}. Waiting 10 seconds...");
227227
await Task.Delay(10000);
228228
(migrationState, _, failureReason) = await githubApi.GetMigration(migrationId);
229229
}
230230

231-
if (migrationState.Trim().ToUpper() == "FAILED")
231+
if (RepositoryMigrationStatus.IsFailed(migrationState))
232232
{
233233
_log.LogError($"Migration Failed. Migration ID: {migrationId}");
234234
throw new OctoshiftCliException(failureReason);

src/gei/Commands/WaitForMigrationCommand.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,18 @@ public async Task Invoke(string migrationId, string githubTargetPat = null, bool
5757

5858
while (true)
5959
{
60-
switch (state)
60+
if (RepositoryMigrationStatus.IsSucceeded(state))
6161
{
62-
case RepositoryMigrationStatus.Failed:
63-
_log.LogError($"Migration {migrationId} failed for {repositoryName}");
64-
throw new OctoshiftCliException(failureReason);
65-
case RepositoryMigrationStatus.Succeeded:
66-
_log.LogSuccess($"Migration {migrationId} succeeded for {repositoryName}");
67-
return;
68-
default: // IN_PROGRESS, QUEUED
69-
_log.LogInformation($"Migration {migrationId} for {repositoryName} is {state}");
70-
break;
62+
_log.LogSuccess($"Migration {migrationId} succeeded for {repositoryName}");
63+
return;
64+
}
65+
else if (RepositoryMigrationStatus.IsFailed(state))
66+
{
67+
_log.LogError($"Migration {migrationId} failed for {repositoryName}");
68+
throw new OctoshiftCliException(failureReason);
7169
}
7270

71+
_log.LogInformation($"Migration {migrationId} for {repositoryName} is {state}");
7372
_log.LogInformation($"Waiting {WaitIntervalInSeconds} seconds...");
7473
await Task.Delay(WaitIntervalInSeconds * 1000);
7574

0 commit comments

Comments
 (0)