package cli //nolint:testpackage // shares runEntry and the hermetic config helpers import ( "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // flagCron is used to build the argument vectors below. const flagCron = "--cron" // plantUnreferencedBlob writes a blob that no snapshot references to the // destination store at storeDir, so that a prune deletes it. Returns its // path. func plantUnreferencedBlob(t *testing.T, storeDir string) string { t.Helper() hash := strings.Repeat("b", 64) path := filepath.Join(storeDir, "blobs", hash[:2], hash[2:4], hash) require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o750)) require.NoError(t, os.WriteFile(path, []byte("unreferenced"), 0o600)) return path } // TestEntrySnapshotCreatePruneIsSilent checks that a successful // `snapshot create --prune` under --cron or --quiet prints nothing on // stdout or stderr, whether or not the prune finds a blob to delete. // //nolint:paralleltest // replaces os.Args, os.Stdout, os.Stderr and the xdg globals func TestEntrySnapshotCreatePruneIsSilent(t *testing.T) { for _, testCase := range []struct { name string args []string plantBlob bool }{ { name: "cron, no blob to delete", args: []string{cmdSnapshot, cmdCreate, flagCron, flagPrune}, }, { name: "cron, blob to delete", args: []string{cmdSnapshot, cmdCreate, flagCron, flagPrune}, plantBlob: true, }, { name: "cron with keep-newer-than", args: []string{ cmdSnapshot, cmdCreate, flagCron, flagPrune, flagKeepNewerThan, "4w", }, plantBlob: true, }, { name: "quiet", args: []string{flagQuiet, cmdSnapshot, cmdCreate, flagPrune}, plantBlob: true, }, } { t.Run(testCase.name, func(t *testing.T) { configPath, storeDir := writeBackupConfig(t) var blob string if testCase.plantBlob { blob = plantUnreferencedBlob(t, storeDir) } code, stdout, stderr := runEntry(t, append([]string{flagConfig, configPath}, testCase.args...)...) assert.Equal(t, 0, code) assert.Empty(t, stdout) assert.Empty(t, stderr) if testCase.plantBlob { assert.NoFileExists(t, blob, "the prune must delete the blob") } }) } } // TestEntryPruneQuietIsSilent checks that a successful `prune --force` // under --quiet prints nothing on stdout or stderr, both when there is // nothing to remove and when it removes a stale local snapshot record // and an unreferenced blob. // //nolint:paralleltest // replaces os.Args, os.Stdout, os.Stderr and the xdg globals func TestEntryPruneQuietIsSilent(t *testing.T) { for _, testCase := range []struct { name string staleRecordAndBlob bool }{ {name: "nothing to remove"}, {name: "stale record and blob to remove", staleRecordAndBlob: true}, } { t.Run(testCase.name, func(t *testing.T) { configPath := writeHermeticPruneConfig(t, testCase.staleRecordAndBlob) var blob string if testCase.staleRecordAndBlob { // writeHermeticPruneConfig puts the store beside the config. blob = plantUnreferencedBlob(t, filepath.Join(filepath.Dir(configPath), "store")) } code, stdout, stderr := runEntry(t, flagConfig, configPath, flagQuiet, cmdPrune, flagForce) assert.Equal(t, 0, code) assert.Empty(t, stdout) assert.Empty(t, stderr) if testCase.staleRecordAndBlob { assert.NoFileExists(t, blob, "the prune must delete the blob") } }) } }