Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ linters:
- errcheck
- forbidigo
settings:
dupl:
threshold: 200
depguard:
rules:
main:
Expand Down
4 changes: 2 additions & 2 deletions backup/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ func BuildRootCommandWithDeps(fs afero.Fs, shell internal.Exec) *cobra.Command {

rootCmd.AddCommand(
buildListCommand(shell),
buildRunCommand(shell),
buildSimulateCommand(shell),
buildRunCommand(fs, shell),
buildSimulateCommand(fs, shell),
buildConfigCommand(),
buildCheckCoverageCommand(fs),
buildVersionCommand(shell),
Expand Down
5 changes: 3 additions & 2 deletions backup/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import (
"fmt"
"time"

"github.com/spf13/afero"
"github.com/spf13/cobra"
)

func buildRunCommand(shell internal.Exec) *cobra.Command {
func buildRunCommand(fs afero.Fs, shell internal.Exec) *cobra.Command {
return &cobra.Command{
Use: "run",
Short: "Execute the sync jobs",
Expand All @@ -21,7 +22,7 @@ func buildRunCommand(shell internal.Exec) *cobra.Command {
return fmt.Errorf("loading config: %w", err)
}

logger, logPath, cleanup, err := internal.CreateMainLogger(configPath, false, time.Now())
logger, logPath, cleanup, err := internal.CreateMainLogger(fs, configPath, false, time.Now())
if err != nil {
return fmt.Errorf("creating logger: %w", err)
}
Expand Down
5 changes: 3 additions & 2 deletions backup/cmd/simulate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import (
"fmt"
"time"

"github.com/spf13/afero"
"github.com/spf13/cobra"
)

func buildSimulateCommand(shell internal.Exec) *cobra.Command {
func buildSimulateCommand(fs afero.Fs, shell internal.Exec) *cobra.Command {
return &cobra.Command{
Use: "simulate",
Short: "Simulate the sync jobs",
Expand All @@ -21,7 +22,7 @@ func buildSimulateCommand(shell internal.Exec) *cobra.Command {
return fmt.Errorf("loading config: %w", err)
}

logger, logPath, cleanup, err := internal.CreateMainLogger(configPath, true, time.Now())
logger, logPath, cleanup, err := internal.CreateMainLogger(fs, configPath, true, time.Now())
if err != nil {
return fmt.Errorf("creating logger: %w", err)
}
Expand Down
17 changes: 6 additions & 11 deletions backup/cmd/test/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,12 @@ jobs:
target: "/backup/docs/"
`)

// Block log directory creation by placing a file named "logs"
tmpDir := t.TempDir()
t.Chdir(tmpDir)

require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "logs"), []byte("block"), 0600))
// Use a read-only filesystem to block log directory creation
fs := afero.NewReadOnlyFs(afero.NewMemMapFs())

shell := &stubExec{output: []byte("rsync version 3.2.7 protocol version 31\n")}

_, err := executeCommandWithDeps(t, afero.NewMemMapFs(), shell, "run", "--config", cfgPath)
_, err := executeCommandWithDeps(t, fs, shell, "run", "--config", cfgPath)

require.Error(t, err)
assert.Contains(t, err.Error(), "creating logger")
Expand Down Expand Up @@ -246,14 +243,12 @@ jobs:
target: "/backup/docs/"
`)

tmpDir := t.TempDir()
t.Chdir(tmpDir)

require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "logs"), []byte("block"), 0600))
// Use a read-only filesystem to block log directory creation
fs := afero.NewReadOnlyFs(afero.NewMemMapFs())

shell := &stubExec{output: []byte("rsync version 3.2.7 protocol version 31\n")}

_, err := executeCommandWithDeps(t, afero.NewMemMapFs(), shell, "simulate", "--config", cfgPath)
_, err := executeCommandWithDeps(t, fs, shell, "simulate", "--config", cfgPath)

require.Error(t, err)
assert.Contains(t, err.Error(), "creating logger")
Expand Down
10 changes: 7 additions & 3 deletions backup/internal/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"path/filepath"
"strings"
"time"

"github.com/spf13/afero"
)

// Path represents a source or target path with optional exclusions.
Expand Down Expand Up @@ -35,16 +37,18 @@ func getLogPath(simulate bool, configPath string, now time.Time) string {
return logPath
}

func CreateMainLogger(configPath string, simulate bool, now time.Time) (*log.Logger, string, func() error, error) {
func CreateMainLogger(
fs afero.Fs, configPath string, simulate bool, now time.Time,
) (*log.Logger, string, func() error, error) {
logPath := getLogPath(simulate, configPath, now)
overallLogPath := logPath + "/summary.log"

err := os.MkdirAll(logPath, LogDirPermission)
err := fs.MkdirAll(logPath, LogDirPermission)
if err != nil {
return nil, "", nil, fmt.Errorf("failed to create log directory: %w", err)
}

overallLogFile, err := os.OpenFile(overallLogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, LogFilePermission)
overallLogFile, err := fs.OpenFile(overallLogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, LogFilePermission)
if err != nil {
return nil, "", nil, fmt.Errorf("failed to open overall log file: %w", err)
}
Expand Down
36 changes: 12 additions & 24 deletions backup/internal/test/helper_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package internal_test

import (
"os"
"testing"
"time"

. "backup-rsync/backup/internal"

"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -33,7 +33,7 @@ func fixedTime() time.Time {
}

func TestCreateMainLogger_Title_IsPresent(t *testing.T) {
logger, logPath, cleanup, err := CreateMainLogger("title", true, fixedTime())
logger, logPath, cleanup, err := CreateMainLogger(afero.NewMemMapFs(), "title", true, fixedTime())
require.NoError(t, err)

defer cleanup()
Expand All @@ -43,7 +43,7 @@ func TestCreateMainLogger_Title_IsPresent(t *testing.T) {
}

func TestCreateMainLogger_IsSimulate_HasSimSuffix(t *testing.T) {
logger, logPath, cleanup, err := CreateMainLogger("", true, fixedTime())
logger, logPath, cleanup, err := CreateMainLogger(afero.NewMemMapFs(), "", true, fixedTime())
require.NoError(t, err)

defer cleanup()
Expand All @@ -53,7 +53,7 @@ func TestCreateMainLogger_IsSimulate_HasSimSuffix(t *testing.T) {
}

func TestCreateMainLogger_NotSimulate_HasNoSimSuffix(t *testing.T) {
logger, logPath, cleanup, err := CreateMainLogger("", false, fixedTime())
logger, logPath, cleanup, err := CreateMainLogger(afero.NewMemMapFs(), "", false, fixedTime())
require.NoError(t, err)

defer cleanup()
Expand All @@ -63,7 +63,7 @@ func TestCreateMainLogger_NotSimulate_HasNoSimSuffix(t *testing.T) {
}

func TestCreateMainLogger_DeterministicLogPath(t *testing.T) {
_, logPath, cleanup, err := CreateMainLogger("backup.yaml", true, fixedTime())
_, logPath, cleanup, err := CreateMainLogger(afero.NewMemMapFs(), "backup.yaml", true, fixedTime())
require.NoError(t, err)

defer cleanup()
Expand All @@ -72,7 +72,7 @@ func TestCreateMainLogger_DeterministicLogPath(t *testing.T) {
}

func TestCreateMainLogger_DeterministicLogPath_NoSimulate(t *testing.T) {
_, logPath, cleanup, err := CreateMainLogger("sync.yaml", false, fixedTime())
_, logPath, cleanup, err := CreateMainLogger(afero.NewMemMapFs(), "sync.yaml", false, fixedTime())
require.NoError(t, err)

defer cleanup()
Expand All @@ -81,34 +81,22 @@ func TestCreateMainLogger_DeterministicLogPath_NoSimulate(t *testing.T) {
}

func TestCreateMainLogger_MkdirError(t *testing.T) {
// Use t.Chdir to a temp dir so we control the filesystem
tmpDir := t.TempDir()
t.Chdir(tmpDir)
// Use a read-only filesystem to block directory creation
fs := afero.NewReadOnlyFs(afero.NewMemMapFs())

// Create "logs" as a regular file to block MkdirAll
err := os.WriteFile("logs", []byte("block"), 0600)
require.NoError(t, err)

_, _, cleanup, err := CreateMainLogger("test.yaml", false, fixedTime())
_, _, cleanup, err := CreateMainLogger(fs, "test.yaml", false, fixedTime())
_ = cleanup

require.Error(t, err)
assert.Contains(t, err.Error(), "failed to create log directory")
}

func TestCreateMainLogger_OpenFileError(t *testing.T) {
tmpDir := t.TempDir()
t.Chdir(tmpDir)

// Pre-create the log path directory and make summary.log a directory to block OpenFile
logDir := "logs/sync-2025-06-15T14-30-45-test"
fs := afero.NewReadOnlyFs(afero.NewMemMapFs())

err := os.MkdirAll(logDir+"/summary.log", 0750)
require.NoError(t, err)

_, _, cleanup, err := CreateMainLogger("test.yaml", false, fixedTime())
_, _, cleanup, err := CreateMainLogger(fs, "test.yaml", false, fixedTime())
_ = cleanup

require.Error(t, err)
assert.Contains(t, err.Error(), "failed to open overall log file")
assert.Contains(t, err.Error(), "failed to create log directory")
}
Loading