Skip to content

Commit

Permalink
Added missing tests and updated flags reference
Browse files Browse the repository at this point in the history
  • Loading branch information
jkellerer committed Aug 5, 2023
1 parent 03d0822 commit 3e0d095
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 31 deletions.
27 changes: 20 additions & 7 deletions config/info_customizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,31 @@ func init() {
info.mayBool = true
info.examples = []string{"true", "false", fmt.Sprintf(`"%s"`, propertyName)}

suffixDefaultTrueV2 := fmt.Sprintf(` Defaults to true for config version 2 in "%s".`, sectionName)

if propertyName == constants.ParameterHost {
info.format = "hostname"
note = `Boolean true is replaced with the hostname of the system.`
} else {
note = fmt.Sprintf(`Boolean true is replaced with the %ss from section "backup".`, propertyName)
}

if sectionName == constants.CommandBackup {
if propertyName != constants.ParameterHost {
info.examples = info.examples[1:] // remove "true" from examples of backup section
note = fmt.Sprintf(`Boolean true is unsupported in section "backup".`)
} else {
note += suffixDefaultTrueV2
}
} else if sectionName == constants.SectionConfigurationRetention {
if propertyName == constants.ParameterHost {
note = `Boolean true is replaced with the hostname that applies in section "backup".`
}
if propertyName == constants.ParameterPath {
note += ` Defaults to true in "retention".`
} else {
note += suffixDefaultTrueV2
}
} else {
note = fmt.Sprintf(`Boolean true is replaced with the %ss from section "backup".`, propertyName)
}

if propertyName == constants.ParameterHost {
info.format = "hostname"
note = `Boolean true is replaced with the hostname of the system.`
}

if note != "" {
Expand Down
20 changes: 16 additions & 4 deletions config/info_customizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ func TestHostTagPathProperty(t *testing.T) {
note := `Boolean true is replaced with the {{property}}s from section "backup".`
hostNote := `Boolean true is replaced with the hostname of the system.`
backupNote := `Boolean true is unsupported in section "backup".`
retentionHostNote := `Boolean true is replaced with the hostname that applies in section "backup".`
defaultSuffix := ` Defaults to true in "{{section}}".`
defaultSuffixV2 := ` Defaults to true for config version 2 in "{{section}}".`

backup := constants.CommandBackup
retention := constants.SectionConfigurationRetention

tests := []struct {
section, property, note, format string
Expand All @@ -83,14 +89,20 @@ func TestHostTagPathProperty(t *testing.T) {
{section: "any", property: constants.ParameterPath},
{section: "any", property: constants.ParameterTag},

{section: constants.CommandBackup, property: constants.ParameterHost, note: hostNote, format: "hostname"},
{section: constants.CommandBackup, property: constants.ParameterPath, note: backupNote, examples: []string{"false", `"{{property}}"`}},
{section: constants.CommandBackup, property: constants.ParameterTag, note: backupNote, examples: []string{"false", `"{{property}}"`}},
{section: retention, property: constants.ParameterHost, note: retentionHostNote + defaultSuffixV2, format: "hostname"},
{section: retention, property: constants.ParameterPath, note: note + defaultSuffix},
{section: retention, property: constants.ParameterTag, note: note + defaultSuffixV2},

{section: backup, property: constants.ParameterHost, note: hostNote + defaultSuffixV2, format: "hostname"},
{section: backup, property: constants.ParameterPath, note: backupNote, examples: []string{"false", `"{{property}}"`}},
{section: backup, property: constants.ParameterTag, note: backupNote, examples: []string{"false", `"{{property}}"`}},
}
for i, test := range tests {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
propertyReplacer := func(s string) string {
return strings.ReplaceAll(s, "{{property}}", test.property)
s = strings.ReplaceAll(s, "{{property}}", test.property)
s = strings.ReplaceAll(s, "{{section}}", test.section)
return s
}
if test.examples == nil {
test.examples = examples
Expand Down
35 changes: 23 additions & 12 deletions config/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ type BackupSection struct {

func (s *BackupSection) IsEmpty() bool { return s == nil }

func (b *BackupSection) resolve(p *Profile) {
func (b *BackupSection) resolve(profile *Profile) {
// Ensure UseStdin is set when Backup.StdinCommand is defined
if len(b.StdinCommand) > 0 {
b.UseStdin = true
Expand All @@ -188,7 +188,15 @@ func (b *BackupSection) resolve(p *Profile) {
if b.unresolvedSource == nil {
b.unresolvedSource = b.Source
}
b.Source = p.resolveSourcePath(b.SourceBase, b.unresolvedSource...)
b.Source = profile.resolveSourcePath(b.SourceBase, b.unresolvedSource...)

// Extras, only enabled for Version >= 2 (to remain backward compatible in version 1)
if profile.config != nil && profile.config.version >= Version02 {
// Ensure that the host is in sync between backup & retention by setting it if missing
if _, found := b.OtherFlags[constants.ParameterHost]; !found {
b.SetOtherFlag(constants.ParameterHost, true)
}
}
}

func (s *BackupSection) setRootPath(p *Profile, rootPath string) {
Expand All @@ -213,16 +221,12 @@ func (r *RetentionSection) IsEmpty() bool { return r == nil }

func (r *RetentionSection) resolve(profile *Profile) {
// Special cases of retention
if r.OtherFlags == nil {
r.OtherFlags = make(map[string]any)
}

isSet := func(of OtherFlags, name string) (found bool) { _, found = of.GetOtherFlags()[name]; return }
isSet := func(flags OtherFlags, name string) (found bool) { _, found = flags.GetOtherFlags()[name]; return }
hasBackup := !profile.Backup.IsEmpty()

// Copy "source" from "backup" as "path" if it hasn't been redefined
if hasBackup && !isSet(r, constants.ParameterPath) {
r.OtherFlags[constants.ParameterPath] = true
r.SetOtherFlag(constants.ParameterPath, true)
}

// Extras, only enabled for Version >= 2 (to remain backward compatible in version 1)
Expand All @@ -243,17 +247,17 @@ func (r *RetentionSection) resolve(profile *Profile) {
!isSet(r, constants.ParameterTag) &&
isSet(profile.Backup, constants.ParameterTag) {

r.OtherFlags[constants.ParameterTag] = true
r.SetOtherFlag(constants.ParameterTag, true)
}

// Copy "host" from "backup" if it was set and hasn't been redefined here
// Or use os.Hostname() same as restic does for backup when not setting it, see:
// https://github.com/restic/restic/blob/master/cmd/restic/cmd_backup.go#L48
if !isSet(r, constants.ParameterHost) {
if hasBackup && isSet(profile.Backup, constants.ParameterHost) {
r.OtherFlags[constants.ParameterHost] = profile.Backup.OtherFlags[constants.ParameterHost]
r.SetOtherFlag(constants.ParameterHost, profile.Backup.OtherFlags[constants.ParameterHost])
} else if !isSet(profile, constants.ParameterHost) {
r.OtherFlags[constants.ParameterHost] = true // resolved with os.Hostname()
r.SetOtherFlag(constants.ParameterHost, true) // resolved with os.Hostname()
}
}
}
Expand Down Expand Up @@ -453,7 +457,14 @@ type OtherFlagsSection struct {
OtherFlags map[string]any `mapstructure:",remain"`
}

func (o OtherFlagsSection) GetOtherFlags() map[string]any { return o.OtherFlags }
func (o *OtherFlagsSection) GetOtherFlags() map[string]any { return o.OtherFlags }

func (o *OtherFlagsSection) SetOtherFlag(name string, value any) {
if o.OtherFlags == nil {
o.OtherFlags = make(map[string]any)
}
o.OtherFlags[name] = value
}

// NewProfile instantiates a new blank profile
func NewProfile(c *Config, name string) (p *Profile) {
Expand Down
75 changes: 67 additions & 8 deletions config/profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/creativeprojects/resticprofile/restic"
"github.com/creativeprojects/resticprofile/shell"
"github.com/creativeprojects/resticprofile/util"
"github.com/creativeprojects/resticprofile/util/bools"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"
Expand Down Expand Up @@ -580,11 +581,13 @@ func TestPathAndTagInRetention(t *testing.T) {
cwd, err := filepath.Abs(".")
require.NoError(t, err)
examples := filepath.Join(cwd, "../examples")
hostname := "rt-test-host"
sourcePattern := filepath.ToSlash(filepath.Join(examples, "[a-p]*"))
backupSource, err := filepath.Glob(sourcePattern)
require.Greater(t, len(backupSource), 5)
require.NoError(t, err)

backupHost := ""
backupTags := []string{"one", "two"}
flatBackupTags := func() []string { return []string{strings.Join(backupTags, ",")} }

Expand All @@ -594,6 +597,10 @@ func TestPathAndTagInRetention(t *testing.T) {
prefix = "profiles."
}

host := ""
if len(backupHost) > 0 {
host = `host = "` + backupHost + `"`
}
tag := ""
if len(backupTags) > 0 {
tag = `tag = ["` + strings.Join(backupTags, `", "`) + `"]`
Expand All @@ -606,6 +613,7 @@ func TestPathAndTagInRetention(t *testing.T) {
base-dir = "` + filepath.ToSlash(baseDir) + `"
[` + prefix + `profile.backup]
` + tag + `
` + host + `
source = ["` + sourcePattern + `"]
[` + prefix + `profile.retention]
Expand All @@ -615,6 +623,7 @@ func TestPathAndTagInRetention(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, profile)
profile.SetRootPath(examples) // ensure relative paths are converted to absolute paths
profile.SetHost(hostname)

return profile
}
Expand All @@ -623,12 +632,66 @@ func TestPathAndTagInRetention(t *testing.T) {
return testProfileWithBase(t, version, retention, "")
}

t.Run("Path", func(t *testing.T) {
pathFlag := func(t *testing.T, profile *Profile) interface{} {
flagGetter := func(flagName string) func(t *testing.T, profile *Profile) any {
return func(t *testing.T, profile *Profile) any {
flags := profile.GetRetentionFlags().ToMap()
assert.NotNil(t, flags)
return flags["path"]
return flags[flagName]
}
}

t.Run("AutoEnable", func(t *testing.T) {
retentionDisabled := func(t *testing.T, profile *Profile) {
assert.Nil(t, profile.Retention.BeforeBackup)
assert.Nil(t, profile.Retention.AfterBackup)
}
t.Run("EnableForAnyKeepInV2", func(t *testing.T) {
profile := testProfile(t, Version02, ``)
retentionDisabled(t, profile)
profile = testProfile(t, Version02, `keep-x = 1`)
assert.Nil(t, profile.Retention.BeforeBackup)
assert.Equal(t, bools.True(), profile.Retention.AfterBackup)
})
t.Run("NotEnabledInV1", func(t *testing.T) {
profile := testProfile(t, Version01, ``)
retentionDisabled(t, profile)
profile = testProfile(t, Version01, `keep-x = 1`)
retentionDisabled(t, profile)
})
})

t.Run("Host", func(t *testing.T) {
hostFlag := flagGetter(constants.ParameterHost)

t.Run("ImplicitCopyHostFromProfileInV2", func(t *testing.T) {
profile := testProfile(t, Version02, ``)
assert.Equal(t, []string{hostname}, hostFlag(t, profile))
})

t.Run("ImplicitCopyHostFromBackupInV2", func(t *testing.T) {
defer func() { backupHost = "" }()
backupHost = "custom-host-from-backup"

profile := testProfile(t, Version02, ``)
assert.Equal(t, []string{backupHost}, hostFlag(t, profile))
})

t.Run("NoImplicitCopyInV1", func(t *testing.T) {
profile := testProfile(t, Version01, ``)
assert.Nil(t, hostFlag(t, profile))
})

t.Run("ExplicitCopyHostInV1", func(t *testing.T) {
defer func() { backupHost = "" }()
backupHost = "custom-host-from-backup"

profile := testProfile(t, Version01, `host = true`)
assert.Equal(t, []string{hostname}, hostFlag(t, profile))
})
})

t.Run("Path", func(t *testing.T) {
pathFlag := flagGetter(constants.ParameterPath)

t.Run("ImplicitCopyPath", func(t *testing.T) {
profile := testProfile(t, Version01, ``)
Expand Down Expand Up @@ -681,11 +744,7 @@ func TestPathAndTagInRetention(t *testing.T) {
})

t.Run("Tag", func(t *testing.T) {
tagFlag := func(t *testing.T, profile *Profile) interface{} {
flags := profile.GetRetentionFlags().ToMap()
assert.NotNil(t, flags)
return flags["tag"]
}
tagFlag := flagGetter(constants.ParameterTag)

t.Run("NoImplicitCopyTagInV1", func(t *testing.T) {
profile := testProfile(t, Version01, ``)
Expand Down

0 comments on commit 3e0d095

Please sign in to comment.