File: logsource_systemd_test.go

package info (click to toggle)
prometheus-postfix-exporter 0.12.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 348 kB
  • sloc: sh: 78; makefile: 52
file content (162 lines) | stat: -rw-r--r-- 4,193 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//go:build !nosystemd && linux
// +build !nosystemd,linux

package main

import (
	"context"
	"os"
	"testing"
	"time"

	"github.com/coreos/go-systemd/v22/sdjournal"
	"github.com/stretchr/testify/assert"
)

func TestNewSystemdLogSource(t *testing.T) {
	j := &fakeSystemdJournal{}
	src, err := NewSystemdLogSource(j, "apath", "aunit", "aslice")
	if err != nil {
		t.Fatalf("NewSystemdLogSource failed: %v", err)
	}

	assert.Equal(t, []string{"_SYSTEMD_SLICE=aslice"}, j.addMatchCalls, "A match should be added for slice.")
	assert.Equal(t, 1, j.seekTailCalls, "A call to SeekTail should be made.")
	assert.Equal(t, 0, len(j.waitCalls), "No call to Wait should be made yet.")

	if err := src.Close(); err != nil {
		t.Fatalf("Close failed: %v", err)
	}

	assert.Equal(t, 1, j.closeCalls, "A call to Close should be made.")
}

func TestSystemdLogSource_Path(t *testing.T) {
	j := &fakeSystemdJournal{}
	src, err := NewSystemdLogSource(j, "apath", "aunit", "aslice")
	if err != nil {
		t.Fatalf("NewSystemdLogSource failed: %v", err)
	}
	defer src.Close()

	assert.Equal(t, "apath", src.Path(), "Path should be set by New.")
}

func TestSystemdLogSource_Read(t *testing.T) {
	ctx := context.Background()

	j := &fakeSystemdJournal{
		getEntryValues: []sdjournal.JournalEntry{
			{
				Fields: map[string]string{
					"_HOSTNAME":         "ahost",
					"SYSLOG_IDENTIFIER": "anid",
					"_PID":              "123",
					"MESSAGE":           "aline",
				},
				RealtimeTimestamp: 1234567890000000,
			},
		},
		nextValues: []uint64{1},
	}
	src, err := NewSystemdLogSource(j, "apath", "aunit", "aslice")
	if err != nil {
		t.Fatalf("NewSystemdLogSource failed: %v", err)
	}
	defer src.Close()

	s, err := src.Read(ctx)
	if err != nil {
		t.Fatalf("Read failed: %v", err)
	}
	assert.Equal(t, []time.Duration{10 * time.Second}, j.waitCalls, "A Wait call should be made")
	assert.Equal(t, 2, j.seekTailCalls, "Two seekTail calls expected")
	assert.Equal(t, []uint64{1}, j.previousSkipCalls, "One previousSkipCall expected.")
	assert.Equal(t, "Feb 13 23:31:30 ahost anid[123]: aline", s, "Read should get data from the journal entry.")
}

func TestSystemdLogSource_ReadEOF(t *testing.T) {
	ctx := context.Background()

	j := &fakeSystemdJournal{
		nextValues: []uint64{0},
	}
	src, err := NewSystemdLogSource(j, "apath", "aunit", "aslice")
	if err != nil {
		t.Fatalf("NewSystemdLogSource failed: %v", err)
	}
	defer src.Close()

	_, err = src.Read(ctx)
	assert.Equal(t, SystemdNoMoreEntries, err, "Should interpret Next 0 as no more entries.")
}

func TestMain(m *testing.M) {
	// We compare Unix timestamps to date strings, so make it deterministic.
	os.Setenv("TZ", "UTC")
	timeNow = func() time.Time { return time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC) }
	defer func() {
		timeNow = time.Now
	}()

	os.Exit(m.Run())
}

type fakeSystemdJournal struct {
	getEntryError     error
	nextError         error
	getEntryValues    []sdjournal.JournalEntry
	nextValues        []uint64
	addMatchCalls     []string
	previousSkipCalls []uint64
	waitCalls         []time.Duration
	closeCalls        int
	seekTailCalls     int
}

func (j *fakeSystemdJournal) AddMatch(match string) error {
	j.addMatchCalls = append(j.addMatchCalls, match)
	return nil
}

func (j *fakeSystemdJournal) Close() error {
	j.closeCalls++
	return nil
}

func (j *fakeSystemdJournal) GetEntry() (*sdjournal.JournalEntry, error) {
	if len(j.getEntryValues) == 0 {
		return nil, j.getEntryError
	}
	e := j.getEntryValues[0]
	j.getEntryValues = j.getEntryValues[1:]
	return &e, nil
}

func (j *fakeSystemdJournal) Next() (uint64, error) {
	if len(j.nextValues) == 0 {
		return 0, j.nextError
	}
	v := j.nextValues[0]
	j.nextValues = j.nextValues[1:]
	return v, nil
}

func (j *fakeSystemdJournal) SeekTail() error {
	j.seekTailCalls++
	return nil
}

func (j *fakeSystemdJournal) PreviousSkip(skip uint64) (uint64, error) {
	j.previousSkipCalls = append(j.previousSkipCalls, skip)
	return skip, nil
}

func (j *fakeSystemdJournal) Wait(timeout time.Duration) int {
	j.waitCalls = append(j.waitCalls, timeout)
	if len(j.waitCalls) == 1 {
		// first wait call
		return sdjournal.SD_JOURNAL_INVALIDATE
	}
	return sdjournal.SD_JOURNAL_APPEND
}