166 lines
4.9 KiB
Go
166 lines
4.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/grmrgecko/repo-sync/fetch"
|
|
"github.com/grmrgecko/repo-sync/mirror"
|
|
)
|
|
|
|
// summaryFields parses a printed summary back into its fields.
|
|
func summaryFields(t *testing.T, out string) map[string]string {
|
|
t.Helper()
|
|
fields := make(map[string]string)
|
|
for _, line := range strings.Split(strings.TrimSpace(out), "\n") {
|
|
if line == "" {
|
|
continue
|
|
}
|
|
name, value, ok := strings.Cut(line, ": ")
|
|
if !ok {
|
|
t.Fatalf("summary line %q is not a field", line)
|
|
}
|
|
fields[name] = value
|
|
}
|
|
return fields
|
|
}
|
|
|
|
// TestPrintSummary verifies the printed summary reports what a calling script
|
|
// keys off, and that a dry run never reports a change.
|
|
func TestPrintSummary(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
summary *mirror.Summary
|
|
want map[string]string
|
|
absent []string
|
|
}{
|
|
{
|
|
name: "a run that transferred files reports a change",
|
|
summary: &mirror.Summary{
|
|
Repositories: 2,
|
|
Fetched: 7,
|
|
FetchedBytes: 4096,
|
|
Unchanged: 31,
|
|
Pruned: 2,
|
|
},
|
|
want: map[string]string{
|
|
"Number of repositories": "2",
|
|
"Number of failed repositories": "0",
|
|
"Number of files transferred": "7",
|
|
"Number of files pruned": "2",
|
|
"Total transferred file size": "4096 bytes",
|
|
"Number of files unchanged": "31",
|
|
"Repository changed": "true",
|
|
},
|
|
absent: []string{"Dry run", "Number of files to transfer"},
|
|
},
|
|
{
|
|
name: "an idle run reports no change",
|
|
summary: &mirror.Summary{Repositories: 1, Unchanged: 12},
|
|
want: map[string]string{
|
|
"Number of files transferred": "0",
|
|
"Number of files pruned": "0",
|
|
"Repository changed": "false",
|
|
},
|
|
},
|
|
{
|
|
name: "pruning alone is a change",
|
|
summary: &mirror.Summary{Repositories: 1, Pruned: 1},
|
|
want: map[string]string{
|
|
"Number of files pruned": "1",
|
|
"Repository changed": "true",
|
|
},
|
|
},
|
|
{
|
|
name: "a failed run still reports its totals",
|
|
summary: &mirror.Summary{Repositories: 3, Failed: 1, Fetched: 4},
|
|
want: map[string]string{
|
|
"Number of repositories": "3",
|
|
"Number of failed repositories": "1",
|
|
"Repository changed": "true",
|
|
},
|
|
},
|
|
{
|
|
name: "a dry run reports the work it planned but no change",
|
|
summary: &mirror.Summary{
|
|
Repositories: 1,
|
|
DryRun: true,
|
|
Fetched: 4,
|
|
Planned: 9,
|
|
PlannedBytes: 8192,
|
|
},
|
|
want: map[string]string{
|
|
"Dry run": "true",
|
|
"Number of files to transfer": "9",
|
|
"Total file size to transfer": "8192 bytes",
|
|
"Repository changed": "false",
|
|
},
|
|
// The metadata a dry run fetches must never be reported as
|
|
// files it transferred.
|
|
absent: []string{"Number of files transferred", "Number of files pruned"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var out bytes.Buffer
|
|
printSummary(&out, tc.summary)
|
|
got := summaryFields(t, out.String())
|
|
for name, want := range tc.want {
|
|
if got[name] != want {
|
|
t.Errorf("%s = %q, want %q", name, got[name], want)
|
|
}
|
|
}
|
|
for _, name := range tc.absent {
|
|
if _, ok := got[name]; ok {
|
|
t.Errorf("%s was reported when it does not apply", name)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestPrintSummaryNone verifies a run with no summary to report prints
|
|
// nothing, rather than an empty set of fields a script would misread.
|
|
func TestPrintSummaryNone(t *testing.T) {
|
|
var out bytes.Buffer
|
|
printSummary(&out, nil)
|
|
if out.Len() != 0 {
|
|
t.Errorf("printed %q without a summary", out.String())
|
|
}
|
|
}
|
|
|
|
// TestSyncArgsMissing verifies missing file handling falls back to the
|
|
// crawler configuration and that the flags override it.
|
|
func TestSyncArgsMissing(t *testing.T) {
|
|
dest := t.TempDir()
|
|
|
|
// An unset flag pair takes the configured defaults, which is what a run
|
|
// without a config file gets from the built-in ones.
|
|
args := SyncArgs{Args: []string{"https://example.com/repo", dest}, MissingRetries: -1}
|
|
opts, err := args.options(nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if opts.Missing != fetch.MissingRetry || opts.MissingRetries != 3 {
|
|
t.Errorf("missing = %q after %d runs, want retry after 3", opts.Missing, opts.MissingRetries)
|
|
}
|
|
|
|
// Explicit flags win, including a zero retry budget, which accepts an
|
|
// absence the first time it is seen.
|
|
args = SyncArgs{Args: []string{"https://example.com/repo", dest}, Missing: "ignore", MissingRetries: 0}
|
|
opts, err = args.options(nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if opts.Missing != fetch.MissingIgnore || opts.MissingRetries != 0 {
|
|
t.Errorf("missing = %q after %d runs, want ignore after 0", opts.Missing, opts.MissingRetries)
|
|
}
|
|
|
|
// An unsupported mode is rejected rather than silently ignored.
|
|
args = SyncArgs{Args: []string{"https://example.com/repo", dest}, Missing: "skip", MissingRetries: -1}
|
|
if _, err := args.options(nil); err == nil {
|
|
t.Error("an unsupported missing mode was accepted")
|
|
}
|
|
}
|