@@ -6,9 +6,12 @@ package integrations
66
77import (
88 "context"
9+ "errors"
10+ "fmt"
911 "net/url"
1012 "os"
1113 "path/filepath"
14+ "reflect"
1215 "strings"
1316 "testing"
1417
@@ -58,6 +61,7 @@ func TestDumpRestore(t *testing.T) {
5861 opts := migrations.MigrateOptions {
5962 GitServiceType : structs .GiteaService ,
6063 Issues : true ,
64+ PullRequests : true ,
6165 Labels : true ,
6266 Milestones : true ,
6367 Comments : true ,
@@ -80,57 +84,252 @@ func TestDumpRestore(t *testing.T) {
8084 // Phase 2: restore from the filesystem to the Gitea instance in restoredrepo
8185 //
8286
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 )
8591 assert .NoError (t , err )
8692
8793 newrepo := unittest .AssertExistsAndLoadBean (t , & repo_model.Repository {Name : newreponame }).(* repo_model.Repository )
8894
8995 //
90- // Phase 3: dump restoredrepo from the Gitea instance to the filesystem
96+ // Phase 3: dump restored from the Gitea instance to the filesystem
9197 //
9298 opts .RepoName = newreponame
9399 opts .CloneAddr = newrepo .CloneLink ().HTTPS
94100 err = migrations .DumpRepository (ctx , basePath , repoOwner .Name , opts )
95101 assert .NoError (t , err )
96102
97103 //
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
99105 //
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 ,
108109 }
110+ comparator .assertEquals (repo , newrepo )
111+ })
112+ }
109113
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
133330 }
134331 }
135- })
332+ assert .EqualValues (c .t , bi , ai )
333+ }
334+ return after .Interface ()
136335}
0 commit comments