Add TestController (#50)
This can be used in tests to get access to a controller that behaves just like a dev controller but doesn't require the normal Command bits (UI, etc). This can be enhanced over time as needed; for now it's pretty simple but it's good enough to be used for API tests. A simple project create/read API test is added as well, which serves to show the programmatic model (as well as test those functions at the API level).
This commit is contained in:
1 parent
2aca5fff03
commit
f8b456f3a7
13 files changed
+306
-32
No files matched your search
@@ -0,0 +1,41 @@
|
||||
package scopes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/watchtower/api"
|
||||
"github.com/hashicorp/watchtower/internal/servers/controller"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProjects_Crud(t *testing.T) {
|
||||
tc := controller.NewTestController(t, nil)
|
||||
defer tc.Shutdown()
|
||||
|
||||
client := tc.Client()
|
||||
org := &Organization{
|
||||
Client: client,
|
||||
}
|
||||
|
||||
name := "foo"
|
||||
|
||||
checkProject := func(step string, p *Project, apiErr *api.Error, err error) {
|
||||
assert := assert.New(t)
|
||||
assert.NoError(err, step)
|
||||
assert.Nil(apiErr, step)
|
||||
assert.NotNil(p, "returned project", step)
|
||||
assert.NotNil(p, "returned project name", step)
|
||||
assert.Equal(name, *p.Name, step)
|
||||
}
|
||||
|
||||
p, apiErr, err := org.CreateProject(tc.Context(), &Project{Name: api.String(name)})
|
||||
checkProject("create", p, apiErr, err)
|
||||
|
||||
p, apiErr, err = org.ReadProject(tc.Context(), &Project{Id: p.Id})
|
||||
checkProject("read", p, apiErr, err)
|
||||
|
||||
// TODO: Update and Delete
|
||||
|
||||
// TODO: Error conditions once the proper errors are being returned.
|
||||
// Probably as parallel subtests against the same DB.
|
||||
}
|
||||
@@ -17,7 +17,7 @@ require (
|
||||
github.com/golang/protobuf v1.4.1
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.5
|
||||
github.com/hashicorp/errwrap v1.0.0
|
||||
github.com/hashicorp/go-alpnmux v0.0.0-20200323180452-dee08f00df54
|
||||
github.com/hashicorp/go-alpnmux v0.0.0-20200513011953-0293f5d23c31
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1
|
||||
github.com/hashicorp/go-hclog v0.13.0
|
||||
github.com/hashicorp/go-kms-wrapping v0.5.8
|
||||
@@ -27,7 +27,7 @@ require (
|
||||
github.com/hashicorp/go-sockaddr v1.0.2
|
||||
github.com/hashicorp/go-uuid v1.0.2
|
||||
github.com/hashicorp/hcl v1.0.0
|
||||
github.com/hashicorp/vault v1.2.1-0.20200511170424-4928e34922e8
|
||||
github.com/hashicorp/vault v1.2.1-0.20200513002223-fc89625cd196
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200511170424-4928e34922e8
|
||||
github.com/jackc/pgx/v4 v4.6.0
|
||||
github.com/jinzhu/gorm v1.9.12
|
||||
|
||||
@@ -570,6 +570,8 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-alpnmux v0.0.0-20200323180452-dee08f00df54 h1:WhMHPQosFuXFjt2wpRzX2eqR1WpnS+35MFzmG3trg3Y=
|
||||
github.com/hashicorp/go-alpnmux v0.0.0-20200323180452-dee08f00df54/go.mod h1:KvpteZzIafT4tRAuQ9vVRBgZyqeVCS2B2177fNAyEZc=
|
||||
github.com/hashicorp/go-alpnmux v0.0.0-20200513011953-0293f5d23c31 h1:pxqI71/0R1WIASjQEJ9W9skCKYiREEkRoXFvHCZH1pg=
|
||||
github.com/hashicorp/go-alpnmux v0.0.0-20200513011953-0293f5d23c31/go.mod h1:KvpteZzIafT4tRAuQ9vVRBgZyqeVCS2B2177fNAyEZc=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
@@ -656,16 +658,18 @@ github.com/hashicorp/raft-snapshot v1.0.2-0.20190827162939-8117efcc5aab/go.mod h
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/serf v0.8.3 h1:MWYcmct5EtKz0efYooPcL0yNkem+7kWxqXDi/UIh+8k=
|
||||
github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k=
|
||||
github.com/hashicorp/vault v1.2.1-0.20200511170424-4928e34922e8 h1:++KBH9oO+9rr5qXvNuPV1tlwcUVmwxuXjxUi2tp5he4=
|
||||
github.com/hashicorp/vault v1.2.1-0.20200511170424-4928e34922e8/go.mod h1:1/d/RuAcpBW/XXJ7RbRdezTGXc3ZLK/AAj6gPg4wIf8=
|
||||
github.com/hashicorp/vault v1.2.1-0.20200513002223-fc89625cd196 h1:QplfqcymYufhcbkqY8aMrp5ukBveoZ3+rQK0CotAFhw=
|
||||
github.com/hashicorp/vault v1.2.1-0.20200513002223-fc89625cd196/go.mod h1:1/d/RuAcpBW/XXJ7RbRdezTGXc3ZLK/AAj6gPg4wIf8=
|
||||
github.com/hashicorp/vault-plugin-auth-alicloud v0.5.5 h1:JYf3VYpKs7mOdtcwZWi73S82oXrC/JR7uoPVUd8c4Hk=
|
||||
github.com/hashicorp/vault-plugin-auth-alicloud v0.5.5/go.mod h1:sQ+VNwPQlemgXHXikYH6onfH9gPwDZ1GUVRLz0ZvHx8=
|
||||
github.com/hashicorp/vault-plugin-auth-azure v0.5.5 h1:kN79ai+aMVU9hUmwscHjmweW2fGa8V/t+ScIchPZGrk=
|
||||
github.com/hashicorp/vault-plugin-auth-azure v0.5.5/go.mod h1:RCVBsf8AJndh4c6iGZtvVZFui9SG0Bj9fnF0SodNIkw=
|
||||
github.com/hashicorp/vault-plugin-auth-centrify v0.5.5 h1:YXxXt6o6I1rOkYW+hADK0vd+uVMj4C6Qs3jBrQlKQcY=
|
||||
github.com/hashicorp/vault-plugin-auth-centrify v0.5.5/go.mod h1:GfRoy7NHsuR/ogmZtbExdJXUwbfwcxPrS9xzkyy2J/c=
|
||||
github.com/hashicorp/vault-plugin-auth-cf v0.5.4 h1:2wl+qK7cLpr4u/lkv5DgvkNoKKhHC69H1QmoXOnArLw=
|
||||
github.com/hashicorp/vault-plugin-auth-cf v0.5.4/go.mod h1:idkFYHc6ske2BE7fe00SpH+SBIlqDKz8vk/IPLJuX2o=
|
||||
github.com/hashicorp/vault-plugin-auth-gcp v0.5.1/go.mod h1:eLj92eX8MPI4vY1jaazVLF2sVbSAJ3LRHLRhF/pUmlI=
|
||||
github.com/hashicorp/vault-plugin-auth-gcp v0.6.1 h1:WXTuja3WC2BdZekYCnzuZGoVvZTAGH8kSDUHzOK2PQY=
|
||||
github.com/hashicorp/vault-plugin-auth-gcp v0.6.1/go.mod h1:8eBRzg+JIhAaDBfDndDAQKIhDrQ3WW8OPklxAYftNFs=
|
||||
github.com/hashicorp/vault-plugin-auth-jwt v0.6.2 h1:fp6Rk89iPjDS8dyEK7lEauYE/UhkgkHbmwRZKuQA01U=
|
||||
github.com/hashicorp/vault-plugin-auth-jwt v0.6.2/go.mod h1:SFadxIfoLGzugEjwUUmUaCGbsYEz2/jJymZDDQjEqYg=
|
||||
|
||||
@@ -3,7 +3,6 @@ package base
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -37,7 +36,7 @@ type WorkerAuthCertInfo struct {
|
||||
}
|
||||
|
||||
// Factory is the factory function to create a listener.
|
||||
type ListenerFactory func(*configutil.Listener, io.Writer, hclog.Logger, cli.Ui) (*alpnmux.ALPNMux, map[string]string, reloadutil.ReloadFunc, error)
|
||||
type ListenerFactory func(*configutil.Listener, hclog.Logger, cli.Ui) (*alpnmux.ALPNMux, map[string]string, reloadutil.ReloadFunc, error)
|
||||
|
||||
// BuiltinListeners is the list of built-in listener types.
|
||||
var BuiltinListeners = map[string]ListenerFactory{
|
||||
@@ -46,16 +45,16 @@ var BuiltinListeners = map[string]ListenerFactory{
|
||||
|
||||
// New creates a new listener of the given type with the given
|
||||
// configuration. The type is looked up in the BuiltinListeners map.
|
||||
func NewListener(l *configutil.Listener, w io.Writer, logger hclog.Logger, ui cli.Ui) (*alpnmux.ALPNMux, map[string]string, reloadutil.ReloadFunc, error) {
|
||||
func NewListener(l *configutil.Listener, logger hclog.Logger, ui cli.Ui) (*alpnmux.ALPNMux, map[string]string, reloadutil.ReloadFunc, error) {
|
||||
f, ok := BuiltinListeners[l.Type]
|
||||
if !ok {
|
||||
return nil, nil, nil, fmt.Errorf("unknown listener type: %q", l.Type)
|
||||
}
|
||||
|
||||
return f(l, w, logger, ui)
|
||||
return f(l, logger, ui)
|
||||
}
|
||||
|
||||
func tcpListenerFactory(l *configutil.Listener, _ io.Writer, logger hclog.Logger, ui cli.Ui) (*alpnmux.ALPNMux, map[string]string, reloadutil.ReloadFunc, error) {
|
||||
func tcpListenerFactory(l *configutil.Listener, logger hclog.Logger, ui cli.Ui) (*alpnmux.ALPNMux, map[string]string, reloadutil.ReloadFunc, error) {
|
||||
if l.Address == "" {
|
||||
if len(l.Purpose) == 1 && l.Purpose[0] == "cluster" {
|
||||
l.Address = "127.0.0.1:9201"
|
||||
@@ -72,6 +71,15 @@ func tcpListenerFactory(l *configutil.Listener, _ io.Writer, logger hclog.Logger
|
||||
bindProto = "tcp4"
|
||||
}
|
||||
|
||||
if l.RandomPort {
|
||||
colon := strings.Index(l.Address, ":")
|
||||
if colon != -1 {
|
||||
// colon+1 because it needs to end in a colon to be automatically
|
||||
// assigned by Go
|
||||
l.Address = l.Address[0 : colon+1]
|
||||
}
|
||||
}
|
||||
|
||||
ln, err := net.Listen(bindProto, l.Address)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/vault/internalshared/configutil"
|
||||
"github.com/hashicorp/vault/internalshared/gatedwriter"
|
||||
"github.com/hashicorp/vault/internalshared/reloadutil"
|
||||
@@ -239,7 +240,7 @@ func (b *Server) SetupListeners(ui cli.Ui, config *configutil.SharedConfig) erro
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
}
|
||||
|
||||
lnMux, props, reloadFunc, err := NewListener(lnConfig, b.GatedWriter, b.Logger, ui)
|
||||
lnMux, props, reloadFunc, err := NewListener(lnConfig, b.Logger, ui)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error initializing listener of type %s: %w", lnConfig.Type, err)
|
||||
}
|
||||
@@ -356,16 +357,18 @@ func (b *Server) SetupKMSes(ui cli.Ui, config *configutil.SharedConfig, purposes
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Server) RunShutdownFuncs(ui cli.Ui) {
|
||||
func (b *Server) RunShutdownFuncs() error {
|
||||
var mErr *multierror.Error
|
||||
for _, f := range b.ShutdownFuncs {
|
||||
if err := f(); err != nil {
|
||||
ui.Error(fmt.Sprintf("Error running a shutdown task: %s", err.Error()))
|
||||
mErr = multierror.Append(mErr, err)
|
||||
}
|
||||
}
|
||||
return mErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
func (b *Server) CreateDevDatabase(dialect string) error {
|
||||
c, url, err := db.InitDbInDocker(dialect)
|
||||
c, url, container, err := db.InitDbInDocker(dialect)
|
||||
if err != nil {
|
||||
c()
|
||||
return fmt.Errorf("unable to start dev database with dialect %s: %w", dialect, err)
|
||||
@@ -376,6 +379,10 @@ func (b *Server) CreateDevDatabase(dialect string) error {
|
||||
|
||||
b.InfoKeys = append(b.InfoKeys, "dev database url")
|
||||
b.Info["dev database url"] = b.DevDatabaseUrl
|
||||
if container != "" {
|
||||
b.InfoKeys = append(b.InfoKeys, "dev database container")
|
||||
b.Info["dev database container"] = strings.TrimPrefix(container, "/")
|
||||
}
|
||||
|
||||
dbase, err := gorm.Open(dialect, url)
|
||||
if err != nil {
|
||||
|
||||
@@ -259,7 +259,11 @@ func (c *Command) Run(args []string) int {
|
||||
c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase)
|
||||
}
|
||||
|
||||
defer c.RunShutdownFuncs(c.UI)
|
||||
defer func() {
|
||||
if err := c.RunShutdownFuncs(); err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error running shutdown tasks: %w", err).Error())
|
||||
}
|
||||
}()
|
||||
|
||||
c.PrintInfo(c.UI, "controller")
|
||||
c.ReleaseLogGate()
|
||||
|
||||
@@ -220,7 +220,11 @@ func (c *Command) Run(args []string) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
defer c.RunShutdownFuncs(c.UI)
|
||||
defer func() {
|
||||
if err := c.RunShutdownFuncs(); err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error running shutdown tasks: %w", err).Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if err := c.CreateDevDatabase("postgres"); err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error creating dev database container: %w", err).Error())
|
||||
|
||||
@@ -176,7 +176,11 @@ func (c *Command) Run(args []string) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
defer c.RunShutdownFuncs(c.UI)
|
||||
defer func() {
|
||||
if err := c.RunShutdownFuncs(); err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error running shutdown tasks: %w", err).Error())
|
||||
}
|
||||
}()
|
||||
|
||||
c.PrintInfo(c.UI, "worker")
|
||||
c.ReleaseLogGate()
|
||||
|
||||
+12
-12
@@ -57,31 +57,31 @@ func Migrate(connectionUrl string, migrationsDirectory string) error {
|
||||
}
|
||||
|
||||
// InitDbInDocker initializes the data store within docker or an existing PG_URL
|
||||
func InitDbInDocker(dialect string) (cleanup func() error, retURL string, err error) {
|
||||
func InitDbInDocker(dialect string) (cleanup func() error, retURL, container string, err error) {
|
||||
switch dialect {
|
||||
case "postgres":
|
||||
if os.Getenv("PG_URL") != "" {
|
||||
if err := InitStore(dialect, func() error { return nil }, os.Getenv("PG_URL")); err != nil {
|
||||
return func() error { return nil }, os.Getenv("PG_URL"), fmt.Errorf("error initializing store: %w", err)
|
||||
return func() error { return nil }, os.Getenv("PG_URL"), "", fmt.Errorf("error initializing store: %w", err)
|
||||
}
|
||||
return func() error { return nil }, os.Getenv("PG_URL"), nil
|
||||
return func() error { return nil }, os.Getenv("PG_URL"), "", nil
|
||||
}
|
||||
}
|
||||
c, url, err := StartDbInDocker(dialect)
|
||||
c, url, container, err := StartDbInDocker(dialect)
|
||||
if err != nil {
|
||||
return func() error { return nil }, "", fmt.Errorf("could not start docker: %w", err)
|
||||
return func() error { return nil }, "", "", fmt.Errorf("could not start docker: %w", err)
|
||||
}
|
||||
if err := InitStore(dialect, c, url); err != nil {
|
||||
return func() error { return nil }, "", fmt.Errorf("error initializing store: %w", err)
|
||||
return func() error { return nil }, "", "", fmt.Errorf("error initializing store: %w", err)
|
||||
}
|
||||
return c, url, nil
|
||||
return c, url, container, nil
|
||||
}
|
||||
|
||||
// StartDbInDocker
|
||||
func StartDbInDocker(dialect string) (cleanup func() error, retURL string, err error) {
|
||||
func StartDbInDocker(dialect string) (cleanup func() error, retURL, container string, err error) {
|
||||
pool, err := dockertest.NewPool("")
|
||||
if err != nil {
|
||||
return func() error { return nil }, "", fmt.Errorf("could not connect to docker: %w", err)
|
||||
return func() error { return nil }, "", "", fmt.Errorf("could not connect to docker: %w", err)
|
||||
}
|
||||
|
||||
var resource *dockertest.Resource
|
||||
@@ -94,7 +94,7 @@ func StartDbInDocker(dialect string) (cleanup func() error, retURL string, err e
|
||||
panic(fmt.Sprintf("unknown dialect %q", dialect))
|
||||
}
|
||||
if err != nil {
|
||||
return func() error { return nil }, "", fmt.Errorf("could not start resource: %w", err)
|
||||
return func() error { return nil }, "", "", fmt.Errorf("could not start resource: %w", err)
|
||||
}
|
||||
|
||||
cleanup = func() error {
|
||||
@@ -115,10 +115,10 @@ func StartDbInDocker(dialect string) (cleanup func() error, retURL string, err e
|
||||
defer db.Close()
|
||||
return nil
|
||||
}); err != nil {
|
||||
return func() error { return nil }, "", fmt.Errorf("could not connect to docker: %w", err)
|
||||
return func() error { return nil }, "", "", fmt.Errorf("could not connect to docker: %w", err)
|
||||
}
|
||||
|
||||
return cleanup, url, nil
|
||||
return cleanup, url, resource.Container.Name, nil
|
||||
}
|
||||
|
||||
// InitStore will execute the migrations needed to initialize the store for tests
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
func TestOpen(t *testing.T) {
|
||||
cleanup, url, err := StartDbInDocker("postgres")
|
||||
cleanup, url, _, err := StartDbInDocker("postgres")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func TestOpen(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMigrate(t *testing.T) {
|
||||
cleanup, url, err := StartDbInDocker("postgres")
|
||||
cleanup, url, _, err := StartDbInDocker("postgres")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestSetup(t *testing.T, dialect string) (func() error, *gorm.DB, string) {
|
||||
cleanup := func() error { return nil }
|
||||
var url string
|
||||
var err error
|
||||
cleanup, url, err = InitDbInDocker(dialect)
|
||||
cleanup, url, _, err = InitDbInDocker(dialect)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -43,4 +43,3 @@ func TestWrapper(t *testing.T) wrapping.Wrapper {
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/watchtower/api"
|
||||
"github.com/hashicorp/watchtower/internal/cmd/base"
|
||||
"github.com/hashicorp/watchtower/internal/cmd/config"
|
||||
)
|
||||
|
||||
// TestController wraps a base.Server and Controller to provide a
|
||||
// fully-programmatic controller for tests. Error checking (for instance, for
|
||||
// valid config) is not stringent at the moment.
|
||||
type TestController struct {
|
||||
b *base.Server
|
||||
c *Controller
|
||||
t *testing.T
|
||||
client *api.Client
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// Controller returns the underlying controller
|
||||
func (tc *TestController) Controller() *Controller {
|
||||
return tc.c
|
||||
}
|
||||
|
||||
func (tc *TestController) Client() *api.Client {
|
||||
return tc.client
|
||||
}
|
||||
|
||||
func (tc *TestController) Context() context.Context {
|
||||
return tc.ctx
|
||||
}
|
||||
|
||||
func (tc *TestController) Cancel() {
|
||||
tc.cancel()
|
||||
}
|
||||
|
||||
func (tc *TestController) buildClient() {
|
||||
var apiLn *base.ServerListener
|
||||
for _, listener := range tc.b.Listeners {
|
||||
if listener.Config.Purpose[0] == "api" {
|
||||
apiLn = listener
|
||||
break
|
||||
}
|
||||
}
|
||||
if apiLn == nil {
|
||||
tc.t.Fatal("could not find api listener")
|
||||
}
|
||||
|
||||
tcpAddr, ok := apiLn.Mux.Addr().(*net.TCPAddr)
|
||||
if !ok {
|
||||
tc.t.Fatal("could not parse address as a TCP addr")
|
||||
}
|
||||
addr := fmt.Sprintf("http://%s:%d", tcpAddr.IP.String(), tcpAddr.Port)
|
||||
client, err := api.NewClient(nil)
|
||||
if err != nil {
|
||||
tc.t.Fatal(fmt.Errorf("error creating client: %w", err))
|
||||
}
|
||||
if err := client.SetAddr(addr); err != nil {
|
||||
tc.t.Fatal(fmt.Errorf("error setting client address: %w", err))
|
||||
}
|
||||
|
||||
tc.client = client
|
||||
}
|
||||
|
||||
// Shutdown runs any cleanup functions; be sure to run this after your test is
|
||||
// done
|
||||
func (tc *TestController) Shutdown() {
|
||||
if tc.b != nil {
|
||||
close(tc.b.ShutdownCh)
|
||||
}
|
||||
|
||||
tc.cancel()
|
||||
|
||||
if tc.c != nil {
|
||||
if err := tc.c.Shutdown(); err != nil {
|
||||
tc.t.Error(err)
|
||||
}
|
||||
}
|
||||
if tc.b != nil {
|
||||
if err := tc.b.RunShutdownFuncs(); err != nil {
|
||||
tc.t.Error(err)
|
||||
}
|
||||
if tc.b.DestroyDevDatabase() != nil {
|
||||
if err := tc.b.DestroyDevDatabase(); err != nil {
|
||||
tc.t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TestControllerOpts struct {
|
||||
// Config; if not provided a dev one will be created
|
||||
Config *config.Config
|
||||
|
||||
// DefaultOrgId is the default org ID to use, if set. Can also be provided
|
||||
// in the normal config.
|
||||
DefaultOrgId string
|
||||
|
||||
// DisableDatabaseCreation can be set true to disable creating a dev
|
||||
// database
|
||||
DisableDatabaseCreation bool
|
||||
}
|
||||
|
||||
func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
tc := &TestController{
|
||||
t: t,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
|
||||
if opts == nil {
|
||||
opts = new(TestControllerOpts)
|
||||
}
|
||||
|
||||
// Base server
|
||||
tc.b = base.NewServer(nil)
|
||||
tc.b.Command = &base.Command{
|
||||
ShutdownCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
// Get dev config, or use a provided one
|
||||
var err error
|
||||
if opts.Config == nil {
|
||||
opts.Config, err = config.DevController()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set default org ID, preferring one passed in from opts over config
|
||||
if opts.Config.DefaultOrgId != "" {
|
||||
tc.b.DefaultOrgId = opts.Config.DefaultOrgId
|
||||
}
|
||||
if opts.DefaultOrgId != "" {
|
||||
tc.b.DefaultOrgId = opts.DefaultOrgId
|
||||
}
|
||||
|
||||
// Start a logger
|
||||
tc.b.Logger = hclog.New(&hclog.LoggerOptions{
|
||||
Level: hclog.Trace,
|
||||
})
|
||||
|
||||
// Set up KMSes
|
||||
if err := tc.b.SetupKMSes(nil, opts.Config.SharedConfig, []string{"controller", "worker-auth"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Ensure the listeners use random port allocation
|
||||
for _, listener := range opts.Config.Listeners {
|
||||
listener.RandomPort = true
|
||||
}
|
||||
if err := tc.b.SetupListeners(nil, opts.Config.SharedConfig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !opts.DisableDatabaseCreation {
|
||||
if err := tc.b.CreateDevDatabase("postgres"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
conf := &Config{
|
||||
RawConfig: opts.Config,
|
||||
Server: tc.b,
|
||||
}
|
||||
|
||||
tc.c, err = New(conf)
|
||||
if err != nil {
|
||||
tc.Shutdown()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tc.buildClient()
|
||||
|
||||
if err := tc.c.Start(); err != nil {
|
||||
tc.Shutdown()
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return tc
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_TestController(t *testing.T) {
|
||||
t.Run("startup and shutdown", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
tc := NewTestController(t, nil)
|
||||
defer tc.Shutdown()
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user