gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package foreachref_test
  4. import (
  5. "errors"
  6. "fmt"
  7. "io"
  8. "strings"
  9. "testing"
  10. "code.gitea.io/gitea/modules/git/foreachref"
  11. "code.gitea.io/gitea/modules/json"
  12. "github.com/stretchr/testify/require"
  13. )
  14. type refSlice = []map[string]string
  15. func TestParser(t *testing.T) {
  16. tests := []struct {
  17. name string
  18. givenFormat foreachref.Format
  19. givenInput io.Reader
  20. wantRefs refSlice
  21. wantErr bool
  22. expectedErr error
  23. }{
  24. // this would, for example, be the result when running `git
  25. // for-each-ref refs/tags` on a repo without tags.
  26. {
  27. name: "no references on empty input",
  28. givenFormat: foreachref.NewFormat("refname:short"),
  29. givenInput: strings.NewReader(``),
  30. wantRefs: []map[string]string{},
  31. },
  32. // note: `git for-each-ref` will add a newline between every
  33. // reference (in addition to the ref-delimiter we've chosen)
  34. {
  35. name: "single field requested, single reference in output",
  36. givenFormat: foreachref.NewFormat("refname:short"),
  37. givenInput: strings.NewReader("refname:short v0.0.1\x00\x00" + "\n"),
  38. wantRefs: []map[string]string{
  39. {"refname:short": "v0.0.1"},
  40. },
  41. },
  42. {
  43. name: "single field requested, multiple references in output",
  44. givenFormat: foreachref.NewFormat("refname:short"),
  45. givenInput: strings.NewReader(
  46. "refname:short v0.0.1\x00\x00" + "\n" +
  47. "refname:short v0.0.2\x00\x00" + "\n" +
  48. "refname:short v0.0.3\x00\x00" + "\n"),
  49. wantRefs: []map[string]string{
  50. {"refname:short": "v0.0.1"},
  51. {"refname:short": "v0.0.2"},
  52. {"refname:short": "v0.0.3"},
  53. },
  54. },
  55. {
  56. name: "multiple fields requested for each reference",
  57. givenFormat: foreachref.NewFormat("refname:short", "objecttype", "objectname"),
  58. givenInput: strings.NewReader(
  59. "refname:short v0.0.1\x00objecttype commit\x00objectname 7b2c5ac9fc04fc5efafb60700713d4fa609b777b\x00\x00" + "\n" +
  60. "refname:short v0.0.2\x00objecttype commit\x00objectname a1f051bc3eba734da4772d60e2d677f47cf93ef4\x00\x00" + "\n" +
  61. "refname:short v0.0.3\x00objecttype commit\x00objectname ef82de70bb3f60c65fb8eebacbb2d122ef517385\x00\x00" + "\n",
  62. ),
  63. wantRefs: []map[string]string{
  64. {
  65. "refname:short": "v0.0.1",
  66. "objecttype": "commit",
  67. "objectname": "7b2c5ac9fc04fc5efafb60700713d4fa609b777b",
  68. },
  69. {
  70. "refname:short": "v0.0.2",
  71. "objecttype": "commit",
  72. "objectname": "a1f051bc3eba734da4772d60e2d677f47cf93ef4",
  73. },
  74. {
  75. "refname:short": "v0.0.3",
  76. "objecttype": "commit",
  77. "objectname": "ef82de70bb3f60c65fb8eebacbb2d122ef517385",
  78. },
  79. },
  80. },
  81. {
  82. name: "must handle multi-line fields such as 'content'",
  83. givenFormat: foreachref.NewFormat("refname:short", "contents", "author"),
  84. givenInput: strings.NewReader(
  85. "refname:short v0.0.1\x00contents Create new buffer if not present yet (#549)\n\nFixes a nil dereference when ProcessFoo is used\nwith multiple commands.\x00author Foo Bar <foo@bar.com> 1507832733 +0200\x00\x00" + "\n" +
  86. "refname:short v0.0.2\x00contents Update CI config (#651)\n\n\x00author John Doe <john.doe@foo.com> 1521643174 +0000\x00\x00" + "\n" +
  87. "refname:short v0.0.3\x00contents Fixed code sample for bash completion (#687)\n\n\x00author Foo Baz <foo@baz.com> 1524836750 +0200\x00\x00" + "\n",
  88. ),
  89. wantRefs: []map[string]string{
  90. {
  91. "refname:short": "v0.0.1",
  92. "contents": "Create new buffer if not present yet (#549)\n\nFixes a nil dereference when ProcessFoo is used\nwith multiple commands.",
  93. "author": "Foo Bar <foo@bar.com> 1507832733 +0200",
  94. },
  95. {
  96. "refname:short": "v0.0.2",
  97. "contents": "Update CI config (#651)",
  98. "author": "John Doe <john.doe@foo.com> 1521643174 +0000",
  99. },
  100. {
  101. "refname:short": "v0.0.3",
  102. "contents": "Fixed code sample for bash completion (#687)",
  103. "author": "Foo Baz <foo@baz.com> 1524836750 +0200",
  104. },
  105. },
  106. },
  107. {
  108. name: "must handle fields without values",
  109. givenFormat: foreachref.NewFormat("refname:short", "object", "objecttype"),
  110. givenInput: strings.NewReader(
  111. "refname:short v0.0.1\x00object \x00objecttype commit\x00\x00" + "\n" +
  112. "refname:short v0.0.2\x00object \x00objecttype commit\x00\x00" + "\n" +
  113. "refname:short v0.0.3\x00object \x00objecttype commit\x00\x00" + "\n",
  114. ),
  115. wantRefs: []map[string]string{
  116. {
  117. "refname:short": "v0.0.1",
  118. "object": "",
  119. "objecttype": "commit",
  120. },
  121. {
  122. "refname:short": "v0.0.2",
  123. "object": "",
  124. "objecttype": "commit",
  125. },
  126. {
  127. "refname:short": "v0.0.3",
  128. "object": "",
  129. "objecttype": "commit",
  130. },
  131. },
  132. },
  133. {
  134. name: "must fail when the number of fields in the input doesn't match expected format",
  135. givenFormat: foreachref.NewFormat("refname:short", "objecttype", "objectname"),
  136. givenInput: strings.NewReader(
  137. "refname:short v0.0.1\x00objecttype commit\x00\x00" + "\n" +
  138. "refname:short v0.0.2\x00objecttype commit\x00\x00" + "\n" +
  139. "refname:short v0.0.3\x00objecttype commit\x00\x00" + "\n",
  140. ),
  141. wantErr: true,
  142. expectedErr: errors.New("unexpected number of reference fields: wanted 2, was 3"),
  143. },
  144. {
  145. name: "must fail input fields don't match expected format",
  146. givenFormat: foreachref.NewFormat("refname:short", "objectname"),
  147. givenInput: strings.NewReader(
  148. "refname:short v0.0.1\x00objecttype commit\x00\x00" + "\n" +
  149. "refname:short v0.0.2\x00objecttype commit\x00\x00" + "\n" +
  150. "refname:short v0.0.3\x00objecttype commit\x00\x00" + "\n",
  151. ),
  152. wantErr: true,
  153. expectedErr: errors.New("unexpected field name at position 1: wanted: 'objectname', was: 'objecttype'"),
  154. },
  155. }
  156. for _, test := range tests {
  157. tc := test // don't close over loop variable
  158. t.Run(tc.name, func(t *testing.T) {
  159. parser := tc.givenFormat.Parser(tc.givenInput)
  160. //
  161. // parse references from input
  162. //
  163. gotRefs := make([]map[string]string, 0)
  164. for {
  165. ref := parser.Next()
  166. if ref == nil {
  167. break
  168. }
  169. gotRefs = append(gotRefs, ref)
  170. }
  171. err := parser.Err()
  172. //
  173. // verify expectations
  174. //
  175. if tc.wantErr {
  176. require.Error(t, err)
  177. require.EqualError(t, err, tc.expectedErr.Error())
  178. } else {
  179. require.NoError(t, err, "for-each-ref parser unexpectedly failed with: %v", err)
  180. require.Equal(t, tc.wantRefs, gotRefs, "for-each-ref parser produced unexpected reference set. wanted: %v, got: %v", pretty(tc.wantRefs), pretty(gotRefs))
  181. }
  182. })
  183. }
  184. }
  185. func pretty(v any) string {
  186. data, err := json.MarshalIndent(v, "", " ")
  187. if err != nil {
  188. // shouldn't happen
  189. panic(fmt.Sprintf("json-marshalling failed: %v", err))
  190. }
  191. return string(data)
  192. }