github.com/wolfi-dev/wolfictl@v0.16.11/pkg/configs/rwfs/os/tester/tester.go (about)

     1  package tester
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/fs"
     9  	"os"
    10  	"path/filepath"
    11  	"sort"
    12  	"strings"
    13  
    14  	"github.com/google/go-cmp/cmp"
    15  	"github.com/samber/lo"
    16  	"github.com/wolfi-dev/wolfictl/pkg/configs/rwfs"
    17  )
    18  
    19  const expectedSuffix = "_expected"
    20  const specialFileContentForSkippingDiff = "# skip"
    21  
    22  var expectedSuffixWithYAML = expectedSuffix + ".yaml"
    23  
    24  var _ rwfs.FS = (*FS)(nil)
    25  
    26  type FS struct {
    27  	rootDir  string
    28  	fixtures map[string]*testFile
    29  }
    30  
    31  func NewFSWithRoot(root string, fixtures ...string) (*FS, error) {
    32  	realDirFS := os.DirFS(root)
    33  	testerFS := new(FS)
    34  	testerFS.rootDir = root
    35  
    36  	testerFS.addDir(".")
    37  
    38  	for _, f := range fixtures {
    39  		stat, err := fs.Stat(realDirFS, f)
    40  		if err != nil {
    41  			return nil, fmt.Errorf("unable to stat file %q: %w", f, err)
    42  		}
    43  
    44  		if stat.IsDir() {
    45  			err := fs.WalkDir(realDirFS, f, func(path string, d fs.DirEntry, err error) error {
    46  				if err != nil {
    47  					return err
    48  				}
    49  
    50  				if d.Type().IsDir() {
    51  					testerFS.addDir(path)
    52  					return nil
    53  				}
    54  
    55  				if d.Type().IsRegular() {
    56  					if strings.HasSuffix(path, expectedSuffixWithYAML) {
    57  						// this is a special file for this tester.FS! Skip.
    58  						return nil
    59  					}
    60  
    61  					err := testerFS.addFixtureFileFromOS(path)
    62  					if err != nil {
    63  						return fmt.Errorf("unable to create new tester.FS: %w", err)
    64  					}
    65  				}
    66  
    67  				return nil
    68  			})
    69  			if err != nil {
    70  				return nil, fmt.Errorf("unable to walk fixture directory %q: %w", f, err)
    71  			}
    72  
    73  			continue
    74  		}
    75  
    76  		err = testerFS.addFixtureFileFromOS(f)
    77  		if err != nil {
    78  			return nil, fmt.Errorf("unable to add fixture file %q to new tester.FS: %w", f, err)
    79  		}
    80  	}
    81  
    82  	return testerFS, nil
    83  }
    84  
    85  func NewFS(fixtures ...string) (*FS, error) {
    86  	return NewFSWithRoot(".", fixtures...)
    87  }
    88  
    89  func expectedName(original string) string {
    90  	dir, file := filepath.Split(original)
    91  	parts := strings.SplitN(file, ".", 2)
    92  
    93  	expectedFile := strings.Join([]string{parts[0] + expectedSuffix, parts[1]}, ".")
    94  	return filepath.Join(dir, expectedFile)
    95  }
    96  
    97  func (fsys *FS) Create(name string) (rwfs.File, error) {
    98  	tf := new(testFile)
    99  	tf.path = name
   100  
   101  	err := tf.loadExpected(fsys)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	tf.writtenBack = new(bytes.Buffer)
   107  
   108  	fsys.addTestFile(name, tf)
   109  
   110  	return tf, nil
   111  }
   112  
   113  func (fsys *FS) Open(name string) (fs.File, error) {
   114  	if f, ok := fsys.fixtures[name]; ok {
   115  		return f, nil
   116  	}
   117  
   118  	return nil, os.ErrNotExist
   119  }
   120  
   121  func (fsys *FS) OpenAsWritable(name string) (rwfs.File, error) {
   122  	if f, ok := fsys.fixtures[name]; ok {
   123  		return f, nil
   124  	}
   125  
   126  	return nil, os.ErrNotExist
   127  }
   128  
   129  func (fsys *FS) Truncate(string, int64) error {
   130  	// TODO: decide if there's a reason for anything but a no-op
   131  	return nil
   132  }
   133  
   134  func (fsys *FS) Diff(name string) string {
   135  	if tf, ok := fsys.fixtures[name]; ok {
   136  		want := tf.expectedRead
   137  		got := tf.writtenBack
   138  
   139  		if want.String() == specialFileContentForSkippingDiff {
   140  			return ""
   141  		}
   142  
   143  		diff := cmp.Diff(want.Bytes(), got.Bytes())
   144  
   145  		if diff == "" {
   146  			return ""
   147  		}
   148  
   149  		return fmt.Sprintf(
   150  			"unexpected result (-want, +got):\n%s\n",
   151  			diff,
   152  		)
   153  	}
   154  
   155  	return fmt.Sprintf("unable to find test file %q in tester.FS", name)
   156  }
   157  
   158  func (fsys *FS) DiffAll() string {
   159  	fixtureFiles := lo.Keys(fsys.fixtures)
   160  	sort.Strings(fixtureFiles)
   161  
   162  	var result string
   163  	for _, ff := range fixtureFiles {
   164  		if fsys.fixtures[ff].isDir {
   165  			continue
   166  		}
   167  
   168  		if diff := fsys.Diff(ff); diff != "" {
   169  			result += fmt.Sprintf("\ndiff found for %q:\n", ff)
   170  			result += diff
   171  		}
   172  	}
   173  
   174  	return result
   175  }
   176  
   177  func (fsys *FS) addTestFile(path string, tf *testFile) {
   178  	if fsys.fixtures == nil {
   179  		fsys.fixtures = make(map[string]*testFile)
   180  	}
   181  
   182  	fsys.fixtures[path] = tf
   183  }
   184  
   185  func (fsys *FS) addFixtureFileFromOS(path string) error {
   186  	tf := new(testFile)
   187  	tf.path = path
   188  
   189  	err := tf.loadOriginal(fsys)
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	err = tf.loadExpected(fsys)
   195  	if err != nil {
   196  		return err
   197  	}
   198  
   199  	tf.writtenBack = new(bytes.Buffer)
   200  
   201  	fsys.addTestFile(path, tf)
   202  
   203  	return nil
   204  }
   205  
   206  func (fsys *FS) readPath(path string) ([]byte, error) {
   207  	effectivePath := filepath.Join(fsys.rootDir, path)
   208  	return os.ReadFile(effectivePath)
   209  }
   210  
   211  func (fsys *FS) addDir(path string) {
   212  	// For dirs, we'll punt to the real os FS.
   213  
   214  	tf := new(testFile)
   215  	tf.isDir = true
   216  	tf.path = path
   217  
   218  	fsys.addTestFile(path, tf)
   219  }
   220  
   221  type testFile struct {
   222  	path                                    string
   223  	isDir                                   bool
   224  	originalBytes                           []byte
   225  	readCompleted                           bool
   226  	originalRead, expectedRead, writtenBack *bytes.Buffer
   227  }
   228  
   229  func (t *testFile) ReadDir(_ int) ([]fs.DirEntry, error) {
   230  	if !t.isDir {
   231  		return nil, fmt.Errorf("not a directory")
   232  	}
   233  
   234  	dirEntries, err := os.ReadDir(t.path)
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  
   239  	filteredDirEntries := lo.Filter(dirEntries, func(e os.DirEntry, _ int) bool {
   240  		return !strings.HasSuffix(e.Name(), expectedSuffixWithYAML)
   241  	})
   242  
   243  	return filteredDirEntries, nil
   244  }
   245  
   246  func (t *testFile) Stat() (fs.FileInfo, error) {
   247  	return os.Stat(t.path)
   248  }
   249  
   250  func (t *testFile) Read(p []byte) (int, error) {
   251  	// We need to reset the t.originalRead buffer if it's already been read fully.
   252  	if t.readCompleted {
   253  		b := make([]byte, len(t.originalBytes))
   254  		copy(b, t.originalBytes)
   255  		t.originalRead = bytes.NewBuffer(b)
   256  
   257  		t.readCompleted = false
   258  	}
   259  
   260  	n, err := t.originalRead.Read(p)
   261  	if err != nil && errors.Is(err, io.EOF) {
   262  		t.readCompleted = true
   263  	}
   264  
   265  	return n, err
   266  }
   267  
   268  func (t *testFile) Close() error {
   269  	return nil
   270  }
   271  
   272  func (t *testFile) Write(p []byte) (n int, err error) {
   273  	return t.writtenBack.Write(p)
   274  }
   275  
   276  func (t *testFile) loadOriginal(fsys *FS) error {
   277  	originalBytes, err := fsys.readPath(t.path)
   278  	if err != nil {
   279  		return fmt.Errorf("unable to load fixture %q into tester.FS: %w", t.path, err)
   280  	}
   281  
   282  	t.originalBytes = originalBytes
   283  
   284  	forBuf := make([]byte, len(originalBytes))
   285  	copy(forBuf, originalBytes)
   286  	t.originalRead = bytes.NewBuffer(forBuf)
   287  
   288  	return nil
   289  }
   290  
   291  func (t *testFile) loadExpected(fsys *FS) error {
   292  	expectedFile := expectedName(t.path)
   293  	expectedBytes, err := fsys.readPath(expectedFile)
   294  	if err != nil {
   295  		return fmt.Errorf("unable to load fixture %q into tester.FS: no expected file %q: %w", t.path, expectedFile, err)
   296  	}
   297  
   298  	t.expectedRead = bytes.NewBuffer(expectedBytes)
   299  
   300  	return nil
   301  }