Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add cli test mode #313

Merged
merged 7 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ jobs:
end-to-end:
name: e2e tests
runs-on: ubuntu-22.04
env:
BATS_LIB_PATH: /usr/lib
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Check out platform
Expand Down Expand Up @@ -98,10 +100,13 @@ jobs:

- name: build the CLI
run: go build .
- name: build the CLI in test mode
run: make build-test
- name: set up the config
run: cp otdfctl-example.yaml otdfctl.yaml
- name: Setup Bats and bats libs
uses: bats-core/[email protected]
- run: tests/encrypt-decrypt.bats
- run: tests/kas-grants.bats
- run: tests/profile.bats
- run: tests/kas-registry.bats
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ target/
.vscode/launch.json
otdfctl.yaml

# Ignore the tructl binary
# Ignore the binaries
otdfctl
otdfctl.*
otdfctl_testbuild
otdfctl_testbuild.*

# Misc
# Test artifacts
creds.json

# Hugo
Expand Down
38 changes: 34 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ GO_MOD_LINE = $(shell head -n 1 go.mod | cut -c 8-)
GO_MOD_NAME = $(word 1,$(subst /, ,$(GO_MOD_LINE)))
APP_CFG = $(GO_MOD_LINE)/pkg/config

GO_BUILD_FLAGS=-ldflags "-X $(APP_CFG).Version=${CURR_VERSION} -X $(APP_CFG).CommitSha=${COMMIT_SHA} -X $(APP_CFG).BuildTime=${BUILD_TIME}"
GO_BUILD_FLAGS=-ldflags " \
-X $(APP_CFG).Version=${CURR_VERSION} \
-X $(APP_CFG).CommitSha=${COMMIT_SHA} \
-X $(APP_CFG).BuildTime=${BUILD_TIME} \
"
GO_BUILD_PREFIX=$(TARGET_DIR)/$(BINARY_NAME)-${CURR_VERSION}

# If commit sha is not available try git
Expand All @@ -35,13 +39,24 @@ TARGET_DIR=target
# Output directory for the zipped artifacts
OUTPUT_DIR=output

# Build commands for each platform
PLATFORMS := darwin-amd64 darwin-arm64 linux-amd64 linux-arm linux-arm64 windows-amd64-.exe windows-arm-.exe windows-arm64-.exe
# Build commands for each platform (extra hyphen used in windows to avoid issues with the .exe extension)
PLATFORMS := \
darwin-amd64 \
darwin-arm64 \
linux-amd64 \
linux-arm \
linux-arm64 \
windows-amd64-.exe \
windows-arm-.exe \
windows-arm64-.exe

build: test clean $(addprefix build-,$(PLATFORMS)) zip-builds verify-checksums

build-%:
GOOS=$(word 1,$(subst -, ,$*)) GOARCH=$(word 2,$(subst -, ,$*)) go build $(GO_BUILD_FLAGS) -o $(GO_BUILD_PREFIX)-$(word 1,$(subst -, ,$*))-$(word 2,$(subst -, ,$*))$(word 3,$(subst -, ,$*))
GOOS=$(word 1,$(subst -, ,$*)) \
GOARCH=$(word 2,$(subst -, ,$*)) \
go build $(GO_BUILD_FLAGS) \
-o $(GO_BUILD_PREFIX)-$(word 1,$(subst -, ,$*))-$(word 2,$(subst -, ,$*))$(word 3,$(subst -, ,$*))

zip-builds:
./.github/scripts/zip-builds.sh $(BINARY_NAME)-$(CURR_VERSION) $(TARGET_DIR) $(OUTPUT_DIR)
Expand All @@ -59,6 +74,21 @@ run:
test:
go test -v ./...

.PHONY: build-test
build-test:
go build \
-ldflags "\
-X $(APP_CFG).TestMode=true \
-X $(APP_CFG).Version=${CURR_VERSION}-testbuild \
-X $(APP_CFG).CommitSha=${COMMIT_SHA} \
-X $(APP_CFG).BuildTime=${BUILD_TIME} \
" \
-o $(BINARY_NAME)_testbuild

.PHONY: test-bats
test-bats: build-test
bats ./tests

# Target for cleaning up the target directory
.PHONY: clean
clean:
Expand Down
49 changes: 22 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,30 +54,25 @@ CLI via the `man.Docs.GetDoc()` function.

## Testing

The [tests](./tests) directory contains e2e Bash Automated Test System (bats) tests for all of the cli functionality.

To install bats on MacOS:
```
$ brew install bats-core
```
Or with NPM on any OS:
```
# To install globally:
$ npm install -g bats

# To install into your project and save it as one of the "devDependencies" in
# your package.json:
$ npm install --save-dev bats
```

These tests require the platform to be running and provisioned with basic keycloak clients/users. Before running, clone https://github.com/opentdf/platform and follow [the quickstart](https://github.com/opentdf/platform?tab=readme-ov-file#quick-start) to spin it up.

