go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/fs/find_files_test.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package fs
     5  
     6  import (
     7  	"fmt"
     8  	"io/fs"
     9  	"os"
    10  	"regexp"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/spf13/afero"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestFindFilesMatcher(t *testing.T) {
    20  	possibleTypes := []byte{'b', 'c', 'd', 'p', 'f', 'l'}
    21  	possibleModes := []fs.FileMode{
    22  		fs.ModeDevice,
    23  		fs.ModeDevice | fs.ModeCharDevice,
    24  		fs.ModeDir,
    25  		fs.ModeNamedPipe,
    26  		fs.ModePerm,
    27  		fs.ModeSymlink,
    28  	}
    29  	t.Run("type matching", func(t *testing.T) {
    30  		testCases := []struct {
    31  			typ     fs.FileMode
    32  			matches byte
    33  		}{
    34  			{
    35  				typ:     fs.ModeDir,
    36  				matches: 'd',
    37  			},
    38  			{
    39  				typ:     fs.ModePerm,
    40  				matches: 'f',
    41  			},
    42  			{
    43  				typ:     fs.ModeDevice | fs.ModeCharDevice,
    44  				matches: 'c',
    45  			},
    46  			{
    47  				typ:     fs.ModeDevice,
    48  				matches: 'b',
    49  			},
    50  			{
    51  				typ:     fs.ModeSymlink,
    52  				matches: 'l',
    53  			},
    54  			{
    55  				typ:     fs.ModeNamedPipe,
    56  				matches: 'p',
    57  			},
    58  		}
    59  
    60  		for _, tc := range testCases {
    61  			excludeTypes := []string{}
    62  			for _, b := range possibleTypes {
    63  				if b != tc.matches {
    64  					excludeTypes = append(excludeTypes, string(b))
    65  				}
    66  			}
    67  			t.Run(fmt.Sprintf("%s matcher", string(tc.matches)), func(t *testing.T) {
    68  				exclusionMatcher := createFindFilesMatcher(strings.Join(excludeTypes, ","), nil)
    69  				exactMatcher := createFindFilesMatcher(string(tc.matches), nil)
    70  				assert.True(t, exactMatcher.Match("/foo", tc.typ), "exact matcher failed to match")
    71  				assert.False(t, exclusionMatcher.Match("/foo", tc.typ), "exclusion matcher matched")
    72  			})
    73  		}
    74  	})
    75  
    76  	t.Run("regex", func(t *testing.T) {
    77  		t.Run("any type", func(t *testing.T) {
    78  			exactMatcher := createFindFilesMatcher("", regexp.MustCompile("foo.*"))
    79  
    80  			for _, m := range possibleModes {
    81  				t.Run(fmt.Sprintf("mode %s", m.String()), func(t *testing.T) {
    82  					assert.True(t, exactMatcher.Match("foobar", m))
    83  					assert.True(t, exactMatcher.Match("foofoobar", m))
    84  					assert.False(t, exactMatcher.Match("barfoo", m))
    85  				})
    86  			}
    87  		})
    88  
    89  		t.Run("specific type", func(t *testing.T) {
    90  			exactMatcher := createFindFilesMatcher("f", regexp.MustCompile("foo.*"))
    91  
    92  			assert.False(t, exactMatcher.Match("foobar", fs.ModeDir))
    93  			assert.True(t, exactMatcher.Match("foobar", fs.ModePerm))
    94  		})
    95  	})
    96  
    97  	t.Run("no type or regex", func(t *testing.T) {
    98  		testCases := []struct {
    99  			typ     fs.FileMode
   100  			matches byte
   101  		}{
   102  			{
   103  				typ:     fs.ModeDir,
   104  				matches: 'd',
   105  			},
   106  			{
   107  				typ:     fs.ModePerm,
   108  				matches: 'f',
   109  			},
   110  			{
   111  				typ:     fs.ModeDevice | fs.ModeCharDevice,
   112  				matches: 'c',
   113  			},
   114  			{
   115  				typ:     fs.ModeDevice,
   116  				matches: 'b',
   117  			},
   118  			{
   119  				typ:     fs.ModeSymlink,
   120  				matches: 'l',
   121  			},
   122  			{
   123  				typ:     fs.ModeNamedPipe,
   124  				matches: 'p',
   125  			},
   126  		}
   127  		for _, tc := range testCases {
   128  			excludeTypes := []string{}
   129  			for _, b := range possibleTypes {
   130  				if b != tc.matches {
   131  					excludeTypes = append(excludeTypes, string(b))
   132  				}
   133  			}
   134  			t.Run(fmt.Sprintf("%s matcher", string(tc.matches)), func(t *testing.T) {
   135  				exactMatcher := createFindFilesMatcher("", nil)
   136  				assert.True(t, exactMatcher.Match("/foo", tc.typ), "matcher failed to match")
   137  			})
   138  		}
   139  	})
   140  }
   141  
   142  func TestFindFiles(t *testing.T) {
   143  	fs := afero.NewMemMapFs()
   144  	mkDir(t, fs, "root/a")
   145  	mkDir(t, fs, "root/b")
   146  	mkDir(t, fs, "root/c")
   147  	mkFile(t, fs, "root/a/file1")
   148  	mkFile(t, fs, "root/a/file2")
   149  	mkFile(t, fs, "root/b/file1")
   150  
   151  	rootAFiles, err := FindFiles(afero.NewIOFS(fs), "root/a", nil, "f")
   152  	require.NoError(t, err)
   153  	assert.ElementsMatch(t, rootAFiles, []string{"root/a/file1", "root/a/file2"})
   154  
   155  	rootAFilesAndDir, err := FindFiles(afero.NewIOFS(fs), "root/a", nil, "f,d")
   156  	require.NoError(t, err)
   157  	assert.ElementsMatch(t, rootAFilesAndDir, []string{"root/a", "root/a/file1", "root/a/file2"})
   158  
   159  	rootBFiles, err := FindFiles(afero.NewIOFS(fs), "root", regexp.MustCompile("root/b.*"), "f")
   160  	assert.ElementsMatch(t, rootBFiles, []string{"root/b/file1"})
   161  }
   162  
   163  func mkFile(t *testing.T, fs afero.Fs, name string) {
   164  	t.Helper()
   165  	f, err := fs.Create(name)
   166  	require.NoError(t, err)
   167  	err = f.Close()
   168  	require.NoError(t, err)
   169  }
   170  
   171  func mkDir(t *testing.T, fs afero.Fs, name string) {
   172  	t.Helper()
   173  	err := fs.MkdirAll(name, os.ModePerm)
   174  	require.NoError(t, err)
   175  }