1package git
2
3import (
4 "context"
5 "os"
6 "path/filepath"
7 "testing"
8
9 "github.com/go-git/go-billy/v5/memfs"
10 "github.com/go-git/go-git/v5/config"
11 "github.com/go-git/go-git/v5/plumbing"
12 "github.com/go-git/go-git/v5/storage/memory"
13 "github.com/stretchr/testify/suite"
14
15 fixtures "github.com/go-git/go-git-fixtures/v4"
16)
17
18type SubmoduleSuite struct {
19 suite.Suite
20 BaseSuite
21 Worktree *Worktree
22}
23
24func TestSubmoduleSuite(t *testing.T) {
25 suite.Run(t, new(SubmoduleSuite))
26}
27
28func (s *SubmoduleSuite) SetupTest() {
29 path := fixtures.ByTag("submodule").One().Worktree().Root()
30
31 dir, err := os.MkdirTemp("", "")
32 s.NoError(err)
33
34 r, err := PlainClone(filepath.Join(dir, "worktree"), false, &CloneOptions{
35 URL: path,
36 })
37
38 s.NoError(err)
39
40 s.Repository = r
41 s.Worktree, err = r.Worktree()
42 s.NoError(err)
43}
44
45func (s *SubmoduleSuite) TestInit() {
46 sm, err := s.Worktree.Submodule("basic")
47 s.NoError(err)
48
49 s.False(sm.initialized)
50 err = sm.Init()
51 s.NoError(err)
52
53 s.True(sm.initialized)
54
55 cfg, err := s.Repository.Config()
56 s.NoError(err)
57
58 s.Len(cfg.Submodules, 1)
59 s.NotNil(cfg.Submodules["basic"])
60
61 status, err := sm.Status()
62 s.NoError(err)
63 s.False(status.IsClean())
64}
65
66func (s *SubmoduleSuite) TestUpdate() {
67 if testing.Short() {
68 s.T().Skip("skipping test in short mode.")
69 }
70
71 sm, err := s.Worktree.Submodule("basic")
72 s.NoError(err)
73
74 err = sm.Update(&SubmoduleUpdateOptions{
75 Init: true,
76 })
77
78 s.NoError(err)
79
80 r, err := sm.Repository()
81 s.NoError(err)
82
83 ref, err := r.Reference(plumbing.HEAD, true)
84 s.NoError(err)
85 s.Equal("6ecf0ef2c2dffb796033e5a02219af86ec6584e5", ref.Hash().String())
86
87 status, err := sm.Status()
88 s.NoError(err)
89 s.True(status.IsClean())
90}
91
92func (s *SubmoduleSuite) TestRepositoryWithoutInit() {
93 sm, err := s.Worktree.Submodule("basic")
94 s.NoError(err)
95
96 r, err := sm.Repository()
97 s.ErrorIs(err, ErrSubmoduleNotInitialized)
98 s.Nil(r)
99}
100
101func (s *SubmoduleSuite) TestUpdateWithoutInit() {
102 sm, err := s.Worktree.Submodule("basic")
103 s.NoError(err)
104
105 err = sm.Update(&SubmoduleUpdateOptions{})
106 s.ErrorIs(err, ErrSubmoduleNotInitialized)
107}
108
109func (s *SubmoduleSuite) TestUpdateWithNotFetch() {
110 sm, err := s.Worktree.Submodule("basic")
111 s.NoError(err)
112
113 err = sm.Update(&SubmoduleUpdateOptions{
114 Init: true,
115 NoFetch: true,
116 })
117
118 // Since we are not fetching, the object is not there
119 s.ErrorIs(err, plumbing.ErrObjectNotFound)
120}
121
122func (s *SubmoduleSuite) TestUpdateWithRecursion() {
123 if testing.Short() {
124 s.T().Skip("skipping test in short mode.")
125 }
126
127 sm, err := s.Worktree.Submodule("itself")
128 s.NoError(err)
129
130 err = sm.Update(&SubmoduleUpdateOptions{
131 Init: true,
132 RecurseSubmodules: 2,
133 })
134
135 s.NoError(err)
136
137 fs := s.Worktree.Filesystem
138 _, err = fs.Stat(fs.Join("itself", "basic", "LICENSE"))
139 s.NoError(err)
140}
141
142func (s *SubmoduleSuite) TestUpdateWithInitAndUpdate() {
143 if testing.Short() {
144 s.T().Skip("skipping test in short mode.")
145 }
146
147 sm, err := s.Worktree.Submodule("basic")
148 s.NoError(err)
149
150 err = sm.Update(&SubmoduleUpdateOptions{
151 Init: true,
152 })
153 s.NoError(err)
154
155 idx, err := s.Repository.Storer.Index()
156 s.NoError(err)
157
158 for i, e := range idx.Entries {
159 if e.Name == "basic" {
160 e.Hash = plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d")
161 }
162
163 idx.Entries[i] = e
164 }
165
166 err = s.Repository.Storer.SetIndex(idx)
167 s.NoError(err)
168
169 err = sm.Update(&SubmoduleUpdateOptions{})
170 s.NoError(err)
171
172 r, err := sm.Repository()
173 s.NoError(err)
174
175 ref, err := r.Reference(plumbing.HEAD, true)
176 s.NoError(err)
177 s.Equal("b029517f6300c2da0f4b651b8642506cd6aaf45d", ref.Hash().String())
178
179}
180
181func (s *SubmoduleSuite) TestSubmodulesInit() {
182 sm, err := s.Worktree.Submodules()
183 s.NoError(err)
184
185 err = sm.Init()
186 s.NoError(err)
187
188 sm, err = s.Worktree.Submodules()
189 s.NoError(err)
190
191 for _, m := range sm {
192 s.True(m.initialized)
193 }
194}
195
196func (s *SubmoduleSuite) TestGitSubmodulesSymlink() {
197 f, err := s.Worktree.Filesystem.Create("badfile")
198 s.NoError(err)
199 defer func() { _ = f.Close() }()
200
201 err = s.Worktree.Filesystem.Remove(gitmodulesFile)
202 s.NoError(err)
203
204 err = s.Worktree.Filesystem.Symlink("badfile", gitmodulesFile)
205 s.NoError(err)
206
207 _, err = s.Worktree.Submodules()
208 s.ErrorIs(err, ErrGitModulesSymlink)
209}
210
211func (s *SubmoduleSuite) TestSubmodulesStatus() {
212 sm, err := s.Worktree.Submodules()
213 s.NoError(err)
214
215 status, err := sm.Status()
216 s.NoError(err)
217 s.Len(status, 2)
218}
219
220func (s *SubmoduleSuite) TestSubmodulesUpdateContext() {
221 if testing.Short() {
222 s.T().Skip("skipping test in short mode.")
223 }
224
225 sm, err := s.Worktree.Submodules()
226 s.NoError(err)
227
228 ctx, cancel := context.WithCancel(context.Background())
229 cancel()
230
231 err = sm.UpdateContext(ctx, &SubmoduleUpdateOptions{Init: true})
232 s.NotNil(err)
233}
234
235func (s *SubmoduleSuite) TestSubmodulesFetchDepth() {
236 if testing.Short() {
237 s.T().Skip("skipping test in short mode.")
238 }
239
240 sm, err := s.Worktree.Submodule("basic")
241 s.NoError(err)
242
243 err = sm.Update(&SubmoduleUpdateOptions{
244 Init: true,
245 Depth: 1,
246 })
247 s.NoError(err)
248
249 r, err := sm.Repository()
250 s.NoError(err)
251
252 lr, err := r.Log(&LogOptions{})
253 s.NoError(err)
254
255 commitCount := 0
256 for _, err := lr.Next(); err == nil; _, err = lr.Next() {
257 commitCount++
258 }
259 s.NoError(err)
260
261 s.Equal(1, commitCount)
262}
263
264func (s *SubmoduleSuite) TestSubmoduleParseScp() {
265 repo := &Repository{
266 Storer: memory.NewStorage(),
267 wt: memfs.New(),
268 }
269 worktree := &Worktree{
270 Filesystem: memfs.New(),
271 r: repo,
272 }
273 submodule := &Submodule{
274 initialized: true,
275 c: nil,
276 w: worktree,
277 }
278
279 submodule.c = &config.Submodule{
280 URL: "git@github.com:username/submodule_repo",
281 }
282
283 _, err := submodule.Repository()
284 s.NoError(err)
285}