Skip to content

Commit 9fcc92b

Browse files
authored
Test against mock release API (#19)
* account for Windows line endings * Test against mock release API * Improve input and HTTP response validation * work around mime type differences * make BinaryName platform-dependent to account for Windows * run E2E tests in CI * bump build timeout * set default missing baseURL * account for executables on Windows
1 parent 6727710 commit 9fcc92b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1671
-154
lines changed

.github/workflows/test.yml

+10-3
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
name: Test
4747
needs: build
4848
runs-on: ${{ matrix.os }}
49-
timeout-minutes: 15
49+
timeout-minutes: 20
5050
strategy:
5151
fail-fast: false
5252
matrix:
@@ -62,7 +62,14 @@ jobs:
6262
- name: Check out code into the Go module directory
6363
uses: actions/checkout@v2
6464

65-
- name: Go tests
66-
timeout-minutes: 10
65+
- name: Unit tests
66+
timeout-minutes: 5
67+
run: |
68+
go test -v ./...
69+
70+
- name: E2E tests
71+
timeout-minutes: 15
72+
env:
73+
E2E_TESTING: 1
6774
run: |
6875
go test -v ./...

build/git_revision.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ import (
1111
"github.com/go-git/go-git/v5"
1212
"github.com/go-git/go-git/v5/plumbing"
1313
isrc "github.com/hashicorp/hc-install/internal/src"
14+
"github.com/hashicorp/hc-install/internal/validators"
1415
"github.com/hashicorp/hc-install/product"
1516
)
1617

1718
var (
1819
cloneTimeout = 1 * time.Minute
19-
buildTimeout = 2 * time.Minute
20+
buildTimeout = 5 * time.Minute
2021
discardLogger = log.New(ioutil.Discard, "", 0)
2122
)
2223

@@ -49,11 +50,11 @@ func (gr *GitRevision) log() *log.Logger {
4950
}
5051

5152
func (gr *GitRevision) Validate() error {
52-
if gr.Product.Name == "" {
53-
return fmt.Errorf("unknown product name")
53+
if !validators.IsProductNameValid(gr.Product.Name) {
54+
return fmt.Errorf("invalid product name: %q", gr.Product.Name)
5455
}
55-
if gr.Product.BinaryName == "" {
56-
return fmt.Errorf("unknown binary name")
56+
if !validators.IsBinaryNameValid(gr.Product.BinaryName()) {
57+
return fmt.Errorf("invalid binary name: %q", gr.Product.BinaryName())
5758
}
5859

5960
bi := gr.Product.BuildInstructions
@@ -151,7 +152,7 @@ func (gr *GitRevision) Build(ctx context.Context) (string, error) {
151152
}
152153

153154
gr.log().Printf("building (timeout: %s)", buildTimeout)
154-
return bi.Build.Build(buildCtx, repoDir, installDir, gr.Product.BinaryName)
155+
return bi.Build.Build(buildCtx, repoDir, installDir, gr.Product.BinaryName())
155156
}
156157

157158
func (gr *GitRevision) Remove(ctx context.Context) error {

checkpoint/latest_version.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/hashicorp/hc-install/internal/pubkey"
1616
rjson "github.com/hashicorp/hc-install/internal/releasesjson"
1717
isrc "github.com/hashicorp/hc-install/internal/src"
18+
"github.com/hashicorp/hc-install/internal/validators"
1819
"github.com/hashicorp/hc-install/product"
1920
)
2021

@@ -55,11 +56,11 @@ func (lv *LatestVersion) log() *log.Logger {
5556
}
5657

5758
func (lv *LatestVersion) Validate() error {
58-
if lv.Product.Name == "" {
59-
return fmt.Errorf("unknown product name")
59+
if !validators.IsProductNameValid(lv.Product.Name) {
60+
return fmt.Errorf("invalid product name: %q", lv.Product.Name)
6061
}
61-
if lv.Product.BinaryName == "" {
62-
return fmt.Errorf("unknown binary name")
62+
if !validators.IsBinaryNameValid(lv.Product.BinaryName()) {
63+
return fmt.Errorf("invalid binary name: %q", lv.Product.BinaryName())
6364
}
6465

6566
return nil
@@ -117,6 +118,7 @@ func (lv *LatestVersion) Install(ctx context.Context) (string, error) {
117118
Logger: lv.log(),
118119
VerifyChecksum: !lv.SkipChecksumVerification,
119120
ArmoredPublicKey: pubkey.DefaultPublicKey,
121+
BaseURL: rels.BaseURL,
120122
}
121123
if lv.ArmoredPublicKey != "" {
122124
d.ArmoredPublicKey = lv.ArmoredPublicKey
@@ -126,7 +128,7 @@ func (lv *LatestVersion) Install(ctx context.Context) (string, error) {
126128
return "", err
127129
}
128130

129-
execPath := filepath.Join(dstDir, lv.Product.BinaryName)
131+
execPath := filepath.Join(dstDir, lv.Product.BinaryName())
130132

131133
lv.pathsToRemove = append(lv.pathsToRemove, execPath)
132134

fs/any_version.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/hashicorp/hc-install/errors"
1010
"github.com/hashicorp/hc-install/internal/src"
11+
"github.com/hashicorp/hc-install/internal/validators"
1112
"github.com/hashicorp/hc-install/product"
1213
)
1314

@@ -26,8 +27,8 @@ func (*AnyVersion) IsSourceImpl() src.InstallSrcSigil {
2627
}
2728

2829
func (av *AnyVersion) Validate() error {
29-
if av.Product.BinaryName == "" {
30-
return fmt.Errorf("unknown binary name")
30+
if !validators.IsBinaryNameValid(av.Product.BinaryName()) {
31+
return fmt.Errorf("invalid binary name: %q", av.Product.BinaryName())
3132
}
3233
return nil
3334
}
@@ -44,7 +45,7 @@ func (av *AnyVersion) log() *log.Logger {
4445
}
4546

4647
func (av *AnyVersion) Find(ctx context.Context) (string, error) {
47-
execPath, err := findFile(lookupDirs(av.ExtraPaths), av.Product.BinaryName, checkExecutable)
48+
execPath, err := findFile(lookupDirs(av.ExtraPaths), av.Product.BinaryName(), checkExecutable)
4849
if err != nil {
4950
return "", errors.SkippableErr(err)
5051
}

fs/exact_version.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/hashicorp/go-version"
1111
"github.com/hashicorp/hc-install/errors"
1212
"github.com/hashicorp/hc-install/internal/src"
13+
"github.com/hashicorp/hc-install/internal/validators"
1314
"github.com/hashicorp/hc-install/product"
1415
)
1516

@@ -41,8 +42,8 @@ func (ev *ExactVersion) log() *log.Logger {
4142
}
4243

4344
func (ev *ExactVersion) Validate() error {
44-
if ev.Product.BinaryName == "" {
45-
return fmt.Errorf("undeclared binary name")
45+
if !validators.IsBinaryNameValid(ev.Product.BinaryName()) {
46+
return fmt.Errorf("invalid binary name: %q", ev.Product.BinaryName())
4647
}
4748
if ev.Version == nil {
4849
return fmt.Errorf("undeclared version")
@@ -61,7 +62,7 @@ func (ev *ExactVersion) Find(ctx context.Context) (string, error) {
6162
ctx, cancelFunc := context.WithTimeout(ctx, timeout)
6263
defer cancelFunc()
6364

64-
execPath, err := findFile(lookupDirs(ev.ExtraPaths), ev.Product.BinaryName, func(file string) error {
65+
execPath, err := findFile(lookupDirs(ev.ExtraPaths), ev.Product.BinaryName(), func(file string) error {
6566
err := checkExecutable(file)
6667
if err != nil {
6768
return err

fs/fs.go

-37
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ package fs
33
import (
44
"io/ioutil"
55
"log"
6-
"os"
7-
"os/exec"
8-
"path/filepath"
96
"time"
107
)
118

@@ -14,38 +11,4 @@ var (
1411
discardLogger = log.New(ioutil.Discard, "", 0)
1512
)
1613

17-
func lookupDirs(extraDirs []string) []string {
18-
pathVar := os.Getenv("PATH")
19-
dirs := filepath.SplitList(pathVar)
20-
for _, ep := range extraDirs {
21-
dirs = append(dirs, ep)
22-
}
23-
return dirs
24-
}
25-
2614
type fileCheckFunc func(path string) error
27-
28-
func findFile(dirs []string, file string, f fileCheckFunc) (string, error) {
29-
for _, dir := range dirs {
30-
if dir == "" {
31-
// Unix shell semantics: path element "" means "."
32-
dir = "."
33-
}
34-
path := filepath.Join(dir, file)
35-
if err := f(path); err == nil {
36-
return path, nil
37-
}
38-
}
39-
return "", exec.ErrNotFound
40-
}
41-
42-
func checkExecutable(file string) error {
43-
d, err := os.Stat(file)
44-
if err != nil {
45-
return err
46-
}
47-
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
48-
return nil
49-
}
50-
return os.ErrPermission
51-
}

fs/fs_test.go

+5-54
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"os"
66
"path/filepath"
7+
"runtime"
78
"testing"
89

910
"github.com/hashicorp/go-version"
@@ -20,60 +21,6 @@ var (
2021
_ src.LoggerSettable = &ExactVersion{}
2122
)
2223

23-
func TestAnyVersion_notExecutable(t *testing.T) {
24-
testutil.EndToEndTest(t)
25-
26-
originalPath := os.Getenv("PATH")
27-
os.Setenv("PATH", "")
28-
t.Cleanup(func() {
29-
os.Setenv("PATH", originalPath)
30-
})
31-
32-
dirPath, fileName := createTempFile(t, "")
33-
os.Setenv("PATH", dirPath)
34-
35-
av := &AnyVersion{
36-
Product: product.Product{
37-
BinaryName: fileName,
38-
},
39-
}
40-
av.SetLogger(testutil.TestLogger())
41-
_, err := av.Find(context.Background())
42-
if err == nil {
43-
t.Fatalf("expected %s not to be found in %s", fileName, dirPath)
44-
}
45-
}
46-
47-
func TestAnyVersion_executable(t *testing.T) {
48-
testutil.EndToEndTest(t)
49-
50-
originalPath := os.Getenv("PATH")
51-
os.Setenv("PATH", "")
52-
t.Cleanup(func() {
53-
os.Setenv("PATH", originalPath)
54-
})
55-
56-
dirPath, fileName := createTempFile(t, "")
57-
os.Setenv("PATH", dirPath)
58-
59-
fullPath := filepath.Join(dirPath, fileName)
60-
err := os.Chmod(fullPath, 0700)
61-
if err != nil {
62-
t.Fatal(err)
63-
}
64-
65-
av := &AnyVersion{
66-
Product: product.Product{
67-
BinaryName: fileName,
68-
},
69-
}
70-
av.SetLogger(testutil.TestLogger())
71-
_, err = av.Find(context.Background())
72-
if err != nil {
73-
t.Fatal(err)
74-
}
75-
}
76-
7724
func TestExactVersion(t *testing.T) {
7825
t.Skip("TODO")
7926
testutil.EndToEndTest(t)
@@ -101,6 +48,10 @@ func createTempFile(t *testing.T, content string) (string, string) {
10148
tmpDir := t.TempDir()
10249
fileName := t.Name()
10350

51+
if runtime.GOOS == "windows" {
52+
fileName += ".exe"
53+
}
54+
10455
filePath := filepath.Join(tmpDir, fileName)
10556
f, err := os.Create(filePath)
10657
if err != nil {

fs/fs_unix.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//go:build !windows
2+
3+
package fs
4+
5+
import (
6+
"fmt"
7+
"os"
8+
"os/exec"
9+
"path/filepath"
10+
)
11+
12+
func lookupDirs(extraDirs []string) []string {
13+
pathVar := os.Getenv("PATH")
14+
dirs := filepath.SplitList(pathVar)
15+
for _, ep := range extraDirs {
16+
dirs = append(dirs, ep)
17+
}
18+
return dirs
19+
}
20+
21+
func findFile(dirs []string, file string, f fileCheckFunc) (string, error) {
22+
for _, dir := range dirs {
23+
if dir == "" {
24+
// Unix shell semantics: path element "" means "."
25+
dir = "."
26+
}
27+
path := filepath.Join(dir, file)
28+
if err := f(path); err == nil {
29+
return path, nil
30+
}
31+
}
32+
return "", fmt.Errorf("%s: %w", file, exec.ErrNotFound)
33+
}
34+
35+
func checkExecutable(file string) error {
36+
d, err := os.Stat(file)
37+
if err != nil {
38+
return err
39+
}
40+
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
41+
return nil
42+
}
43+
return os.ErrPermission
44+
}

0 commit comments

Comments
 (0)