Build the cli:
```
$ go build .
```

Run the bats with:
```
$ bats tests/*.bats
```
The CLI is equipped with a test mode that can be enabled by building the CLI with `config.TestMode = true`.
For convenience, the CLI can be built with `make build-test`.

**Test Mode features**:

- Use the in-memory keyring provider for user profiles
- Enable provisioning profiles for testing via `OTDFCTL_TEST_PROFILE` environment variable

### BATS

> [!NOTE]
> Bat Automated Test System (bats) is a TAP-compliant testing framework for Bash. It provides a simple way to verify that the UNIX programs you write behave as expected.

BATS is used to test the CLI from an end-to-end perspective. To run the tests you will need to ensure the following
prerequisites are met:

- bats is installed on your system
- MacOS: `brew install bats-core bats-support bats-assert`
- The platform is running and provisioned with basic keycloak clients/users
- See the [platform README](https://github.com/opentdf/platform) for instructions

To run the tests you can either run `make test-bats` or execute specific test suites with `bats tests/<test>.bats`.
5 changes: 3 additions & 2 deletions cmd/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"runtime"

"github.com/opentdf/otdfctl/pkg/cli"
"github.com/opentdf/otdfctl/pkg/config"
"github.com/opentdf/otdfctl/pkg/profiles"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -167,8 +168,8 @@ var profileSetEndpointCmd = &cobra.Command{
}

func init() {
// Profiles are not supported on Linux
if runtime.GOOS == "linux" {
// Profiles are not supported on Linux (unless mocked in test mode)
if runtime.GOOS == "linux" && config.TestMode != "true" {
return
}

Expand Down
22 changes: 14 additions & 8 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,20 @@ import (
"github.com/spf13/viper"
)

// AppName is the name of the application
// Note: use caution when renaming as it is used in various places within the CLI including for config file naming
// and in the profile store
var AppName = "otdfctl"

var Version = "0.0.0"
var BuildTime = "1970-01-01T00:00:00Z"
var CommitSha = "0000000"
var (
// AppName is the name of the application
// Note: use caution when renaming as it is used in various places within the CLI including for
// config file naming and in the profile store
AppName = "otdfctl"

Version = "0.0.0"
BuildTime = "1970-01-01T00:00:00Z"
CommitSha = "0000000"

// Test mode is used to determine if the application is running in test mode
// "true" = running in test mode
TestMode = ""
)

type Output struct {
Format string `yaml:"format" default:"styled"`
Expand Down
4 changes: 4 additions & 0 deletions pkg/profiles/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ func newStoreFactory(driver string) NewStoreInterface {
func New(opts ...profileConfigVariadicFunc) (*Profile, error) {
var err error

if testProfile != nil {
return testProfile, nil
}

config := profileConfig{
driver: PROFILE_DRIVER_DEFAULT,
}
Expand Down
2 changes: 0 additions & 2 deletions pkg/profiles/storeKeyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"github.com/zalando/go-keyring"
)

// TODO: update the store to use alternative storage methods besides keyring

type KeyringStore struct {
namespace string
key string
Expand Down
74 changes: 74 additions & 0 deletions pkg/profiles/test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package profiles

import (
"bytes"
"encoding/json"
"fmt"
"os"

"github.com/opentdf/otdfctl/pkg/config"
"github.com/zalando/go-keyring"
)

const testModeMsg = `
********************
RUNNING IN TEST MODE

test config: %s
********************

`

var (
testProfile *Profile
testCfg = os.Getenv("OTDFCTL_TEST_PROFILE")
)

type testConfig struct {
// global config is used to get the store in a bad state
GlobalConfig config.Config `json:"globalConfig,omitempty"`

// set the default profile
DefaultProfile string `json:"defaultProfile,omitempty"`

// profiles to add
Profiles []ProfileConfig `json:"profiles,omitempty"`
}

func init() {
// If running in test mode, use the mock keyring
if config.TestMode == "true" {
fmt.Printf(testModeMsg, testCfg)

keyring.MockInit()

// configure the keyring based on the test config
// unmarsal the test config
if testCfg != "" {
var err error
var cfg testConfig
if err := json.NewDecoder(bytes.NewReader([]byte(testCfg))).Decode(&cfg); err != nil {
panic(err)
}

testProfile, err = New()
if err != nil {
panic(err)
}

for _, p := range cfg.Profiles {
err := testProfile.AddProfile(p.Name, p.Endpoint, p.TlsNoVerify, cfg.DefaultProfile == p.Name)
if err != nil {
panic(err)
}
}

// set default
if cfg.DefaultProfile != "" {
if err := testProfile.SetDefaultProfile(cfg.DefaultProfile); err != nil {
panic(err)
}
}
}
}
}
Loading
Loading