package selfupdate import ( "fmt" "os" "regexp" "strings" "testing" "github.com/blang/semver" "github.com/google/go-github/v30/github" ) func TestDetectReleaseWithVersionPrefix(t *testing.T) { r, ok, err := DetectLatest("rhysd/github-clone-all") if err != nil { t.Fatal("Fetch failed:", err) } if !ok { t.Fatal("Failed to detect latest") } if r == nil { t.Fatal("Release detected but nil returned for it") } if r.Version.LE(semver.MustParse("2.0.0")) { t.Error("Incorrect version:", r.Version) } if !strings.HasSuffix(r.AssetURL, ".zip") && !strings.HasSuffix(r.AssetURL, ".tar.gz") { t.Error("Incorrect URL for asset:", r.AssetURL) } if r.URL == "" { t.Error("Document URL should not be empty") } if r.ReleaseNotes == "" { t.Error("Description should not be empty for this repo") } if r.Name == "" { t.Error("Release name is unexpectedly empty") } if r.AssetByteSize == 0 { t.Error("Asset's size is unexpectedly zero") } if r.AssetID == 0 { t.Error("Asset's ID is unexpectedly zero") } if r.PublishedAt.IsZero() { t.Error("Release time is unexpectedly zero") } if r.RepoOwner != "rhysd" { t.Error("Repo owner is not correct:", r.RepoOwner) } if r.RepoName != "github-clone-all" { t.Error("Repo name was not properly detectd:", r.RepoName) } } func TestDetectVersionExisting(t *testing.T) { testVersion := "v2.2.0" r, ok, err := DetectVersion("rhysd/github-clone-all", testVersion) if err != nil { t.Fatal("Fetch failed:", err) } if !ok { t.Fatalf("Failed to detect %s", testVersion) } if r == nil { t.Fatal("Release detected but nil returned for it") } } func TestDetectVersionNotExisting(t *testing.T) { r, ok, err := DetectVersion("rhysd/github-clone-all", "foobar") if err != nil { t.Fatal("Fetch failed:", err) } if ok { t.Fatal("Failed to correctly detect foobar") } if r != nil { t.Fatal("Release not detected but got a returned value for it") } } func TestDetectReleasesForVariousArchives(t *testing.T) { for _, tc := range []struct { slug string prefix string }{ {"rhysd-test/test-release-zip", "v"}, {"rhysd-test/test-release-tar", "v"}, {"rhysd-test/test-release-gzip", "v"}, {"rhysd-test/test-release-xz", "release-v"}, {"rhysd-test/test-release-tar-xz", "release-"}, } { t.Run(tc.slug, func(t *testing.T) { r, ok, err := DetectLatest(tc.slug) if err != nil { t.Fatal("Fetch failed:", err) } if !ok { t.Fatal(tc.slug, "not found") } if r == nil { t.Fatal("Release not detected") } if !r.Version.Equals(semver.MustParse("1.2.3")) { t.Error("") } url := fmt.Sprintf("https://github.com/%s/releases/tag/%s1.2.3", tc.slug, tc.prefix) if r.URL != url { t.Error("URL is not correct. Want", url, "but got", r.URL) } if r.ReleaseNotes == "" { t.Error("Release note is unexpectedly empty") } if !strings.HasPrefix(r.AssetURL, fmt.Sprintf("https://github.com/%s/releases/download/%s1.2.3/", tc.slug, tc.prefix)) { t.Error("Unexpected asset URL:", r.AssetURL) } if r.Name == "" { t.Error("Release name is unexpectedly empty") } if r.AssetByteSize == 0 { t.Error("Asset's size is unexpectedly zero") } if r.AssetID == 0 { t.Error("Asset's ID is unexpectedly zero") } if r.PublishedAt.IsZero() { t.Error("Release time is unexpectedly zero") } if r.RepoOwner != "rhysd-test" { t.Error("Repo owner should be rhysd-test:", r.RepoOwner) } if !strings.HasPrefix(r.RepoName, "test-release-") { t.Error("Repo name was not properly detectd:", r.RepoName) } }) } } func TestDetectReleaseButNoAsset(t *testing.T) { _, ok, err := DetectLatest("rhysd/clever-f.vim") if err != nil { t.Fatal("Fetch failed:", err) } if ok { t.Fatal("When no asset found, result should be marked as 'not found'") } } func TestDetectNoRelease(t *testing.T) { _, ok, err := DetectLatest("rhysd/clever-f.vim") if err != nil { t.Fatal("Fetch failed:", err) } if ok { t.Fatal("When no release found, result should be marked as 'not found'") } } func TestInvalidSlug(t *testing.T) { up := DefaultUpdater() for _, slug := range []string{ "foo", "/", "foo/", "/bar", "foo/bar/piyo", } { _, _, err := up.DetectLatest(slug) if err == nil { t.Error(slug, "should be invalid slug") } if !strings.Contains(err.Error(), "Invalid slug format") { t.Error("Unexpected error for", slug, ":", err) } } } func TestNonExistingRepo(t *testing.T) { v, ok, err := DetectLatest("rhysd/non-existing-repo") if err != nil { t.Fatal("Non-existing repo should not cause an error:", v) } if ok { t.Fatal("Release for non-existing repo should not be found") } } func TestNoReleaseFound(t *testing.T) { _, ok, err := DetectLatest("rhysd/misc") if err != nil { t.Fatal("Repo having no release should not cause an error:", err) } if ok { t.Fatal("Repo having no release should not be found") } } func TestDetectFromBrokenGitHubEnterpriseURL(t *testing.T) { up, err := NewUpdater(Config{APIToken: "hogehoge", EnterpriseBaseURL: "https://example.com"}) if err != nil { t.Fatal(err) } _, ok, _ := up.DetectLatest("foo/bar") if ok { t.Fatal("Invalid GitHub Enterprise base URL should raise an error") } } func TestDetectFromGitHubEnterpriseRepo(t *testing.T) { token := os.Getenv("GITHUB_ENTERPRISE_TOKEN") base := os.Getenv("GITHUB_ENTERPRISE_BASE_URL") repo := os.Getenv("GITHUB_ENTERPRISE_REPO") if token == "" { t.Skip("because token for GHE is not found") } if base == "" { t.Skip("because base URL for GHE is not found") } if repo == "" { t.Skip("because repo slug for GHE is not found") } up, err := NewUpdater(Config{APIToken: token, EnterpriseBaseURL: base}) if err != nil { t.Fatal(err) } r, ok, err := up.DetectLatest(repo) if err != nil { t.Fatal("Fetch failed:", err) } if !ok { t.Fatal(repo, "not found") } if r == nil { t.Fatal("Release not detected") } if !r.Version.Equals(semver.MustParse("1.2.3")) { t.Error("") } } func TestFindReleaseAndAsset(t *testing.T) { EnableLog() type findReleaseAndAssetFixture struct { name string rels *github.RepositoryRelease targetVersion string filters []*regexp.Regexp expectedAsset string expectedVersion string expectedFound bool } rel1 := "rel1" v1 := "1.0.0" rel11 := "rel11" v11 := "1.1.0" asset1 := "asset1.gz" asset2 := "asset2.gz" wrongAsset1 := "asset1.yaml" asset11 := "asset11.gz" url1 := "https://asset1" url2 := "https://asset2" url11 := "https://asset11" for _, fixture := range []findReleaseAndAssetFixture{ { name: "empty fixture", rels: nil, targetVersion: "", filters: nil, expectedFound: false, }, { name: "find asset, no filters", rels: &github.RepositoryRelease{ Name: &rel1, TagName: &v1, Assets: []*github.ReleaseAsset{ { Name: &asset1, URL: &url1, }, }, }, targetVersion: "1.0.0", expectedAsset: asset1, expectedVersion: "1.0.0", expectedFound: true, }, { name: "don't find asset with wrong extension, no filters", rels: &github.RepositoryRelease{ Name: &rel11, TagName: &v11, Assets: []*github.ReleaseAsset{ { Name: &wrongAsset1, URL: &url11, }, }, }, targetVersion: "1.1.0", expectedFound: false, }, { name: "find asset with different name, no filters", rels: &github.RepositoryRelease{ Name: &rel11, TagName: &v11, Assets: []*github.ReleaseAsset{ { Name: &asset1, URL: &url11, }, }, }, targetVersion: "1.1.0", expectedAsset: asset1, expectedVersion: "1.1.0", expectedFound: true, }, { name: "find asset, no filters (2)", rels: &github.RepositoryRelease{ Name: &rel11, TagName: &v11, Assets: []*github.ReleaseAsset{ { Name: &asset11, URL: &url11, }, }, }, targetVersion: "1.1.0", expectedAsset: asset11, expectedVersion: "1.1.0", filters: nil, expectedFound: true, }, { name: "find asset, match filter", rels: &github.RepositoryRelease{ Name: &rel11, TagName: &v11, Assets: []*github.ReleaseAsset{ { Name: &asset11, URL: &url11, }, { Name: &asset1, URL: &url1, }, }, }, targetVersion: "1.1.0", filters: []*regexp.Regexp{regexp.MustCompile("11")}, expectedAsset: asset11, expectedVersion: "1.1.0", expectedFound: true, }, { name: "find asset, match another filter", rels: &github.RepositoryRelease{ Name: &rel11, TagName: &v11, Assets: []*github.ReleaseAsset{ { Name: &asset11, URL: &url11, }, { Name: &asset1, URL: &url1, }, }, }, targetVersion: "1.1.0", filters: []*regexp.Regexp{regexp.MustCompile("([^1])1{1}([^1])")}, expectedAsset: asset1, expectedVersion: "1.1.0", expectedFound: true, }, { name: "find asset, match any filter", rels: &github.RepositoryRelease{ Name: &rel11, TagName: &v11, Assets: []*github.ReleaseAsset{ { Name: &asset11, URL: &url11, }, { Name: &asset2, URL: &url2, }, }, }, targetVersion: "1.1.0", filters: []*regexp.Regexp{ regexp.MustCompile("([^1])1{1}([^1])"), regexp.MustCompile("([^1])2{1}([^1])"), }, expectedAsset: asset2, expectedVersion: "1.1.0", expectedFound: true, }, { name: "find asset, match no filter", rels: &github.RepositoryRelease{ Name: &rel11, TagName: &v11, Assets: []*github.ReleaseAsset{ { Name: &asset11, URL: &url11, }, { Name: &asset2, URL: &url2, }, }, }, targetVersion: "1.1.0", filters: []*regexp.Regexp{ regexp.MustCompile("another"), regexp.MustCompile("binary"), }, expectedFound: false, }, } { asset, ver, found := findAssetFromRelease(fixture.rels, []string{".gz"}, fixture.targetVersion, fixture.filters) if fixture.expectedFound { if !found { t.Errorf("expected to find an asset for this fixture: %q", fixture.name) continue } if asset.Name == nil { t.Errorf("invalid asset struct returned from fixture: %q, got: %v", fixture.name, asset) continue } if *asset.Name != fixture.expectedAsset { t.Errorf("expected asset %q in fixture: %q, got: %s", fixture.expectedAsset, fixture.name, *asset.Name) continue } t.Logf("asset %v, %v", asset, ver) } else if found { t.Errorf("expected not to find an asset for this fixture: %q, but got: %v", fixture.name, asset) } } }