Files
teleport/lib/versioncontrol/github/github_test.go
T

357 lines
7.2 KiB
Go

/*
Copyright 2022 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package github
import (
_ "embed"
"fmt"
"os"
"testing"
apiutils "github.com/gravitational/teleport/api/utils"
vc "github.com/gravitational/teleport/lib/versioncontrol"
"github.com/stretchr/testify/require"
)
// TestGithubAPI tests the github releases iterator against the real github
// api. this test is disabled by default since we use the unauthenticated endpoint
// which doesn't like frequent request spamming.
func TestGithubAPI(t *testing.T) {
if run, _ := apiutils.ParseBool(os.Getenv("TEST_GITHUB_API")); !run {
t.Skip("Test disabled in CI. Enable it by setting env variable TEST_GITHUB_API=yes")
}
t.Parallel()
var rr []vc.Target
var iter Iterator
iter.halt = "v4"
for iter.Next() {
rr = append(rr, iter.Page()...)
}
require.NoError(t, iter.Error())
seen := make(map[string]struct{})
for _, r := range rr {
seen[r.Version()] = struct{}{}
}
// some arbitrary versions we expect to exist (this may need to be updated
// at some point in the future if we ever start trimming history).
expected := []string{
"v8.0.0-beta.2",
"v7.3.19",
"v7.1.1",
"v10.0.0",
"v5.0.0-rc.2",
"v7.3.21",
"v9.3.12",
}
for _, e := range expected {
require.Contains(t, seen, e)
}
// some known tags that we aught to be filtering out
notExpected := []string{
"teleport-connect-preview-1.0.2",
"teleport-connect-preview-1.0.1",
}
for _, n := range notExpected {
require.NotContains(t, seen, n)
}
}
// TestGithubAPIError tests the api error message extraction.
func TestGithubAPIError(t *testing.T) {
if run, _ := apiutils.ParseBool(os.Getenv("TEST_GITHUB_API")); !run {
t.Skip("Test disabled in CI. Enable it by setting env variable TEST_GITHUB_API=yes")
}
t.Parallel()
var iter Iterator
// any page size + number combo that would look further than 1000 releases
// into the past should generate an error.
iter.n = 20
iter.size = 100
for iter.Next() {
require.Fail(t, "request should fail on first iteration")
}
require.Error(t, iter.Error())
require.Contains(t, iter.Error().Error(), "Only the first 1000 results are available. (422)")
}
//go:embed test_page_1.json
var page1 string
//go:embed test_page_2.json
var page2 string
//go:embed test_page_3.json
var page3 string
const cachedPageSize = 10
// TestCachedReleases verifies expected behavior of the github release iterator
// using cached pages.
func TestCachedReleases(t *testing.T) {
tts := []struct {
pages []string
pageCount int
secCount int
desc string
hitsEmptyPage bool
haltingPoint string
err error
}{
{
pages: []string{
page1,
page2,
page3, // page 3 is a partial page, which triggers iteration to halt
},
pageCount: 3,
secCount: 1,
desc: "full iteration, ending on partial page",
},
{
pages: []string{
page3,
page2,
page1,
},
pageCount: 1,
desc: "stop on first partial page",
},
{
pages: []string{
page1,
page2,
},
pageCount: 2,
secCount: 1,
hitsEmptyPage: true,
desc: "end on full page",
},
{
pages: []string{
page1,
page2,
page3,
},
pageCount: 1,
secCount: 1,
haltingPoint: "v10",
desc: "halt on fist v10.0.X release",
},
{
pages: []string{
page1,
page2,
page3,
},
pageCount: 2,
secCount: 1,
haltingPoint: "v7.3",
desc: "halt on fist v7.3.X release",
},
{
hitsEmptyPage: true,
desc: "empty case",
},
{
err: fmt.Errorf("failure"),
desc: "error case",
},
}
for _, tt := range tts {
lastPageWasEmpty := false
var iter Iterator
iter.size = cachedPageSize
iter.halt = tt.haltingPoint
iter.getPage = func(n, size int) ([]release, error) {
require.NotZero(t, n, tt.desc)
if tt.err != nil {
return nil, tt.err
}
if n > len(tt.pages) {
lastPageWasEmpty = true
return nil, nil
}
page := tt.pages[n-1]
return parsePage([]byte(page))
}
var ct int
var sec int
for iter.Next() {
ct++
require.NotZero(t, len(iter.Page()), tt.desc)
for _, target := range iter.Page() {
if target.SecurityPatch() {
sec++
}
}
}
if tt.err == nil {
require.NoError(t, iter.Error(), tt.desc)
} else {
require.Error(t, iter.Error(), tt.desc)
}
require.Equal(t, tt.pageCount, ct, tt.desc)
require.Equal(t, tt.secCount, sec, tt.desc)
require.Equal(t, tt.hitsEmptyPage, lastPageWasEmpty)
}
}
func TestVisit(t *testing.T) {
tts := []struct {
pages []string
expect string
desc string
}{
{
pages: []string{
page1,
page2,
page3,
},
expect: "v10.0.2",
desc: "all cached pages",
},
{
pages: []string{
page2,
page3,
},
expect: "v9.3.7",
desc: "earlier pages subset",
},
{
pages: []string{
page1,
},
expect: "v10.0.2",
desc: "page 1 only",
},
{
pages: []string{
page2,
},
expect: "v9.3.7",
desc: "page 2 only",
},
{
pages: []string{
page3,
},
expect: "v9.2.4",
desc: "page 3 only",
},
}
for _, tt := range tts {
var iter Iterator
iter.size = cachedPageSize
iter.getPage = func(n, size int) ([]release, error) {
if n > len(tt.pages) {
return nil, nil
}
page := tt.pages[n-1]
return parsePage([]byte(page))
}
visitor := vc.Visitor{
Current: vc.NewTarget("v1.2.3"),
}
require.NoError(t, visit(iter, &visitor), tt.desc)
require.Equal(t, tt.expect, visitor.Newest().Version(), tt.desc)
}
}
func TestLabelParse(t *testing.T) {
tts := []struct {
notes string
expect map[string]string
desc string
}{
{
notes: "Labels: Spam=Eggs, Foo=Bar",
expect: map[string]string{
"spam": "eggs",
"foo": "bar",
},
desc: "normalize caps and spaces",
},
{
notes: "labels: hello=world, greeting='Hey there! how are you?', , count=7",
expect: map[string]string{
"hello": "world",
"count": "7",
},
desc: "ignore invalid and empty pairs",
},
{
notes: `
## Heading
- list1
- list2
not real: "labels: some-label=some-val,other-label=other-val"
---
Labels: security-patch=yes
other notes`,
expect: map[string]string{
"security-patch": "yes",
},
desc: "ignore non-label lines",
},
{
notes: "labels: invalid_key=valid-val, valid-key=@invalid-val, a=b",
expect: map[string]string{
"a": "b",
},
desc: "skip labels with invalid characters",
},
{
notes: "labels foo=bar",
desc: "no valid labels line",
},
{
desc: "empty release notes",
},
}
for _, tt := range tts {
// require.Equal doesn't think nil map is the same as empty map
if tt.expect == nil {
tt.expect = map[string]string{}
}
require.Equal(t, tt.expect, parseReleaseNoteLabels(tt.notes), tt.desc)
}
}