package main
import (
+ "bytes"
"fmt"
+ "log"
"os"
- "reflect"
"testing"
- "github.com/google/go-cmp/cmp"
-
+ "ruderich.org/simon/safcm"
"ruderich.org/simon/safcm/cmd/safcm/config"
+ "ruderich.org/simon/safcm/rpc"
+ "ruderich.org/simon/safcm/testutil"
)
func TestHostsToSync(t *testing.T) {
t.Fatal(err)
}
+ const errMsg = `
+
+Groups depending on "detected" groups cannot be used to select hosts as these
+are only available after the hosts were contacted.
+`
+
tests := []struct {
name string
names []string
{
"group: single name",
- []string{"group"},
+ []string{"group3"},
[]*config.Host{
allHosts.Map["host1.example.org"],
},
},
{
"group: multiple names",
- []string{"group", "group2"},
+ []string{"group3", "group2"},
[]*config.Host{
allHosts.Map["host1.example.org"],
allHosts.Map["host2"],
},
{
"group: multiple identical names",
- []string{"group", "group2", "group"},
+ []string{"group3", "group2", "group3"},
[]*config.Host{
allHosts.Map["host1.example.org"],
allHosts.Map["host2"],
},
{
"group: multiple names, including unknown",
- []string{"group", "group2", "unknown-group"},
+ []string{"group3", "group2", "unknown-group"},
nil,
fmt.Errorf("hosts/groups not found: \"unknown-group\""),
},
nil,
},
+ {
+ "group: single name (detected)",
+ []string{"group"},
+ nil,
+ fmt.Errorf(`group "group" depends on "detected" groups` + errMsg),
+ },
+ {
+ "group: multiple names (detected)",
+ []string{"group", "group2"},
+ nil,
+ fmt.Errorf(`group "group" depends on "detected" groups` + errMsg),
+ },
+
{
"\"all\" and name",
[]string{"all", "group2"},
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- res, err := hostsToSync(tc.names, allHosts, allGroups)
- if !reflect.DeepEqual(tc.exp, res) {
- t.Errorf("res: %s",
- cmp.Diff(tc.exp, res))
- }
- // Ugly but the simplest way to compare errors (including nil)
- if fmt.Sprintf("%s", err) != fmt.Sprintf("%s", tc.expErr) {
- t.Errorf("err = %#v, want %#v",
- err, tc.expErr)
- }
+ res, err := hostsToSync(tc.names, allHosts, allGroups)
+ testutil.AssertEqual(t, "res", res, tc.exp)
+ testutil.AssertErrorEqual(t, "err", err, tc.expErr)
+ })
+ }
+}
+
+func TestLogEvent(t *testing.T) {
+ // Restore default logger
+ defer log.SetFlags(log.Flags())
+ defer log.SetOutput(os.Stderr)
+
+ tests := []struct {
+ name string
+ event Event
+ level safcm.LogLevel
+ isTTY bool
+ exp string
+ expFailed bool
+ }{
+
+ {
+ "Error",
+ Event{
+ Error: fmt.Errorf("fake error"),
+ },
+ safcm.LogDebug3,
+ false,
+ "[error] [fake-host] fake error\n",
+ true,
+ },
+ {
+ "Error (tty)",
+ Event{
+ Error: fmt.Errorf("fake error"),
+ },
+ safcm.LogDebug3,
+ true,
+ "[error] [\x1b[31mfake-host\x1b[0m] fake error\n",
+ true,
+ },
+ {
+ "Error: escape",
+ Event{
+ Error: fmt.Errorf("\x00"),
+ },
+ safcm.LogDebug3,
+ false,
+ "[error] [fake-host] \\x00\n",
+ true,
+ },
+ {
+ "Error: escape (tty)",
+ Event{
+ Error: fmt.Errorf("\x00"),
+ },
+ safcm.LogDebug3,
+ true,
+ "[error] [\x1b[31mfake-host\x1b[0m] \x1b[35m\\x00\x1b[0m\n",
+ true,
+ },
+
+ {
+ "Log: info",
+ Event{
+ Log: Log{
+ Level: safcm.LogInfo,
+ Text: "info log",
+ },
+ },
+ safcm.LogDebug3,
+ false,
+ "[info] [fake-host] info log\n",
+ false,
+ },
+ {
+ "Log: info (tty)",
+ Event{
+ Log: Log{
+ Level: safcm.LogInfo,
+ Text: "info log",
+ },
+ },
+ safcm.LogDebug3,
+ true,
+ "[info] [fake-host] info log\n",
+ false,
+ },
+ {
+ "Log: verbose",
+ Event{
+ Log: Log{
+ Level: safcm.LogVerbose,
+ Text: "verbose log",
+ },
+ },
+ safcm.LogDebug3,
+ false,
+ "[verbose] [fake-host] verbose log\n",
+ false,
+ },
+ {
+ "Log: debug",
+ Event{
+ Log: Log{
+ Level: safcm.LogDebug,
+ Text: "debug log",
+ },
+ },
+ safcm.LogDebug3,
+ false,
+ "[debug] [fake-host] debug log\n",
+ false,
+ },
+ {
+ "Log: debug2",
+ Event{
+ Log: Log{
+ Level: safcm.LogDebug2,
+ Text: "debug2 log",
+ },
+ },
+ safcm.LogDebug3,
+ false,
+ "[debug2] [fake-host] debug2 log\n",
+ false,
+ },
+ {
+ "Log: debug3",
+ Event{
+ Log: Log{
+ Level: safcm.LogDebug3,
+ Text: "debug3 log",
+ },
+ },
+ safcm.LogDebug3,
+ false,
+ fmt.Sprintf("[INVALID=%d] [fake-host] debug3 log\n",
+ safcm.LogDebug3),
+ false,
+ },
+ {
+ "Log: debug3 (tty)",
+ Event{
+ Log: Log{
+ Level: safcm.LogDebug3,
+ Text: "debug3 log",
+ },
+ },
+ safcm.LogDebug3,
+ true,
+ fmt.Sprintf("[INVALID=%d] [\x1b[31mfake-host\x1b[0m] debug3 log\n",
+ safcm.LogDebug3),
+ false,
+ },
+ {
+ "Log: escape",
+ Event{
+ Log: Log{
+ Level: safcm.LogInfo,
+ Text: "\x00",
+ },
+ },
+ safcm.LogDebug3,
+ false,
+ "[info] [fake-host] \\x00\n",
+ false,
+ },
+ {
+ "Log: escape (tty)",
+ Event{
+ Log: Log{
+ Level: safcm.LogInfo,
+ Text: "\x00",
+ },
+ },
+ safcm.LogDebug3,
+ true,
+ "[info] [fake-host] \x1b[35m\\x00\x1b[0m\n",
+ false,
+ },
+
+ {
+ "ConnEvent: stderr",
+ Event{
+ ConnEvent: rpc.ConnEvent{
+ Type: rpc.ConnEventStderr,
+ Data: "fake stderr",
+ },
+ },
+ safcm.LogDebug3,
+ false,
+ "[stderr] [fake-host] fake stderr\n",
+ false,
+ },
+ {
+ "ConnEvent: stderr (tty)",
+ Event{
+ ConnEvent: rpc.ConnEvent{
+ Type: rpc.ConnEventStderr,
+ Data: "fake stderr",
+ },
+ },
+ safcm.LogDebug3,
+ true,
+ "[stderr] [fake-host] fake stderr\n",
+ false,
+ },
+ {
+ "ConnEvent: debug",
+ Event{
+ ConnEvent: rpc.ConnEvent{
+ Type: rpc.ConnEventDebug,
+ Data: "conn debug",
+ },
+ },
+ safcm.LogDebug3,
+ false,
+ "[debug3] [fake-host] conn debug\n",
+ false,
+ },
+ {
+ "ConnEvent: upload",
+ Event{
+ ConnEvent: rpc.ConnEvent{
+ Type: rpc.ConnEventUpload,
+ },
+ },
+ safcm.LogDebug3,
+ false,
+ "[info] [fake-host] remote helper upload in progress\n",
+ false,
+ },
+ {
+ "ConnEvent: upload (ignored)",
+ Event{
+ ConnEvent: rpc.ConnEvent{
+ Type: rpc.ConnEventUpload,
+ },
+ },
+ safcm.LogError,
+ false,
+ "",
+ false,
+ },
+ {
+ "ConnEvent: invalid",
+ Event{
+ ConnEvent: rpc.ConnEvent{
+ Type: 42,
+ Data: "invalid",
+ },
+ },
+ safcm.LogError,
+ false,
+ "[INVALID=42] [fake-host] invalid\n",
+ false,
+ },
+ {
+ "ConnEvent: invalid (tty)",
+ Event{
+ ConnEvent: rpc.ConnEvent{
+ Type: 42,
+ Data: "invalid",
+ },
+ },
+ safcm.LogError,
+ true,
+ "[INVALID=42] [\x1b[31mfake-host\x1b[0m] invalid\n",
+ false,
+ },
+ {
+ "ConnEvent: escape",
+ Event{
+ ConnEvent: rpc.ConnEvent{
+ Type: rpc.ConnEventStderr,
+ Data: "\x00",
+ },
+ },
+ safcm.LogDebug3,
+ false,
+ "[stderr] [fake-host] \\x00\n",
+ false,
+ },
+ {
+ "ConnEvent: escape (tty)",
+ Event{
+ ConnEvent: rpc.ConnEvent{
+ Type: rpc.ConnEventDebug,
+ Data: "\x01",
+ },
+ },
+ safcm.LogDebug3,
+ true,
+ "[debug3] [fake-host] \x1b[35m\\x01\x1b[0m\n",
+ false,
+ },
+
+ {
+ "Escaped",
+ Event{
+ Log: Log{
+ Level: safcm.LogInfo,
+ Text: "\x00",
+ },
+ Escaped: true,
+ },
+ safcm.LogDebug3,
+ false,
+ "[info] [fake-host] \x00\n",
+ false,
+ },
+ {
+ "Escaped (tty)",
+ Event{
+ Log: Log{
+ Level: safcm.LogInfo,
+ Text: "\x00",
+ },
+ Escaped: true,
+ },
+ safcm.LogDebug3,
+ true,
+ "[info] [fake-host] \x00\n",
+ false,
+ },
+
+ {
+ "empty (invalid)",
+ Event{},
+ safcm.LogDebug3,
+ false,
+ "[INVALID=0] [fake-host] \n",
+ false,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ tc.event.Host = &config.Host{
+ Name: "fake-host",
+ }
+
+ var buf bytes.Buffer
+ log.SetFlags(0)
+ log.SetOutput(&buf)
+
+ var failed bool
+ logEvent(tc.event, tc.level, tc.isTTY, &failed)
+
+ testutil.AssertEqual(t, "log",
+ buf.String(), tc.exp)
+ testutil.AssertEqual(t, "failed",
+ failed, tc.expFailed)
})
}
}