@@ -4,32 +4,48 @@ namespace AgDatabaseMove.Unit
44 using System . Collections ;
55 using System . Collections . Generic ;
66 using System . Linq ;
7+ using Exceptions ;
78 using Moq ;
89 using SmoFacade ;
910 using Xunit ;
1011
1112
1213 public class BackupOrder
1314 {
14- private readonly List < BackupMetadata > _listBackups ;
1515
16- public BackupOrder ( )
16+ private static IEnumerable < BackupMetadata > CloneBackupMetaDataList ( List < BackupMetadata > list )
1717 {
18- _listBackups = ListBackups ( ) ;
18+ var result = new List < BackupMetadata > ( ) ;
19+ list . ForEach ( b => {
20+ result . Add ( ( BackupMetadata ) b . Clone ( ) ) ;
21+ } ) ;
22+ result . Reverse ( ) ;
23+ return result ;
1924 }
2025
21- private static List < BackupMetadata > ListBackups ( )
26+ private static List < BackupMetadata > GetBackupList ( )
2227 {
2328 return new List < BackupMetadata > {
2429 new BackupMetadata {
2530 BackupType = BackupFileTools . BackupType . Log ,
2631 DatabaseBackupLsn = 126000000943800037 ,
2732 CheckpointLsn = 126000000953600034 ,
28- FirstLsn = 126000000955500001 ,
29- LastLsn = 126000000955800001 ,
33+ FirstLsn = 126000000955200001 ,
34+ LastLsn = 126000000955500001 ,
35+ DatabaseName = "TestDb" ,
36+ ServerName = "ServerA" ,
37+ PhysicalDeviceName = @"\\DFS\BACKUP\ServerA\testDb\Testdb_backup_2018_10_29_020007_343.trn" ,
38+ StartTime = DateTime . Parse ( "2018-10-29 02:00:07.000" )
39+ } ,
40+ new BackupMetadata {
41+ BackupType = BackupFileTools . BackupType . Log ,
42+ DatabaseBackupLsn = 126000000943800037 ,
43+ CheckpointLsn = 126000000953600034 ,
44+ FirstLsn = 126000000955800001 ,
45+ LastLsn = 126000000965800001 ,
3046 DatabaseName = "TestDb" ,
3147 ServerName = "ServerB" ,
32- PhysicalDeviceName = @"\\DFS\BACKUP\ServerB\testDb\Testdb_backup_2018_10_29_030006_660 .trn" ,
48+ PhysicalDeviceName = @"\\DFS\BACKUP\ServerB\testDb\Testdb_backup_2018_10_29_040005_900 .trn" ,
3349 StartTime = DateTime . Parse ( "2018-10-29 03:00:06.000" )
3450 } ,
3551 new BackupMetadata {
@@ -44,51 +60,155 @@ private static List<BackupMetadata> ListBackups()
4460 StartTime = DateTime . Parse ( "2018-10-28 00:02:28.000" )
4561 } ,
4662 new BackupMetadata {
47- BackupType = BackupFileTools . BackupType . Diff ,
63+ BackupType = BackupFileTools . BackupType . Log ,
4864 DatabaseBackupLsn = 126000000943800037 ,
4965 CheckpointLsn = 126000000953600034 ,
50- FirstLsn = 126000000943800037 ,
51- LastLsn = 126000000955200001 ,
66+ FirstLsn = 126000000955500001 ,
67+ LastLsn = 126000000955800001 ,
5268 DatabaseName = "TestDb" ,
53- ServerName = "ServerA " ,
54- PhysicalDeviceName = @"\\DFS\BACKUP\ServerA \testDb\Testdb_backup_2018_10_29_000339_780.diff " ,
55- StartTime = DateTime . Parse ( "2018-10-29 00: 03:39 .000" )
69+ ServerName = "ServerB " ,
70+ PhysicalDeviceName = @"\\DFS\BACKUP\ServerB \testDb\Testdb_backup_2018_10_29_030006_660.trn " ,
71+ StartTime = DateTime . Parse ( "2018-10-29 03:00:06 .000" )
5672 } ,
5773 new BackupMetadata {
58- BackupType = BackupFileTools . BackupType . Log ,
59- DatabaseBackupLsn = 126000000882000037 ,
74+ BackupType = BackupFileTools . BackupType . Diff ,
75+ DatabaseBackupLsn = 126000000943800037 ,
6076 CheckpointLsn = 126000000953600034 ,
61- FirstLsn = 126000000955200001 ,
62- LastLsn = 126000000955500001 ,
77+ FirstLsn = 126000000945600000 ,
78+ LastLsn = 126000000955200001 ,
6379 DatabaseName = "TestDb" ,
6480 ServerName = "ServerA" ,
65- PhysicalDeviceName = @"\\DFS\BACKUP\ServerA\testDb\Testdb_backup_2018_10_29_020007_343.trn " ,
66- StartTime = DateTime . Parse ( "2018-10-29 02: 00:07 .000" )
81+ PhysicalDeviceName = @"\\DFS\BACKUP\ServerA\testDb\Testdb_backup_2018_10_29_000339_780.diff " ,
82+ StartTime = DateTime . Parse ( "2018-10-29 00:03:39 .000" )
6783 }
6884 } ;
6985 }
7086
71- [ Fact ]
72- public void BackupChainOrdered ( )
87+ private static List < BackupMetadata > GetBackupListWithoutLogs ( )
88+ {
89+ var list = GetBackupList ( ) ;
90+ list . RemoveAll ( b => b . BackupType == BackupFileTools . BackupType . Log ) ;
91+ return list ;
92+ }
93+
94+ private static List < BackupMetadata > GetBackupListWithoutDiff ( )
95+ {
96+ var list = GetBackupList ( ) ;
97+ list . RemoveAll ( b => b . BackupType == BackupFileTools . BackupType . Diff ) ;
98+ return list ;
99+ }
100+
101+ private static List < BackupMetadata > GetBackupListWithStripes ( )
102+ {
103+ var list = GetBackupList ( ) ;
104+ var listWithStripes = CloneBackupMetaDataList ( list ) . ToList ( ) ;
105+ listWithStripes . ForEach ( b => {
106+ var path = b . PhysicalDeviceName . Split ( '.' ) ;
107+ b . PhysicalDeviceName = $ "{ path [ 0 ] } _striped.{ path [ 1 ] } ";
108+ } ) ;
109+ list . AddRange ( listWithStripes ) ;
110+ return list ;
111+ }
112+
113+ private static List < BackupMetadata > GetBackupListWithStripesAndDuplicates ( )
114+ {
115+ var listWithStripes = GetBackupListWithStripes ( ) ;
116+ var duplicate = CloneBackupMetaDataList ( listWithStripes ) ;
117+ listWithStripes . AddRange ( duplicate ) ;
118+ return listWithStripes ;
119+ }
120+
121+ private static List < BackupMetadata > GetBackupListWithoutFull ( )
122+ {
123+ var list = GetBackupList ( ) ;
124+ list . RemoveAll ( b => b . BackupType == BackupFileTools . BackupType . Full ) ;
125+ return list ;
126+ }
127+
128+ private static void VerifyListIsAValidBackupChain ( List < BackupMetadata > backupChain )
129+ {
130+ bool foundFull , foundDiff , foundLog ;
131+ foundFull = foundDiff = foundLog = false ;
132+ BackupMetadata full = null ;
133+ BackupMetadata lastBackup = null ;
134+
135+ BackupMetadata currentBackup ;
136+ while ( ( currentBackup = backupChain . FirstOrDefault ( ) ) != null ) {
137+
138+ if ( currentBackup . BackupType == BackupFileTools . BackupType . Full ) {
139+ Assert . True ( ! foundFull && ! foundDiff && ! foundLog ) ;
140+ foundFull = true ;
141+ full = currentBackup ;
142+ }
143+ else if ( currentBackup . BackupType == BackupFileTools . BackupType . Diff ) {
144+ Assert . True ( foundFull && ! foundDiff && ! foundLog ) ;
145+ Assert . Equal ( full . CheckpointLsn , currentBackup . DatabaseBackupLsn ) ;
146+ Assert . True ( currentBackup . FirstLsn >= lastBackup . LastLsn ) ;
147+ foundDiff = true ;
148+ }
149+ else if ( currentBackup . BackupType == BackupFileTools . BackupType . Log ) {
150+ Assert . True ( foundFull ) ;
151+ Assert . True ( currentBackup . FirstLsn >= lastBackup . LastLsn ) ;
152+ foundLog = true ;
153+ }
154+
155+ lastBackup = currentBackup ;
156+ backupChain . RemoveAll ( b => b . LastLsn == currentBackup . LastLsn ) ;
157+ }
158+ }
159+
160+ public static IEnumerable < object [ ] > PositiveTestData => new List < object [ ] > {
161+ new object [ ] { GetBackupList ( ) } ,
162+ new object [ ] { GetBackupListWithStripes ( ) } ,
163+ new object [ ] { GetBackupListWithoutDiff ( ) } ,
164+ new object [ ] { GetBackupListWithoutLogs ( ) }
165+ } ;
166+
167+ [ Theory ]
168+ [ MemberData ( nameof ( PositiveTestData ) ) ]
169+ public void BackupChainIsCorrect ( List < BackupMetadata > backupList )
73170 {
74171 var agDatabase = new Mock < IAgDatabase > ( ) ;
75- agDatabase . Setup ( agd => agd . RecentBackups ( ) ) . Returns ( _listBackups ) ;
172+ agDatabase . Setup ( agd => agd . RecentBackups ( ) ) . Returns ( backupList ) ;
76173 var backupChain = new BackupChain ( agDatabase . Object ) ;
174+ VerifyListIsAValidBackupChain ( backupChain . OrderedBackups . ToList ( ) ) ;
175+ }
176+
77177
78- var expected = _listBackups . OrderBy ( bu => bu . FirstLsn ) ;
178+ public static IEnumerable < object [ ] > NegativeTestData => new List < object [ ] > {
179+ new object [ ] { GetBackupListWithoutFull ( ) } ,
180+ new object [ ] { new List < BackupMetadata > ( ) }
181+ } ;
79182
80- Assert . Equal < IEnumerable > ( backupChain . OrderedBackups , expected ) ;
183+ [ Theory ]
184+ [ MemberData ( nameof ( NegativeTestData ) ) ]
185+ public void CanDetectBackupChainIsWrong ( List < BackupMetadata > backupList )
186+ {
187+ var agDatabase = new Mock < IAgDatabase > ( ) ;
188+ agDatabase . Setup ( agd => agd . RecentBackups ( ) ) . Returns ( backupList ) ;
189+ Assert . Throws < BackupChainException > ( ( ) => new BackupChain ( agDatabase . Object ) ) ;
81190 }
82191
83192 [ Fact ]
84193 public void MissingLink ( )
85194 {
86- var backups = ListBackups ( ) . Where ( b => b . FirstLsn != 126000000955200001 ) . ToList ( ) ;
195+ var backups = GetBackupList ( ) . Where ( b => b . FirstLsn != 126000000955200001 ) . ToList ( ) ;
87196 var agDatabase = new Mock < IAgDatabase > ( ) ;
88197 agDatabase . Setup ( agd => agd . RecentBackups ( ) ) . Returns ( backups ) ;
89198
90- var chain = new BackupChain ( agDatabase . Object ) . OrderedBackups ;
91- Assert . NotEqual ( chain . Last ( ) . LastLsn , ListBackups ( ) . Max ( b => b . LastLsn ) ) ;
199+ var chain = new BackupChain ( agDatabase . Object ) . OrderedBackups . ToList ( ) ;
200+ Assert . NotEqual ( chain . Last ( ) . LastLsn , GetBackupList ( ) . Max ( b => b . LastLsn ) ) ;
201+ VerifyListIsAValidBackupChain ( chain ) ;
202+ }
203+
204+ [ Fact ]
205+ public void DuplicateFiles ( )
206+ {
207+ var backups = GetBackupListWithStripesAndDuplicates ( ) ;
208+ var agDatabase = new Mock < IAgDatabase > ( ) ;
209+ agDatabase . Setup ( agd => agd . RecentBackups ( ) ) . Returns ( backups ) ;
210+ var chain = new BackupChain ( agDatabase . Object ) . OrderedBackups . ToList ( ) ;
211+ Assert . Equal ( backups . GroupBy ( b => b . PhysicalDeviceName ) . Count ( ) , chain . Count ) ;
92212 }
93213
94214 // TODO: test skipping of logs if diff last LSN and log last LSN matches
0 commit comments