1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Threading ;
4
+ using System . Threading . Tasks ;
5
+ using FluentAssertions ;
6
+ using LinqToDB ;
7
+ using LinqToDB . Data ;
8
+ using Microsoft . Extensions . DependencyInjection ;
9
+ using Synnotech . Migrations . Core ;
10
+ using Synnotech . Migrations . Core . Int64TimestampVersions ;
11
+ using Synnotech . Migrations . Linq2Db . Int64TimestampVersions ;
12
+ using Synnotech . MsSqlServer ;
13
+ using Xunit ;
14
+ using Xunit . Abstractions ;
15
+
16
+ namespace Synnotech . Migrations . Linq2Db . Tests . Int64TimestampVersions
17
+ {
18
+ public sealed class MigrationEngineTests
19
+ {
20
+ public MigrationEngineTests ( ITestOutputHelper output ) => Output = output ;
21
+ private ITestOutputHelper Output { get ; }
22
+
23
+ [ SkippableFact ]
24
+ public async Task ApplyAllMigrations ( )
25
+ {
26
+ await using var container = await InitializeOrSkipTestAsync ( ) ;
27
+ var now = DateTime . UtcNow ;
28
+ var migrationEngine = container . GetRequiredService < MigrationEngine > ( ) ;
29
+
30
+ var summary = await migrationEngine . MigrateAsync ( now ) ;
31
+ Output . LogSummary ( summary ) ;
32
+
33
+ await using var dataConnection = container . GetRequiredService < DataConnection > ( ) ;
34
+ var migrationInfos = await dataConnection . GetTable < MigrationInfo > ( )
35
+ . ToListAsync ( ) ;
36
+ var expectedMigrationInfos = new MigrationInfo [ ]
37
+ {
38
+ new ( ) { Id = 1 , Name = nameof ( InitialTableStructure ) , Version = 20211008111155 , AppliedAt = now } ,
39
+ new ( ) { Id = 2 , Name = nameof ( SomeContacts ) , Version = 20211008111259 , AppliedAt = now }
40
+ } ;
41
+ migrationInfos . Should ( ) . BeEquivalentTo ( expectedMigrationInfos , options => options . WithStrictOrdering ( ) ) ;
42
+ }
43
+
44
+ [ SkippableFact ]
45
+ public async Task ApplyNewestMigration ( )
46
+ {
47
+ await using var container = await InitializeOrSkipTestAsync ( ) ;
48
+ await using ( var dataConnection = container . GetRequiredService < DataConnection > ( ) )
49
+ {
50
+ await dataConnection . CreateTableAsync < MigrationInfo > ( ) ;
51
+ await dataConnection . CreateTableAsync < Contact > ( ) ;
52
+ await dataConnection . InsertAsync ( new MigrationInfo { Name = nameof ( InitialTableStructure ) , Version = 20211008111155 , AppliedAt = DateTime . UtcNow } ) ;
53
+ }
54
+
55
+ var now = DateTime . UtcNow ;
56
+ var migrationEngine = container . GetRequiredService < MigrationEngine > ( ) ;
57
+ var summary = await migrationEngine . MigrateAsync ( now ) ;
58
+ Output . LogSummary ( summary ) ;
59
+
60
+ var expectedMigrations = new List < MigrationInfo > ( 1 )
61
+ {
62
+ new ( ) { Id = 2 , Name = nameof ( SomeContacts ) , Version = 20211008111259 , AppliedAt = now }
63
+ } ;
64
+ summary . TryGetAppliedMigrations ( out var appliedMigrations ) . Should ( ) . BeTrue ( ) ;
65
+ appliedMigrations . Should ( ) . BeEquivalentTo ( expectedMigrations ) ;
66
+ summary . EnsureSuccess ( ) ;
67
+ }
68
+
69
+ [ SkippableFact ]
70
+ public async Task NoMigrationsAvailable ( )
71
+ {
72
+ await using var container = await InitializeOrSkipTestAsync ( ) ;
73
+ var now = DateTime . UtcNow ;
74
+ await using ( var dataConnection = container . GetRequiredService < DataConnection > ( ) )
75
+ {
76
+ await dataConnection . CreateTableAsync < MigrationInfo > ( ) ;
77
+ await dataConnection . InsertAsync ( new MigrationInfo { Name = nameof ( InitialTableStructure ) , Version = 20211008111155 , AppliedAt = now } ) ;
78
+ await dataConnection . InsertAsync ( new MigrationInfo { Name = nameof ( SomeContacts ) , Version = 20211008111259 , AppliedAt = now } ) ;
79
+ }
80
+
81
+ var migrationEngine = container . GetRequiredService < MigrationEngine > ( ) ;
82
+ var summary = await migrationEngine . MigrateAsync ( ) ;
83
+ Output . LogSummary ( summary ) ;
84
+
85
+ summary . TryGetAppliedMigrations ( out var appliedMigrations ) . Should ( ) . BeFalse ( ) ;
86
+ appliedMigrations . Should ( ) . BeNull ( ) ;
87
+ summary . EnsureSuccess ( ) ;
88
+ }
89
+
90
+ [ Fact ]
91
+ public async Task RunPreviousMigration ( )
92
+ {
93
+ await using var container = await InitializeOrSkipTestAsync ( ) ;
94
+ var now = DateTime . UtcNow ;
95
+ await using ( var dataConnection = container . GetRequiredService < DataConnection > ( ) )
96
+ {
97
+ await dataConnection . CreateTableAsync < MigrationInfo > ( ) ;
98
+ await dataConnection . InsertAsync ( new MigrationInfo { Name = nameof ( SomeContacts ) , Version = 20211008111259 , AppliedAt = now . AddDays ( - 2 ) } ) ;
99
+ }
100
+
101
+ var migrationEngine = container . GetRequiredService < MigrationEngine > ( ) ;
102
+ var summary = await migrationEngine . MigrateAsync ( now , approach : MigrationApproach . AllNonAppliedMigrations ) ;
103
+ Output . LogSummary ( summary ) ;
104
+
105
+ summary . TryGetAppliedMigrations ( out var appliedMigrations ) . Should ( ) . BeTrue ( ) ;
106
+ var expectedMigrationInfos = new List < MigrationInfo >
107
+ {
108
+ new ( ) { Id = 1 , Name = nameof ( InitialTableStructure ) , Version = 20211008111155 , AppliedAt = now }
109
+ } ;
110
+ appliedMigrations . Should ( ) . BeEquivalentTo ( expectedMigrationInfos ) ;
111
+ }
112
+
113
+ private static async Task < ServiceProvider > InitializeOrSkipTestAsync ( )
114
+ {
115
+ var ( connectionString , sqlServerVersion ) = TestSettings . GetConnectionSettingsOrSkip ( ) ;
116
+ await Database . DropAndCreateDatabaseAsync ( connectionString ) ;
117
+ return new ServiceCollection ( ) . AddDatabaseContext ( connectionString , sqlServerVersion )
118
+ . AddSynnotechMigrations ( )
119
+ . BuildServiceProvider ( ) ;
120
+ }
121
+
122
+ [ MigrationVersion ( "2021-10-08T11:11:55Z" ) ]
123
+ public sealed class InitialTableStructure : EmbeddedScriptMigration
124
+ {
125
+ public InitialTableStructure ( ) : base ( "InitialScript.sql" ) { }
126
+ }
127
+
128
+ [ MigrationVersion ( "2021-10-08T11:12:59Z" ) ]
129
+ public sealed class SomeContacts : Migration
130
+ {
131
+ public override async Task ApplyAsync ( DataConnection dataConnection , CancellationToken cancellationToken = default )
132
+ {
133
+ await dataConnection . InsertAsync ( new Contact { Name = "John Doe" } , token : cancellationToken ) ;
134
+ await dataConnection . InsertAsync ( new Contact { Name = "Jane Foe" } , token : cancellationToken ) ;
135
+ }
136
+ }
137
+ }
138
+ }
0 commit comments