1 package cmd
2
3 import (
4 "encoding/json"
5 "fmt"
6 "log"
7 "os"
8 "os/exec"
9 "runtime"
10 "strings"
11 "testing"
12 "time"
13
14 "github.com/letsencrypt/boulder/core"
15 blog "github.com/letsencrypt/boulder/log"
16 "github.com/letsencrypt/boulder/test"
17 "github.com/prometheus/client_golang/prometheus"
18 )
19
20 var (
21 validPAConfig = []byte(`{
22 "dbConnect": "dummyDBConnect",
23 "enforcePolicyWhitelist": false,
24 "challenges": { "http-01": true }
25 }`)
26 invalidPAConfig = []byte(`{
27 "dbConnect": "dummyDBConnect",
28 "enforcePolicyWhitelist": false,
29 "challenges": { "nonsense": true }
30 }`)
31 noChallengesPAConfig = []byte(`{
32 "dbConnect": "dummyDBConnect",
33 "enforcePolicyWhitelist": false
34 }`)
35
36 emptyChallengesPAConfig = []byte(`{
37 "dbConnect": "dummyDBConnect",
38 "enforcePolicyWhitelist": false,
39 "challenges": {}
40 }`)
41 )
42
43 func TestPAConfigUnmarshal(t *testing.T) {
44 var pc1 PAConfig
45 err := json.Unmarshal(validPAConfig, &pc1)
46 test.AssertNotError(t, err, "Failed to unmarshal PAConfig")
47 test.AssertNotError(t, pc1.CheckChallenges(), "Flagged valid challenges as bad")
48
49 var pc2 PAConfig
50 err = json.Unmarshal(invalidPAConfig, &pc2)
51 test.AssertNotError(t, err, "Failed to unmarshal PAConfig")
52 test.AssertError(t, pc2.CheckChallenges(), "Considered invalid challenges as good")
53
54 var pc3 PAConfig
55 err = json.Unmarshal(noChallengesPAConfig, &pc3)
56 test.AssertNotError(t, err, "Failed to unmarshal PAConfig")
57 test.AssertError(t, pc3.CheckChallenges(), "Disallow empty challenges map")
58
59 var pc4 PAConfig
60 err = json.Unmarshal(emptyChallengesPAConfig, &pc4)
61 test.AssertNotError(t, err, "Failed to unmarshal PAConfig")
62 test.AssertError(t, pc4.CheckChallenges(), "Disallow empty challenges map")
63 }
64
65 func TestMysqlLogger(t *testing.T) {
66 log := blog.UseMock()
67 mLog := mysqlLogger{log}
68
69 testCases := []struct {
70 args []interface{}
71 expected string
72 }{
73 {
74 []interface{}{nil},
75 `ERR: [AUDIT] [mysql] <nil>`,
76 },
77 {
78 []interface{}{""},
79 `ERR: [AUDIT] [mysql] `,
80 },
81 {
82 []interface{}{"Sup ", 12345, " Sup sup"},
83 `ERR: [AUDIT] [mysql] Sup 12345 Sup sup`,
84 },
85 }
86
87 for _, tc := range testCases {
88
89 mLog.Print(tc.args...)
90 logged := log.GetAll()
91
92 test.AssertEquals(t, len(logged), 1)
93 test.AssertEquals(t, logged[0], tc.expected)
94 log.Clear()
95 }
96 }
97
98 func TestCaptureStdlibLog(t *testing.T) {
99 logger := blog.UseMock()
100 oldDest := log.Writer()
101 defer func() {
102 log.SetOutput(oldDest)
103 }()
104 log.SetOutput(logWriter{logger})
105 log.Print("thisisatest")
106 results := logger.GetAllMatching("thisisatest")
107 if len(results) != 1 {
108 t.Fatalf("Expected logger to receive 'thisisatest', got: %s",
109 strings.Join(logger.GetAllMatching(".*"), "\n"))
110 }
111 }
112
113 func TestVersionString(t *testing.T) {
114 core.BuildID = "TestBuildID"
115 core.BuildTime = "RightNow!"
116 core.BuildHost = "Localhost"
117
118 versionStr := VersionString()
119 expected := fmt.Sprintf("Versions: cmd.test=(TestBuildID RightNow!) Golang=(%s) BuildHost=(Localhost)", runtime.Version())
120 test.AssertEquals(t, versionStr, expected)
121 }
122
123 func TestReadConfigFile(t *testing.T) {
124 err := ReadConfigFile("", nil)
125 test.AssertError(t, err, "ReadConfigFile('') did not error")
126
127 type config struct {
128 NotifyMailer struct {
129 DB DBConfig
130 SMTPConfig
131 }
132 Syslog SyslogConfig
133 }
134 var c config
135 err = ReadConfigFile("../test/config/notify-mailer.json", &c)
136 test.AssertNotError(t, err, "ReadConfigFile(../test/config/notify-mailer.json) errored")
137 test.AssertEquals(t, c.NotifyMailer.SMTPConfig.Server, "localhost")
138 }
139
140 func TestLogWriter(t *testing.T) {
141 mock := blog.UseMock()
142 lw := logWriter{mock}
143 _, _ = lw.Write([]byte("hi\n"))
144 lines := mock.GetAllMatching(".*")
145 test.AssertEquals(t, len(lines), 1)
146 test.AssertEquals(t, lines[0], "INFO: hi")
147 }
148
149 func TestGRPCLoggerWarningFilter(t *testing.T) {
150 m := blog.NewMock()
151 l := grpcLogger{m}
152 l.Warningln("asdf", "qwer")
153 lines := m.GetAllMatching(".*")
154 test.AssertEquals(t, len(lines), 1)
155
156 m = blog.NewMock()
157 l = grpcLogger{m}
158 l.Warningln("Server.processUnaryRPC failed to write status: connection error: desc = \"transport is closing\"")
159 lines = m.GetAllMatching(".*")
160 test.AssertEquals(t, len(lines), 0)
161 }
162
163 func Test_newVersionCollector(t *testing.T) {
164
165 core.BuildTime = core.Unspecified
166 version := newVersionCollector()
167
168 test.AssertMetricWithLabelsEquals(t, version, prometheus.Labels{"buildTime": core.Unspecified}, 1)
169
170 now := time.Now().UTC()
171 core.BuildTime = now.Format(time.UnixDate)
172 version = newVersionCollector()
173 test.AssertMetricWithLabelsEquals(t, version, prometheus.Labels{"buildTime": now.Format(time.RFC3339)}, 1)
174
175 core.BuildTime = "outta time"
176 version = newVersionCollector()
177 test.AssertMetricWithLabelsEquals(t, version, prometheus.Labels{"buildTime": "Unparsable"}, 1)
178
179
180 expectedBuildID := "TestBuildId"
181 core.BuildID = expectedBuildID
182 version = newVersionCollector()
183 test.AssertMetricWithLabelsEquals(t, version, prometheus.Labels{"buildId": expectedBuildID}, 1)
184
185
186 test.AssertMetricWithLabelsEquals(t, version, prometheus.Labels{"goVersion": runtime.Version()}, 1)
187 }
188
189 func loadConfigFile(t *testing.T, path string) *os.File {
190 cf, err := os.Open(path)
191 if err != nil {
192 t.Fatal(err)
193 }
194 return cf
195 }
196
197 func TestFailedConfigValidation(t *testing.T) {
198 type FooConfig struct {
199 VitalValue string `yaml:"vitalValue" validate:"required"`
200 VoluntarilyVoid string `yaml:"voluntarilyVoid"`
201 VisciouslyVetted string `yaml:"visciouslyVetted" validate:"omitempty,endswith=baz"`
202 }
203
204
205 cf := loadConfigFile(t, "testdata/1_missing_endswith.json")
206 defer cf.Close()
207 err := ValidateJSONConfig(&ConfigValidator{&FooConfig{}, nil}, cf)
208 test.AssertError(t, err, "Expected validation error")
209 test.AssertContains(t, err.Error(), "'endswith'")
210
211
212 cf = loadConfigFile(t, "testdata/1_missing_endswith.yaml")
213 defer cf.Close()
214 err = ValidateYAMLConfig(&ConfigValidator{&FooConfig{}, nil}, cf)
215 test.AssertError(t, err, "Expected validation error")
216 test.AssertContains(t, err.Error(), "'endswith'")
217
218
219 cf = loadConfigFile(t, "testdata/2_missing_required.json")
220 defer cf.Close()
221 err = ValidateJSONConfig(&ConfigValidator{&FooConfig{}, nil}, cf)
222 test.AssertError(t, err, "Expected validation error")
223 test.AssertContains(t, err.Error(), "'required'")
224
225
226 cf = loadConfigFile(t, "testdata/2_missing_required.yaml")
227 defer cf.Close()
228 err = ValidateYAMLConfig(&ConfigValidator{&FooConfig{}, nil}, cf)
229 test.AssertError(t, err, "Expected validation error")
230 test.AssertContains(t, err.Error(), "'required'")
231 }
232
233 func TestFailExit(t *testing.T) {
234
235
236
237
238 if os.Getenv("TIME_TO_DIE") == "1" {
239 defer AuditPanic()
240 Fail("tears in the rain")
241 return
242 }
243
244
245
246
247 cmd := exec.Command(os.Args[0], "-test.run=TestFailExit")
248 cmd.Env = append(os.Environ(), "TIME_TO_DIE=1")
249 output, err := cmd.CombinedOutput()
250 test.AssertError(t, err, "running a failing program")
251 test.AssertContains(t, string(output), "[AUDIT] tears in the rain")
252
253
254 test.AssertNotContains(t, string(output), "goroutine")
255 }
256
257 func testPanicStackTraceHelper() {
258 var x *int
259 *x = 1
260 }
261
262 func TestPanicStackTrace(t *testing.T) {
263
264
265
266
267 if os.Getenv("AT_THE_DISCO") == "1" {
268 defer AuditPanic()
269 testPanicStackTraceHelper()
270 return
271 }
272
273
274
275
276 cmd := exec.Command(os.Args[0], "-test.run=TestPanicStackTrace")
277 cmd.Env = append(os.Environ(), "AT_THE_DISCO=1")
278 output, err := cmd.CombinedOutput()
279 test.AssertError(t, err, "running a failing program")
280 test.AssertContains(t, string(output), "nil pointer dereference")
281 test.AssertContains(t, string(output), "Stack Trace")
282 test.AssertContains(t, string(output), "cmd/shell_test.go:")
283 }
284
View as plain text