1
+ #nullable enable
2
+ using System ;
3
+ using System . Collections ;
4
+ using System . Collections . Generic ;
5
+ using System . Linq ;
6
+ using System . Reflection ;
7
+ using NUnit . Framework ;
8
+ using Xtensive . Modelling . Actions ;
9
+ using Xtensive . Modelling . Actions . Extensions ;
10
+ using Xtensive . Modelling . Comparison ;
11
+ using Xtensive . Modelling . Comparison . Hints ;
12
+ using Xtensive . Orm . Upgrade . Model ;
13
+ using Xtensive . Sql ;
14
+ using Comparer = Xtensive . Modelling . Comparison . Comparer ;
15
+
16
+ namespace Xtensive . Extensions
17
+ {
18
+ [ TestFixture ]
19
+ public class ActionSequenceExtensionsTests
20
+ {
21
+ [ Test ]
22
+ [ TestCaseSource ( nameof ( AddSecondaryIndexTestCases ) ) ]
23
+ public void ContainsActionsOfType_WhenCreatedIndex_ReturnsExpectedResult ( IEnumerable < Type > types , bool expectedResult )
24
+ {
25
+ // Arrange
26
+ var originalStorage = CreateStorage ( ) ;
27
+ var originalTable = CreateTable ( originalStorage ) ;
28
+ var originalColumn = AddColumn < string > ( originalTable ) ;
29
+
30
+ var actionSequence = GetActionSequence ( originalStorage , ( storage , _ ) => {
31
+ var table = storage . Tables [ originalTable . Name ] ;
32
+ var column = table . Columns [ originalColumn . Name ] ;
33
+ AddSecondaryIndex ( column ) ;
34
+ } ) ;
35
+
36
+ // Act
37
+ var result =
38
+ actionSequence . ContainsActionsOfType < SecondaryIndexInfo > ( types ) ;
39
+
40
+ // Assert
41
+ Assert . AreEqual ( result , expectedResult ) ;
42
+ }
43
+
44
+ [ Test ]
45
+ [ TestCaseSource ( nameof ( RemoveSecondaryIndexTestCases ) ) ]
46
+ public void ContainsActionsOfType_WhenRemovedIndex_ReturnsExpectedResult ( IEnumerable < Type > types , bool expectedResult )
47
+ {
48
+ // Arrange
49
+ var originalStorage = CreateStorage ( ) ;
50
+ var originalTable = CreateTable ( originalStorage ) ;
51
+ var originalColumn = AddColumn < string > ( originalTable ) ;
52
+ var originalIndex = AddSecondaryIndex ( originalColumn ) ;
53
+
54
+ var actionSequence = GetActionSequence ( originalStorage , ( storage , _ ) => {
55
+ var table = storage . Tables [ originalTable . Name ] ;
56
+ var index = table . SecondaryIndexes [ originalIndex . Name ] ;
57
+ table . SecondaryIndexes . Remove ( index ) ;
58
+ } ) ;
59
+
60
+ // Act
61
+ var result =
62
+ actionSequence . ContainsActionsOfType < SecondaryIndexInfo > ( types ) ;
63
+
64
+ // Assert
65
+ Assert . AreEqual ( result , expectedResult ) ;
66
+ }
67
+
68
+ [ Test ]
69
+ [ TestCaseSource ( nameof ( AllActionsTestCaseData ) ) ]
70
+ public void ContainsActionsOfType_WhenChangedOtherNodeThenExpected_ReturnsFalse ( Type actionType )
71
+ {
72
+ // Arrange
73
+ var originalStorage = CreateStorage ( ) ;
74
+ var originalTable = CreateTable ( originalStorage ) ;
75
+
76
+ var actionSequence = GetActionSequence ( originalStorage , ( storage , _ ) => {
77
+ var table = storage . Tables [ originalTable . Name ] ;
78
+ AddColumn < long > ( table ) ;
79
+ } ) ;
80
+
81
+ // Act
82
+ var result =
83
+ actionSequence . ContainsActionsOfType < SecondaryIndexInfo > ( new [ ] { actionType } ) ;
84
+
85
+ // Assert
86
+ Assert . IsFalse ( result ) ;
87
+ }
88
+
89
+ [ Test ]
90
+ [ TestCaseSource ( nameof ( DisallowedStorageChangesTestCaseData ) ) ]
91
+ public void ContainsActionsOfType_WhenDisallowedStorageChangesLogged_ReturnsFalse ( Action < StorageModel > action )
92
+ {
93
+ // Arrange
94
+ var originalStorage = CreateStorage ( ) ;
95
+ CreateTable ( originalStorage ) ;
96
+
97
+ var actionSequence = GetActionSequence ( originalStorage , ( storage , _ ) => {
98
+ action ( storage ) ;
99
+ } ) ;
100
+
101
+ // Act
102
+ var result =
103
+ actionSequence . ContainsActionsOfType < SecondaryIndexInfo > ( new [ ] {
104
+ typeof ( CreateNodeAction ) , typeof ( RemoveNodeAction )
105
+ } ) ;
106
+
107
+ // Assert
108
+ Assert . IsFalse ( result ) ;
109
+ }
110
+
111
+ [ Test ]
112
+ [ TestCaseSource ( nameof ( DisallowedTableChangesTestCaseData ) ) ]
113
+ public void ContainsActionsOfType_WhenDisallowedTableActionsLogged_ReturnsFalse ( Action < TableInfo > action )
114
+ {
115
+ // Arrange
116
+ var originalStorage = CreateStorage ( ) ;
117
+ var originalTable = CreateTable ( originalStorage ) ;
118
+ AddColumn < long > ( originalTable ) ;
119
+ AddColumn < string > ( originalTable ) ;
120
+
121
+ var actionSequence = GetActionSequence ( originalStorage , ( storage , _ ) => {
122
+ var table = storage . Tables [ originalTable . Name ] ;
123
+ action ( table ) ;
124
+ } ) ;
125
+
126
+ // Act
127
+ var result =
128
+ actionSequence . ContainsActionsOfType < SecondaryIndexInfo > ( new [ ] {
129
+ typeof ( CreateNodeAction ) , typeof ( RemoveNodeAction )
130
+ } ) ;
131
+
132
+ // Assert
133
+ Assert . IsFalse ( result ) ;
134
+ }
135
+
136
+ private static StorageModel CreateStorage ( ) => new ( "storage" ) ;
137
+
138
+ private static TableInfo CreateTable ( StorageModel storageInfo )
139
+ {
140
+ var table = new TableInfo ( storageInfo , Guid . NewGuid ( ) . ToString ( ) ) ;
141
+ var id = new StorageColumnInfo ( table , "Id" , new StorageTypeInfo ( typeof ( int ) , new SqlValueType ( SqlType . Int32 ) ) ) ;
142
+ var pk = new PrimaryIndexInfo ( table , "PK_A" ) ;
143
+ _ = new KeyColumnRef ( pk , id ) ;
144
+ pk . PopulateValueColumns ( ) ;
145
+
146
+ return table ;
147
+ }
148
+
149
+ private static StorageColumnInfo AddColumn < TType > ( TableInfo table , string ? name = null )
150
+ {
151
+ var column = new StorageColumnInfo ( table , name ?? Guid . NewGuid ( ) . ToString ( ) ,
152
+ new StorageTypeInfo ( typeof ( TType ) , new SqlValueType ( typeof ( TType ) . Name ) , false ) ) ;
153
+
154
+ var pk = table . PrimaryIndex ;
155
+ pk . ValueColumns . Clear ( ) ;
156
+ pk . PopulateValueColumns ( ) ;
157
+
158
+ return column ;
159
+ }
160
+
161
+ private static SecondaryIndexInfo AddSecondaryIndex ( params StorageColumnInfo [ ] columns )
162
+ {
163
+ var table = columns [ 0 ] . Parent ;
164
+ var index = new SecondaryIndexInfo ( table , $ "index{ Guid . NewGuid ( ) } ") ;
165
+
166
+ foreach ( var column in columns ) {
167
+ _ = new KeyColumnRef ( index , column ) ;
168
+ }
169
+ index . PopulatePrimaryKeyColumns ( ) ;
170
+
171
+ return index ;
172
+ }
173
+
174
+ private static ActionSequence GetActionSequence ( StorageModel origin , Action < StorageModel , HintSet > mutator )
175
+ {
176
+ var clonedStorage = Clone ( origin ) ;
177
+ var hints = new HintSet ( origin , clonedStorage ) ;
178
+ mutator . Invoke ( clonedStorage , hints ) ;
179
+ origin . Validate ( ) ;
180
+ clonedStorage . Validate ( ) ;
181
+
182
+ var comparer = new Comparer ( ) ;
183
+ var diff = comparer . Compare ( origin , clonedStorage , hints ) ;
184
+ return new ActionSequence ( ) {
185
+ new Upgrader ( ) . GetUpgradeSequence ( diff , hints , comparer )
186
+ } ;
187
+ }
188
+
189
+ private static StorageModel Clone ( StorageModel storage ) => ( StorageModel ) storage . Clone ( null , storage . Name ) ;
190
+
191
+ private static readonly IEnumerable < Type > AllNodeActionsBesidesGroup = Assembly . GetAssembly ( typeof ( NodeAction ) ) !
192
+ . DefinedTypes
193
+ . Where ( type => type . IsSubclassOf ( typeof ( NodeAction ) ) ) . Except ( new [ ] { typeof ( GroupingNodeAction ) } ) ;
194
+
195
+ private static readonly IEnumerable < Type > AllActionBesidesCreateAndRemove =
196
+ AllNodeActionsBesidesGroup . Except ( new [ ] { typeof ( CreateNodeAction ) , typeof ( RemoveNodeAction ) } ) ;
197
+
198
+ public static IEnumerable AddSecondaryIndexTestCases
199
+ {
200
+ get {
201
+ yield return new TestCaseData ( new object ? [ ] { new [ ] { typeof ( CreateNodeAction ) } , true } ) ;
202
+ yield return new TestCaseData ( new object ? [ ] { new [ ] { typeof ( RemoveNodeAction ) } , false } ) ;
203
+ yield return new TestCaseData ( new object ? [ ] { new [ ] { typeof ( CreateNodeAction ) , typeof ( RemoveNodeAction ) } , true } ) ;
204
+ yield return new TestCaseData ( new object ? [ ] { AllActionBesidesCreateAndRemove , false } ) ;
205
+ }
206
+ }
207
+
208
+ public static IEnumerable RemoveSecondaryIndexTestCases
209
+ {
210
+ get {
211
+ yield return new TestCaseData ( new object ? [ ] { new [ ] { typeof ( CreateNodeAction ) } , false } ) ;
212
+ yield return new TestCaseData ( new object ? [ ] { new [ ] { typeof ( RemoveNodeAction ) } , true } ) ;
213
+ yield return new TestCaseData ( new object ? [ ] { new [ ] { typeof ( CreateNodeAction ) , typeof ( RemoveNodeAction ) } , true } ) ;
214
+ yield return new TestCaseData ( new object ? [ ] { AllActionBesidesCreateAndRemove , false } ) ;
215
+ }
216
+ }
217
+
218
+
219
+ public static IEnumerable AllActionsTestCaseData => AllNodeActionsBesidesGroup . Select ( actionType => new TestCaseData ( actionType ) ) ;
220
+
221
+ public static IEnumerable DisallowedStorageChangesTestCaseData
222
+ {
223
+ get {
224
+ yield return new TestCaseData ( new object ? [ ] { new Action < StorageModel > ( storageInfo => CreateTable ( storageInfo ) ) } ) ;
225
+ yield return new TestCaseData ( new object ? [ ] { new Action < StorageModel > ( storageInfo => storageInfo . Tables . First ( ) . Remove ( ) ) } ) ;
226
+ }
227
+ }
228
+
229
+ public static IEnumerable DisallowedTableChangesTestCaseData
230
+ {
231
+ get {
232
+ yield return new TestCaseData ( new object ? [ ] { new Action < TableInfo > ( table => table . PrimaryIndex . Remove ( ) ) } ) ;
233
+ // TODO: possibly not supported
234
+ // yield return new TestCaseData(new object?[] {
235
+ // new Action<TableInfo>(table => {
236
+ // table.PrimaryIndex.KeyColumns.First().Remove();
237
+ // new KeyColumnRef(table.PrimaryIndex, table.Columns.Last());
238
+ // table.PrimaryIndex.ValueColumns.Clear();
239
+ // table.PrimaryIndex.PopulateValueColumns();
240
+ // })
241
+ // });
242
+ yield return new TestCaseData ( new object ? [ ] { new Action < TableInfo > ( table => table . PrimaryIndex . Name = $ "OtherName{ Guid . NewGuid ( ) } ") } ) ;
243
+ yield return new TestCaseData ( new object ? [ ] { new Action < TableInfo > ( table => AddColumn < string > ( table ) ) } ) ;
244
+ yield return new TestCaseData ( new object ? [ ] { new Action < TableInfo > ( table => {
245
+ table . Columns . Last ( ) . Remove ( ) ;
246
+ table . PrimaryIndex . ValueColumns . Clear ( ) ;
247
+ table . PrimaryIndex . PopulateValueColumns ( ) ;
248
+ } ) } ) ;
249
+ yield return new TestCaseData ( new object ? [ ] { new Action < TableInfo > ( table => table . Columns . Last ( ) . Name = $ "OtherName{ Guid . NewGuid ( ) } ") } ) ;
250
+ // TODO: DefaultValue is marked as IgnoreInComparison
251
+ //yield return new TestCaseData(new object?[] { new Action<TableInfo>(table => table.Columns.Last(c => c.Type.Type == typeof(string)).DefaultValue = $"") });
252
+ yield return new TestCaseData ( new object ? [ ] { new Action < TableInfo > ( table => table . Columns . Last ( ) . Type = new StorageTypeInfo ( typeof ( byte ) , new SqlValueType ( SqlType . Int8 ) ) ) } ) ;
253
+ // TODO: seems like index change not handled in upgrade
254
+ // yield return new TestCaseData(new object?[] { new Action<TableInfo>(table => {
255
+ // table.Columns.Last().Index -= 1;
256
+ // table.PrimaryIndex.ValueColumns.Clear();
257
+ // table.PrimaryIndex.PopulateValueColumns();
258
+ // }) });
259
+ }
260
+ }
261
+ }
262
+ }
0 commit comments