Fix unit tests on Windows and add related workflow

This commit is contained in:
Andrew Keesler 2020-10-28 09:43:27 -04:00
parent 7bce16737b
commit fb53a5dc13
No known key found for this signature in database
GPG Key ID: 27CE0444346F9413
8 changed files with 164 additions and 14 deletions

47
.github/workflows/go.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: unit-tests
on: workflow_dispatch
jobs:
macos-unit-tests:
name: macOS Unit Tests
runs-on: macos-latest
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ^1.15
id: go
- name: Checkout
uses: actions/checkout@v2
- name: Cache Dependencies
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Build
run: go build -v ./...
- name: Test
run: go test -short -race -v ./...
windows-unit-tests:
name: Windows Unit Tests
runs-on: windows-latest
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ^1.15
id: go
- name: Checkout
uses: actions/checkout@v2
- name: Cache Dependencies
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Build
run: go build -v ./...
- name: Test
run: go test -short -race -v ./...

47
.github/workflows/unit-tests.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: unit-tests
on: workflow_dispatch
jobs:
macos-unit-tests:
name: macOS Unit Tests
runs-on: macos-latest
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ^1.15
id: go
- name: Checkout
uses: actions/checkout@v2
- name: Cache Dependencies
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Build
run: go build -v ./...
- name: Test
run: go test -short -race -v ./...
windows-unit-tests:
name: Windows Unit Tests
runs-on: windows-latest
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ^1.15
id: go
- name: Checkout
uses: actions/checkout@v2
- name: Cache Dependencies
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Build
run: go build -v ./...
- name: Test
run: go test -short -race -v ./...

View File

