Skip to content

Commit 79d96b6

Browse files
committed
Add pending migration test (#7143)
Address #4934
1 parent 7c1f349 commit 79d96b6

4 files changed

Lines changed: 148 additions & 1 deletion

File tree

src/DatabaseMigrationTools/MigrationTargetDatabaseArgumentNames.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace NuGetGallery.DatabaseMigrationTools
55
{
6-
internal static class MigrationTargetDatabaseArgumentNames
6+
public static class MigrationTargetDatabaseArgumentNames
77
{
88
public const string GalleryDatabase = "GalleryDatabase";
99
public const string SupportRequestDatabase = "SupportRequestDatabase";

tests/NuGet.Services.DatabaseMigration.Facts/NuGet.Services.DatabaseMigration.Facts.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,13 @@
4444
</ItemGroup>
4545
<ItemGroup>
4646
<Compile Include="DatabaseMigrationFacts.cs" />
47+
<Compile Include="PendingMigrationsFacts.cs" />
4748
<Compile Include="Properties\AssemblyInfo.cs" />
4849
</ItemGroup>
4950
<ItemGroup>
51+
<PackageReference Include="Moq">
52+
<Version>4.8.2</Version>
53+
</PackageReference>
5054
<PackageReference Include="xunit">
5155
<Version>2.4.1</Version>
5256
</PackageReference>
@@ -63,6 +67,10 @@
6367
</PackageReference>
6468
</ItemGroup>
6569
<ItemGroup>
70+
<ProjectReference Include="..\..\src\DatabaseMigrationTools\DatabaseMigrationTools.csproj">
71+
<Project>{dced7162-a24c-4579-96c8-544bfaf4c305}</Project>
72+
<Name>DatabaseMigrationTools</Name>
73+
</ProjectReference>
6674
<ProjectReference Include="..\..\src\NuGet.Services.DatabaseMigration\NuGet.Services.DatabaseMigration.csproj">
6775
<Project>{f4c8c34f-72a9-4773-a315-8fa3f2d5ce4e}</Project>
6876
<Name>NuGet.Services.DatabaseMigration</Name>
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Data.Entity.Migrations.Design;
7+
using System.Data.SqlClient;
8+
using System.Linq;
9+
using System.Threading.Tasks;
10+
using Moq;
11+
using NuGet.Jobs;
12+
using NuGet.Jobs.Configuration;
13+
using NuGetGallery.DatabaseMigrationTools;
14+
using Xunit;
15+
using Xunit.Abstractions;
16+
17+
namespace NuGet.Services.DatabaseMigration.Facts
18+
{
19+
public class PendingMigrationsFacts : IAsyncLifetime
20+
{
21+
private string _dbName;
22+
private readonly ITestOutputHelper _output;
23+
24+
public PendingMigrationsFacts(ITestOutputHelper output)
25+
{
26+
_output = output;
27+
}
28+
29+
public Task InitializeAsync()
30+
{
31+
return Task.CompletedTask;
32+
}
33+
34+
public async Task DisposeAsync()
35+
{
36+
if (_dbName == null)
37+
{
38+
return;
39+
}
40+
41+
const string connectionString = @"Data Source=(localdb)\mssqllocaldb; Initial Catalog=master; Integrated Security=True; MultipleActiveResultSets=True";
42+
using (var sqlConnection = new SqlConnection(connectionString))
43+
{
44+
await sqlConnection.OpenAsync();
45+
using (var sqlCommand = sqlConnection.CreateCommand())
46+
{
47+
sqlCommand.CommandText = $"ALTER DATABASE {_dbName} SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE {_dbName};";
48+
await sqlCommand.ExecuteNonQueryAsync();
49+
}
50+
}
51+
}
52+
53+
public static IEnumerable<object[]> TestData
54+
{
55+
get
56+
{
57+
var factory = new MigrationContextFactory();
58+
59+
var currentTimestamp = DateTimeOffset.UtcNow.ToString("yyyyMMddHHmmssFFFFFFF");
60+
61+
var galleryDbName = $"PendingMigrationsTest{currentTimestamp}Gallery";
62+
var gallerySqlConnectionFactory = new Mock<ISqlConnectionFactory<GalleryDbConfiguration>>();
63+
gallerySqlConnectionFactory
64+
.Setup(x => x.CreateAsync())
65+
.ReturnsAsync(() => new SqlConnection(
66+
$@"Data Source=(localdb)\mssqllocaldb; Initial Catalog={galleryDbName}; Integrated Security=True; MultipleActiveResultSets=True"));
67+
68+
var supportRequestsDbName = $"PendingMigrationsTest{currentTimestamp}SupportRequests";
69+
var supportRequestsSqlConnectionFactory = new Mock<ISqlConnectionFactory<SupportRequestDbConfiguration>>();
70+
supportRequestsSqlConnectionFactory
71+
.Setup(x => x.CreateAsync())
72+
.ReturnsAsync(() => new SqlConnection(
73+
$@"Data Source=(localdb)\mssqllocaldb; Initial Catalog={supportRequestsDbName}; Integrated Security=True; MultipleActiveResultSets=True"));
74+
75+
var serviceProvider = new Mock<IServiceProvider>();
76+
77+
serviceProvider
78+
.Setup(x => x.GetService(typeof(ISqlConnectionFactory<GalleryDbConfiguration>)))
79+
.Returns(() => gallerySqlConnectionFactory.Object);
80+
81+
serviceProvider
82+
.Setup(x => x.GetService(typeof(ISqlConnectionFactory<SupportRequestDbConfiguration>)))
83+
.Returns(() => supportRequestsSqlConnectionFactory.Object);
84+
85+
yield return new object[] { galleryDbName, MigrationTargetDatabaseArgumentNames.GalleryDatabase, factory, serviceProvider.Object };
86+
yield return new object[] { supportRequestsDbName, MigrationTargetDatabaseArgumentNames.SupportRequestDatabase, factory, serviceProvider.Object };
87+
88+
// Validation DB is not tested here because:
89+
// 1) The migrations do no live in this repository so it's a bit late if we find out there are pending changes at this point.
90+
// 2) There is a type load exception since a type of name EntitiesConfiguration is already loaded by Gallery DB or Suppport Requests DB.
91+
}
92+
}
93+
94+
[Theory]
95+
[MemberData(nameof(TestData))]
96+
public async Task NoPendingMigrations(string dbName, string argumentName, MigrationContextFactory factory, IServiceProvider serviceProvider)
97+
{
98+
_dbName = dbName;
99+
100+
var migrationContext = await factory.CreateMigrationContextAsync(argumentName, serviceProvider);
101+
102+
var dbMigrator = migrationContext.GetDbMigrator();
103+
var migrations = dbMigrator.GetLocalMigrations();
104+
dbMigrator.Update(migrations.Last());
105+
106+
var migrationScaffolder = new MigrationScaffolder(dbMigrator.Configuration);
107+
108+
var migrationName = $"TestMigration{DateTimeOffset.UtcNow:yyyyMMddHHmmssFFFFFFF}";
109+
var result = migrationScaffolder.Scaffold(migrationName);
110+
111+
_output.WriteLine("Migration content:");
112+
_output.WriteLine(new string('-', 60));
113+
_output.WriteLine(result.UserCode);
114+
_output.WriteLine(new string('-', 60));
115+
116+
Assert.Equal(
117+
$@"namespace {dbMigrator.Configuration.MigrationsNamespace}
118+
{{
119+
using System;
120+
using System.Data.Entity.Migrations;
121+
122+
public partial class {migrationName} : DbMigration
123+
{{
124+
public override void Up()
125+
{{
126+
}}
127+
128+
public override void Down()
129+
{{
130+
}}
131+
}}
132+
}}
133+
", result.UserCode);
134+
}
135+
}
136+
}

tests/NuGet.Services.DatabaseMigration.Facts/Properties/AssemblyInfo.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System.Reflection;
5+
using Xunit;
56

67
[assembly: AssemblyTitle("NuGet.Services.DatabaseMigration.Facts")]
8+
9+
[assembly: CollectionBehavior(DisableTestParallelization = true)]

0 commit comments

Comments
 (0)