Files
warewulf/internal/app/wwctl/overlay/edit/main_test.go
Christian Goll 6f4fd60d8f refactored overlay class
overlay.GetOverlay(name) returns now an error if the overlay doesn't
exist. This is the most canonical way to act if there is no overlay.
2025-08-27 22:24:31 -06:00

218 lines
7.6 KiB
Go

package edit
import (
"bytes"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/warewulf/warewulf/internal/pkg/config"
"github.com/warewulf/warewulf/internal/pkg/testenv"
"github.com/warewulf/warewulf/internal/pkg/warewulfd"
)
func Test_OverlayEdit(t *testing.T) {
tests := []struct {
name string
overlayName string
fileName string
parents bool
editorScript string
setup func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string) (mtime time.Time)
wantErr bool
verify func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string, mtime time.Time)
}{
{
name: "edit existing file",
overlayName: "test-overlay",
fileName: "test.txt",
editorScript: `#!/bin/sh
sed -i 's/hello/world/' "$1"`,
setup: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string) time.Time {
overlayDir := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName)
assert.NoError(t, os.MkdirAll(overlayDir, 0755))
assert.NoError(t, os.WriteFile(filepath.Join(overlayDir, fileName), []byte("hello"), 0644))
return time.Time{}
},
wantErr: false,
verify: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string, mtime time.Time) {
overlayDir := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName)
content, err := os.ReadFile(filepath.Join(overlayDir, fileName))
assert.NoError(t, err)
assert.Equal(t, "world", string(content))
},
},
{
name: "edit non-existing file",
overlayName: "test-overlay",
fileName: "new-file.txt",
editorScript: `#!/bin/sh
echo "new content" > "$1"`,
setup: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string) time.Time {
overlayDir := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName)
assert.NoError(t, os.MkdirAll(overlayDir, 0755))
return time.Time{}
},
wantErr: false,
verify: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string, mtime time.Time) {
overlayDir := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName)
content, err := os.ReadFile(filepath.Join(overlayDir, fileName))
assert.NoError(t, err)
assert.Equal(t, "new content\n", string(content))
},
},
{
name: "edit non-existing .ww file",
overlayName: "test-overlay",
fileName: "template.ww",
editorScript: `#!/bin/sh
sed -i 's/autogenerated by warewulf/edited by test/' "$1"`,
setup: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string) time.Time {
overlayDir := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName)
assert.NoError(t, os.MkdirAll(overlayDir, 0755))
return time.Time{}
},
wantErr: false,
verify: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string, mtime time.Time) {
overlayDir := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName)
content, err := os.ReadFile(filepath.Join(overlayDir, fileName))
assert.NoError(t, err)
assert.Contains(t, string(content), "edited by test")
},
},
{
name: "edit without changes",
overlayName: "test-overlay",
fileName: "no-change.txt",
editorScript: `#!/bin/sh
# Do nothing to the file
exit 0`,
setup: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string) time.Time {
overlayDir := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName)
assert.NoError(t, os.MkdirAll(overlayDir, 0755))
filePath := filepath.Join(overlayDir, fileName)
assert.NoError(t, os.WriteFile(filePath, []byte("original"), 0644))
stat, err := os.Stat(filePath)
assert.NoError(t, err)
return stat.ModTime()
},
wantErr: false,
verify: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string, mtime time.Time) {
overlayDir := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName)
filePath := filepath.Join(overlayDir, fileName)
stat, err := os.Stat(filePath)
assert.NoError(t, err)
assert.True(t, stat.ModTime().Equal(mtime), "ModTime should not have changed")
},
},
{
name: "edit file in dist overlay (CoW)",
overlayName: "dist-overlay",
fileName: "test.txt",
editorScript: `#!/bin/sh
sed -i 's/dist/site/' "$1"`,
setup: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string) time.Time {
overlayDir := filepath.Join(conf.Paths.DistributionOverlaydir(), overlayName)
assert.NoError(t, os.MkdirAll(overlayDir, 0755))
assert.NoError(t, os.WriteFile(filepath.Join(overlayDir, fileName), []byte("dist content"), 0644))
return time.Time{}
},
wantErr: false,
verify: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string, mtime time.Time) {
distOverlayFile := filepath.Join(conf.Paths.DistributionOverlaydir(), overlayName, fileName)
content, err := os.ReadFile(distOverlayFile)
assert.NoError(t, err)
assert.Equal(t, "dist content", string(content))
siteOverlayFile := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName, fileName)
content, err = os.ReadFile(siteOverlayFile)
assert.NoError(t, err)
assert.Equal(t, "site content", string(content))
},
},
{
name: "edit in missing dir no parents",
overlayName: "test-overlay",
fileName: "newdir/test.txt",
editorScript: `#!/bin/sh
echo "content" > "$1"`,
setup: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string) time.Time {
overlayDir := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName)
assert.NoError(t, os.MkdirAll(overlayDir, 0755))
return time.Time{}
},
wantErr: true,
verify: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string, mtime time.Time) {
overlayFile := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName, fileName)
_, err := os.Stat(overlayFile)
assert.True(t, os.IsNotExist(err))
},
},
{
name: "edit in missing dir with parents",
overlayName: "test-overlay",
fileName: "newdir/test.txt",
parents: true,
editorScript: `#!/bin/sh
echo "content" > "$1"`,
setup: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string) time.Time {
overlayDir := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName)
assert.NoError(t, os.MkdirAll(overlayDir, 0755))
return time.Time{}
},
wantErr: false,
verify: func(t *testing.T, conf *config.WarewulfYaml, overlayName, fileName string, mtime time.Time) {
overlayFile := filepath.Join(conf.Paths.SiteOverlaydir(), overlayName, fileName)
content, err := os.ReadFile(overlayFile)
assert.NoError(t, err)
assert.Equal(t, "content\n", string(content))
},
},
}
warewulfd.SetNoDaemon()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
env := testenv.New(t)
defer env.RemoveAll()
conf := env.Configure()
scriptPath := filepath.Join(t.TempDir(), "fake_editor.sh")
err := os.WriteFile(scriptPath, []byte(tt.editorScript), 0755)
assert.NoError(t, err)
t.Setenv("EDITOR", scriptPath)
var mtime time.Time
if tt.setup != nil {
mtime = tt.setup(t, conf, tt.overlayName, tt.fileName)
}
var args []string
if tt.parents {
args = []string{"--parents", tt.overlayName, tt.fileName}
} else {
args = []string{tt.overlayName, tt.fileName}
}
baseCmd := GetCommand()
baseCmd.SetArgs(args)
buf := new(bytes.Buffer)
baseCmd.SetOut(buf)
baseCmd.SetErr(buf)
err = baseCmd.Execute()
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
if tt.verify != nil {
tt.verify(t, conf, tt.overlayName, tt.fileName, mtime)
}
})
}
}