mirror of https://github.com/knative/func.git
680 lines
20 KiB
Go
680 lines
20 KiB
Go
package faas_test
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/boson-project/faas"
|
|
"github.com/boson-project/faas/mock"
|
|
)
|
|
|
|
// TestNew ensures that instantiation succeeds or fails as expected.
|
|
func TestNew(t *testing.T) {
|
|
// New client with all defaults
|
|
_, err := faas.New()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// New client with optional verbosity enabled
|
|
_, err = faas.New(faas.WithVerbose(true))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestNewWithInterferingFiles asserts that attempting to create a new client rooted
|
|
// to a directory with any visible files or any known contentious files (configs) fails.
|
|
func TestNewWithInterferingFiles(t *testing.T) {
|
|
// TODO
|
|
}
|
|
|
|
// TestCreate ensures that creation of a supported runtime succeeds with all
|
|
// defaults (base case).
|
|
func TestCreate(t *testing.T) {
|
|
// Client with all defaults other than an initializer that verifies the
|
|
// specified runtime.
|
|
client, err := faas.New(
|
|
faas.WithInitializer(mock.NewInitializer()))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create the test function root
|
|
root := "testdata/example.com/admin"
|
|
err = os.MkdirAll(root, 0700)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
|
|
// A supported langauge should not error
|
|
if err := client.Create("go", "", "", root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestCreateUnderivableName ensures that attempting to create a new function
|
|
// when the name is underivable (and no explicit name is provided) generates
|
|
// an error.
|
|
func TestCreateUnderivableName(t *testing.T) {
|
|
|
|
// Create the test function root
|
|
root := "testdata/example.com/admin"
|
|
err := os.MkdirAll(root, 0700)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
|
|
// Instantiation without an explicit service name, but no derivable service
|
|
// name (because of limiting path recursion) should fail.
|
|
client, err := faas.New(
|
|
faas.WithDomainSearchLimit(0)) // limit ability to derive from path.
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// create a Function with a missing name, but when the name is
|
|
// underivable (in this case due to limited recursion, but would equally
|
|
// apply if run from /tmp or similar)
|
|
if err := client.Create("go", "", "", ""); err == nil {
|
|
t.Fatal("did not receive expected error")
|
|
}
|
|
|
|
}
|
|
|
|
// TestCreateMissingRuntime ensures that instantiation fails if the required
|
|
// runtime parameter is not passed to Create.
|
|
func TestCreateMissingRuntime(t *testing.T) {
|
|
client, err := faas.New(
|
|
faas.WithInitializer(mock.NewInitializer()))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// create a Function call missing runtime should error
|
|
if err := client.Create("", "", "", ""); err == nil {
|
|
t.Fatal("missing runtime did not generate error")
|
|
}
|
|
}
|
|
|
|
// TestCreateUnsupportedRuntime ensures that instantiation fails if the required
|
|
// runtime parameter is of an unsupported runtime.
|
|
func TestCreateUnsupportedRuntime(t *testing.T) {
|
|
client, err := faas.New(
|
|
faas.WithInitializer(mock.NewInitializer())) // validtes runtime passed
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// create a Function call witn an unsupported runtime should bubble
|
|
// the error generated by the underlying initializer.
|
|
if err := client.Create("cobol", "", "", ""); err == nil {
|
|
t.Fatal("unsupported runtime did not generate error")
|
|
}
|
|
}
|
|
|
|
// TestCreateDelegeates ensures that a call to Create invokes the Function
|
|
// Initializer, Builder, Pusher and Deployer with expected parameters.
|
|
func TestCreateDelegates(t *testing.T) {
|
|
var (
|
|
root = "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()
|
|
deployer = mock.NewDeployer()
|
|
)
|
|
|
|
// Create the test function root
|
|
err := os.MkdirAll(root, 0700)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
|
|
client, err := faas.New(
|
|
faas.WithInitializer(initializer), // will receive the final value
|
|
faas.WithBuilder(builder), // builds an image
|
|
faas.WithPusher(pusher), // pushes images to a registry
|
|
faas.WithDeployer(deployer), // deploys images as a running service
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Register function delegates on the mocks which validate assertions
|
|
// -------------
|
|
|
|
// The initializer should receive the name expected from the path,
|
|
// the passed runtime, and an absolute path to the funciton soruce.
|
|
initializer.InitializeFn = func(runtime, context, path string) error {
|
|
if runtime != "go" {
|
|
t.Fatalf("initializer expected runtime 'go', got '%v'", runtime)
|
|
}
|
|
if context != "" {
|
|
t.Fatalf("initializer expected empty context template name, got '%v'", name)
|
|
}
|
|
expectedPath, err := filepath.Abs("./testdata/example.com/admin")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if path != expectedPath {
|
|
t.Fatalf("initializer expected path '%v', got '%v'", expectedPath, path)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 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) (string, error) {
|
|
if name != name2 {
|
|
t.Fatalf("builder expected name %v, got '%v'", name, name2)
|
|
}
|
|
expectedPath, err := filepath.Abs(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if path2 != expectedPath {
|
|
t.Fatalf("builder expected path '%v', got '%v'", expectedPath, root)
|
|
}
|
|
// The final image name will be determined by the builder implementation,
|
|
// but whatever it is (in this case fabricated); it should be returned
|
|
// and later provided to the pusher.
|
|
return image, nil
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
// image of given name wouold be pushed to the configured registry.
|
|
return nil
|
|
}
|
|
|
|
// 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) {
|
|
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
|
|
}
|
|
|
|
// Invocation
|
|
// -------------
|
|
|
|
// Invoke the creation, triggering the function delegates, and
|
|
// perform follow-up assertions that the functions were indeed invoked.
|
|
if err := client.Create("go", "", "", root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Confirm that each delegate was invoked.
|
|
if !initializer.InitializeInvoked {
|
|
t.Fatal("initializer was not invoked")
|
|
}
|
|
if !builder.BuildInvoked {
|
|
t.Fatal("builder was not invoked")
|
|
}
|
|
if !pusher.PushInvoked {
|
|
t.Fatal("pusher was not invoked")
|
|
}
|
|
if !deployer.DeployInvoked {
|
|
t.Fatal("deployer was not invoked")
|
|
}
|
|
}
|
|
|
|
// TestCreateLocal ensures that when set to local-only mode, Create only invokes
|
|
// the initializer and builder.
|
|
func TestCreateLocal(t *testing.T) {
|
|
var (
|
|
root = "testdata/example.com/admin"
|
|
initializer = mock.NewInitializer()
|
|
builder = mock.NewBuilder()
|
|
pusher = mock.NewPusher()
|
|
deployer = mock.NewDeployer()
|
|
dnsProvider = mock.NewDNSProvider()
|
|
)
|
|
|
|
// Create the test function root
|
|
err := os.MkdirAll(root, 0700)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
|
|
// Create the test function root
|
|
err = os.MkdirAll(root, 0700)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
|
|
client, err := faas.New(
|
|
faas.WithInitializer(initializer), // will receive the final value
|
|
faas.WithBuilder(builder), // builds an image
|
|
faas.WithPusher(pusher), // pushes images to a registry
|
|
faas.WithDeployer(deployer), // deploys images as a running service
|
|
faas.WithDNSProvider(dnsProvider), // will receive the final value
|
|
faas.WithLocal(true), // set to local function mode.
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create a new Function
|
|
if err := client.Create("go", "", "", root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Ensure that none of the remote delegates were invoked
|
|
if pusher.PushInvoked {
|
|
t.Fatal("Push invoked in local mode.")
|
|
}
|
|
if deployer.DeployInvoked {
|
|
t.Fatal("Deploy invoked in local mode.")
|
|
}
|
|
if dnsProvider.ProvideInvoked {
|
|
t.Fatal("DNS provider invoked in local mode.")
|
|
}
|
|
|
|
}
|
|
|
|
// TestCreateInternal ensures that when set to internal mode, Creation invokes the deployer with the "no public route" option and subsequent updates also are flagged to not create the route.
|
|
func TestCreateInternal(t *testing.T) {
|
|
fmt.Printf("TODO: TestCreateInternal")
|
|
}
|
|
|
|
// TestCreateDomain ensures that the effective domain is dervied from
|
|
// directory structure. See the unit tests for pathToDomain for details.
|
|
func TestCreateDomain(t *testing.T) {
|
|
// Create the test function root
|
|
root := "testdata/example.com/admin"
|
|
err := os.MkdirAll(root, 0700)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
|
|
// the mock dns provider does nothing but receive the caluclated
|
|
// domain name via it's Provide(domain) method, which is the value
|
|
// being tested here.
|
|
dnsProvider := mock.NewDNSProvider()
|
|
|
|
client, err := faas.New(
|
|
faas.WithDomainSearchLimit(1), // Limit recursion to one level
|
|
faas.WithDNSProvider(dnsProvider), // will receive the final value
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := client.Create("go", "", "", root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !dnsProvider.ProvideInvoked {
|
|
t.Fatal("dns provider was not invoked")
|
|
}
|
|
if dnsProvider.NameRequested != "admin.example.com" {
|
|
t.Fatalf("expected 'example.com', got '%v'", dnsProvider.NameRequested)
|
|
}
|
|
}
|
|
|
|
// TestCreateSubdomain ensures that a subdirectory is interpreted as a subdomain
|
|
// when calculating final domain. See the unit tests for pathToDomain for the
|
|
// details and edge cases of this caluclation.
|
|
func TestCreateSubdomain(t *testing.T) {
|
|
// Create the test function root
|
|
root := "testdata/example.com/admin"
|
|
err := os.MkdirAll(root, 0700)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
|
|
dnsProvider := mock.NewDNSProvider()
|
|
client, err := faas.New(
|
|
faas.WithDomainSearchLimit(2),
|
|
faas.WithDNSProvider(dnsProvider),
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := client.Create("go", "", "", root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !dnsProvider.ProvideInvoked {
|
|
t.Fatal("dns provider was not invoked")
|
|
}
|
|
if dnsProvider.NameRequested != "admin.example.com" {
|
|
t.Fatalf("expected 'admin.example.com', got '%v'", dnsProvider.NameRequested)
|
|
}
|
|
}
|
|
|
|
// TestRun ensures that the runner is invoked with the absolute path requested.
|
|
func TestRun(t *testing.T) {
|
|
// a previously-initilized function's root
|
|
root := "testdata/example.com/www"
|
|
|
|
runner := mock.NewRunner()
|
|
client, err := faas.New(
|
|
faas.WithRunner(runner))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := client.Run(root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !runner.RunInvoked {
|
|
t.Fatal("run did not invoke the runner")
|
|
}
|
|
absRoot, err := filepath.Abs(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if runner.RootRequested != absRoot {
|
|
t.Fatalf("expected path '%v', got '%v'", absRoot, runner.RootRequested)
|
|
}
|
|
}
|
|
|
|
// TestUpdate ensures that the updater properly invokes the build/push/deploy
|
|
// process, erroring if run on a directory uncreated.
|
|
func TestUpdate(t *testing.T) {
|
|
var (
|
|
root = "testdata/example.com/www" // .. expected to be initialized
|
|
name = "www.example.com" // expected to be derived
|
|
image = "my.hub/user/admin.exampe.com" // expected image
|
|
builder = mock.NewBuilder()
|
|
pusher = mock.NewPusher()
|
|
updater = mock.NewUpdater()
|
|
)
|
|
|
|
client, err := faas.New(
|
|
faas.WithBuilder(builder), // builds an image
|
|
faas.WithPusher(pusher), // pushes images to a registry
|
|
faas.WithUpdater(updater), // updates deployed image
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Register function delegates on the mocks which validate assertions
|
|
// -------------
|
|
|
|
// 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) (string, error) {
|
|
if name != name2 {
|
|
t.Fatalf("builder expected name %v, got '%v'", name, name2)
|
|
}
|
|
// The final image name will be determined by the builder implementation,
|
|
// but whatever it is (in this case fabricated); it should be returned
|
|
// and later provided to the pusher.
|
|
return image, nil
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
// image of given name wouold be pushed to the configured registry.
|
|
return nil
|
|
}
|
|
|
|
// The updater should be invoked with the service name and image.
|
|
// Actual logic of updating is an implementation detail.
|
|
updater.UpdateFn = func(name2, image2 string) error {
|
|
if name2 != name {
|
|
t.Fatalf("updater expected name '%v', got '%v'", name, name2)
|
|
}
|
|
if image2 != image {
|
|
t.Fatalf("updater expected image '%v', got '%v'", image, image2)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Invocation
|
|
// -------------
|
|
|
|
// Invoke the creation, triggering the function delegates, and
|
|
// perform follow-up assertions that the functions were indeed invoked.
|
|
if err := client.Update(root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !builder.BuildInvoked {
|
|
t.Fatal("builder was not invoked")
|
|
}
|
|
if !pusher.PushInvoked {
|
|
t.Fatal("pusher was not invoked")
|
|
}
|
|
if !updater.UpdateInvoked {
|
|
t.Fatal("updater was not invoked")
|
|
}
|
|
}
|
|
|
|
// TestRemove ensures that the remover is invoked with the name provided, and that
|
|
func TestRemove(t *testing.T) {
|
|
var (
|
|
name = "admin.example.com"
|
|
remover = mock.NewRemover()
|
|
)
|
|
|
|
client, err := faas.New(faas.WithRemover(remover))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
remover.RemoveFn = func(name2 string) error {
|
|
if name2 != name {
|
|
t.Fatalf("remover expected name '%v' got '%v'", name, name2)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Call with explicit name and no root.
|
|
if err := client.Remove(name, ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Call with explicit name and root; name should take precidence.
|
|
if err := client.Remove(name, "testdata/example.com/www"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestRemoveUninitializedFailes ensures that attempting to remove a function
|
|
// by path only (no name) fails unless the funciton has been initialized. I.e.
|
|
// the name will not be derived from path and the function removed by thi
|
|
// derived name; that could be unexpected and destructive.
|
|
func TestRemoveUninitializedFails(t *testing.T) {
|
|
var (
|
|
root = "testdata/example.com/admin"
|
|
remover = mock.NewRemover()
|
|
)
|
|
err := os.MkdirAll(root, 0700)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
|
|
// Create a remover delegate which fails if invoked.
|
|
remover.RemoveFn = func(name string) error {
|
|
return fmt.Errorf("remove invoked for unitialized function %v", name)
|
|
}
|
|
|
|
// Instantiate the client with the failing remover.
|
|
client, err := faas.New(faas.WithRemover(remover))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Attempt to remove by path, expecting an error.
|
|
if err := client.Remove("", root); err == nil {
|
|
t.Fatalf("did not received expeced error removing an uninitialized func")
|
|
}
|
|
}
|
|
|
|
// TestRemoveDefaultCurrent ensures that, if a name is not provided but a path is,
|
|
// the funciton defined by path will be removed.
|
|
// Note that the prior test RemoveUninitializedFails ensures that only
|
|
// initialized functions are removed, which prevents the case of a function
|
|
// being removed by path-derived name without having been initialized (an
|
|
// easily destrcutive and likely unwanted behavior)
|
|
func TestRemoveDefaultCurrent(t *testing.T) {
|
|
var (
|
|
root = "testdata/example.com/www" // an initialized function (has config)
|
|
remover = mock.NewRemover()
|
|
)
|
|
|
|
// remover delegate which ensures the correct name is received.
|
|
remover.RemoveFn = func(name2 string) error {
|
|
if name2 != "www.example.com" {
|
|
t.Fatalf("remover expected name 'www.example.com' got '%v'", name2)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
client, err := faas.New(faas.WithRemover(remover))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Without a name provided, but with a path, the function will be loaded and
|
|
// its name used by default. Note that it will fail if uninitialized (no path
|
|
// derivation for removal.)
|
|
if err := client.Remove("", root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestWithName ensures that an explicitly passed name is used in leau of the
|
|
// path derived name when provided, and persists through instantiations.
|
|
// This also ensures that an initialized service function's name persists if
|
|
// the path is changed after creation.
|
|
func TestWithName(t *testing.T) {
|
|
// Explicit name to use
|
|
name := "service.example.com"
|
|
|
|
// Path which would derive to service.groupA.example.com were it not for the
|
|
// explicitly provided name.
|
|
path := "testdata/example.com/groupA/service"
|
|
err := os.MkdirAll(path, 0700)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer os.RemoveAll(path)
|
|
|
|
initializer := mock.NewInitializer()
|
|
c, err := faas.New(
|
|
faas.WithInitializer(initializer))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Ensure that initializing receives the specified path.
|
|
initializer.InitializeFn = func(runtime, context, path2 string) error {
|
|
expectedPath, err := filepath.Abs(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if path2 != expectedPath {
|
|
t.Fatalf("initializer expected path '%v', got '%v'", expectedPath, path2)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Create the service with the explict name at the non-matching path.
|
|
if err := c.Create("go", "", name, path); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// TODO: create a Function about the path and check the name is loaded.
|
|
|
|
// Create a new client about the now initialized path and test that
|
|
// the explicitly-provided name is sent to the updater, proving that
|
|
// it was
|
|
updater := mock.NewUpdater()
|
|
c, err = faas.New(
|
|
faas.WithUpdater(updater))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Ensure that updating takes place using the previously initialized name
|
|
updater.UpdateFn = func(name2, image string) error {
|
|
if name2 != name {
|
|
t.Fatalf("updater expected name '%v', got '%v'", name, name2)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Invoke update
|
|
if err := c.Update(path); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
}
|
|
|
|
// TestList ensures that the client invokes the configured lister.
|
|
func TestList(t *testing.T) {
|
|
var lister = mock.NewLister()
|
|
|
|
client, err := faas.New(
|
|
faas.WithLister(lister)) // lists deployed service functions.
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = client.List()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !lister.ListInvoked {
|
|
t.Fatal("list did not invoke lister implementation")
|
|
}
|
|
}
|
|
|
|
// TestListOutsideRoot ensures that a call to a function (in this case list)
|
|
// that is not contextually dependent on being associated with a function,
|
|
// can be run from anywhere, thus ensuring that the client itself makes
|
|
// a distinction between function-scoped methods and not.
|
|
func TestListOutsideRoot(t *testing.T) {
|
|
var lister = mock.NewLister()
|
|
|
|
// Instantiate in the current working directory, with no name, and explicitly
|
|
// disallowing name path inferrence by limiting recursion. This emulates
|
|
// running the client (and subsequently list) from some arbitrary location
|
|
// without a derivable funciton context.
|
|
client, err := faas.New(
|
|
faas.WithDomainSearchLimit(0),
|
|
faas.WithLister(lister),
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = client.List()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !lister.ListInvoked {
|
|
t.Fatal("list did not invoke lister implementation")
|
|
}
|
|
}
|