Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions pkg/module/dummy/dummy-example-cfg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ devices:
uses:
- module: dummy-status
main: true
args:
- foo
- bar
repeat:
desc: "Interactively repeat user input via the dummy-repeat module"
uses:
Expand Down
2 changes: 1 addition & 1 deletion pkg/module/ipmi/ipmi-example-cfg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ devices:
power:
desc: "Power on the server using IPMI"
uses:
- module: ipmi-power
- module: ipmi
with:
host: 192.168.1.100
port: 623
Expand Down
103 changes: 103 additions & 0 deletions test/integration/dutagent/checkconfig_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2025 Blindspot Software
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package dutagent_integration contains integration tests for the dutagent binary.
package dutagent_integration

import (
"bytes"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
)

func TestMain(m *testing.M) {
bin, err := buildDutagent()
if err != nil {
os.Stderr.WriteString("failed to build dutagent: " + err.Error() + "\n")
os.Exit(1)
}

os.Setenv("DUTAGENT_TEST_BINARY", bin)

code := m.Run()
os.Remove(bin)
os.Exit(code)
}

func dutagentBinary(t *testing.T) string {
t.Helper()

bin := os.Getenv("DUTAGENT_TEST_BINARY")
if bin == "" {
t.Fatal("DUTAGENT_TEST_BINARY not set — was TestMain skipped?")
}

return bin
}

func buildDutagent() (string, error) {
bin, err := os.CreateTemp("", "dutagent-test-*")
if err != nil {
return "", err
}

bin.Close()

cmd := exec.Command("go", "build", "-o", bin.Name(), "../../../cmds/dutagent")
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr
Comment thread
RiSKeD marked this conversation as resolved.

if err := cmd.Run(); err != nil {
Comment thread
RiSKeD marked this conversation as resolved.
return "", err
}

return bin.Name(), nil
Comment thread
RiSKeD marked this conversation as resolved.
}

func exampleConfigs(t *testing.T) []string {
t.Helper()

// Resolve relative to this source file so the test works regardless of
// working directory (e.g. go test ./...).
_, thisFile, _, _ := runtime.Caller(0)
root := filepath.Join(filepath.Dir(thisFile), "..", "..", "..")

configs, err := filepath.Glob(filepath.Join(root, "pkg", "module", "*", "*-example-cfg.yml"))
if err != nil {
t.Fatalf("glob failed: %v", err)
}

if len(configs) == 0 {
t.Fatal("no example config files found — check the glob pattern")
}

return configs
}

// TestCheckConfigExampleFiles runs dutagent --check-config against every example
// config file shipped with the module packages and asserts:
// - exit code 0
// - "Configuration is valid" in output
func TestCheckConfigExampleFiles(t *testing.T) {
bin := dutagentBinary(t)

for _, cfgPath := range exampleConfigs(t) {
t.Run(filepath.Base(filepath.Dir(cfgPath)), func(t *testing.T) {
cmd := exec.CommandContext(t.Context(), bin, "--check-config", "-c", cfgPath)
out, err := cmd.CombinedOutput()

if err != nil {
t.Errorf("unexpected exit: %v\n%s", err, out)
return
}

if !bytes.Contains(out, []byte("Configuration is valid")) {
t.Errorf("expected 'Configuration is valid' in output, got:\n%s", out)
}
})
}
}