diff --git a/CHANGELOG.md b/CHANGELOG.md index 645de56c..dd57199a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 removes a limit on the number of overlays that could be included in a node or profile. #852, #876, #883, #896, #903 - During node discovery, prefer the primary network interface, if defined. #775 +- added general test framework un ww4test ## [4.4.0] 2023-01-18 diff --git a/internal/app/wwctl/node/set/main_test.go b/internal/app/wwctl/node/set/main_test.go index 4feda0d3..d8a42d8d 100644 --- a/internal/app/wwctl/node/set/main_test.go +++ b/internal/app/wwctl/node/set/main_test.go @@ -5,22 +5,78 @@ import ( "os" "testing" - warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" - "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/warewulfd" + "github.com/hpcng/warewulf/internal/pkg/ww4test" "github.com/stretchr/testify/assert" ) -func Test_Add(t *testing.T) { - tests := []struct { - name string - args []string - wantErr bool - stdout string - chkout bool - outDb string - inDB string - }{ +type test_description struct { + name string + args []string + wantErr bool + stdout string + outDb string + inDB string +} + +func run_test(t *testing.T, test test_description) { + //wwlog.SetLogLevel(wwlog.DEBUG) + var env ww4test.WarewulfTestEnv + env.NodesConf = test.inDB + env.New(t) + defer os.RemoveAll(env.BaseDir) + warewulfd.SetNoDaemon() + name := test.name + if name == "" { + name = t.Name() + } + t.Run(name, func(t *testing.T) { + baseCmd := GetCommand() + test.args = append(test.args, "--yes") + baseCmd.SetArgs(test.args) + buf := new(bytes.Buffer) + baseCmd.SetOut(buf) + baseCmd.SetErr(buf) + err := baseCmd.Execute() + if test.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, buf.String(), test.stdout) + content, err := os.ReadFile(env.NodesConfFile) + assert.NoError(t, err) + assert.Equal(t, test.outDb, string(content)) + } + }) +} + +func Test_Single_Node_Change_Profile(t *testing.T) { + test := test_description{ + args: []string{"--profile=foo", "n01"}, + wantErr: false, + stdout: "", + inDB: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default`, + outDb: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - foo +`} + run_test(t, test) +} + +func Test_Multiple_Add_Tests(t *testing.T) { + tests := []test_description{ {name: "single node change profile", args: []string{"--profile=foo", "n01"}, wantErr: false, @@ -296,53 +352,7 @@ nodes: path: /var `}, } - conf_yml := `WW_INTERNAL: 0` - tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") - assert.NoError(t, warewulfConfErr) - defer os.Remove(tempWarewulfConf.Name()) - _, warewulfConfErr = tempWarewulfConf.Write([]byte(conf_yml)) - assert.NoError(t, warewulfConfErr) - assert.NoError(t, tempWarewulfConf.Sync()) - assert.NoError(t, warewulfconf.New().Read(tempWarewulfConf.Name())) - - tempNodeConf, nodesConfErr := os.CreateTemp("", "nodes.conf-") - assert.NoError(t, nodesConfErr) - defer os.Remove(tempNodeConf.Name()) - node.ConfigFile = tempNodeConf.Name() - warewulfd.SetNoDaemon() for _, tt := range tests { - var err error - _, err = tempNodeConf.Seek(0, 0) - assert.NoError(t, err) - assert.NoError(t, tempNodeConf.Truncate(0)) - _, err = tempNodeConf.Write([]byte(tt.inDB)) - assert.NoError(t, err) - assert.NoError(t, tempNodeConf.Sync()) - t.Logf("Running test: %s\n", tt.name) - t.Run(tt.name, func(t *testing.T) { - baseCmd := GetCommand() - tt.args = append(tt.args, "--yes") - baseCmd.SetArgs(tt.args) - buf := new(bytes.Buffer) - baseCmd.SetOut(buf) - baseCmd.SetErr(buf) - err = baseCmd.Execute() - if (err != nil) != tt.wantErr { - t.Errorf("Got unwanted error: %s", err) - t.FailNow() - } - config, configErr := node.New() - assert.NoError(t, configErr) - dumpBytes, _ := config.Dump() - dump := string(dumpBytes) - if dump != tt.outDb { - t.Errorf("DB dump is wrong, got:'%s'\nwant:'%s'", dump, tt.outDb) - t.FailNow() - } - if tt.chkout && buf.String() != tt.stdout { - t.Errorf("Got wrong output, got:'%s'\nwant:'%s'", buf.String(), tt.stdout) - t.FailNow() - } - }) + run_test(t, tt) } } diff --git a/internal/pkg/ww4test/ww4test.go b/internal/pkg/ww4test/ww4test.go new file mode 100644 index 00000000..724e0018 --- /dev/null +++ b/internal/pkg/ww4test/ww4test.go @@ -0,0 +1,136 @@ +package ww4test + +import ( + "fmt" + "io/fs" + "os" + "path" + "testing" + + "github.com/hpcng/warewulf/internal/pkg/config" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +var ( + Env *WarewulfTestEnv +) + +func init() { + Env = new(WarewulfTestEnv) + Env.WarewulfConf = `WW_INTERNAL: 0` + Env.DefaultsConf = `WW_INTERNAL: 43` + Env.NodesConf = `WW_INTERNAL: 43 +nodeprofiles: + default: {} +nodes: + node1: {} +` + +} + +type ConfFile struct { + // directory in which file exists + Dir string + // filename, if empty only directory is create + FileName string + // content of file + Content string + // permissions of file, 644 for file and 755 for + // leading directory otherwise + Mode fs.FileMode +} + +type WarewulfTestEnv struct { + // content of warewulf.conf + WarewulfConf string + // path to warewulf.conf + WarewulfConfFile string + // content of nodes.conf + NodesConf string + // path to nodes.conf + NodesConfFile string + // content for defaults.conf + DefaultsConf string + // tmpe dir where the files are created + BaseDir string + // additional files to be created by New(t) + WarewulfFiles []ConfFile +} + +// creates the given file, if DIR is "" only the dir is created +// any error is returned +func (env *WarewulfTestEnv) CreateFile(file ConfFile) (err error) { + if env.BaseDir == "" { + return fmt.Errorf("the variable Basedir is empty, perhaps New() wasn't called") + } + var dirM fs.FileMode = file.Mode + // var fileM fs.FileMode = file.Mode + if file.Mode == 0 { + dirM = 0755 + // fileM = 0644 + } + err = os.MkdirAll(path.Join(env.BaseDir, file.Dir), dirM) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("couldn't create dir: %s", path.Join(env.BaseDir, file.Dir))) + } + if file.FileName != "" { + f, err := os.Create(path.Join(env.BaseDir, file.Dir, file.FileName)) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("couldn't open file: %s", path.Join(env.BaseDir, file.Dir, file.FileName))) + } + defer f.Close() + _, err = f.WriteString(file.Content) + return err + } + return +} + +/* +Creates a test environment so that warewulf functions can be tested. +nodes.conf content can be provided either via global Env.WarewulfConf. +Caller is responsible to delete the created Env.BaseDir +*/ +func (env *WarewulfTestEnv) New(t *testing.T) { + tmpDir, err := os.MkdirTemp(os.TempDir(), "ww4test-*") + assert.NoError(t, err) + env.BaseDir = tmpDir + err = env.CreateFile(ConfFile{ + Dir: "etc/warewulf", + FileName: "nodes.conf", + Content: env.NodesConf, + }) + assert.NoError(t, err) + err = env.CreateFile(ConfFile{ + Dir: "etc/warewulf", + FileName: "warewulf.conf", + Content: env.WarewulfConf, + }) + assert.NoError(t, err) + err = env.CreateFile(ConfFile{ + Dir: "share/warewulf", + FileName: "defaults.conf", + Content: env.WarewulfConf, + }) + assert.NoError(t, err) + env.WarewulfConfFile = path.Join(env.BaseDir, "etc/warewulf/warewulf.conf") + env.NodesConfFile = path.Join(env.BaseDir, "etc/warewulf/nodes.conf") + conf := config.New() + err = conf.Read(env.WarewulfConfFile) + assert.NoError(t, err) + // init() of node has run before, so set the config path again + node.ConfigFile = env.NodesConfFile + conf.Paths.Sysconfdir = path.Join(env.BaseDir, "etc") + conf.Paths.Bindir = path.Join(env.BaseDir, "bin") + conf.Paths.Datadir = path.Join(env.BaseDir, "share") + conf.Paths.Localstatedir = path.Join(env.BaseDir, "var/local") + conf.Paths.Srvdir = path.Join(env.BaseDir, "srv") + conf.Paths.Tftpdir = path.Join(env.BaseDir, "srv/tftp") + conf.Paths.Firewallddir = path.Join(env.BaseDir, "usr/lib/firewalld/services") + conf.Paths.Systemddir = path.Join(env.BaseDir, "usr/lib/systemd/system") + conf.Paths.WWOverlaydir = path.Join(env.BaseDir, conf.Paths.Localstatedir, "warewulfoverlays") + conf.Paths.WWChrootdir = path.Join(env.BaseDir, conf.Paths.Localstatedir, "warewulf/chroots") + conf.Paths.WWProvisiondir = path.Join(env.BaseDir, conf.Paths.Srvdir, "warewulf") + conf.Paths.WWClientdir = path.Join(env.BaseDir, "warewulf") +} diff --git a/internal/pkg/ww4test/ww4test_test.go b/internal/pkg/ww4test/ww4test_test.go new file mode 100644 index 00000000..3e6cdf76 --- /dev/null +++ b/internal/pkg/ww4test/ww4test_test.go @@ -0,0 +1,36 @@ +package ww4test + +import ( + "os" + "testing" + + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/stretchr/testify/assert" +) + +func Test_Basic(t *testing.T) { + Env.New(t) + defer os.RemoveAll(Env.BaseDir) + nodedb, err := node.New() + assert.NoError(t, err) + nodes, err := nodedb.FindAllNodes() + assert.NoError(t, err) + assert.Len(t, nodes, 1) +} + +func Test_two_nodes(t *testing.T) { + Env.NodesConf = `WW_INTERNAL: 43 +nodeprofiles: + default: {} +nodes: + node1: {} + node2: {} +` + Env.New(t) + defer os.RemoveAll(Env.BaseDir) + nodedb, err := node.New() + assert.NoError(t, err) + nodes, err := nodedb.FindAllNodes() + assert.NoError(t, err) + assert.Len(t, nodes, 2) +}