@@ -6,9 +6,12 @@ package integrations
6
6
7
7
import (
8
8
"context"
9
+ "errors"
10
+ "fmt"
9
11
"net/url"
10
12
"os"
11
13
"path/filepath"
14
+ "reflect"
12
15
"strings"
13
16
"testing"
14
17
@@ -58,6 +61,7 @@ func TestDumpRestore(t *testing.T) {
58
61
opts := migrations.MigrateOptions {
59
62
GitServiceType : structs .GiteaService ,
60
63
Issues : true ,
64
+ PullRequests : true ,
61
65
Labels : true ,
62
66
Milestones : true ,
63
67
Comments : true ,
@@ -80,57 +84,252 @@ func TestDumpRestore(t *testing.T) {
80
84
// Phase 2: restore from the filesystem to the Gitea instance in restoredrepo
81
85
//
82
86
83
- newreponame := "restoredrepo"
84
- err = migrations .RestoreRepository (ctx , d , repo .OwnerName , newreponame , []string {"labels" , "milestones" , "issues" , "comments" }, false )
87
+ newreponame := "restored"
88
+ err = migrations .RestoreRepository (ctx , d , repo .OwnerName , newreponame , []string {
89
+ "labels" , "issues" , "comments" , "milestones" , "pull_requests" ,
90
+ }, false )
85
91
assert .NoError (t , err )
86
92
87
93
newrepo := unittest .AssertExistsAndLoadBean (t , & repo_model.Repository {Name : newreponame }).(* repo_model.Repository )
88
94
89
95
//
90
- // Phase 3: dump restoredrepo from the Gitea instance to the filesystem
96
+ // Phase 3: dump restored from the Gitea instance to the filesystem
91
97
//
92
98
opts .RepoName = newreponame
93
99
opts .CloneAddr = newrepo .CloneLink ().HTTPS
94
100
err = migrations .DumpRepository (ctx , basePath , repoOwner .Name , opts )
95
101
assert .NoError (t , err )
96
102
97
103
//
98
- // Verify the dump of restoredrepo is the same as the dump of repo1
104
+ // Verify the dump of restored is the same as the dump of repo1
99
105
//
100
- newd := filepath .Join (basePath , newrepo .OwnerName , newrepo .Name )
101
- for _ , filename := range []string {"repo.yml" , "label.yml" , "milestone.yml" } {
102
- beforeBytes , err := os .ReadFile (filepath .Join (d , filename ))
103
- assert .NoError (t , err )
104
- before := strings .ReplaceAll (string (beforeBytes ), reponame , newreponame )
105
- after , err := os .ReadFile (filepath .Join (newd , filename ))
106
- assert .NoError (t , err )
107
- assert .EqualValues (t , before , string (after ))
106
+ comparator := & compareDump {
107
+ t : t ,
108
+ basePath : basePath ,
108
109
}
110
+ comparator .assertEquals (repo , newrepo )
111
+ })
112
+ }
109
113
110
- beforeBytes , err := os .ReadFile (filepath .Join (d , "issue.yml" ))
111
- assert .NoError (t , err )
112
- before := make ([]* base.Issue , 0 , 10 )
113
- assert .NoError (t , yaml .Unmarshal (beforeBytes , & before ))
114
- afterBytes , err := os .ReadFile (filepath .Join (newd , "issue.yml" ))
115
- assert .NoError (t , err )
116
- after := make ([]* base.Issue , 0 , 10 )
117
- assert .NoError (t , yaml .Unmarshal (afterBytes , & after ))
118
-
119
- assert .EqualValues (t , len (before ), len (after ))
120
- if len (before ) == len (after ) {
121
- for i := 0 ; i < len (before ); i ++ {
122
- assert .EqualValues (t , before [i ].Number , after [i ].Number )
123
- assert .EqualValues (t , before [i ].Title , after [i ].Title )
124
- assert .EqualValues (t , before [i ].Content , after [i ].Content )
125
- assert .EqualValues (t , before [i ].Ref , after [i ].Ref )
126
- assert .EqualValues (t , before [i ].Milestone , after [i ].Milestone )
127
- assert .EqualValues (t , before [i ].State , after [i ].State )
128
- assert .EqualValues (t , before [i ].IsLocked , after [i ].IsLocked )
129
- assert .EqualValues (t , before [i ].Created , after [i ].Created )
130
- assert .EqualValues (t , before [i ].Updated , after [i ].Updated )
131
- assert .EqualValues (t , before [i ].Labels , after [i ].Labels )
132
- assert .EqualValues (t , before [i ].Reactions , after [i ].Reactions )
114
+ type compareDump struct {
115
+ t * testing.T
116
+ basePath string
117
+ repoBefore * repo_model.Repository
118
+ dirBefore string
119
+ repoAfter * repo_model.Repository
120
+ dirAfter string
121
+ }
122
+
123
+ type compareField struct {
124
+ before interface {}
125
+ after interface {}
126
+ ignore bool
127
+ transform func (string ) string
128
+ nested * compareFields
129
+ }
130
+
131
+ type compareFields map [string ]compareField
132
+
133
+ func (c * compareDump ) replaceRepoName (original string ) string {
134
+ return strings .ReplaceAll (original , c .repoBefore .Name , c .repoAfter .Name )
135
+ }
136
+
137
+ func (c * compareDump ) assertEquals (repoBefore , repoAfter * repo_model.Repository ) {
138
+ c .repoBefore = repoBefore
139
+ c .dirBefore = filepath .Join (c .basePath , repoBefore .OwnerName , repoBefore .Name )
140
+ c .repoAfter = repoAfter
141
+ c .dirAfter = filepath .Join (c .basePath , repoAfter .OwnerName , repoAfter .Name )
142
+
143
+ for _ , filename := range []string {"repo.yml" , "label.yml" } {
144
+ beforeBytes , err := os .ReadFile (filepath .Join (c .dirBefore , filename ))
145
+ assert .NoError (c .t , err )
146
+ before := c .replaceRepoName (string (beforeBytes ))
147
+ after , err := os .ReadFile (filepath .Join (c .dirAfter , filename ))
148
+ assert .NoError (c .t , err )
149
+ assert .EqualValues (c .t , before , string (after ))
150
+ }
151
+
152
+ //
153
+ // base.Repository
154
+ //
155
+ _ = c .assertEqual ("repo.yml" , base.Repository {}, compareFields {
156
+ "Name" : {
157
+ before : c .repoBefore .Name ,
158
+ after : c .repoAfter .Name ,
159
+ },
160
+ "CloneURL" : {transform : c .replaceRepoName },
161
+ "OriginalURL" : {transform : c .replaceRepoName },
162
+ })
163
+
164
+ //
165
+ // base.Label
166
+ //
167
+ labels , ok := c .assertEqual ("label.yml" , []base.Label {}, compareFields {}).([]* base.Label )
168
+ assert .True (c .t , ok )
169
+ assert .GreaterOrEqual (c .t , len (labels ), 1 )
170
+
171
+ //
172
+ // base.Milestone
173
+ //
174
+ milestones , ok := c .assertEqual ("milestone.yml" , []base.Milestone {}, compareFields {
175
+ "Updated" : {ignore : true }, // the database updates that field independently
176
+ }).([]* base.Milestone )
177
+ assert .True (c .t , ok )
178
+ assert .GreaterOrEqual (c .t , len (milestones ), 1 )
179
+
180
+ //
181
+ // base.Issue and the associated comments
182
+ //
183
+ issues , ok := c .assertEqual ("issue.yml" , []base.Issue {}, compareFields {
184
+ "Assignees" : {ignore : true }, // not implemented yet
185
+ }).([]* base.Issue )
186
+ assert .True (c .t , ok )
187
+ assert .GreaterOrEqual (c .t , len (issues ), 1 )
188
+ for _ , issue := range issues {
189
+ filename := filepath .Join ("comments" , fmt .Sprintf ("%d.yml" , issue .Number ))
190
+ comments , ok := c .assertEqual (filename , []base.Comment {}, compareFields {}).([]* base.Comment )
191
+ assert .True (c .t , ok )
192
+ for _ , comment := range comments {
193
+ assert .EqualValues (c .t , issue .Number , comment .IssueIndex )
194
+ }
195
+ }
196
+
197
+ //
198
+ // base.PullRequest and the associated comments
199
+ //
200
+ comparePullRequestBranch := & compareFields {
201
+ "RepoName" : {
202
+ before : c .repoBefore .Name ,
203
+ after : c .repoAfter .Name ,
204
+ },
205
+ "CloneURL" : {transform : c .replaceRepoName },
206
+ }
207
+ prs , ok := c .assertEqual ("pull_request.yml" , []base.PullRequest {}, compareFields {
208
+ "Assignees" : {ignore : true }, // not implemented yet
209
+ "Head" : {nested : comparePullRequestBranch },
210
+ "Base" : {nested : comparePullRequestBranch },
211
+ "Labels" : {ignore : true }, // because org labels are not handled propery
212
+ }).([]* base.PullRequest )
213
+ assert .True (c .t , ok )
214
+ assert .GreaterOrEqual (c .t , len (prs ), 1 )
215
+ for _ , pr := range prs {
216
+ filename := filepath .Join ("comments" , fmt .Sprintf ("%d.yml" , pr .Number ))
217
+ comments , ok := c .assertEqual (filename , []base.Comment {}, compareFields {}).([]* base.Comment )
218
+ assert .True (c .t , ok )
219
+ for _ , comment := range comments {
220
+ assert .EqualValues (c .t , pr .Number , comment .IssueIndex )
221
+ }
222
+ }
223
+ }
224
+
225
+ func (c * compareDump ) assertLoadYAMLFiles (beforeFilename , afterFilename string , before , after interface {}) {
226
+ _ , beforeErr := os .Stat (beforeFilename )
227
+ _ , afterErr := os .Stat (afterFilename )
228
+ assert .EqualValues (c .t , errors .Is (beforeErr , os .ErrNotExist ), errors .Is (afterErr , os .ErrNotExist ))
229
+ if errors .Is (beforeErr , os .ErrNotExist ) {
230
+ return
231
+ }
232
+
233
+ beforeBytes , err := os .ReadFile (beforeFilename )
234
+ assert .NoError (c .t , err )
235
+ assert .NoError (c .t , yaml .Unmarshal (beforeBytes , before ))
236
+ afterBytes , err := os .ReadFile (afterFilename )
237
+ assert .NoError (c .t , err )
238
+ assert .NoError (c .t , yaml .Unmarshal (afterBytes , after ))
239
+ }
240
+
241
+ func (c * compareDump ) assertLoadFiles (beforeFilename , afterFilename string , t reflect.Type ) (before , after reflect.Value ) {
242
+ var beforePtr , afterPtr reflect.Value
243
+ if t .Kind () == reflect .Slice {
244
+ //
245
+ // Given []Something{} create afterPtr, beforePtr []*Something{}
246
+ //
247
+ sliceType := reflect .SliceOf (reflect .PtrTo (t .Elem ()))
248
+ beforeSlice := reflect .MakeSlice (sliceType , 0 , 10 )
249
+ beforePtr = reflect .New (beforeSlice .Type ())
250
+ beforePtr .Elem ().Set (beforeSlice )
251
+ afterSlice := reflect .MakeSlice (sliceType , 0 , 10 )
252
+ afterPtr = reflect .New (afterSlice .Type ())
253
+ afterPtr .Elem ().Set (afterSlice )
254
+ } else {
255
+ //
256
+ // Given Something{} create afterPtr, beforePtr *Something{}
257
+ //
258
+ beforePtr = reflect .New (t )
259
+ afterPtr = reflect .New (t )
260
+ }
261
+ c .assertLoadYAMLFiles (beforeFilename , afterFilename , beforePtr .Interface (), afterPtr .Interface ())
262
+ return beforePtr .Elem (), afterPtr .Elem ()
263
+ }
264
+
265
+ func (c * compareDump ) assertEqual (filename string , kind interface {}, fields compareFields ) (i interface {}) {
266
+ beforeFilename := filepath .Join (c .dirBefore , filename )
267
+ afterFilename := filepath .Join (c .dirAfter , filename )
268
+
269
+ typeOf := reflect .TypeOf (kind )
270
+ before , after := c .assertLoadFiles (beforeFilename , afterFilename , typeOf )
271
+ if typeOf .Kind () == reflect .Slice {
272
+ i = c .assertEqualSlices (before , after , fields )
273
+ } else {
274
+ i = c .assertEqualValues (before , after , fields )
275
+ }
276
+ return i
277
+ }
278
+
279
+ func (c * compareDump ) assertEqualSlices (before , after reflect.Value , fields compareFields ) interface {} {
280
+ assert .EqualValues (c .t , before .Len (), after .Len ())
281
+ if before .Len () == after .Len () {
282
+ for i := 0 ; i < before .Len (); i ++ {
283
+ _ = c .assertEqualValues (
284
+ reflect .Indirect (before .Index (i ).Elem ()),
285
+ reflect .Indirect (after .Index (i ).Elem ()),
286
+ fields )
287
+ }
288
+ }
289
+ return after .Interface ()
290
+ }
291
+
292
+ func (c * compareDump ) assertEqualValues (before , after reflect.Value , fields compareFields ) interface {} {
293
+ for _ , field := range reflect .VisibleFields (before .Type ()) {
294
+ bf := before .FieldByName (field .Name )
295
+ bi := bf .Interface ()
296
+ af := after .FieldByName (field .Name )
297
+ ai := af .Interface ()
298
+ if compare , ok := fields [field .Name ]; ok {
299
+ if compare .ignore == true {
300
+ //
301
+ // Ignore
302
+ //
303
+ continue
304
+ }
305
+ if compare .transform != nil {
306
+ //
307
+ // Transform these strings before comparing them
308
+ //
309
+ bs , ok := bi .(string )
310
+ assert .True (c .t , ok )
311
+ as , ok := ai .(string )
312
+ assert .True (c .t , ok )
313
+ assert .EqualValues (c .t , compare .transform (bs ), compare .transform (as ))
314
+ continue
315
+ }
316
+ if compare .before != nil && compare .after != nil {
317
+ //
318
+ // The fields are expected to have different values
319
+ //
320
+ assert .EqualValues (c .t , compare .before , bi )
321
+ assert .EqualValues (c .t , compare .after , ai )
322
+ continue
323
+ }
324
+ if compare .nested != nil {
325
+ //
326
+ // The fields are a struct, recurse
327
+ //
328
+ c .assertEqualValues (bf , af , * compare .nested )
329
+ continue
133
330
}
134
331
}
135
- })
332
+ assert .EqualValues (c .t , bi , ai )
333
+ }
334
+ return after .Interface ()
136
335
}
0 commit comments