github.com/creativeprojects/go-selfupdate@v1.2.0/detect_test.go (about)

     1  package selfupdate
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	stdlog "log"
     8  	"os"
     9  	"strconv"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  var (
    18  	testGithubRepository = NewRepositorySlug("creativeprojects", "resticprofile")
    19  )
    20  
    21  func skipRateLimitExceeded(t *testing.T, err error) {
    22  	if err == nil {
    23  		return
    24  	}
    25  	if strings.Contains(err.Error(), "403 API rate limit") {
    26  		t.Skip("Test skipped because of GitHub API rate limit exceeded")
    27  	}
    28  }
    29  
    30  func TestDetectReleaseWithVersionPrefix(t *testing.T) {
    31  	testData := []struct {
    32  		run     bool
    33  		name    string
    34  		updater *Updater
    35  	}{
    36  		{true, "Mock", newMockUpdater(t, Config{Source: mockSourceRepository(t)})},
    37  		{!testing.Short(), "GitHub", DefaultUpdater()},
    38  	}
    39  
    40  	for _, testItem := range testData {
    41  		if !testItem.run {
    42  			continue
    43  		}
    44  		t.Run(testItem.name, func(t *testing.T) {
    45  			r, ok, err := testItem.updater.DetectLatest(context.Background(), testGithubRepository)
    46  			skipRateLimitExceeded(t, err)
    47  			require.NoError(t, err)
    48  			assert.True(t, ok, "Failed to detect latest")
    49  			require.NotNil(t, r, "No release returned")
    50  			if r.LessThan("0.10.0") {
    51  				t.Error("Incorrect version:", r.Version())
    52  			}
    53  			if !strings.HasSuffix(r.AssetURL, ".zip") && !strings.HasSuffix(r.AssetURL, ".tar.gz") {
    54  				t.Error("Incorrect URL for asset:", r.AssetURL)
    55  			}
    56  			assert.NotEmpty(t, r.URL, "Document URL should not be empty")
    57  			assert.NotEmpty(t, r.ReleaseNotes, "Description should not be empty for this repo")
    58  			assert.NotEmpty(t, r.Name, "Release name is unexpectedly empty")
    59  			assert.NotEmpty(t, r.AssetByteSize, "Asset's size is unexpectedly zero")
    60  			assert.NotEmpty(t, r.AssetID, "Asset's ID is unexpectedly zero")
    61  			assert.NotZero(t, r.PublishedAt, "Release time is unexpectedly zero")
    62  		})
    63  	}
    64  }
    65  
    66  func TestDetectVersionExisting(t *testing.T) {
    67  	testVersion := "v0.10.0"
    68  	gitHub, _ := NewUpdater(Config{Validator: &ChecksumValidator{UniqueFilename: "checksums.txt"}})
    69  	testData := []struct {
    70  		run     bool
    71  		name    string
    72  		updater *Updater
    73  	}{
    74  		{true, "Mock", newMockUpdater(t, Config{Source: mockSourceRepository(t), Validator: &ChecksumValidator{UniqueFilename: "checksums.txt"}})},
    75  		{!testing.Short(), "GitHub", gitHub},
    76  	}
    77  
    78  	for _, testItem := range testData {
    79  		if !testItem.run {
    80  			continue
    81  		}
    82  		t.Run(testItem.name, func(t *testing.T) {
    83  			r, ok, err := testItem.updater.DetectVersion(context.Background(), testGithubRepository, testVersion)
    84  			skipRateLimitExceeded(t, err)
    85  			require.NoError(t, err)
    86  			assert.Truef(t, ok, "Failed to detect %s", testVersion)
    87  			require.NotNil(t, r, "No release returned")
    88  			assert.Greater(t, r.ValidationAssetID, int64(-1))
    89  		})
    90  	}
    91  }
    92  
    93  func TestDetectVersionExistingWithNoValidationFile(t *testing.T) {
    94  	testVersion := "v0.10.0"
    95  	gitHub, _ := NewUpdater(Config{Validator: &ChecksumValidator{UniqueFilename: "notfound.txt"}})
    96  	testData := []struct {
    97  		run     bool
    98  		name    string
    99  		updater *Updater
   100  	}{
   101  		{true, "Mock", newMockUpdater(t, Config{Source: mockSourceRepository(t), Validator: &ChecksumValidator{UniqueFilename: "notfound.txt"}})},
   102  		{!testing.Short(), "GitHub", gitHub},
   103  	}
   104  
   105  	for _, testItem := range testData {
   106  		if !testItem.run {
   107  			continue
   108  		}
   109  		t.Run(testItem.name, func(t *testing.T) {
   110  			_, _, err := testItem.updater.DetectVersion(context.Background(), testGithubRepository, testVersion)
   111  			skipRateLimitExceeded(t, err)
   112  			require.Error(t, err)
   113  			assert.True(t, errors.Is(err, ErrValidationAssetNotFound))
   114  		})
   115  	}
   116  }
   117  
   118  func TestDetectVersionNotExisting(t *testing.T) {
   119  	testData := []struct {
   120  		run     bool
   121  		name    string
   122  		updater *Updater
   123  	}{
   124  		{true, "Mock", newMockUpdater(t, Config{Source: mockSourceRepository(t)})},
   125  		{!testing.Short(), "GitHub", DefaultUpdater()},
   126  	}
   127  
   128  	for _, testItem := range testData {
   129  		if !testItem.run {
   130  			continue
   131  		}
   132  		t.Run(testItem.name, func(t *testing.T) {
   133  			r, ok, err := testItem.updater.DetectVersion(context.Background(), testGithubRepository, "foobar")
   134  			skipRateLimitExceeded(t, err)
   135  			require.NoError(t, err)
   136  			assert.False(t, ok, "Failed to correctly detect foobar")
   137  			assert.Nil(t, r, "Release not detected but got a returned value for it")
   138  		})
   139  	}
   140  }
   141  
   142  func TestDetectPrerelease(t *testing.T) {
   143  	testFixtures := []struct {
   144  		prerelease bool
   145  		version    string
   146  	}{
   147  		{false, "1.0.0"},
   148  		{true, "2.0.0-beta"},
   149  	}
   150  	for _, testFixture := range testFixtures {
   151  		t.Run(strconv.FormatBool(testFixture.prerelease), func(t *testing.T) {
   152  			updater := newMockUpdater(t, Config{
   153  				Source:     mockSourceRepository(t),
   154  				Prerelease: testFixture.prerelease,
   155  			})
   156  			r, ok, err := updater.DetectLatest(context.Background(), RepositorySlug{owner: "owner", repo: "repo"})
   157  			require.NotNil(t, r)
   158  			assert.True(t, ok)
   159  			assert.NoError(t, err)
   160  
   161  			assert.Equal(t, testFixture.prerelease, r.Prerelease)
   162  			assert.Equal(t, testFixture.version, r.Version())
   163  		})
   164  	}
   165  }
   166  
   167  func TestDetectReleasesForVariousArchives(t *testing.T) {
   168  	for _, tc := range []struct {
   169  		slug   string
   170  		prefix string
   171  	}{
   172  		{"rhysd-test/test-release-zip", "v"},
   173  		{"rhysd-test/test-release-tar", "v"},
   174  		{"rhysd-test/test-release-gzip", "v"},
   175  		{"rhysd-test/test-release-xz", "release-v"},
   176  		{"rhysd-test/test-release-tar-xz", "release-"},
   177  	} {
   178  		t.Run(tc.slug, func(t *testing.T) {
   179  			source, err := NewGitHubSource(GitHubConfig{})
   180  			require.NoError(t, err, "failed to create source")
   181  			updater, err := NewUpdater(Config{Source: source, Arch: "amd64"})
   182  			require.NoError(t, err, "failed to create updater")
   183  			r, ok, err := updater.DetectLatest(context.Background(), ParseSlug(tc.slug))
   184  			skipRateLimitExceeded(t, err)
   185  
   186  			assert.NoError(t, err, "fetch failed")
   187  			assert.True(t, ok, "not found")
   188  			require.NotNil(t, r, "release not detected")
   189  			assert.Truef(t, r.Equal("1.2.3"), "incorrect release: expected 1.2.3 but got %v", r.Version())
   190  
   191  			url := fmt.Sprintf("https://github.com/%s/releases/tag/%s1.2.3", tc.slug, tc.prefix)
   192  			assert.Equal(t, url, r.URL)
   193  			assert.NotEmpty(t, r.ReleaseNotes, "Release note is unexpectedly empty")
   194  
   195  			if !strings.HasPrefix(r.AssetURL, fmt.Sprintf("https://github.com/%s/releases/download/%s1.2.3/", tc.slug, tc.prefix)) {
   196  				t.Error("Unexpected asset URL:", r.AssetURL)
   197  			}
   198  
   199  			assert.NotEmpty(t, r.Name, "Release name is unexpectedly empty")
   200  			assert.NotEmpty(t, r.AssetByteSize, "Asset's size is unexpectedly zero")
   201  			assert.NotEmpty(t, r.AssetID, "Asset's ID is unexpectedly zero")
   202  			assert.NotZero(t, r.PublishedAt)
   203  		})
   204  	}
   205  }
   206  
   207  func TestDetectReleaseButNoAsset(t *testing.T) {
   208  	testData := []struct {
   209  		run     bool
   210  		name    string
   211  		updater *Updater
   212  	}{
   213  		{true, "Mock", newMockUpdater(t, Config{Source: NewMockSource(
   214  			[]SourceRelease{
   215  				&GitHubRelease{
   216  					name:    "first",
   217  					tagName: "v1.0",
   218  					assets:  nil,
   219  				},
   220  				&GitHubRelease{
   221  					name:    "second",
   222  					tagName: "v2.0",
   223  					assets:  nil,
   224  				},
   225  			},
   226  			nil,
   227  		)})},
   228  		{!testing.Short(), "GitHub", DefaultUpdater()},
   229  	}
   230  
   231  	for _, testItem := range testData {
   232  		if !testItem.run {
   233  			continue
   234  		}
   235  		t.Run(testItem.name, func(t *testing.T) {
   236  			_, ok, err := testItem.updater.DetectLatest(context.Background(), ParseSlug("rhysd/clever-f.vim"))
   237  			skipRateLimitExceeded(t, err)
   238  			require.NoError(t, err)
   239  			assert.False(t, ok, "When no asset found, result should be marked as 'not found'")
   240  		})
   241  	}
   242  }
   243  
   244  func TestNonExistingRepo(t *testing.T) {
   245  	testData := []struct {
   246  		run     bool
   247  		name    string
   248  		updater *Updater
   249  	}{
   250  		{true, "Mock", newMockUpdater(t, Config{Source: NewMockSource(nil, nil)})},
   251  		{!testing.Short(), "GitHub", DefaultUpdater()},
   252  	}
   253  
   254  	for _, testItem := range testData {
   255  		if !testItem.run {
   256  			continue
   257  		}
   258  		t.Run(testItem.name, func(t *testing.T) {
   259  			_, ok, err := testItem.updater.DetectLatest(context.Background(), ParseSlug("rhysd/non-existing-repo"))
   260  			skipRateLimitExceeded(t, err)
   261  			require.NoError(t, err)
   262  			assert.False(t, ok, "Release for non-existing repo should not be found")
   263  		})
   264  	}
   265  }
   266  
   267  func TestNoReleaseFound(t *testing.T) {
   268  	testData := []struct {
   269  		run     bool
   270  		name    string
   271  		updater *Updater
   272  	}{
   273  		{true, "Mock", newMockUpdater(t, Config{Source: NewMockSource(nil, nil)})},
   274  		{!testing.Short(), "GitHub", DefaultUpdater()},
   275  	}
   276  
   277  	for _, testItem := range testData {
   278  		if !testItem.run {
   279  			continue
   280  		}
   281  		t.Run(testItem.name, func(t *testing.T) {
   282  			_, ok, err := testItem.updater.DetectLatest(context.Background(), ParseSlug("rhysd/misc"))
   283  			skipRateLimitExceeded(t, err)
   284  			require.NoError(t, err)
   285  			assert.False(t, ok, "Repo having no release should not be found")
   286  		})
   287  	}
   288  }
   289  
   290  func TestFindAssetFromRelease(t *testing.T) {
   291  	type findReleaseAndAssetFixture struct {
   292  		name            string
   293  		config          Config
   294  		release         SourceRelease
   295  		targetVersion   string
   296  		expectedAsset   string
   297  		expectedVersion string
   298  		expectedFound   bool
   299  	}
   300  
   301  	rel1 := "rel1"
   302  	v1 := "1.0.0"
   303  	rel11 := "rel11"
   304  	v11 := "1.1.0"
   305  	asset1 := "asset1.gz"
   306  	asset2 := "asset2.gz"
   307  	wrongAsset1 := "asset1.yaml"
   308  	asset11 := "asset11.gz"
   309  	url1 := "https://asset1"
   310  	url2 := "https://asset2"
   311  	url11 := "https://asset11"
   312  
   313  	testData := []findReleaseAndAssetFixture{
   314  		{
   315  			name:          "empty fixture",
   316  			config:        Config{},
   317  			release:       nil,
   318  			targetVersion: "",
   319  			expectedFound: false,
   320  		},
   321  		{
   322  			name: "find asset, no filters",
   323  			release: &GitHubRelease{
   324  				name:    rel1,
   325  				tagName: v1,
   326  				assets: []SourceAsset{
   327  					&GitHubAsset{name: asset1, url: url1},
   328  				},
   329  			},
   330  			targetVersion:   "1.0.0",
   331  			expectedAsset:   asset1,
   332  			expectedVersion: "1.0.0",
   333  			expectedFound:   true,
   334  		},
   335  		{
   336  			name: "find asset, no target version",
   337  			release: &GitHubRelease{
   338  				name:    rel1,
   339  				tagName: v1,
   340  				assets: []SourceAsset{
   341  					&GitHubAsset{name: asset1, url: url1},
   342  				},
   343  			},
   344  			targetVersion:   "",
   345  			expectedAsset:   asset1,
   346  			expectedVersion: "1.0.0",
   347  			expectedFound:   true,
   348  		},
   349  		{
   350  			name: "don't find prerelease",
   351  			release: &GitHubRelease{
   352  				name:    rel1,
   353  				tagName: v1,
   354  				assets: []SourceAsset{
   355  					&GitHubAsset{name: asset1, url: url1},
   356  				},
   357  				prerelease: true,
   358  			},
   359  			targetVersion:   "",
   360  			expectedAsset:   asset1,
   361  			expectedVersion: "1.0.0",
   362  			expectedFound:   false,
   363  		},
   364  		{
   365  			name: "find named prerelease",
   366  			release: &GitHubRelease{
   367  				name:    rel1,
   368  				tagName: v1,
   369  				assets: []SourceAsset{
   370  					&GitHubAsset{name: asset1, url: url1},
   371  				},
   372  				prerelease: true,
   373  			},
   374  			targetVersion:   "1.0.0",
   375  			expectedAsset:   asset1,
   376  			expectedVersion: "1.0.0",
   377  			expectedFound:   true,
   378  		},
   379  		{
   380  			name:   "find prerelease",
   381  			config: Config{Prerelease: true},
   382  			release: &GitHubRelease{
   383  				name:    rel1,
   384  				tagName: v1,
   385  				assets: []SourceAsset{
   386  					&GitHubAsset{name: asset1, url: url1},
   387  				},
   388  				prerelease: true,
   389  			},
   390  			targetVersion:   "",
   391  			expectedAsset:   asset1,
   392  			expectedVersion: "1.0.0",
   393  			expectedFound:   true,
   394  		},
   395  		{
   396  			name: "don't find asset with wrong extension, no filters",
   397  			release: &GitHubRelease{
   398  				name:    rel11,
   399  				tagName: v11,
   400  				assets: []SourceAsset{
   401  					&GitHubAsset{name: wrongAsset1, url: url11},
   402  				},
   403  			},
   404  			targetVersion: "1.1.0",
   405  			expectedFound: false,
   406  		},
   407  		{
   408  			name: "find asset with different name, no filters",
   409  			release: &GitHubRelease{
   410  				name:    rel11,
   411  				tagName: v11,
   412  				assets: []SourceAsset{
   413  					&GitHubAsset{name: asset1, url: url11},
   414  				},
   415  			},
   416  			targetVersion:   "1.1.0",
   417  			expectedAsset:   asset1,
   418  			expectedVersion: "1.1.0",
   419  			expectedFound:   true,
   420  		},
   421  		{
   422  			name: "find asset, no filters (2)",
   423  			release: &GitHubRelease{
   424  				name:    rel11,
   425  				tagName: v11,
   426  				assets: []SourceAsset{
   427  					&GitHubAsset{name: asset11, url: url11},
   428  				},
   429  			},
   430  			targetVersion:   "1.1.0",
   431  			expectedAsset:   asset11,
   432  			expectedVersion: "1.1.0",
   433  			expectedFound:   true,
   434  		},
   435  		{
   436  			name: "find asset, match filter",
   437  			release: &GitHubRelease{
   438  				name:    rel11,
   439  				tagName: v11,
   440  				assets: []SourceAsset{
   441  					&GitHubAsset{name: asset11, url: url11},
   442  					&GitHubAsset{name: asset1, url: url1},
   443  				},
   444  			},
   445  			targetVersion:   "1.1.0",
   446  			config:          Config{Filters: []string{"11"}},
   447  			expectedAsset:   asset11,
   448  			expectedVersion: "1.1.0",
   449  			expectedFound:   true,
   450  		},
   451  		{
   452  			name: "find asset, match another filter",
   453  			release: &GitHubRelease{
   454  				name:    rel11,
   455  				tagName: v11,
   456  				assets: []SourceAsset{
   457  					&GitHubAsset{name: asset11, url: url11},
   458  					&GitHubAsset{name: asset1, url: url1},
   459  				},
   460  			},
   461  			targetVersion:   "1.1.0",
   462  			config:          Config{Filters: []string{"([^1])1{1}([^1])"}},
   463  			expectedAsset:   asset1,
   464  			expectedVersion: "1.1.0",
   465  			expectedFound:   true,
   466  		},
   467  		{
   468  			name: "find asset, match any filter",
   469  			release: &GitHubRelease{
   470  				name:    rel11,
   471  				tagName: v11,
   472  				assets: []SourceAsset{
   473  					&GitHubAsset{name: asset11, url: url11},
   474  					&GitHubAsset{name: asset2, url: url2},
   475  				},
   476  			},
   477  			targetVersion:   "1.1.0",
   478  			config:          Config{Filters: []string{"([^1])1{1}([^1])", "([^1])2{1}([^1])"}},
   479  			expectedAsset:   asset2,
   480  			expectedVersion: "1.1.0",
   481  			expectedFound:   true,
   482  		},
   483  		{
   484  			name: "find asset, match no filter",
   485  			release: &GitHubRelease{
   486  				name:    rel11,
   487  				tagName: v11,
   488  				assets: []SourceAsset{
   489  					&GitHubAsset{name: asset11, url: url11},
   490  					&GitHubAsset{name: asset2, url: url2},
   491  				},
   492  			},
   493  			targetVersion: "",
   494  			config:        Config{Filters: []string{"another", "binary"}},
   495  			expectedFound: false,
   496  		},
   497  	}
   498  
   499  	for _, fixture := range testData {
   500  		t.Run(fixture.name, func(t *testing.T) {
   501  			updater := newMockUpdater(t, fixture.config)
   502  			asset, ver, found := updater.findAssetFromRelease(fixture.release, []string{".gz"}, fixture.targetVersion)
   503  			if fixture.expectedFound {
   504  				if !found {
   505  					t.Fatalf("expected to find an asset for this fixture: %q", fixture.name)
   506  				}
   507  				if asset.GetName() == "" {
   508  					t.Fatalf("invalid asset struct returned from fixture: %q, got: %v", fixture.name, asset)
   509  				}
   510  				if asset.GetName() != fixture.expectedAsset {
   511  					t.Fatalf("expected asset %q in fixture: %q, got: %s", fixture.expectedAsset, fixture.name, asset.GetName())
   512  				}
   513  				t.Logf("asset %v, %v", asset, ver)
   514  			} else if found {
   515  				t.Fatalf("expected not to find an asset for this fixture: %q, but got: %v", fixture.name, asset)
   516  			}
   517  		})
   518  	}
   519  }
   520  
   521  func TestFindReleaseAndAsset(t *testing.T) {
   522  	SetLogger(stdlog.New(os.Stderr, "", 0))
   523  	defer SetLogger(&emptyLogger{})
   524  
   525  	tag2 := "v2.0.0"
   526  	rel2 := "rel2"
   527  	assetLinux386 := "asset_linux_386.tgz"
   528  	assetLinuxAMD64 := "asset_linux_amd64.tgz"
   529  	assetLinuxX86_64 := "asset_linux_x86_64.tgz"
   530  	assetLinuxARM := "asset_linux_arm.tgz"
   531  	assetLinuxARMv5 := "asset_linux_armv5.tgz"
   532  	assetLinuxARMv6 := "asset_linux_armv6.tgz"
   533  	assetLinuxARMv7 := "asset_linux_armv7.tgz"
   534  	assetLinuxARM64 := "asset_linux_arm64.tgz"
   535  	testData := []struct {
   536  		name              string
   537  		os                string
   538  		arch              string
   539  		arm               uint8
   540  		releases          []SourceRelease
   541  		version           string
   542  		filters           []string
   543  		found             bool
   544  		expectedAssetName string
   545  	}{
   546  		{
   547  			name: "no match",
   548  			os:   "darwin",
   549  			arch: "amd64",
   550  			releases: []SourceRelease{
   551  				&GitHubRelease{
   552  					name:    rel2,
   553  					tagName: tag2,
   554  					assets: []SourceAsset{
   555  						&GitHubAsset{
   556  							name: assetLinux386,
   557  						},
   558  						&GitHubAsset{
   559  							name: assetLinuxAMD64,
   560  						},
   561  					},
   562  				},
   563  			},
   564  			version:           "v2.0.0",
   565  			filters:           nil,
   566  			found:             false,
   567  			expectedAssetName: assetLinuxAMD64,
   568  		},
   569  		{
   570  			name: "simple match",
   571  			os:   "linux",
   572  			arch: "amd64",
   573  			releases: []SourceRelease{
   574  				&GitHubRelease{
   575  					name:    rel2,
   576  					tagName: tag2,
   577  					assets: []SourceAsset{
   578  						&GitHubAsset{
   579  							name: assetLinux386,
   580  						},
   581  						&GitHubAsset{
   582  							name: assetLinuxAMD64,
   583  						},
   584  					},
   585  				},
   586  			},
   587  			version:           "v2.0.0",
   588  			filters:           nil,
   589  			found:             true,
   590  			expectedAssetName: assetLinuxAMD64,
   591  		},
   592  		{
   593  			name: "simple match case insensitive",
   594  			os:   "linux",
   595  			arch: "amd64",
   596  			releases: []SourceRelease{
   597  				&GitHubRelease{
   598  					name:    rel2,
   599  					tagName: tag2,
   600  					assets: []SourceAsset{
   601  						&GitHubAsset{
   602  							name: assetLinux386,
   603  						},
   604  						&GitHubAsset{
   605  							name: "asset_Linux_AMD64.tgz",
   606  						},
   607  					},
   608  				},
   609  			},
   610  			version:           "v2.0.0",
   611  			filters:           nil,
   612  			found:             true,
   613  			expectedAssetName: "asset_Linux_AMD64.tgz",
   614  		},
   615  		{
   616  			name: "match default arm",
   617  			os:   "linux",
   618  			arch: "arm",
   619  			releases: []SourceRelease{
   620  				&GitHubRelease{
   621  					name:    rel2,
   622  					tagName: tag2,
   623  					assets: []SourceAsset{
   624  						&GitHubAsset{
   625  							name: assetLinuxARM,
   626  						},
   627  						&GitHubAsset{
   628  							name: assetLinuxARM64,
   629  						},
   630  						&GitHubAsset{
   631  							name: assetLinuxARMv5,
   632  						},
   633  						&GitHubAsset{
   634  							name: assetLinuxARMv6,
   635  						},
   636  						&GitHubAsset{
   637  							name: assetLinuxARMv7,
   638  						},
   639  					},
   640  				},
   641  			},
   642  			version:           "v2.0.0",
   643  			filters:           nil,
   644  			found:             true,
   645  			expectedAssetName: assetLinuxARM,
   646  		},
   647  		{
   648  			name: "match armv6",
   649  			os:   "linux",
   650  			arch: "arm",
   651  			arm:  6,
   652  			releases: []SourceRelease{
   653  				&GitHubRelease{
   654  					name:    rel2,
   655  					tagName: tag2,
   656  					assets: []SourceAsset{
   657  						&GitHubAsset{
   658  							name: assetLinuxARM,
   659  						},
   660  						&GitHubAsset{
   661  							name: assetLinuxARM64,
   662  						},
   663  						&GitHubAsset{
   664  							name: assetLinuxARMv5,
   665  						},
   666  						&GitHubAsset{
   667  							name: assetLinuxARMv6,
   668  						},
   669  						&GitHubAsset{
   670  							name: assetLinuxARMv7,
   671  						},
   672  					},
   673  				},
   674  			},
   675  			version:           "v2.0.0",
   676  			filters:           nil,
   677  			found:             true,
   678  			expectedAssetName: assetLinuxARMv6,
   679  		},
   680  		{
   681  			name: "fallback to armv5",
   682  			os:   "linux",
   683  			arch: "arm",
   684  			arm:  7,
   685  			releases: []SourceRelease{
   686  				&GitHubRelease{
   687  					name:    rel2,
   688  					tagName: tag2,
   689  					assets: []SourceAsset{
   690  						&GitHubAsset{
   691  							name: assetLinuxARM,
   692  						},
   693  						&GitHubAsset{
   694  							name: assetLinuxARM64,
   695  						},
   696  						&GitHubAsset{
   697  							name: assetLinuxARMv5,
   698  						},
   699  					},
   700  				},
   701  			},
   702  			version:           "v2.0.0",
   703  			filters:           nil,
   704  			found:             true,
   705  			expectedAssetName: assetLinuxARMv5,
   706  		},
   707  		{
   708  			name: "fallback to arm",
   709  			os:   "linux",
   710  			arch: "arm",
   711  			arm:  5,
   712  			releases: []SourceRelease{
   713  				&GitHubRelease{
   714  					name:    rel2,
   715  					tagName: tag2,
   716  					assets: []SourceAsset{
   717  						&GitHubAsset{
   718  							name: assetLinuxARM,
   719  						},
   720  						&GitHubAsset{
   721  							name: assetLinuxARM64,
   722  						},
   723  					},
   724  				},
   725  			},
   726  			version:           "v2.0.0",
   727  			filters:           nil,
   728  			found:             true,
   729  			expectedAssetName: assetLinuxARM,
   730  		},
   731  		{
   732  			name: "arm not found",
   733  			os:   "linux",
   734  			arch: "arm",
   735  			arm:  6,
   736  			releases: []SourceRelease{
   737  				&GitHubRelease{
   738  					name:    rel2,
   739  					tagName: tag2,
   740  					assets: []SourceAsset{
   741  						&GitHubAsset{
   742  							name: assetLinuxARMv7,
   743  						},
   744  						&GitHubAsset{
   745  							name: assetLinuxARM64,
   746  						},
   747  					},
   748  				},
   749  			},
   750  			version:           "v2.0.0",
   751  			filters:           nil,
   752  			found:             false,
   753  			expectedAssetName: assetLinuxARM,
   754  		},
   755  		{
   756  			name: "match x86_64 for adm64",
   757  			os:   "linux",
   758  			arch: "amd64",
   759  			releases: []SourceRelease{
   760  				&GitHubRelease{
   761  					name:    rel2,
   762  					tagName: tag2,
   763  					assets: []SourceAsset{
   764  						&GitHubAsset{
   765  							name: assetLinux386,
   766  						},
   767  						&GitHubAsset{
   768  							name: assetLinuxX86_64,
   769  						},
   770  					},
   771  				},
   772  			},
   773  			version:           "v2.0.0",
   774  			filters:           nil,
   775  			found:             true,
   776  			expectedAssetName: assetLinuxX86_64,
   777  		},
   778  	}
   779  
   780  	for _, testItem := range testData {
   781  		t.Run(testItem.name, func(t *testing.T) {
   782  			updater, err := NewUpdater(Config{
   783  				Filters: testItem.filters,
   784  				OS:      testItem.os,
   785  				Arch:    testItem.arch,
   786  				Arm:     testItem.arm,
   787  			})
   788  			require.NoError(t, err)
   789  			_, asset, _, found := updater.findReleaseAndAsset(testItem.releases, testItem.version)
   790  			assert.Equal(t, testItem.found, found)
   791  			if found {
   792  				assert.Equal(t, testItem.expectedAssetName, asset.GetName())
   793  			}
   794  		})
   795  	}
   796  }
   797  
   798  func TestBuildMultistepValidationChain(t *testing.T) {
   799  	testVersion := "v0.10.0"
   800  	source, keyRing := mockPGPSourceRepository(t)
   801  	checksumValidator := &ChecksumValidator{UniqueFilename: "checksums.txt"}
   802  
   803  	t.Run("ValidConfig", func(t *testing.T) {
   804  		updater, _ := NewUpdater(Config{
   805  			Source:    source,
   806  			Validator: NewChecksumWithPGPValidator("checksums.txt", keyRing),
   807  		})
   808  
   809  		release, found, err := updater.DetectVersion(context.Background(), testGithubRepository, testVersion)
   810  		require.True(t, found)
   811  		assert.NoError(t, err)
   812  		assert.Equal(t, 2, len(release.ValidationChain))
   813  		assert.Equal(t, "checksums.txt", release.ValidationChain[0].ValidationAssetName)
   814  		assert.Equal(t, "checksums.txt.asc", release.ValidationChain[1].ValidationAssetName)
   815  	})
   816  
   817  	t.Run("LoopConfig", func(t *testing.T) {
   818  		updater, _ := NewUpdater(Config{
   819  			Source: source,
   820  			Validator: new(PatternValidator).
   821  				Add("*", checksumValidator),
   822  		})
   823  
   824  		_, _, err := updater.DetectVersion(context.Background(), testGithubRepository, testVersion)
   825  		assert.NoError(t, err)
   826  	})
   827  
   828  	t.Run("InvalidLoopConfig", func(t *testing.T) {
   829  		updater, _ := NewUpdater(Config{
   830  			Source: source,
   831  			Validator: new(PatternValidator).
   832  				Add("*.*z*", checksumValidator).
   833  				Add("*", new(SHAValidator)),
   834  		})
   835  
   836  		_, _, err := updater.DetectVersion(context.Background(), testGithubRepository, testVersion)
   837  		assert.EqualError(t, err, "validation file not found: \"checksums.txt.sha256\"")
   838  	})
   839  }
   840  
   841  func newMockUpdater(t *testing.T, config Config) *Updater {
   842  	t.Helper()
   843  	updater, err := NewUpdater(config)
   844  	require.NoError(t, err)
   845  	return updater
   846  }