package exec import ( "fmt" "github.com/influxdb/telegraf/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "math" "testing" "time" ) // Midnight 9/22/2015 const baseTimeSeconds = 1442905200 const validJson = ` { "status": "green", "num_processes": 82, "cpu": { "status": "red", "nil_status": null, "used": 8234, "free": 32 }, "percent": 0.81, "users": [0, 1, 2, 3] }` const malformedJson = ` { "status": "green", ` type runnerMock struct { out []byte err error } type clockMock struct { now time.Time } func newRunnerMock(out []byte, err error) Runner { return &runnerMock{ out: out, err: err, } } func (r runnerMock) Run(command *Command) ([]byte, error) { if r.err != nil { return nil, r.err } return r.out, nil } func newClockMock(now time.Time) Clock { return &clockMock{now: now} } func (c clockMock) Now() time.Time { return c.now } func TestExec(t *testing.T) { runner := newRunnerMock([]byte(validJson), nil) clock := newClockMock(time.Unix(baseTimeSeconds+20, 0)) command := Command{ Command: "testcommand arg1", Name: "mycollector", Interval: 10, lastRunAt: time.Unix(baseTimeSeconds, 0), } e := &Exec{ runner: runner, clock: clock, Commands: []*Command{&command}, } var acc testutil.Accumulator initialPoints := len(acc.Points) err := e.Gather(&acc) deltaPoints := len(acc.Points) - initialPoints require.NoError(t, err) checkFloat := []struct { name string value float64 }{ {"mycollector_num_processes", 82}, {"mycollector_cpu_used", 8234}, {"mycollector_cpu_free", 32}, {"mycollector_percent", 0.81}, } for _, c := range checkFloat { assert.True(t, acc.CheckValue(c.name, c.value)) } assert.Equal(t, deltaPoints, 4, "non-numeric measurements should be ignored") } func TestExecMalformed(t *testing.T) { runner := newRunnerMock([]byte(malformedJson), nil) clock := newClockMock(time.Unix(baseTimeSeconds+20, 0)) command := Command{ Command: "badcommand arg1", Name: "mycollector", Interval: 10, lastRunAt: time.Unix(baseTimeSeconds, 0), } e := &Exec{ runner: runner, clock: clock, Commands: []*Command{&command}, } var acc testutil.Accumulator initialPoints := len(acc.Points) err := e.Gather(&acc) deltaPoints := len(acc.Points) - initialPoints require.Error(t, err) assert.Equal(t, deltaPoints, 0, "No new points should have been added") } func TestCommandError(t *testing.T) { runner := newRunnerMock(nil, fmt.Errorf("exit status code 1")) clock := newClockMock(time.Unix(baseTimeSeconds+20, 0)) command := Command{ Command: "badcommand", Name: "mycollector", Interval: 10, lastRunAt: time.Unix(baseTimeSeconds, 0), } e := &Exec{ runner: runner, clock: clock, Commands: []*Command{&command}, } var acc testutil.Accumulator initialPoints := len(acc.Points) err := e.Gather(&acc) deltaPoints := len(acc.Points) - initialPoints require.Error(t, err) assert.Equal(t, deltaPoints, 0, "No new points should have been added") } func TestExecNotEnoughTime(t *testing.T) { runner := newRunnerMock([]byte(validJson), nil) clock := newClockMock(time.Unix(baseTimeSeconds+5, 0)) command := Command{ Command: "testcommand arg1", Name: "mycollector", Interval: 10, lastRunAt: time.Unix(baseTimeSeconds, 0), } e := &Exec{ runner: runner, clock: clock, Commands: []*Command{&command}, } var acc testutil.Accumulator initialPoints := len(acc.Points) err := e.Gather(&acc) deltaPoints := len(acc.Points) - initialPoints require.NoError(t, err) assert.Equal(t, deltaPoints, 0, "No new points should have been added") } func TestExecUninitializedLastRunAt(t *testing.T) { runner := newRunnerMock([]byte(validJson), nil) clock := newClockMock(time.Unix(baseTimeSeconds, 0)) command := Command{ Command: "testcommand arg1", Name: "mycollector", Interval: math.MaxInt32, // Uninitialized lastRunAt should default to time.Unix(0, 0), so this should // run no matter what the interval is } e := &Exec{ runner: runner, clock: clock, Commands: []*Command{&command}, } var acc testutil.Accumulator initialPoints := len(acc.Points) err := e.Gather(&acc) deltaPoints := len(acc.Points) - initialPoints require.NoError(t, err) checkFloat := []struct { name string value float64 }{ {"mycollector_num_processes", 82}, {"mycollector_cpu_used", 8234}, {"mycollector_cpu_free", 32}, {"mycollector_percent", 0.81}, } for _, c := range checkFloat { assert.True(t, acc.CheckValue(c.name, c.value)) } assert.Equal(t, deltaPoints, 4, "non-numeric measurements should be ignored") } func TestExecOneNotEnoughTimeAndOneEnoughTime(t *testing.T) { runner := newRunnerMock([]byte(validJson), nil) clock := newClockMock(time.Unix(baseTimeSeconds+5, 0)) notEnoughTimeCommand := Command{ Command: "testcommand arg1", Name: "mycollector", Interval: 10, lastRunAt: time.Unix(baseTimeSeconds, 0), } enoughTimeCommand := Command{ Command: "testcommand arg1", Name: "mycollector", Interval: 3, lastRunAt: time.Unix(baseTimeSeconds, 0), } e := &Exec{ runner: runner, clock: clock, Commands: []*Command{¬EnoughTimeCommand, &enoughTimeCommand}, } var acc testutil.Accumulator initialPoints := len(acc.Points) err := e.Gather(&acc) deltaPoints := len(acc.Points) - initialPoints require.NoError(t, err) checkFloat := []struct { name string value float64 }{ {"mycollector_num_processes", 82}, {"mycollector_cpu_used", 8234}, {"mycollector_cpu_free", 32}, {"mycollector_percent", 0.81}, } for _, c := range checkFloat { assert.True(t, acc.CheckValue(c.name, c.value)) } assert.Equal(t, deltaPoints, 4, "Only one command should have been run") }