mirror of
https://github.com/onyx-and-iris/exclude.git
synced 2026-04-16 06:03:39 +00:00
simplify del/reset commands by using readWriteTruncater interface.
remove the type assertions update the tests: the tests now check output as well as file contents separately.
This commit is contained in:
@@ -7,43 +7,50 @@ import (
|
||||
|
||||
func TestRunAddCommand(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
existing string
|
||||
args []string
|
||||
expectedOutput string
|
||||
name string
|
||||
existing string
|
||||
args []string
|
||||
expectedOut string
|
||||
expectedContent string
|
||||
}{
|
||||
{
|
||||
name: "Add new patterns",
|
||||
existing: "",
|
||||
args: []string{"*.log", "temp/"},
|
||||
expectedOutput: "Added pattern '*.log' to the exclude file.\nAdded pattern 'temp/' to the exclude file.\n",
|
||||
name: "Add new patterns",
|
||||
existing: "",
|
||||
args: []string{"*.log", "temp/"},
|
||||
expectedOut: "Added pattern '*.log' to the exclude file.\nAdded pattern 'temp/' to the exclude file.\n",
|
||||
expectedContent: "*.log\ntemp/\n",
|
||||
},
|
||||
{
|
||||
name: "Add duplicate patterns",
|
||||
existing: "*.log\ntemp/\n",
|
||||
args: []string{"*.log", "temp/"},
|
||||
expectedOutput: "Pattern '*.log' already exists in the exclude file. Skipping.\nPattern 'temp/' already exists in the exclude file. Skipping.\n",
|
||||
name: "Add duplicate patterns",
|
||||
existing: "*.log\ntemp/\n",
|
||||
args: []string{"*.log", "temp/"},
|
||||
expectedOut: "Pattern '*.log' already exists in the exclude file. Skipping.\nPattern 'temp/' already exists in the exclude file. Skipping.\n",
|
||||
expectedContent: "",
|
||||
},
|
||||
{
|
||||
name: "Add mix of new and duplicate patterns",
|
||||
existing: "*.log\n",
|
||||
args: []string{"*.log", "temp/"},
|
||||
expectedOutput: "Pattern '*.log' already exists in the exclude file. Skipping.\nAdded pattern 'temp/' to the exclude file.\n",
|
||||
name: "Add mix of new and duplicate patterns",
|
||||
existing: "*.log\n",
|
||||
args: []string{"*.log", "temp/"},
|
||||
expectedOut: "Pattern '*.log' already exists in the exclude file. Skipping.\nAdded pattern 'temp/' to the exclude file.\n",
|
||||
expectedContent: "temp/\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
var out bytes.Buffer
|
||||
f := bytes.NewBufferString(tt.existing)
|
||||
|
||||
err := runAddCommand(&buf, f, tt.args)
|
||||
err := runAddCommand(&out, f, tt.args)
|
||||
if err != nil {
|
||||
t.Fatalf("runAddCommand returned an error: %v", err)
|
||||
}
|
||||
|
||||
if buf.String() != tt.expectedOutput {
|
||||
t.Errorf("Expected output:\n%s\nGot:\n%s", tt.expectedOutput, buf.String())
|
||||
if out.String() != tt.expectedOut {
|
||||
t.Errorf("Expected output:\n%s\nGot:\n%s", tt.expectedOut, out.String())
|
||||
}
|
||||
if f.String() != tt.expectedContent {
|
||||
t.Errorf("Expected file content:\n%s\nGot:\n%s", tt.expectedContent, f.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
34
cmd/del.go
34
cmd/del.go
@@ -32,13 +32,8 @@ func init() {
|
||||
}
|
||||
|
||||
// runDelCommand deletes the specified pattern from the exclude file and writes the updated content back
|
||||
// It handles both file and in-memory buffer cases for testing
|
||||
func runDelCommand(out io.Writer, f any, pattern string) error {
|
||||
r, ok := f.(io.Reader)
|
||||
if !ok {
|
||||
return fmt.Errorf("provided file does not support Reader")
|
||||
}
|
||||
existingPatterns, err := readExistingPatterns(r)
|
||||
func runDelCommand(out io.Writer, f readWriteTruncater, pattern string) error {
|
||||
existingPatterns, err := readExistingPatterns(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading existing patterns: %v", err)
|
||||
}
|
||||
@@ -55,29 +50,18 @@ func runDelCommand(out io.Writer, f any, pattern string) error {
|
||||
}
|
||||
}
|
||||
|
||||
var w io.Writer
|
||||
if t, ok := f.(truncater); ok {
|
||||
if err := t.Truncate(0); err != nil {
|
||||
return fmt.Errorf("error truncating exclude file: %w", err)
|
||||
}
|
||||
if s, ok := f.(io.Seeker); ok {
|
||||
if _, err := s.Seek(0, 0); err != nil {
|
||||
return fmt.Errorf("error seeking to the beginning of exclude file: %w", err)
|
||||
}
|
||||
}
|
||||
w, _ = f.(io.Writer)
|
||||
} else if buf, ok := f.(interface{ Reset() }); ok {
|
||||
buf.Reset()
|
||||
w, _ = f.(io.Writer)
|
||||
} else {
|
||||
return fmt.Errorf("provided file does not support writing")
|
||||
if err := f.Truncate(0); err != nil {
|
||||
return fmt.Errorf("error truncating exclude file: %w", err)
|
||||
}
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return fmt.Errorf("error seeking to the beginning of exclude file: %w", err)
|
||||
}
|
||||
|
||||
if err := writeDefaultExcludeContent(w); err != nil {
|
||||
if err := writeDefaultExcludeContent(f); err != nil {
|
||||
return fmt.Errorf("error writing default exclude content: %w", err)
|
||||
}
|
||||
for _, p := range updatedPatterns {
|
||||
if _, err := fmt.Fprintln(w, p); err != nil {
|
||||
if _, err := fmt.Fprintln(f, p); err != nil {
|
||||
return fmt.Errorf("error writing updated patterns to exclude file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,40 +10,43 @@ func TestRunDelCommand(t *testing.T) {
|
||||
name string
|
||||
initialContent string
|
||||
patternToDelete string
|
||||
expectedOutput string
|
||||
expectedOut string
|
||||
expectedContent string
|
||||
}{
|
||||
{
|
||||
name: "Delete existing pattern",
|
||||
initialContent: defaultExcludeFileContent + "node_modules\n.DS_Store\n",
|
||||
patternToDelete: "node_modules",
|
||||
expectedOutput: defaultExcludeFileContent + ".DS_Store\n" + "Deleted pattern 'node_modules' from the exclude file.\n",
|
||||
expectedOut: "Deleted pattern 'node_modules' from the exclude file.\n",
|
||||
expectedContent: defaultExcludeFileContent + ".DS_Store\n",
|
||||
},
|
||||
{
|
||||
name: "Delete non-existing pattern",
|
||||
initialContent: defaultExcludeFileContent + "node_modules\n.DS_Store\n",
|
||||
patternToDelete: "dist",
|
||||
expectedOutput: "Pattern 'dist' not found in the exclude file. Nothing to delete.\n",
|
||||
expectedOut: "Pattern 'dist' not found in the exclude file. Nothing to delete.\n",
|
||||
expectedContent: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(tt.initialContent)
|
||||
var out bytes.Buffer
|
||||
var f seekBuffer
|
||||
f.WriteString(tt.initialContent)
|
||||
|
||||
err := runDelCommand(&buf, &buf, tt.patternToDelete)
|
||||
err := runDelCommand(&out, &f, tt.patternToDelete)
|
||||
if err != nil {
|
||||
t.Fatalf("runDelCommand returned an error: %v", err)
|
||||
}
|
||||
|
||||
if buf.String() != tt.expectedOutput {
|
||||
t.Errorf(
|
||||
"Expected output and content:\n%s\nGot:\n%s",
|
||||
tt.expectedOutput,
|
||||
buf.String(),
|
||||
)
|
||||
if out.String() != tt.expectedOut {
|
||||
t.Errorf("Expected output:\n%s\nGot:\n%s", tt.expectedOut, out.String())
|
||||
}
|
||||
if f.String() != tt.expectedContent {
|
||||
t.Errorf("Expected file content:\n%s\nGot:\n%s", tt.expectedContent, f.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
26
cmd/reset.go
26
cmd/reset.go
@@ -27,33 +27,15 @@ func init() {
|
||||
RootCmd.AddCommand(resetCmd)
|
||||
}
|
||||
|
||||
// Truncate and seek to beginning
|
||||
type truncater interface{ Truncate(size int64) error }
|
||||
|
||||
// resetAndWriteExcludeFile truncates and resets the file, then writes the default content
|
||||
func resetAndWriteExcludeFile(out io.Writer, f any) error {
|
||||
// Try to assert to io.ReadWriteSeeker for file operations
|
||||
rws, ok := f.(io.ReadWriteSeeker)
|
||||
if !ok {
|
||||
// If not a file, try as io.Writer (for test buffers)
|
||||
if w, ok := f.(io.Writer); ok {
|
||||
return writeDefaultExcludeContent(w)
|
||||
}
|
||||
return fmt.Errorf("provided file does not support ReadWriteSeeker or Writer")
|
||||
}
|
||||
|
||||
t, ok := f.(truncater)
|
||||
if !ok {
|
||||
return fmt.Errorf("provided file does not support Truncate")
|
||||
}
|
||||
if err := t.Truncate(0); err != nil {
|
||||
func resetAndWriteExcludeFile(out io.Writer, f readWriteTruncater) error {
|
||||
if err := f.Truncate(0); err != nil {
|
||||
return fmt.Errorf("error truncating exclude file: %w", err)
|
||||
}
|
||||
if _, err := rws.Seek(0, 0); err != nil {
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return fmt.Errorf("error seeking to the beginning of exclude file: %w", err)
|
||||
}
|
||||
err := writeDefaultExcludeContent(rws)
|
||||
if err != nil {
|
||||
if err := writeDefaultExcludeContent(f); err != nil {
|
||||
return fmt.Errorf("error writing default exclude content: %w", err)
|
||||
}
|
||||
fmt.Fprintf(out, "Exclude file reset successfully.\n")
|
||||
|
||||
@@ -2,27 +2,23 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunResetCommand(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
var out bytes.Buffer
|
||||
var f seekBuffer
|
||||
|
||||
if err := resetAndWriteExcludeFile(&buf, &buf); err != nil {
|
||||
if err := resetAndWriteExcludeFile(&out, &f); err != nil {
|
||||
t.Fatalf("resetAndWriteExcludeFile failed: %v", err)
|
||||
}
|
||||
|
||||
resetContent, err := io.ReadAll(&buf)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read from temp file: %v", err)
|
||||
}
|
||||
|
||||
if string(resetContent) != defaultExcludeFileContent {
|
||||
if f.String() != defaultExcludeFileContent {
|
||||
t.Errorf(
|
||||
"unexpected content after reset:\nGot:\n%s\nExpected:\n%s",
|
||||
string(resetContent),
|
||||
f.String(),
|
||||
defaultExcludeFileContent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
cmd/testhelpers_test.go
Normal file
18
cmd/testhelpers_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package cmd
|
||||
|
||||
import "bytes"
|
||||
|
||||
// seekBuffer wraps bytes.Buffer to satisfy the readWriteTruncater interface,
|
||||
// allowing it to be used in place of *os.File in tests.
|
||||
type seekBuffer struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (s *seekBuffer) Seek(offset int64, whence int) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (s *seekBuffer) Truncate(size int64) error {
|
||||
s.Buffer.Reset()
|
||||
return nil
|
||||
}
|
||||
@@ -7,6 +7,13 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// readWriteTruncater is the interface satisfied by *os.File that allows
|
||||
// reading, writing, seeking, and truncating — the operations needed to rewrite the exclude file.
|
||||
type readWriteTruncater interface {
|
||||
io.ReadWriteSeeker
|
||||
Truncate(size int64) error
|
||||
}
|
||||
|
||||
// readExistingPatterns reads the existing patterns from the exclude file, ignoring comments and empty lines
|
||||
func readExistingPatterns(f io.Reader) ([]string, error) {
|
||||
var patterns []string
|
||||
|
||||
Reference in New Issue
Block a user