From 6359f8eaba2bb1fd325a1e36d180c0a2bbadf115 Mon Sep 17 00:00:00 2001 From: David Allen Date: Tue, 9 Jul 2024 17:52:46 -0600 Subject: [PATCH 1/8] Added initial general unit tests --- tests/generate.go | 257 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 tests/generate.go diff --git a/tests/generate.go b/tests/generate.go new file mode 100644 index 0000000..afd21da --- /dev/null +++ b/tests/generate.go @@ -0,0 +1,257 @@ +package tests + +import ( + "fmt" + "net/http" + "os" + "os/exec" + "testing" + + "command-line-arguments/Users/allend/Desktop/projects/ochami/configurator/internal/server/server.go" + + configurator "github.com/OpenCHAMI/configurator/internal" + "github.com/OpenCHAMI/configurator/internal/generator" + "github.com/OpenCHAMI/configurator/internal/util" + "github.com/docker/docker/api/server" +) + +// A valid test generator that implements the `Generator` interface. +type TestGenerator struct{} + +func (g *TestGenerator) GetName() string { return "test" } +func (g *TestGenerator) GetVersion() string { return "v1.0.0" } +func (g *TestGenerator) GetDescription() string { + return "This is a plugin creating for running tests." +} +func (g *TestGenerator) Generate(config *configurator.Config, opts ...util.Option) (generator.FileMap, error) { + // Jinja 2 template file + files := [][]byte{ + []byte(` +Name: {{plugin_name}} +Version: {{plugin_version}} +Description: {{plugin_description}} + +This is the first test template file. + `), + []byte(` +This is another testing Jinja 2 template file using {{plugin_name}}. + `), + } + + // apply Jinja templates to file + fileList, err := generator.ApplyTemplates(generator.Mappings{ + "plugin_name": g.GetName(), + "plugin_version": g.GetVersion(), + "plugin_description": g.GetDescription(), + }, files...) + if err != nil { + return nil, fmt.Errorf("failed to apply templates: %v", err) + } + + // make sure we're able to receive certain arguments when passed + params := generator.GetParams(opts...) + if len(params) <= 0 { + return nil, fmt.Errorf("expect at least one params, but found none") + } + + // TODO: make sure we can get the target + + // make sure we're able to get the client as well + client := generator.GetClient(params) + if client == nil { + return nil, fmt.Errorf("invalid client (client is nil)") + } + + // make sure we have the same number of files in file list + if len(files) != len(fileList) { + return nil, fmt.Errorf("file list output count is not the same as the input") + } + + // convert file list to file map + fileMap := make(generator.FileMap, len(fileList)) + for i, contents := range fileList { + fileMap[fmt.Sprintf("t-%d.txt", i)] = contents + } + + return fileMap, nil +} + +// An invalid generator that does not or partially implements +// the `Generator` interface. +type InvalidGenerator struct{} + +// Test building and loading plugins +func TestPlugin(t *testing.T) { + var ( + tempDir = t.TempDir() + testPlugin = []byte(` +package main + +type TestGenerator struct{} + +func (g *TestGenerator) GetName() string { return "test" } +func (g *TestGenerator) GetVersion() string { return "v1.0.0" } +func (g *TestGenerator) GetDescription() string { return "This is a plugin creating for running tests." } +func (g *TestGenerator) Generate(config *configurator.Config, opts ...util.Option) (generator.FileMap, error) { + return generator.FileMap{"test": []byte("test")}, nil +} +var Generator TestGenerator + `) + testPluginPath = fmt.Sprintf("%s/testplugin.go") + ) + + // build a test plugin + t.Run("build", func(t *testing.T) { + // dump the plugin source code to a file + err := os.WriteFile(testPluginPath, testPlugin, os.ModePerm) + if err != nil { + t.Fatalf("failed to write test plugin file: %v", err) + } + + // execute command to build the plugin + cmd := exec.Command("go", "build", "-buildmode=plugin", fmt.Sprintf("-o=%s/test.so", tempDir)) + if cmd.Err != nil { + t.Fatalf("failed to build plugin: %v", cmd.Err) + } + + // stat the file to confirm that it was built + fileInfo, err := os.Stat(testPluginPath) + if err != nil { + t.Fatalf("failed to stat plugin file: %v", err) + } + if fileInfo.IsDir() { + t.Fatalf("directory file but a file was expected") + } + if fileInfo.Size() <= 0 { + t.Fatal("found an empty file or file with size of 0 bytes") + } + + }) + + // test loading plugins both individually and in a dir + t.Run("load", func(t *testing.T) { + gen, err := generator.LoadPlugin(testPluginPath) + if err != nil { + t.Fatalf("failed to load the test plugin: %v", err) + } + + // test that we have all expected methods with type assertions + if _, ok := gen.(interface { + GetName() string + GetVersion() string + GetDescription() string + Generate(*configurator.Config, ...util.Option) (generator.FileMap, error) + }); !ok { + t.Error("plugin does not implement all of the generator interface") + } + + // test loading plugins from a directory (should just load a single one) + gens, err := generator.LoadPlugins(tempDir) + if err != nil { + t.Fatalf("failed to load plugins in '%s': %v", tempDir, err) + } + + // test all of the plugins loaded from a directory (should expect same result as above) + for _, gen := range gens { + if _, ok := gen.(interface { + GetName() string + GetVersion() string + GetDescription() string + Generate(*configurator.Config, ...util.Option) (generator.FileMap, error) + }); !ok { + t.Error("plugin does not implement all of the generator interface") + } + } + }) + +} + +// Test that expects to successfully "generate" a file using the built-in +// example plugin with no fetching. +// +// NOTE: Normally we would dynamically load a generator from a plugin, but +// we're not doing it here since that's not what is being tested. +func TestGenerateExample(t *testing.T) { + var ( + config = configurator.NewConfig() + client = configurator.NewSmdClient() + gen = TestGenerator{} + ) + + // make sure our generator returns expected strings + t.Run("properties", func(t *testing.T) { + if gen.GetName() != "test" { + t.Error("test generator return unexpected name") + } + if gen.GetVersion() != "v1.0.0" { + t.Error("test generator return unexpected version") + } + if gen.GetDescription() != "test" { + t.Error("test generator return unexpected description") + } + }) + + // try to generate a file with templating applied + fileMap, err := gen.Generate( + &config, + generator.WithTarget("test"), + generator.WithClient(client), + ) + if err != nil { + t.Fatal("failed to generate file: %v", err) + } + + // test for 2 expected files to be generated in the output (hint: check the + // TestGenerator.Generate implementation) + if len(fileMap) != 2 { + t.Error("expected 2 files in generated output") + } +} + +// Test that expects to successfully "generate" a file using the built-in +// example plugin but by making a HTTP request to a service instance instead. +// +// NOTE: This test uses the default server settings to run. +func TestGenerateExampleWithServer(t *testing.T) { + var ( + config = configurator.NewConfig() + gen = TestGenerator{} + headers = make(map[string]string, 0) + ) + + // add a test target to config + config.Targets["test"] = configurator.Target{} + + // start up a new server in background + server := server.New(config) + go server.Server() + + // make request to server to generate a file + res, b, err := util.MakeRequest("http://127.0.0.1:3334/generate?target=test", http.MethodGet, nil, headers) + if err != nil { + t.Fatalf("failed to make request: %v", err) + } + if res.StatusCode != http.StatusOK { + t.Fatalf("expect status code 200 from response but received %d instead", res.StatusCode) + } + + // test for specific output from request + fileMap, err := gen.Generate(&config) + if err != nil { + t.Fatalf("failed to generate file: %v", err) + } + if string(fileMap["test"]) != string(b) { + t.Fatal("response does not match expected output") + } + +} + +// Test that expects to fail with a specific error: "failed to loook up +// symbol at path" +func TestGenerateWithNoSymbol(t *testing.T) { +} + +// Test that expects to fail with a specific error: "failed to load the +// correct symbol type at path" +func TestGenerateWithInvalidGenerator() { +} From 9f3d4c3fe2a9caf1501d590c73cb97b42cc04a0a Mon Sep 17 00:00:00 2001 From: David Allen Date: Wed, 10 Jul 2024 09:56:20 -0600 Subject: [PATCH 2/8] Updated Makefile to include a test rule --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6b5d2ef..689a632 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # build everything at once -all: plugins exe +all: plugins exe test # build the main executable to make configs main: exe @@ -21,8 +21,11 @@ plugins: go build -buildmode=plugin -o lib/syslog.so internal/generator/plugins/syslog/syslog.go go build -buildmode=plugin -o lib/warewulf.so internal/generator/plugins/warewulf/warewulf.go -# remove executable and all plugins +# remove executable and all built plugins clean: rm configurator rm lib/* +# run all of the unit tests +test: + go test ./tests --tags=all From 075b1a1f7f7d08b778dc059fc52b2d499869bbd8 Mon Sep 17 00:00:00 2001 From: David Allen Date: Wed, 10 Jul 2024 09:57:20 -0600 Subject: [PATCH 3/8] Fixed minor issues with test syntax --- tests/{generate.go => generate_test.go} | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) rename tests/{generate.go => generate_test.go} (95%) diff --git a/tests/generate.go b/tests/generate_test.go similarity index 95% rename from tests/generate.go rename to tests/generate_test.go index afd21da..37ca89e 100644 --- a/tests/generate.go +++ b/tests/generate_test.go @@ -7,12 +7,10 @@ import ( "os/exec" "testing" - "command-line-arguments/Users/allend/Desktop/projects/ochami/configurator/internal/server/server.go" - configurator "github.com/OpenCHAMI/configurator/internal" "github.com/OpenCHAMI/configurator/internal/generator" + "github.com/OpenCHAMI/configurator/internal/server" "github.com/OpenCHAMI/configurator/internal/util" - "github.com/docker/docker/api/server" ) // A valid test generator that implements the `Generator` interface. @@ -83,8 +81,9 @@ type InvalidGenerator struct{} // Test building and loading plugins func TestPlugin(t *testing.T) { var ( - tempDir = t.TempDir() - testPlugin = []byte(` + tempDir = t.TempDir() + testPluginPath = fmt.Sprintf("%s/testplugin.go", tempDir) + testPlugin = []byte(` package main type TestGenerator struct{} @@ -97,7 +96,6 @@ func (g *TestGenerator) Generate(config *configurator.Config, opts ...util.Optio } var Generator TestGenerator `) - testPluginPath = fmt.Sprintf("%s/testplugin.go") ) // build a test plugin @@ -198,7 +196,7 @@ func TestGenerateExample(t *testing.T) { generator.WithClient(client), ) if err != nil { - t.Fatal("failed to generate file: %v", err) + t.Fatalf("failed to generate file: %v", err) } // test for 2 expected files to be generated in the output (hint: check the @@ -223,8 +221,8 @@ func TestGenerateExampleWithServer(t *testing.T) { config.Targets["test"] = configurator.Target{} // start up a new server in background - server := server.New(config) - go server.Server() + server := server.New(&config) + go server.Serve() // make request to server to generate a file res, b, err := util.MakeRequest("http://127.0.0.1:3334/generate?target=test", http.MethodGet, nil, headers) @@ -253,5 +251,5 @@ func TestGenerateWithNoSymbol(t *testing.T) { // Test that expects to fail with a specific error: "failed to load the // correct symbol type at path" -func TestGenerateWithInvalidGenerator() { +func TestGenerateWithInvalidGenerator(t *testing.T) { } From 7361ec739fd71ad3d9d568f4ec4ac2bc247a9f52 Mon Sep 17 00:00:00 2001 From: David Allen Date: Wed, 10 Jul 2024 12:11:28 -0600 Subject: [PATCH 4/8] Moved internal/ to pkg/ to make building external plugins possible --- internal/generator/plugins/hostfile/hostfile_test.go | 1 - internal/schema.go | 2 -- {internal => pkg}/auth.go | 0 {internal => pkg}/client.go | 0 {internal => pkg}/config.go | 0 {internal => pkg}/configurator.go | 0 {internal => pkg}/generator/generator.go | 0 {internal => pkg}/generator/plugins/conman/conman.go | 0 {internal => pkg}/generator/plugins/coredhcp/coredhcp.go | 0 {internal => pkg}/generator/plugins/dhcpd/dhcpd.go | 0 {internal => pkg}/generator/plugins/dnsmasq/dnsmasq.go | 0 {internal => pkg}/generator/plugins/example/example.go | 0 {internal => pkg}/generator/plugins/hostfile/hostfile.go | 0 {internal => pkg}/generator/plugins/powerman/powerman.go | 0 {internal => pkg}/generator/plugins/syslog/syslog.go | 0 {internal => pkg}/generator/plugins/warewulf/warewulf.go | 0 {internal => pkg}/server/server.go | 4 ++++ {internal => pkg}/util/params.go | 0 {internal => pkg}/util/util.go | 0 19 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 internal/generator/plugins/hostfile/hostfile_test.go delete mode 100644 internal/schema.go rename {internal => pkg}/auth.go (100%) rename {internal => pkg}/client.go (100%) rename {internal => pkg}/config.go (100%) rename {internal => pkg}/configurator.go (100%) rename {internal => pkg}/generator/generator.go (100%) rename {internal => pkg}/generator/plugins/conman/conman.go (100%) rename {internal => pkg}/generator/plugins/coredhcp/coredhcp.go (100%) rename {internal => pkg}/generator/plugins/dhcpd/dhcpd.go (100%) rename {internal => pkg}/generator/plugins/dnsmasq/dnsmasq.go (100%) rename {internal => pkg}/generator/plugins/example/example.go (100%) rename {internal => pkg}/generator/plugins/hostfile/hostfile.go (100%) rename {internal => pkg}/generator/plugins/powerman/powerman.go (100%) rename {internal => pkg}/generator/plugins/syslog/syslog.go (100%) rename {internal => pkg}/generator/plugins/warewulf/warewulf.go (100%) rename {internal => pkg}/server/server.go (99%) rename {internal => pkg}/util/params.go (100%) rename {internal => pkg}/util/util.go (100%) diff --git a/internal/generator/plugins/hostfile/hostfile_test.go b/internal/generator/plugins/hostfile/hostfile_test.go deleted file mode 100644 index 06ab7d0..0000000 --- a/internal/generator/plugins/hostfile/hostfile_test.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/internal/schema.go b/internal/schema.go deleted file mode 100644 index dc794a1..0000000 --- a/internal/schema.go +++ /dev/null @@ -1,2 +0,0 @@ -// TODO: implement a way to fetch schemas from node orchestrator -package configurator diff --git a/internal/auth.go b/pkg/auth.go similarity index 100% rename from internal/auth.go rename to pkg/auth.go diff --git a/internal/client.go b/pkg/client.go similarity index 100% rename from internal/client.go rename to pkg/client.go diff --git a/internal/config.go b/pkg/config.go similarity index 100% rename from internal/config.go rename to pkg/config.go diff --git a/internal/configurator.go b/pkg/configurator.go similarity index 100% rename from internal/configurator.go rename to pkg/configurator.go diff --git a/internal/generator/generator.go b/pkg/generator/generator.go similarity index 100% rename from internal/generator/generator.go rename to pkg/generator/generator.go diff --git a/internal/generator/plugins/conman/conman.go b/pkg/generator/plugins/conman/conman.go similarity index 100% rename from internal/generator/plugins/conman/conman.go rename to pkg/generator/plugins/conman/conman.go diff --git a/internal/generator/plugins/coredhcp/coredhcp.go b/pkg/generator/plugins/coredhcp/coredhcp.go similarity index 100% rename from internal/generator/plugins/coredhcp/coredhcp.go rename to pkg/generator/plugins/coredhcp/coredhcp.go diff --git a/internal/generator/plugins/dhcpd/dhcpd.go b/pkg/generator/plugins/dhcpd/dhcpd.go similarity index 100% rename from internal/generator/plugins/dhcpd/dhcpd.go rename to pkg/generator/plugins/dhcpd/dhcpd.go diff --git a/internal/generator/plugins/dnsmasq/dnsmasq.go b/pkg/generator/plugins/dnsmasq/dnsmasq.go similarity index 100% rename from internal/generator/plugins/dnsmasq/dnsmasq.go rename to pkg/generator/plugins/dnsmasq/dnsmasq.go diff --git a/internal/generator/plugins/example/example.go b/pkg/generator/plugins/example/example.go similarity index 100% rename from internal/generator/plugins/example/example.go rename to pkg/generator/plugins/example/example.go diff --git a/internal/generator/plugins/hostfile/hostfile.go b/pkg/generator/plugins/hostfile/hostfile.go similarity index 100% rename from internal/generator/plugins/hostfile/hostfile.go rename to pkg/generator/plugins/hostfile/hostfile.go diff --git a/internal/generator/plugins/powerman/powerman.go b/pkg/generator/plugins/powerman/powerman.go similarity index 100% rename from internal/generator/plugins/powerman/powerman.go rename to pkg/generator/plugins/powerman/powerman.go diff --git a/internal/generator/plugins/syslog/syslog.go b/pkg/generator/plugins/syslog/syslog.go similarity index 100% rename from internal/generator/plugins/syslog/syslog.go rename to pkg/generator/plugins/syslog/syslog.go diff --git a/internal/generator/plugins/warewulf/warewulf.go b/pkg/generator/plugins/warewulf/warewulf.go similarity index 100% rename from internal/generator/plugins/warewulf/warewulf.go rename to pkg/generator/plugins/warewulf/warewulf.go diff --git a/internal/server/server.go b/pkg/server/server.go similarity index 99% rename from internal/server/server.go rename to pkg/server/server.go index 425a594..0e63a24 100644 --- a/internal/server/server.go +++ b/pkg/server/server.go @@ -108,6 +108,10 @@ func (s *Server) Serve() error { return s.ListenAndServe() } +func (s *Server) Close() { + +} + // This is the corresponding service function to generate templated files, that // works similarly to the CLI variant. This function takes similiar arguments as // query parameters that are included in the HTTP request URL. diff --git a/internal/util/params.go b/pkg/util/params.go similarity index 100% rename from internal/util/params.go rename to pkg/util/params.go diff --git a/internal/util/util.go b/pkg/util/util.go similarity index 100% rename from internal/util/util.go rename to pkg/util/util.go From 7115954cf13210770b1c2a663c2edca8e5827cc8 Mon Sep 17 00:00:00 2001 From: David Allen Date: Wed, 10 Jul 2024 12:19:27 -0600 Subject: [PATCH 5/8] Updated imports to use pkg/ instead of internal/ --- cmd/config.go | 4 +- cmd/inspect.go | 2 +- cmd/root.go | 4 +- pkg/client.go | 2 +- pkg/generator/generator.go | 4 +- pkg/generator/plugins/conman/conman.go | 6 +- pkg/generator/plugins/dnsmasq/dnsmasq.go | 6 +- pkg/server/server.go | 4 +- tests/generate_test.go | 143 ++++++++++++++--------- 9 files changed, 102 insertions(+), 73 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index 6596988..bd26e7c 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -5,8 +5,8 @@ import ( "github.com/spf13/cobra" - configurator "github.com/OpenCHAMI/configurator/internal" - "github.com/OpenCHAMI/configurator/internal/util" + configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/util" ) var configCmd = &cobra.Command{ diff --git a/cmd/inspect.go b/cmd/inspect.go index 0a543c3..a2b0adf 100644 --- a/cmd/inspect.go +++ b/cmd/inspect.go @@ -5,7 +5,7 @@ import ( "maps" "strings" - "github.com/OpenCHAMI/configurator/internal/generator" + "github.com/OpenCHAMI/configurator/pkg/generator" "github.com/spf13/cobra" ) diff --git a/cmd/root.go b/cmd/root.go index 353523e..cbbfab7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,8 +4,8 @@ import ( "fmt" "os" - configurator "github.com/OpenCHAMI/configurator/internal" - "github.com/OpenCHAMI/configurator/internal/util" + configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/util" "github.com/spf13/cobra" ) diff --git a/pkg/client.go b/pkg/client.go index f5de764..bee15d8 100644 --- a/pkg/client.go +++ b/pkg/client.go @@ -12,7 +12,7 @@ import ( "os" "time" - "github.com/OpenCHAMI/configurator/internal/util" + "github.com/OpenCHAMI/configurator/pkg/util" ) type ClientOption func(*SmdClient) diff --git a/pkg/generator/generator.go b/pkg/generator/generator.go index 1ed6c9d..34cb25b 100644 --- a/pkg/generator/generator.go +++ b/pkg/generator/generator.go @@ -8,8 +8,8 @@ import ( "path/filepath" "plugin" - configurator "github.com/OpenCHAMI/configurator/internal" - "github.com/OpenCHAMI/configurator/internal/util" + configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/util" "github.com/nikolalohinski/gonja/v2" "github.com/nikolalohinski/gonja/v2/exec" "github.com/sirupsen/logrus" diff --git a/pkg/generator/plugins/conman/conman.go b/pkg/generator/plugins/conman/conman.go index b324238..2f04978 100644 --- a/pkg/generator/plugins/conman/conman.go +++ b/pkg/generator/plugins/conman/conman.go @@ -3,9 +3,9 @@ package main import ( "fmt" - configurator "github.com/OpenCHAMI/configurator/internal" - "github.com/OpenCHAMI/configurator/internal/generator" - "github.com/OpenCHAMI/configurator/internal/util" + configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/generator" + "github.com/OpenCHAMI/configurator/pkg/util" ) type Conman struct{} diff --git a/pkg/generator/plugins/dnsmasq/dnsmasq.go b/pkg/generator/plugins/dnsmasq/dnsmasq.go index 6483fd1..c5e0ede 100644 --- a/pkg/generator/plugins/dnsmasq/dnsmasq.go +++ b/pkg/generator/plugins/dnsmasq/dnsmasq.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - configurator "github.com/OpenCHAMI/configurator/internal" - "github.com/OpenCHAMI/configurator/internal/generator" - "github.com/OpenCHAMI/configurator/internal/util" + configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/generator" + "github.com/OpenCHAMI/configurator/pkg/util" ) type DnsMasq struct{} diff --git a/pkg/server/server.go b/pkg/server/server.go index 0e63a24..6b969cd 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -9,8 +9,8 @@ import ( "net/http" "time" - configurator "github.com/OpenCHAMI/configurator/internal" - "github.com/OpenCHAMI/configurator/internal/generator" + configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/generator" "github.com/OpenCHAMI/jwtauth/v5" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" diff --git a/tests/generate_test.go b/tests/generate_test.go index 37ca89e..9a21d1e 100644 --- a/tests/generate_test.go +++ b/tests/generate_test.go @@ -7,10 +7,10 @@ import ( "os/exec" "testing" - configurator "github.com/OpenCHAMI/configurator/internal" - "github.com/OpenCHAMI/configurator/internal/generator" - "github.com/OpenCHAMI/configurator/internal/server" - "github.com/OpenCHAMI/configurator/internal/util" + configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/generator" + "github.com/OpenCHAMI/configurator/pkg/server" + "github.com/OpenCHAMI/configurator/pkg/util" ) // A valid test generator that implements the `Generator` interface. @@ -81,11 +81,20 @@ type InvalidGenerator struct{} // Test building and loading plugins func TestPlugin(t *testing.T) { var ( - tempDir = t.TempDir() - testPluginPath = fmt.Sprintf("%s/testplugin.go", tempDir) - testPlugin = []byte(` + testPluginDir = t.TempDir() + testPluginPath = fmt.Sprintf("%s/testplugin.so", testPluginDir) + testPluginSourcePath = fmt.Sprintf("%s/testplugin.go", testPluginDir) + testPluginSource = []byte(` package main +import ( + "fmt" + + configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/generator" + "github.com/OpenCHAMI/configurator/pkg/util" +) + type TestGenerator struct{} func (g *TestGenerator) GetName() string { return "test" } @@ -98,42 +107,80 @@ var Generator TestGenerator `) ) - // build a test plugin - t.Run("build", func(t *testing.T) { - // dump the plugin source code to a file - err := os.WriteFile(testPluginPath, testPlugin, os.ModePerm) - if err != nil { - t.Fatalf("failed to write test plugin file: %v", err) - } + // make temporary directory to test plugin + err := os.MkdirAll(testPluginDir, os.ModeDir) + if err != nil { + t.Fatalf("failed to make temporary directory: %v", err) + } - // execute command to build the plugin - cmd := exec.Command("go", "build", "-buildmode=plugin", fmt.Sprintf("-o=%s/test.so", tempDir)) - if cmd.Err != nil { - t.Fatalf("failed to build plugin: %v", cmd.Err) - } + // show all paths to make sure we're using the correct ones + fmt.Printf("test directory: %v\n", testPluginDir) + fmt.Printf("test plugin path: %v\n", testPluginPath) + fmt.Printf("test plugin source path: %v\n", testPluginSourcePath) - // stat the file to confirm that it was built - fileInfo, err := os.Stat(testPluginPath) - if err != nil { - t.Fatalf("failed to stat plugin file: %v", err) - } - if fileInfo.IsDir() { - t.Fatalf("directory file but a file was expected") - } - if fileInfo.Size() <= 0 { - t.Fatal("found an empty file or file with size of 0 bytes") - } + // dump the plugin source code to a file + err = os.WriteFile(testPluginSourcePath, testPluginSource, os.ModePerm) + if err != nil { + t.Fatalf("failed to write test plugin file: %v", err) + } - }) + // make sure the source file was actually written + fileInfo, err := os.Stat(testPluginSourcePath) + if err != nil { + t.Fatalf("failed to stat path: %v", err) + } + if fileInfo.IsDir() { + t.Fatalf("expected file but found directory") + } + + // change to testing directory to run command + // err = os.Chdir(testPluginDir) + // if err != nil { + // t.Fatalf("failed to 'cd' to temporary directory: %v", err) + // } + + // execute command to build the plugin + cmd := exec.Command("go", "build", "-buildmode=plugin", fmt.Sprintf("-o=%s", testPluginPath), testPluginSourcePath) + if output, err := cmd.Output(); err != nil { + t.Fatalf("failed to execute command: %v\n%s", err, string(output)) + } + + // stat the file to confirm that the plugin was built + fileInfo, err = os.Stat(testPluginPath) + if err != nil { + t.Fatalf("failed to stat plugin file: %v", err) + } + if fileInfo.IsDir() { + t.Fatalf("directory file but a file was expected") + } + if fileInfo.Size() <= 0 { + t.Fatal("found an empty file or file with size of 0 bytes") + } // test loading plugins both individually and in a dir - t.Run("load", func(t *testing.T) { - gen, err := generator.LoadPlugin(testPluginPath) - if err != nil { - t.Fatalf("failed to load the test plugin: %v", err) - } + gen, err := generator.LoadPlugin(testPluginSourcePath) + if err != nil { + t.Fatalf("failed to load the test plugin: %v", err) + } - // test that we have all expected methods with type assertions + // test that we have all expected methods with type assertions + if _, ok := gen.(interface { + GetName() string + GetVersion() string + GetDescription() string + Generate(*configurator.Config, ...util.Option) (generator.FileMap, error) + }); !ok { + t.Error("plugin does not implement all of the generator interface") + } + + // test loading plugins from a directory (should just load a single one) + gens, err := generator.LoadPlugins(testPluginDir) + if err != nil { + t.Fatalf("failed to load plugins in '%s': %v", testPluginDir, err) + } + + // test all of the plugins loaded from a directory (should expect same result as above) + for _, gen := range gens { if _, ok := gen.(interface { GetName() string GetVersion() string @@ -142,25 +189,7 @@ var Generator TestGenerator }); !ok { t.Error("plugin does not implement all of the generator interface") } - - // test loading plugins from a directory (should just load a single one) - gens, err := generator.LoadPlugins(tempDir) - if err != nil { - t.Fatalf("failed to load plugins in '%s': %v", tempDir, err) - } - - // test all of the plugins loaded from a directory (should expect same result as above) - for _, gen := range gens { - if _, ok := gen.(interface { - GetName() string - GetVersion() string - GetDescription() string - Generate(*configurator.Config, ...util.Option) (generator.FileMap, error) - }); !ok { - t.Error("plugin does not implement all of the generator interface") - } - } - }) + } } @@ -184,7 +213,7 @@ func TestGenerateExample(t *testing.T) { if gen.GetVersion() != "v1.0.0" { t.Error("test generator return unexpected version") } - if gen.GetDescription() != "test" { + if gen.GetDescription() != "This is a plugin creating for running tests." { t.Error("test generator return unexpected description") } }) From e14a27cf848edb8ad7aeb12207c478fd7519aab5 Mon Sep 17 00:00:00 2001 From: David Allen Date: Wed, 10 Jul 2024 17:00:48 -0600 Subject: [PATCH 6/8] Updated Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 689a632..0ca212f 100644 --- a/Makefile +++ b/Makefile @@ -28,4 +28,4 @@ clean: # run all of the unit tests test: - go test ./tests --tags=all + go test ./tests/generate_test.go --tags=all From 02406dec9fc4ffa2dcc6eb27e84295218de23376 Mon Sep 17 00:00:00 2001 From: David Allen Date: Wed, 10 Jul 2024 17:02:11 -0600 Subject: [PATCH 7/8] Minor changes --- pkg/config.go | 12 +++---- pkg/generator/generator.go | 38 ++++++++++------------ pkg/generator/plugins/conman/conman.go | 2 +- pkg/generator/plugins/dnsmasq/dnsmasq.go | 4 +-- pkg/generator/plugins/warewulf/warewulf.go | 12 +++---- pkg/server/server.go | 16 ++++----- 6 files changed, 41 insertions(+), 43 deletions(-) diff --git a/pkg/config.go b/pkg/config.go index ee113dc..10da3c6 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -11,9 +11,9 @@ import ( type Options struct{} type Target struct { - Templates []string `yaml:"templates,omitempty"` - FilePaths []string `yaml:"files,omitempty"` - RunTargets []string `yaml:"targets,omitempty"` + TemplatePaths []string `yaml:"templates,omitempty"` + FilePaths []string `yaml:"files,omitempty"` + RunTargets []string `yaml:"targets,omitempty"` } type Jwks struct { @@ -48,13 +48,13 @@ func NewConfig() Config { }, Targets: map[string]Target{ "dnsmasq": Target{ - Templates: []string{}, + TemplatePaths: []string{}, }, "conman": Target{ - Templates: []string{}, + TemplatePaths: []string{}, }, "warewulf": Target{ - Templates: []string{ + TemplatePaths: []string{ "templates/warewulf/defaults/node.jinja", "templates/warewulf/defaults/provision.jinja", }, diff --git a/pkg/generator/generator.go b/pkg/generator/generator.go index 34cb25b..27a032e 100644 --- a/pkg/generator/generator.go +++ b/pkg/generator/generator.go @@ -12,7 +12,6 @@ import ( "github.com/OpenCHAMI/configurator/pkg/util" "github.com/nikolalohinski/gonja/v2" "github.com/nikolalohinski/gonja/v2/exec" - "github.com/sirupsen/logrus" ) type Mappings map[string]any @@ -32,6 +31,7 @@ type Generator interface { type Params struct { Args []string PluginPaths []string + Generators map[string]Generator Target string Verbose bool } @@ -259,14 +259,14 @@ func ApplyTemplateFromFiles(mappings Mappings, paths ...string) (FileMap, error) // Main function to generate a collection of files as a map with the path as the key and // the contents of the file as the value. This function currently expects a list of plugin -// paths to load all plugins within a directory. Then, each plugin's generator.Generate() +// paths to load all plugins within a directory. Then, each plugin's generator.GenerateWithTarget() // function is called for each target specified. // // This function is the corresponding implementation for the "generate" CLI subcommand. // It is also call when running the configurator as a service with the "/generate" route. // // TODO: Separate loading plugins so we can load them once when running as a service. -func Generate(config *configurator.Config, params Params) (FileMap, error) { +func GenerateWithTarget(config *configurator.Config, params Params) (FileMap, error) { // load generator plugins to generate configs or to print var ( generators = make(map[string]Generator) @@ -278,12 +278,12 @@ func Generate(config *configurator.Config, params Params) (FileMap, error) { ) ) - // load all plugins from params + // load all plugins from supplied arguments for _, path := range params.PluginPaths { if params.Verbose { fmt.Printf("loading plugins from '%s'\n", path) } - gens, err := LoadPlugins(path) + plugins, err := LoadPlugins(path) if err != nil { fmt.Printf("failed to load plugins: %v\n", err) err = nil @@ -291,9 +291,12 @@ func Generate(config *configurator.Config, params Params) (FileMap, error) { } // add loaded generator plugins to set - maps.Copy(generators, gens) + maps.Copy(generators, plugins) } + // copy all generators supplied from arguments + maps.Copy(generators, params.Generators) + // show available targets then exit if len(params.Args) == 0 && params.Target == "" { for g := range generators { @@ -302,19 +305,14 @@ func Generate(config *configurator.Config, params Params) (FileMap, error) { return nil, nil } - if params.Target == "" { - logrus.Errorf("no target supplied (--target name)") - } else { - // run the generator plugin from target passed - gen := generators[params.Target] - if gen == nil { - return nil, fmt.Errorf("invalid generator target (%s)", params.Target) - } - return gen.Generate( - config, - WithTarget(gen.GetName()), - WithClient(client), - ) + // run the generator plugin from target passed + gen := generators[params.Target] + if gen == nil { + return nil, fmt.Errorf("invalid generator target (%s)", params.Target) } - return nil, fmt.Errorf("an unknown error has occurred") + return gen.Generate( + config, + WithTarget(gen.GetName()), + WithClient(client), + ) } diff --git a/pkg/generator/plugins/conman/conman.go b/pkg/generator/plugins/conman/conman.go index 2f04978..6a14f89 100644 --- a/pkg/generator/plugins/conman/conman.go +++ b/pkg/generator/plugins/conman/conman.go @@ -62,7 +62,7 @@ func (g *Conman) Generate(config *configurator.Config, opts ...util.Option) (gen "plugin_description": g.GetDescription(), "server_opts": "", "global_opts": "", - }, target.Templates...) + }, target.TemplatePaths...) } var Generator Conman diff --git a/pkg/generator/plugins/dnsmasq/dnsmasq.go b/pkg/generator/plugins/dnsmasq/dnsmasq.go index c5e0ede..9150009 100644 --- a/pkg/generator/plugins/dnsmasq/dnsmasq.go +++ b/pkg/generator/plugins/dnsmasq/dnsmasq.go @@ -58,7 +58,7 @@ func (g *DnsMasq) Generate(config *configurator.Config, opts ...util.Option) (ge // print message if verbose param found if verbose, ok := params["verbose"].(bool); ok { if verbose { - fmt.Printf("template: \n%s\nethernet interfaces found: %v\n", strings.Join(target.Templates, "\n\t"), len(eths)) + fmt.Printf("template: \n%s\nethernet interfaces found: %v\n", strings.Join(target.TemplatePaths, "\n\t"), len(eths)) } } @@ -79,7 +79,7 @@ func (g *DnsMasq) Generate(config *configurator.Config, opts ...util.Option) (ge "plugin_version": g.GetVersion(), "plugin_description": g.GetDescription(), "dhcp-hosts": output, - }, target.Templates...) + }, target.TemplatePaths...) } var Generator DnsMasq diff --git a/pkg/generator/plugins/warewulf/warewulf.go b/pkg/generator/plugins/warewulf/warewulf.go index f9646b8..8f40d7a 100644 --- a/pkg/generator/plugins/warewulf/warewulf.go +++ b/pkg/generator/plugins/warewulf/warewulf.go @@ -5,9 +5,9 @@ import ( "maps" "strings" - configurator "github.com/OpenCHAMI/configurator/internal" - "github.com/OpenCHAMI/configurator/internal/generator" - "github.com/OpenCHAMI/configurator/internal/util" + configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/generator" + "github.com/OpenCHAMI/configurator/pkg/util" ) type Warewulf struct{} @@ -30,7 +30,7 @@ func (g *Warewulf) Generate(config *configurator.Config, opts ...util.Option) (g client = generator.GetClient(params) targetKey = params["target"].(string) target = config.Targets[targetKey] - outputs = make(generator.FileMap, len(target.FilePaths)+len(target.Templates)) + outputs = make(generator.FileMap, len(target.FilePaths)+len(target.TemplatePaths)) ) // check if our client is included and is valid @@ -55,7 +55,7 @@ func (g *Warewulf) Generate(config *configurator.Config, opts ...util.Option) (g // print message if verbose param found if verbose, ok := params["verbose"].(bool); ok { if verbose { - fmt.Printf("template: \n%s\n ethernet interfaces found: %v\n", strings.Join(target.Templates, "\n\t"), len(eths)) + fmt.Printf("template: \n%s\n ethernet interfaces found: %v\n", strings.Join(target.TemplatePaths, "\n\t"), len(eths)) } } @@ -78,7 +78,7 @@ func (g *Warewulf) Generate(config *configurator.Config, opts ...util.Option) (g } templates, err := generator.ApplyTemplateFromFiles(generator.Mappings{ "node_entries": nodeEntries, - }, target.Templates...) + }, target.TemplatePaths...) if err != nil { return nil, fmt.Errorf("failed to load templates: %v", err) } diff --git a/pkg/server/server.go b/pkg/server/server.go index 6b969cd..1144adc 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -119,14 +119,14 @@ func (s *Server) Generate(w http.ResponseWriter, r *http.Request) { // get all of the expect query URL params and validate s.GeneratorParams.Target = r.URL.Query().Get("target") if s.GeneratorParams.Target == "" { - writeError(w, "no targets supplied") + writeErrorResponse(w, "no targets supplied") return } // generate a new config file from supplied params - outputs, err := generator.Generate(s.Config, s.GeneratorParams) + outputs, err := generator.GenerateWithTarget(s.Config, s.GeneratorParams) if err != nil { - writeError(w, "failed to generate config: %v", err) + writeErrorResponse(w, "failed to generate config: %v", err) return } @@ -134,12 +134,12 @@ func (s *Server) Generate(w http.ResponseWriter, r *http.Request) { tmp := generator.ConvertContentsToString(outputs) b, err := json.Marshal(tmp) if err != nil { - writeError(w, "failed to marshal output: %v", err) + writeErrorResponse(w, "failed to marshal output: %v", err) return } _, err = w.Write(b) if err != nil { - writeError(w, "failed to write response: %v", err) + writeErrorResponse(w, "failed to write response: %v", err) return } } @@ -151,15 +151,15 @@ func (s *Server) Generate(w http.ResponseWriter, r *http.Request) { func (s *Server) ManageTemplates(w http.ResponseWriter, r *http.Request) { _, err := w.Write([]byte("this is not implemented yet")) if err != nil { - writeError(w, "failed to write response: %v", err) + writeErrorResponse(w, "failed to write response: %v", err) return } } // Wrapper function to simplify writting error message responses. This function // is only intended to be used with the service and nothing else. -func writeError(w http.ResponseWriter, format string, a ...any) { +func writeErrorResponse(w http.ResponseWriter, format string, a ...any) error { errmsg := fmt.Sprintf(format, a...) - fmt.Printf(errmsg) w.Write([]byte(errmsg)) + return fmt.Errorf(errmsg) } From fc1232ddebd3e70708b01e1384afdef586682635 Mon Sep 17 00:00:00 2001 From: David Allen Date: Wed, 10 Jul 2024 17:02:57 -0600 Subject: [PATCH 8/8] Updated generation unit tests --- tests/generate_test.go | 192 +++++++++++++++++++++++++++++++++-------- 1 file changed, 155 insertions(+), 37 deletions(-) diff --git a/tests/generate_test.go b/tests/generate_test.go index 9a21d1e..31e6e04 100644 --- a/tests/generate_test.go +++ b/tests/generate_test.go @@ -1,6 +1,7 @@ package tests import ( + "encoding/json" "fmt" "net/http" "os" @@ -19,7 +20,7 @@ type TestGenerator struct{} func (g *TestGenerator) GetName() string { return "test" } func (g *TestGenerator) GetVersion() string { return "v1.0.0" } func (g *TestGenerator) GetDescription() string { - return "This is a plugin creating for running tests." + return "This is a plugin created for running tests." } func (g *TestGenerator) Generate(config *configurator.Config, opts ...util.Option) (generator.FileMap, error) { // Jinja 2 template file @@ -52,14 +53,19 @@ This is another testing Jinja 2 template file using {{plugin_name}}. return nil, fmt.Errorf("expect at least one params, but found none") } - // TODO: make sure we can get the target + // make sure we have a valid config we can access + if config == nil { + return nil, fmt.Errorf("invalid config (config is nil)") + } - // make sure we're able to get the client as well + // make sure we're able to get a valid client as well client := generator.GetClient(params) if client == nil { return nil, fmt.Errorf("invalid client (client is nil)") } + // TODO: make sure we can get a target + // make sure we have the same number of files in file list if len(files) != len(fileList) { return nil, fmt.Errorf("file list output count is not the same as the input") @@ -74,16 +80,12 @@ This is another testing Jinja 2 template file using {{plugin_name}}. return fileMap, nil } -// An invalid generator that does not or partially implements -// the `Generator` interface. -type InvalidGenerator struct{} - // Test building and loading plugins func TestPlugin(t *testing.T) { var ( testPluginDir = t.TempDir() - testPluginPath = fmt.Sprintf("%s/testplugin.so", testPluginDir) - testPluginSourcePath = fmt.Sprintf("%s/testplugin.go", testPluginDir) + testPluginPath = fmt.Sprintf("%s/test-plugin.so", testPluginDir) + testPluginSourcePath = fmt.Sprintf("%s/test-plugin.go", testPluginDir) testPluginSource = []byte(` package main @@ -107,16 +109,22 @@ var Generator TestGenerator `) ) - // make temporary directory to test plugin - err := os.MkdirAll(testPluginDir, os.ModeDir) + wd, err := os.Getwd() if err != nil { - t.Fatalf("failed to make temporary directory: %v", err) + t.Errorf("failed to get working directory: %v", err) } // show all paths to make sure we're using the correct ones - fmt.Printf("test directory: %v\n", testPluginDir) - fmt.Printf("test plugin path: %v\n", testPluginPath) - fmt.Printf("test plugin source path: %v\n", testPluginSourcePath) + fmt.Printf("(TestPlugin) working directory: %v\n", wd) + fmt.Printf("(TestPlugin) plugin directory: %v\n", testPluginDir) + fmt.Printf("(TestPlugin) plugin path: %v\n", testPluginPath) + fmt.Printf("(TestPlugin) plugin source path: %v\n", testPluginSourcePath) + + // make temporary directory to test plugin + err = os.MkdirAll(testPluginDir, os.ModeDir) + if err != nil { + t.Fatalf("failed to make temporary directory: %v", err) + } // dump the plugin source code to a file err = os.WriteFile(testPluginSourcePath, testPluginSource, os.ModePerm) @@ -134,10 +142,10 @@ var Generator TestGenerator } // change to testing directory to run command - // err = os.Chdir(testPluginDir) - // if err != nil { - // t.Fatalf("failed to 'cd' to temporary directory: %v", err) - // } + err = os.Chdir(testPluginDir) + if err != nil { + t.Fatalf("failed to 'cd' to temporary directory: %v", err) + } // execute command to build the plugin cmd := exec.Command("go", "build", "-buildmode=plugin", fmt.Sprintf("-o=%s", testPluginPath), testPluginSourcePath) @@ -193,6 +201,97 @@ var Generator TestGenerator } +// Test that expects to fail with a specific error using a partially +// implemented generator. The purpose of this test is to make sure we're +// seeing the correct error that we would expect in these situations. +// The errors should be something like: +// - no symbol: "failed to look up symbol at path" +// - invalid symbol: "failed to load the correct symbol type at path" +func TestPluginWithInvalidOrNoSymbol(t *testing.T) { + var ( + testPluginDir = t.TempDir() + testPluginPath = fmt.Sprintf("%s/invalid-plugin.so", testPluginDir) + testPluginSourcePath = fmt.Sprintf("%s/invalid-plugin.go", testPluginDir) + testPluginSource = []byte(` +package main + +import ( + "fmt" + + configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/generator" + "github.com/OpenCHAMI/configurator/pkg/util" +) + +// An invalid generator that does not or partially implements +// the "Generator" interface. +type InvalidGenerator struct{} +var Generator TestGenerator + `) + ) + + wd, err := os.Getwd() + if err != nil { + t.Errorf("failed to get working directory: %v", err) + } + // show all paths to make sure we're using the correct ones + fmt.Printf("(TestPluginWithInvalidOrNoSymbol) working directory: %v\n", wd) + fmt.Printf("(TestPluginWithInvalidOrNoSymbol) plugin directory: %v\n", testPluginDir) + fmt.Printf("(TestPluginWithInvalidOrNoSymbol) plugin path: %v\n", testPluginPath) + fmt.Printf("(TestPluginWithInvalidOrNoSymbol) plugin source path: %v\n", testPluginSourcePath) + + // make temporary directory to test plugin + err = os.MkdirAll(testPluginDir, os.ModeDir) + if err != nil { + t.Fatalf("failed to make temporary directory: %v", err) + } + + // dump the plugin source code to a file + err = os.WriteFile(testPluginSourcePath, testPluginSource, os.ModePerm) + if err != nil { + t.Fatalf("failed to write test plugin file: %v", err) + } + + // make sure the source file was actually written + fileInfo, err := os.Stat(testPluginSourcePath) + if err != nil { + t.Fatalf("failed to stat path: %v", err) + } + if fileInfo.IsDir() { + t.Fatalf("expected file but found directory") + } + + // change to testing directory to run command + err = os.Chdir(testPluginDir) + if err != nil { + t.Fatalf("failed to 'cd' to temporary directory: %v", err) + } + + // execute command to build the plugin + cmd := exec.Command("go", "build", "-buildmode=plugin", fmt.Sprintf("-o=%s", testPluginPath), testPluginSourcePath) + if output, err := cmd.Output(); err != nil { + t.Fatalf("failed to execute command: %v\n%s", err, string(output)) + } + + // stat the file to confirm that the plugin was built + fileInfo, err = os.Stat(testPluginPath) + if err != nil { + t.Fatalf("failed to stat plugin file: %v", err) + } + if fileInfo.IsDir() { + t.Fatalf("directory file but a file was expected") + } + if fileInfo.Size() <= 0 { + t.Fatal("found an empty file or file with size of 0 bytes") + } + + // try and load plugin, but expect specific error + _, err = generator.LoadPlugin(testPluginSourcePath) + if err == nil { + t.Fatalf("expected an error, but returned nil") + } +} + // Test that expects to successfully "generate" a file using the built-in // example plugin with no fetching. // @@ -238,19 +337,35 @@ func TestGenerateExample(t *testing.T) { // Test that expects to successfully "generate" a file using the built-in // example plugin but by making a HTTP request to a service instance instead. // -// NOTE: This test uses the default server settings to run. +// NOTE: This test uses the default server settings to run. Also, no need to +// try and load the plugin from a lib here either. func TestGenerateExampleWithServer(t *testing.T) { var ( config = configurator.NewConfig() + client = configurator.NewSmdClient() gen = TestGenerator{} headers = make(map[string]string, 0) ) - // add a test target to config - config.Targets["test"] = configurator.Target{} + // NOTE: Currently, the server needs a config to know where to get load plugins, + // and how to handle targets/templates. This will be simplified in the future to + // decoupled the server from required a config altogether. + config.Targets["test"] = configurator.Target{ + TemplatePaths: []string{}, + FilePaths: []string{}, + } - // start up a new server in background + // show which targets are availabe in the config + fmt.Printf("targets:\n") + for target, _ := range config.Targets { + fmt.Printf("\t- %s\n", target) + } + + // create new server, add test generator, and start in background server := server.New(&config) + server.GeneratorParams.Generators = map[string]generator.Generator{ + "test": &gen, + } go server.Serve() // make request to server to generate a file @@ -263,22 +378,25 @@ func TestGenerateExampleWithServer(t *testing.T) { } // test for specific output from request - fileMap, err := gen.Generate(&config) + // + // NOTE: we don't actually use the config in this plugin implementation, + // but we do check that a valid config was passed. + fileMap, err := gen.Generate( + &config, + generator.WithClient(client), + ) if err != nil { t.Fatalf("failed to generate file: %v", err) } - if string(fileMap["test"]) != string(b) { - t.Fatal("response does not match expected output") + for path, contents := range fileMap { + tmp := make(map[string]string, 1) + err := json.Unmarshal(b, &tmp) + if err != nil { + t.Errorf("failed to unmarshal response: %v", err) + continue + } + if string(contents) != string(tmp[path]) { + t.Fatalf("response does not match expected output...\nexpected:%s\noutput:%s", string(contents), string(tmp[path])) + } } - -} - -// Test that expects to fail with a specific error: "failed to loook up -// symbol at path" -func TestGenerateWithNoSymbol(t *testing.T) { -} - -// Test that expects to fail with a specific error: "failed to load the -// correct symbol type at path" -func TestGenerateWithInvalidGenerator(t *testing.T) { }