@ -7,13 +7,14 @@ import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"runtime"
"strings" "strings"
"testing" "testing"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" k8sruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
coretesting "k8s.io/client-go/testing" coretesting "k8s.io/client-go/testing"
@ -191,6 +192,12 @@ func newCredentialIssuerConfig(name, namespace, server, certificateAuthorityData
func TestRun(t *testing.T) { func TestRun(t *testing.T) {
t.Parallel() t.Parallel()
fileDoesNotExistError := "stat ./testdata/does-not-exist.yaml: no such file or directory"
if runtime.GOOS == "windows" {
fileDoesNotExistError = "CreateFile ./testdata/does-not-exist.yaml: The system cannot find the file specified."
}
tests := []struct { tests := []struct {
name string name string
mocks func(*getKubeConfigCommand) mocks func(*getKubeConfigCommand)
@ -212,7 +219,7 @@ func TestRun(t *testing.T) {
mocks: func(cmd *getKubeConfigCommand) { mocks: func(cmd *getKubeConfigCommand) {
cmd.flags.kubeconfig = "./testdata/does-not-exist.yaml" cmd.flags.kubeconfig = "./testdata/does-not-exist.yaml"
}, },
wantError: "stat ./testdata/does-not-exist.yaml: no such file or directory", wantError: fileDoesNotExistError,
}, },
{ {
name: "fail to get client", name: "fail to get client",
@ -229,7 +236,7 @@ func TestRun(t *testing.T) {
cmd.flags.idpName = "" cmd.flags.idpName = ""
cmd.flags.idpType = "" cmd.flags.idpType = ""
clientset := pinnipedfake.NewSimpleClientset() clientset := pinnipedfake.NewSimpleClientset()
clientset.PrependReactor("*", "*", func(_ coretesting.Action) (bool, runtime.Object, error) { clientset.PrependReactor("*", "*", func(_ coretesting.Action) (bool, k8sruntime.Object, error) {
return true, nil, fmt.Errorf("some error getting IDPs") return true, nil, fmt.Errorf("some error getting IDPs")
}) })
cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) {
@ -267,7 +274,7 @@ func TestRun(t *testing.T) {
name: "fail to get CredentialIssuerConfigs", name: "fail to get CredentialIssuerConfigs",
mocks: func(cmd *getKubeConfigCommand) { mocks: func(cmd *getKubeConfigCommand) {
clientset := pinnipedfake.NewSimpleClientset() clientset := pinnipedfake.NewSimpleClientset()
clientset.PrependReactor("*", "*", func(_ coretesting.Action) (bool, runtime.Object, error) { clientset.PrependReactor("*", "*", func(_ coretesting.Action) (bool, k8sruntime.Object, error) {
return true, nil, fmt.Errorf("some error getting CredentialIssuerConfigs") return true, nil, fmt.Errorf("some error getting CredentialIssuerConfigs")
}) })
cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) { cmd.kubeClientCreator = func(_ *rest.Config) (pinnipedclientset.Interface, error) {

View File

@ -5,6 +5,8 @@ package cmd
import ( import (
"bytes" "bytes"
"path/filepath"
"strings"
"testing" "testing"
"time" "time"
@ -47,7 +49,7 @@ func TestLoginOIDCCommand(t *testing.T) {
--issuer string OpenID Connect issuer URL. --issuer string OpenID Connect issuer URL.
--listen-port uint16 TCP port for localhost listener (authorization code flow only). --listen-port uint16 TCP port for localhost listener (authorization code flow only).
--scopes strings OIDC scopes to request during login. (default [offline_access,openid,email,profile]) --scopes strings OIDC scopes to request during login. (default [offline_access,openid,email,profile])
--session-cache string Path to session cache file. (default "` + cfgDir + `/sessions.yaml") --session-cache string Path to session cache file. (default "` + windowsSafeJoin(cfgDir, "sessions.yaml") + `")
--skip-browser Skip opening the browser (just print the URL). --skip-browser Skip opening the browser (just print the URL).
`), `),
}, },
@ -125,3 +127,17 @@ func TestLoginOIDCCommand(t *testing.T) {
}) })
} }
} }
// windowsSafeJoin is a function to help us get around a weird double-slash behavior in our help
// test. It should not affect the behavior of a path using '/' separators.
//
// When we create a flag with this help text
// C:\some\path\to\file.yaml
// then it shows up on the command line as this
// C:\\some\\path\\to\\file.yaml
// because (I think) the cobra library is doing some backslash escaping.
func windowsSafeJoin(s ...string) string {
joined := filepath.Join(s...)
joined = strings.ReplaceAll(joined, `\`, `\\`)
return joined
}

View File

@ -702,6 +702,7 @@ func readJWKJSON(t *testing.T, path string) []byte {
// our implementation. // our implementation.
data = bytes.ReplaceAll(data, []byte(" "), []byte{}) data = bytes.ReplaceAll(data, []byte(" "), []byte{})
data = bytes.ReplaceAll(data, []byte("\n"), []byte{}) data = bytes.ReplaceAll(data, []byte("\n"), []byte{})
data = bytes.ReplaceAll(data, []byte("\r"), []byte{})
return data return data
} }

View File

@ -4,6 +4,8 @@
package downward package downward
import ( import (
"path/filepath"
"runtime"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -11,6 +13,14 @@ import (
func TestLoad(t *testing.T) { func TestLoad(t *testing.T) {
t.Parallel() t.Parallel()
fileDoesNotExistError := "no such file or directory"
directoryDoesNotExistError := "no such file or directory"
if runtime.GOOS == "windows" {
fileDoesNotExistError = "The system cannot find the file specified."
directoryDoesNotExistError = "The system cannot find the path specified."
}
tests := []struct { tests := []struct {
name string name string
inputDir string inputDir string
@ -20,12 +30,12 @@ func TestLoad(t *testing.T) {
{ {
name: "missing directory", name: "missing directory",
inputDir: "./testdata/no-such-directory", inputDir: "./testdata/no-such-directory",
wantErr: "could not load namespace: open testdata/no-such-directory/namespace: no such file or directory", wantErr: "could not load namespace: open " + filepath.Join("testdata", "no-such-directory", "namespace") + ": " + directoryDoesNotExistError,
}, },
{ {
name: "missing labels file", name: "missing labels file",
inputDir: "./testdata/missinglabels", inputDir: "./testdata/missinglabels",
wantErr: "could not load labels: open testdata/missinglabels/labels: no such file or directory", wantErr: "could not load labels: open " + filepath.Join("testdata", "missinglabels", "labels") + ": " + fileDoesNotExistError,
}, },
{ {
name: "invalid labels file", name: "invalid labels file",

View File

@ -5,6 +5,7 @@ package filesession
import ( import (
"os" "os"
"runtime"
"testing" "testing"
"time" "time"
@ -47,6 +48,12 @@ var validSession = sessionCache{
func TestReadSessionCache(t *testing.T) { func TestReadSessionCache(t *testing.T) {
t.Parallel() t.Parallel()
directoryError := "is a directory"
if runtime.GOOS == "windows" {
directoryError = "The handle is invalid."
}
tests := []struct { tests := []struct {
name string name string
path string path string
@ -64,7 +71,7 @@ func TestReadSessionCache(t *testing.T) {
{ {
name: "other file error", name: "other file error",
path: "./testdata/", path: "./testdata/",
wantErr: "could not read session file: read ./testdata/: is a directory", wantErr: "could not read session file: read ./testdata/: " + directoryError,
}, },
{ {
name: "invalid YAML", name: "invalid YAML",

View File

@ -8,6 +8,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -20,7 +21,7 @@ import (
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
t.Parallel() t.Parallel()
tmp := t.TempDir() + "/sessions.yaml" tmp := filepath.Join(t.TempDir(), "sessions.yaml")
c := New(tmp) c := New(tmp)
require.NotNil(t, c) require.NotNil(t, c)
require.Equal(t, tmp, c.path) require.Equal(t, tmp, c.path)
@ -30,6 +31,12 @@ func TestNew(t *testing.T) {
func TestGetToken(t *testing.T) { func TestGetToken(t *testing.T) {
t.Parallel() t.Parallel()
isADirectoryError := "is a directory"
if runtime.GOOS == "windows" {
isADirectoryError = "The handle is invalid."
}
now := time.Now().Round(1 * time.Second) now := time.Now().Round(1 * time.Second)
tests := []struct { tests := []struct {
name string name string
@ -81,7 +88,7 @@ func TestGetToken(t *testing.T) {
}, },
key: oidcclient.SessionCacheKey{}, key: oidcclient.SessionCacheKey{},
wantErrors: []string{ wantErrors: []string{
"failed to read cache, resetting: could not read session file: read TEMPFILE: is a directory", "failed to read cache, resetting: could not read session file: read TEMPFILE: " + isADirectoryError,
"could not write session cache: open TEMPFILE: is a directory", "could not write session cache: open TEMPFILE: is a directory",
}, },
}, },
@ -186,7 +193,7 @@ func TestGetToken(t *testing.T) {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Parallel() t.Parallel()
tmp := t.TempDir() + "/sessions.yaml" tmp := filepath.Join(t.TempDir(), "sessions.yaml")
if tt.makeTestFile != nil { if tt.makeTestFile != nil {
tt.makeTestFile(t, tmp) tt.makeTestFile(t, tmp)
} }
@ -213,6 +220,14 @@ func TestGetToken(t *testing.T) {
func TestPutToken(t *testing.T) { func TestPutToken(t *testing.T) {
t.Parallel() t.Parallel()
notADirectoryError := "not a directory"
isADirectoryError := "is a directory"
if runtime.GOOS == "windows" {
notADirectoryError = "The system cannot find the path specified."
isADirectoryError = "The handle is invalid."
}
now := time.Now().Round(1 * time.Second) now := time.Now().Round(1 * time.Second)
tests := []struct { tests := []struct {
name string name string
@ -228,7 +243,7 @@ func TestPutToken(t *testing.T) {
require.NoError(t, ioutil.WriteFile(filepath.Dir(tmp), []byte{}, 0600)) require.NoError(t, ioutil.WriteFile(filepath.Dir(tmp), []byte{}, 0600))
}, },
wantErrors: []string{ wantErrors: []string{
"could not create session cache directory: mkdir TEMPDIR: not a directory", "could not create session cache directory: mkdir TEMPDIR: " + notADirectoryError,
}, },
}, },
{ {
@ -403,7 +418,7 @@ func TestPutToken(t *testing.T) {
}, },
}, },
wantErrors: []string{ wantErrors: []string{
"failed to read cache, resetting: could not read session file: read TEMPFILE: is a directory", "failed to read cache, resetting: could not read session file: read TEMPFILE: " + isADirectoryError,
"could not write session cache: open TEMPFILE: is a directory", "could not write session cache: open TEMPFILE: is a directory",
}, },
wantTestFile: func(t *testing.T, tmp string) { wantTestFile: func(t *testing.T, tmp string) {
@ -417,7 +432,7 @@ func TestPutToken(t *testing.T) {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Parallel() t.Parallel()
tmp := t.TempDir() + "/sessiondir/sessions.yaml" tmp := filepath.Join(t.TempDir(), "sessiondir", "sessions.yaml")
if tt.makeTestFile != nil { if tt.makeTestFile != nil {
tt.makeTestFile(t, tmp) tt.makeTestFile(t, tmp)
} }