1package lfs_test // to avoid import cycles
2
3// This is for doing complete git-level tests using test utils
4// Needs to be a separate file from scanner_test so that we can use a diff package
5// which avoids import cycles with testutils
6
7import (
8	"fmt"
9	"sort"
10	"testing"
11	"time"
12
13	"github.com/git-lfs/git-lfs/v3/config"
14	. "github.com/git-lfs/git-lfs/v3/lfs"
15	test "github.com/git-lfs/git-lfs/v3/t/cmd/util"
16	"github.com/stretchr/testify/assert"
17)
18
19func TestScanUnpushed(t *testing.T) {
20	repo := test.NewRepo(t)
21	repo.Pushd()
22	defer func() {
23		repo.Popd()
24		repo.Cleanup()
25	}()
26
27	inputs := []*test.CommitInput{
28		{ // 0
29			Files: []*test.FileInput{
30				{Filename: "file1.txt", Size: 20},
31			},
32		},
33		{ // 1
34			NewBranch: "branch2",
35			Files: []*test.FileInput{
36				{Filename: "file1.txt", Size: 25},
37			},
38		},
39		{ // 2
40			ParentBranches: []string{"master"}, // back on master
41			Files: []*test.FileInput{
42				{Filename: "file1.txt", Size: 30},
43			},
44		},
45		{ // 3
46			NewBranch: "branch3",
47			Files: []*test.FileInput{
48				{Filename: "file1.txt", Size: 32},
49			},
50		},
51	}
52	repo.AddCommits(inputs)
53
54	// Add a couple of remotes and test state depending on what's pushed
55	repo.AddRemote("origin")
56	repo.AddRemote("upstream")
57
58	pointers, err := scanUnpushed("")
59	assert.Nil(t, err, "Should be no error calling ScanUnpushed")
60	assert.Len(t, pointers, 4, "Should be 4 pointers because none pushed")
61
62	test.RunGitCommand(t, true, "push", "origin", "branch2")
63	// Branch2 will have pushed 2 commits
64	pointers, err = scanUnpushed("")
65	assert.Nil(t, err, "Should be no error calling ScanUnpushed")
66	assert.Len(t, pointers, 2, "Should be 2 pointers")
67
68	test.RunGitCommand(t, true, "push", "upstream", "master")
69	// Master pushes 1 more commit
70	pointers, err = scanUnpushed("")
71	assert.Nil(t, err, "Should be no error calling ScanUnpushed")
72	assert.Len(t, pointers, 1, "Should be 1 pointer")
73
74	test.RunGitCommand(t, true, "push", "origin", "branch3")
75	// All pushed (somewhere)
76	pointers, err = scanUnpushed("")
77	assert.Nil(t, err, "Should be no error calling ScanUnpushed")
78	assert.Empty(t, pointers, "Should be 0 pointers unpushed")
79
80	// Check origin
81	pointers, err = scanUnpushed("origin")
82	assert.Nil(t, err, "Should be no error calling ScanUnpushed")
83	assert.Empty(t, pointers, "Should be 0 pointers unpushed to origin")
84
85	// Check upstream
86	pointers, err = scanUnpushed("upstream")
87	assert.Nil(t, err, "Should be no error calling ScanUnpushed")
88	assert.Len(t, pointers, 2, "Should be 2 pointers unpushed to upstream")
89}
90
91func scanUnpushed(remoteName string) ([]*WrappedPointer, error) {
92	pointers := make([]*WrappedPointer, 0, 10)
93	var multiErr error
94
95	gitscanner := NewGitScanner(config.New(), func(p *WrappedPointer, err error) {
96		if err != nil {
97			if multiErr != nil {
98				multiErr = fmt.Errorf("%v\n%v", multiErr, err)
99			} else {
100				multiErr = err
101			}
102			return
103		}
104
105		pointers = append(pointers, p)
106	})
107
108	if err := gitscanner.ScanUnpushed(remoteName, nil); err != nil {
109		return nil, err
110	}
111
112	gitscanner.Close()
113	return pointers, multiErr
114}
115
116func TestScanPreviousVersions(t *testing.T) {
117	repo := test.NewRepo(t)
118	repo.Pushd()
119	defer func() {
120		repo.Popd()
121		repo.Cleanup()
122	}()
123
124	now := time.Now()
125
126	inputs := []*test.CommitInput{
127		{ // 0
128			CommitDate: now.AddDate(0, 0, -20),
129			Files: []*test.FileInput{
130				{Filename: "file1.txt", Size: 20},
131				{Filename: "file2.txt", Size: 30},
132				{Filename: "folder/nested.txt", Size: 40},
133				{Filename: "folder/nested2.txt", Size: 31},
134			},
135		},
136		{ // 1
137			CommitDate: now.AddDate(0, 0, -10),
138			Files: []*test.FileInput{
139				{Filename: "file2.txt", Size: 22},
140			},
141		},
142		{ // 2
143			NewBranch:  "excluded",
144			CommitDate: now.AddDate(0, 0, -6),
145			Files: []*test.FileInput{
146				{Filename: "file2.txt", Size: 12},
147				{Filename: "folder/nested2.txt", Size: 16},
148			},
149		},
150		{ // 3
151			ParentBranches: []string{"master"},
152			CommitDate:     now.AddDate(0, 0, -4),
153			Files: []*test.FileInput{
154				{Filename: "folder/nested.txt", Size: 42},
155				{Filename: "folder/nested2.txt", Size: 6},
156			},
157		},
158		{ // 4
159			Files: []*test.FileInput{
160				{Filename: "folder/nested.txt", Size: 22},
161			},
162		},
163	}
164	outputs := repo.AddCommits(inputs)
165
166	// Previous commits excludes final state of each file, which is:
167	// file1.txt            [0] (unchanged since first commit so excluded)
168	// file2.txt            [1] (because [2] is on another branch so excluded)
169	// folder/nested.txt    [4] (updated at last commit)
170	// folder/nested2.txt   [3]
171
172	// The only changes which will be included are changes prior to final state
173	// where the '-' side of the diff is inside the date range
174
175	// 7 day limit excludes [0] commit, but includes state from that if there
176	// was a subsequent chang
177	pointers, err := scanPreviousVersions(t, "master", now.AddDate(0, 0, -7))
178	assert.Equal(t, nil, err)
179
180	// Includes the following 'before' state at commits:
181	// folder/nested.txt [-diff at 4, ie 3, -diff at 3 ie 0]
182	// folder/nested2.txt [-diff at 3 ie 0]
183	// others are either on diff branches, before this window, or unchanged
184	expected := []*WrappedPointer{
185		{Name: "folder/nested.txt", Pointer: outputs[3].Files[0]},
186		{Name: "folder/nested.txt", Pointer: outputs[0].Files[2]},
187		{Name: "folder/nested2.txt", Pointer: outputs[0].Files[3]},
188	}
189	// Need to sort to compare equality
190	sort.Sort(test.WrappedPointersByOid(expected))
191	sort.Sort(test.WrappedPointersByOid(pointers))
192	assert.Equal(t, expected, pointers)
193}
194
195func scanPreviousVersions(t *testing.T, ref string, since time.Time) ([]*WrappedPointer, error) {
196	pointers := make([]*WrappedPointer, 0, 10)
197	gitscanner := NewGitScanner(config.New(), func(p *WrappedPointer, err error) {
198		if err != nil {
199			t.Error(err)
200			return
201		}
202		pointers = append(pointers, p)
203	})
204
205	err := gitscanner.ScanPreviousVersions(ref, since, nil)
206	return pointers, err
207}
208