core tests with mocks for pusher and deployer

This commit is contained in:
Luke K 2020-04-11 19:44:47 +00:00
parent 713ae960dd
commit 8d09582051
No known key found for this signature in database
GPG Key ID: 4896F75BAF2E1966
5 changed files with 55 additions and 43 deletions

View File

@ -34,28 +34,28 @@ type DNSProvider interface {
// Initializer creates the initial/stub Service Function code on first create.
type Initializer interface {
// Initialize a Service Function of the given language, in root, with name.
// Initialize a Service Function of the given name, using the templates for
// the given language, written into the given path.
Initialize(name, language, path string) error
}
// Builder of function source to runnable image.
type Builder interface {
// Build a runnable image of the function whose source is located at path,
// returning an image.
// Build a function service of the given name with source located at path.
// returns the image name built.
Build(name, path string) (image string, err error)
}
// Pusher of function image to a registry.
type Pusher interface {
// Push a runnable image of the function to a registry.
// Push the image of the function service.
Push(image string) error
}
// Deployer of function source to running status.
type Deployer interface {
// Deploy a function of the given name whose source is located at path,
// returning an address.
Deploy(name, path string) (address string, err error)
// Deploy a service function of given name, using given backing image.
Deploy(name, image string) (address string, err error)
}
// Runner runs the function locally.
@ -212,7 +212,7 @@ func (c *Client) Create(language string) (err error) {
return
}
// Push the image to the configured registry
// Push the image for the names service to the configured registry
if err = c.pusher.Push(image); err != nil {
return
}
@ -351,7 +351,7 @@ type manualBuilder struct {
output io.Writer
}
func (i *manualBuilder) Build(name, root string) (string, error) {
func (i *manualBuilder) Build(name, root string) (image string, err error) {
fmt.Fprintf(i.output, "Please manually build image for '%v' using code at '%v'\n", name, root)
return "", nil
}
@ -361,7 +361,7 @@ type manualPusher struct {
}
func (i *manualPusher) Push(image string) error {
fmt.Fprintf(i.output, "Please manually push image '%v'", image)
fmt.Fprintf(i.output, "Please manually push image '%v'\n", image)
return nil
}
@ -369,8 +369,8 @@ type manualDeployer struct {
output io.Writer
}
func (i *manualDeployer) Deploy(name, root string) (string, error) {
fmt.Fprintf(i.output, "Please manually deploy '%v' using code at '%v'\n", name, root)
func (i *manualDeployer) Deploy(name, image string) (string, error) {
fmt.Fprintf(i.output, "Please manually deploy '%v'\n", name)
return "", nil
}

View File

@ -8,13 +8,11 @@ import (
"github.com/lkingland/faas/client/mock"
)
// TestNew ensures that instantiation succeeds when invoked normally
// and fails if not given a deriveable domain.
// TestNew ensures that instantiation succeeds or fails as expected.
func TestNew(t *testing.T) {
// Instantiation with optional explicit service name should succeed.
_, err := client.New(
client.WithName("my.example.com"), // be explicity rather than path-derive
)
client.WithName("my.example.com")) // be explicit rather than path-derive
if err != nil {
t.Fatal(err)
}
@ -28,8 +26,8 @@ func TestNew(t *testing.T) {
t.Fatal(err)
}
// Instantiation without an explicit service name, and no derivable service
// name (because of limiting path recursion for deriviation) should fail.
// Instantiation without an explicit service name, but no derivable service
// name (because of limiting path recursion) should fail.
_, err = client.New(
client.WithDomainSearchLimit(0), // limit ability to derive from path.
)
@ -44,18 +42,25 @@ func TestNew(t *testing.T) {
// parameter, as it is derived from path by default.
func TestCreate(t *testing.T) {
client, err := client.New(
client.WithName("my.example.com"), // be explicity rather than path-derive
client.WithVerbose(true), // enable verbose logging
)
client.WithName("my.example.com"),
client.WithInitializer(mock.NewInitializer()),
) // be explicit rather than path-derive
if err != nil {
t.Fatal(err)
}
// missing language should error
// create a Function Service call missing language should error
if err := client.Create(""); err == nil {
t.Fatal("missing language did not generate error")
}
// Any language provided works by default, with the concrete implementation
// of the initializer being the decider if the language provided is supported.
// create a Function Service call witn an unsupported language should bubble
// the error generated by the underlying initializer.
if err := client.Create("cobol"); err == nil {
t.Fatal("unsupported language did not generate error")
}
// A supported langauge should not error.
if err := client.Create("go"); err != nil {
t.Fatal(err)
}
@ -65,10 +70,10 @@ func TestCreate(t *testing.T) {
// Initializer, Builder, Pusher and Deployer with expected parameters.
func TestCreateDelegates(t *testing.T) {
var (
path = "testdata/example.com/admin"
name = "admin.example.com"
image = "example.registry/user/datestamp-hash"
route = "https://admin.example.com/"
path = "testdata/example.com/admin" // .. in which to initialize
name = "admin.example.com" // expected to be derived
image = "my.hub/user/imagestamp" // expected image
route = "https://admin.example.com/" // expected final route
initializer = mock.NewInitializer()
builder = mock.NewBuilder()
pusher = mock.NewPusher()
@ -86,7 +91,10 @@ func TestCreateDelegates(t *testing.T) {
t.Fatal(err)
}
// The initializer should receive the name expected from the path,
// Register function delegates on the mocks which validate assertions
// -------------
// The initializer should receive the name expected from the path,
// the passed language, and an absolute path to the funciton soruce.
initializer.InitializeFn = func(name, language, path string) error {
if name != "admin.example.com" {
@ -108,22 +116,24 @@ func TestCreateDelegates(t *testing.T) {
// The builder should be invoked with a service name and path to its source
// function code. For this test, it is a name derived from the test path.
// An example image name is returned.
builder.BuildFn = func(name2, path2 string) (image2 string, err error) {
builder.BuildFn = func(name2, path2 string) (string, error) {
if name != name {
t.Fatalf("deployer expected name %v, got '%v'", name, name2)
t.Fatalf("builder expected name %v, got '%v'", name, name2)
}
expectedPath, err := filepath.Abs(path)
if err != nil {
t.Fatal(err)
}
if path2 != expectedPath {
t.Fatalf("deployer expected path '%v', got '%v'", expectedPath, path)
t.Fatalf("builder expected path '%v', got '%v'", expectedPath, path)
}
// image name would be calculated from registry, user, time and git hash
// The final image name will be determined by the builder implementation,
// but whatever it is (in this case fabricarted); it should be returned
// and later provided to the pusher.
return image, nil
}
// The pusher should be invoked with the image name generated by the builder.
// The pusher should be invoked with the image to push.
pusher.PushFn = func(image2 string) error {
if image2 != image {
t.Fatalf("pusher expected image '%v', got '%v'", image, image2)
@ -134,19 +144,20 @@ func TestCreateDelegates(t *testing.T) {
// The deployer should be invoked with the service name and image, and return
// the final accessible address.
deployer.DeployFn = func(name2, image2 string) (address string, err error) {
deployer.DeployFn = func(name2 string) (address string, err error) {
if name2 != name {
t.Fatalf("deployer expected name '%v', got '%v'", name, name2)
}
if image2 != image {
t.Fatalf("deployer expected image '%v', got '%v'", image, image2)
}
// service of given name would be deployed using the given image and
// allocated route returned.
return route, nil
}
// Invoke the Creation task using a valid language.
// Invocation
// -------------
// Invoke the creation, triggering the function delegates, and
// perform follow-up assertions that the functions were indeed invoked.
if err := client.Create("go"); err != nil {
t.Fatal(err)
}

View File

@ -11,7 +11,7 @@ func NewBuilder() *Builder {
}
}
func (i *Builder) Build(name, path string) (image string, err error) {
func (i *Builder) Build(name, path string) (string, error) {
i.BuildInvoked = true
return i.BuildFn(name, path)
}

View File

@ -2,7 +2,7 @@ package mock
type Deployer struct {
DeployInvoked bool
DeployFn func(name, path string) (address string, err error)
DeployFn func(name, image string) (address string, err error)
}
func NewDeployer() *Deployer {
@ -11,7 +11,7 @@ func NewDeployer() *Deployer {
}
}
func (i *Deployer) Deploy(name, path string) (address string, err error) {
func (i *Deployer) Deploy(name, image string) (address string, err error) {
i.DeployInvoked = true
return i.DeployFn(name, path)
return i.DeployFn(name, image)
}

View File

@ -20,6 +20,7 @@ func NewInitializer() *Initializer {
}
func (i *Initializer) Initialize(name, language, path string) error {
fmt.Printf("Validating language supported: %v\n", language)
i.InitializeInvoked = true
if !i.supportsLanguage(language) {
return errors.New(fmt.Sprintf("unsupported language '%v'", language))