github.com/anchore/syft@v1.38.2/cmd/syft/internal/options/cache_test.go (about)

     1  package options
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/adrg/xdg"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/anchore/go-homedir"
    15  	"github.com/anchore/syft/internal"
    16  	"github.com/anchore/syft/internal/cache"
    17  )
    18  
    19  func Test_defaultDir(t *testing.T) {
    20  	tmpDir := filepath.Join(t.TempDir(), "cache-temp")
    21  	xdgCacheDir := filepath.Join(tmpDir, "fake-xdg-cache")
    22  	homeDir := filepath.Join(tmpDir, "fake-home")
    23  
    24  	tests := []struct {
    25  		name     string
    26  		env      map[string]string
    27  		expected string
    28  	}{
    29  		{
    30  			name: "no-xdg",
    31  			env: map[string]string{
    32  				"HOME": homeDir,
    33  			},
    34  			expected: homeDir,
    35  		},
    36  		{
    37  			name: "xdg-cache",
    38  			env: map[string]string{
    39  				"XDG_CACHE_HOME": xdgCacheDir,
    40  			},
    41  			expected: xdgCacheDir,
    42  		},
    43  	}
    44  
    45  	// capture all the initial environment variables to reset them before we reset library caches
    46  	env := map[string]string{
    47  		"HOME":            "",
    48  		"XDG_DATA_HOME":   "",
    49  		"XDG_DATA_DIRS":   "",
    50  		"XDG_CONFIG_HOME": "",
    51  		"XDG_CONFIG_DIRS": "",
    52  		"XDG_STATE_HOME":  "",
    53  		"XDG_CACHE_HOME":  "",
    54  		"XDG_RUNTIME_DIR": "",
    55  	}
    56  	for k := range env {
    57  		env[k] = os.Getenv(k)
    58  	}
    59  
    60  	for _, test := range tests {
    61  		t.Run(test.name, func(t *testing.T) {
    62  			restoreCache(t)
    63  
    64  			for k, v := range test.env {
    65  				patchEnv(t, k, v)
    66  			}
    67  			xdg.Reload()
    68  
    69  			got := defaultDir()
    70  
    71  			require.True(t, strings.HasPrefix(got, test.expected))
    72  		})
    73  	}
    74  }
    75  
    76  func Test_cacheOptions(t *testing.T) {
    77  	tmp := t.TempDir()
    78  	tests := []struct {
    79  		name string
    80  		opts Cache
    81  		test func(t *testing.T)
    82  	}{
    83  		{
    84  			name: "disable-1",
    85  			opts: Cache{
    86  				Dir: "some-dir",
    87  				TTL: "0",
    88  			},
    89  			test: func(t *testing.T) {
    90  				c := cache.GetManager().GetCache("test-disable-1", "v-disable-1")
    91  				err := c.Write("key-disable-1", strings.NewReader("some-value-disable-1"))
    92  				require.NoError(t, err)
    93  				rdr, err := c.Read("key-disable-1")
    94  				require.Nil(t, rdr)
    95  				require.Error(t, err)
    96  			},
    97  		},
    98  		{
    99  			name: "disable-2",
   100  			opts: Cache{
   101  				Dir: "some-dir",
   102  				TTL: "0s",
   103  			},
   104  			test: func(t *testing.T) {
   105  				c := cache.GetManager().GetCache("test-disable-2", "v-disable-2")
   106  				err := c.Write("key-disable-2", strings.NewReader("some-value-disable-2"))
   107  				require.NoError(t, err)
   108  				rdr, err := c.Read("key-disable-2")
   109  				require.Nil(t, rdr)
   110  				require.Error(t, err)
   111  			},
   112  		},
   113  
   114  		{
   115  			name: "disable-3",
   116  			opts: Cache{
   117  				Dir: "some-dir",
   118  				TTL: "0d",
   119  			},
   120  			test: func(t *testing.T) {
   121  				c := cache.GetManager().GetCache("test-disable-3", "v-disable-3")
   122  				err := c.Write("key-disable-3", strings.NewReader("some-value-disable-3"))
   123  				require.NoError(t, err)
   124  				rdr, err := c.Read("key-disable-3")
   125  				require.Nil(t, rdr)
   126  				require.Error(t, err)
   127  			},
   128  		},
   129  		{
   130  			name: "in-memory",
   131  			opts: Cache{
   132  				Dir: "",
   133  				TTL: "10m",
   134  			},
   135  			test: func(t *testing.T) {
   136  				c := cache.GetManager().GetCache("test-mem", "v-mem")
   137  				err := c.Write("key-mem", strings.NewReader("some-value-mem"))
   138  				require.NoError(t, err)
   139  				rdr, err := c.Read("key-mem")
   140  				require.NotNil(t, rdr)
   141  				defer internal.CloseAndLogError(rdr, "")
   142  				require.NoError(t, err)
   143  				data, err := io.ReadAll(rdr)
   144  				require.NoError(t, err)
   145  				require.Equal(t, "some-value-mem", string(data))
   146  				require.NoDirExists(t, filepath.Join("test-mem", "v-mem"))
   147  			},
   148  		},
   149  		{
   150  			name: "on disk",
   151  			opts: Cache{
   152  				Dir: tmp,
   153  				TTL: "10m",
   154  			},
   155  			test: func(t *testing.T) {
   156  				c := cache.GetManager().GetCache("test-disk", "v-disk")
   157  				err := c.Write("key-disk", strings.NewReader("some-value-disk"))
   158  				require.NoError(t, err)
   159  				rdr, err := c.Read("key-disk")
   160  				require.NotNil(t, rdr)
   161  				defer internal.CloseAndLogError(rdr, "")
   162  				require.NoError(t, err)
   163  				data, err := io.ReadAll(rdr)
   164  				require.NoError(t, err)
   165  				require.Equal(t, "some-value-disk", string(data))
   166  				require.DirExists(t, filepath.Join(tmp, "test-disk", "v-disk"))
   167  			},
   168  		},
   169  	}
   170  	for _, test := range tests {
   171  		t.Run(test.name, func(t *testing.T) {
   172  			original := cache.GetManager()
   173  			defer cache.SetManager(original)
   174  
   175  			err := test.opts.PostLoad()
   176  			require.NoError(t, err)
   177  
   178  			test.test(t)
   179  		})
   180  	}
   181  }
   182  
   183  func Test_parseDuration(t *testing.T) {
   184  	tests := []struct {
   185  		duration string
   186  		expect   time.Duration
   187  		err      require.ErrorAssertionFunc
   188  	}{
   189  		{
   190  			duration: "0d",
   191  			expect:   0,
   192  		},
   193  		{
   194  			duration: "0m",
   195  			expect:   0,
   196  		},
   197  		{
   198  			duration: "0s",
   199  			expect:   0,
   200  		},
   201  		{
   202  			duration: "0",
   203  			expect:   0,
   204  		},
   205  		{
   206  			duration: "1d",
   207  			expect:   24 * time.Hour,
   208  		},
   209  		{
   210  			duration: "7d",
   211  			expect:   7 * 24 * time.Hour,
   212  		},
   213  		{
   214  			duration: "365D",
   215  			expect:   365 * 24 * time.Hour,
   216  		},
   217  		{
   218  			duration: "7d1h1m1s",
   219  			expect:   7*24*time.Hour + time.Hour + time.Minute + time.Second,
   220  		},
   221  		{
   222  			duration: "7d  1h 1m 1s",
   223  			expect:   7*24*time.Hour + time.Hour + time.Minute + time.Second,
   224  		},
   225  		{
   226  			duration: "2h",
   227  			expect:   2 * time.Hour,
   228  		},
   229  		{
   230  			duration: "2h5m",
   231  			expect:   2*time.Hour + 5*time.Minute,
   232  		},
   233  		{
   234  			duration: "2h 5m",
   235  			expect:   2*time.Hour + 5*time.Minute,
   236  		},
   237  		{
   238  			duration: "d24h",
   239  			err:      require.Error,
   240  		},
   241  	}
   242  
   243  	for _, test := range tests {
   244  		t.Run(test.duration, func(t *testing.T) {
   245  			got, err := parseDuration(test.duration)
   246  			if test.err != nil {
   247  				test.err(t, err)
   248  				return
   249  			} else {
   250  				require.NoError(t, err)
   251  			}
   252  			require.Equal(t, test.expect, got)
   253  		})
   254  	}
   255  }
   256  
   257  func Test_durationToString(t *testing.T) {
   258  	tests := []struct {
   259  		duration time.Duration
   260  		expect   string
   261  		err      require.ErrorAssertionFunc
   262  	}{
   263  		{
   264  			expect:   "1d",
   265  			duration: 24 * time.Hour,
   266  		},
   267  		{
   268  			expect:   "7d",
   269  			duration: 7 * 24 * time.Hour,
   270  		},
   271  		{
   272  			expect:   "7d1h1m1s",
   273  			duration: 7*24*time.Hour + time.Hour + time.Minute + time.Second,
   274  		},
   275  		{
   276  			expect:   "2h0m0s",
   277  			duration: 2 * time.Hour,
   278  		},
   279  		{
   280  			expect:   "2h5m0s",
   281  			duration: 2*time.Hour + 5*time.Minute,
   282  		},
   283  	}
   284  
   285  	for _, test := range tests {
   286  		t.Run(test.expect, func(t *testing.T) {
   287  			got := durationToString(test.duration)
   288  			require.Equal(t, test.expect, got)
   289  		})
   290  	}
   291  }
   292  
   293  // restoreCache ensures cache settings are restored after test
   294  func restoreCache(t testing.TB) {
   295  	t.Helper()
   296  	origEnabled := homedir.CacheEnabled()
   297  
   298  	t.Cleanup(func() {
   299  		homedir.SetCacheEnable(origEnabled)
   300  		homedir.Reset()
   301  	})
   302  }
   303  
   304  // patchEnv modifies an environment variable for the duration of the test
   305  func patchEnv(t testing.TB, key, value string) {
   306  	t.Helper()
   307  	original := os.Getenv(key)
   308  
   309  	if value != "" {
   310  		os.Setenv(key, value)
   311  	} else {
   312  		os.Unsetenv(key)
   313  	}
   314  
   315  	t.Cleanup(func() {
   316  		if original != "" {
   317  			os.Setenv(key, original)
   318  		} else {
   319  			os.Unsetenv(key)
   320  		}
   321  		xdg.Reload()
   322  	})
   323  }