...

Source file src/github.com/letsencrypt/boulder/cmd/caa-log-checker/main_test.go

Documentation: github.com/letsencrypt/boulder/cmd/caa-log-checker

     1  package notmain
     2  
     3  import (
     4  	"compress/gzip"
     5  	"fmt"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/letsencrypt/boulder/test"
    11  )
    12  
    13  // A timestamp which matches the format we put in our logs. Note that it has
    14  // sub-second precision only out to microseconds (not nanoseconds), and must
    15  // include the timezone indicator.
    16  // 0001-01-01T01:01:01.001001+00:00
    17  var testTime = time.Time{}.Add(time.Hour + time.Minute + time.Second + time.Millisecond + time.Microsecond).Local()
    18  
    19  func TestOpenFile(t *testing.T) {
    20  	tmpPlain, err := os.CreateTemp(os.TempDir(), "plain")
    21  	test.AssertNotError(t, err, "failed to create temporary file")
    22  	defer os.Remove(tmpPlain.Name())
    23  	_, err = tmpPlain.Write([]byte("test-1\ntest-2"))
    24  	test.AssertNotError(t, err, "failed to write to temp file")
    25  	tmpPlain.Close()
    26  
    27  	tmpGzip, err := os.CreateTemp(os.TempDir(), "gzip-*.gz")
    28  	test.AssertNotError(t, err, "failed to create temporary file")
    29  	defer os.Remove(tmpGzip.Name())
    30  	gzipWriter := gzip.NewWriter(tmpGzip)
    31  	_, err = gzipWriter.Write([]byte("test-1\ntest-2"))
    32  	test.AssertNotError(t, err, "failed to write to temp file")
    33  	gzipWriter.Flush()
    34  	gzipWriter.Close()
    35  	tmpGzip.Close()
    36  
    37  	checkFile := func(path string) {
    38  		t.Helper()
    39  		scanner, err := openFile(path)
    40  		test.AssertNotError(t, err, fmt.Sprintf("failed to open %q", path))
    41  		var lines []string
    42  		for scanner.Scan() {
    43  			lines = append(lines, scanner.Text())
    44  		}
    45  		test.AssertNotError(t, scanner.Err(), fmt.Sprintf("failed to read from %q", path))
    46  		test.AssertEquals(t, len(lines), 2)
    47  		test.AssertDeepEquals(t, lines, []string{"test-1", "test-2"})
    48  	}
    49  
    50  	checkFile(tmpPlain.Name())
    51  	checkFile(tmpGzip.Name())
    52  }
    53  
    54  func TestLoadIssuanceLog(t *testing.T) {
    55  
    56  	for _, tc := range []struct {
    57  		name        string
    58  		loglines    string
    59  		expMap      map[string][]time.Time
    60  		expEarliest time.Time
    61  		expLatest   time.Time
    62  		expErrStr   string
    63  	}{
    64  		{
    65  			"empty file",
    66  			"",
    67  			map[string][]time.Time{},
    68  			time.Time{},
    69  			time.Time{},
    70  			"",
    71  		},
    72  		{
    73  			"no matches",
    74  			"some text\nsome other text",
    75  			map[string][]time.Time{},
    76  			time.Time{},
    77  			time.Time{},
    78  			"",
    79  		},
    80  		{
    81  			"bad json",
    82  			"Certificate request - successful JSON=this is not valid json",
    83  			map[string][]time.Time{},
    84  			time.Time{},
    85  			time.Time{},
    86  			"failed to unmarshal JSON",
    87  		},
    88  		{
    89  			"bad timestamp",
    90  			"2009-11-10 23:00:00 UTC Certificate request - successful JSON={}",
    91  			map[string][]time.Time{},
    92  			time.Time{},
    93  			time.Time{},
    94  			"failed to parse timestamp",
    95  		},
    96  		{
    97  			"normal behavior",
    98  			`header
    99  0001-01-01T01:01:01.001001+00:00 Certificate request - successful JSON={"SerialNumber": "1", "Names":["example.com"], "Requester":0}
   100  0001-01-01T02:01:01.001001+00:00 Certificate request - successful JSON={"SerialNumber": "2", "Names":["2.example.com", "3.example.com"], "Requester":0}
   101  filler
   102  0001-01-01T03:01:01.001001+00:00 Certificate request - successful JSON={"SerialNumber": "3", "Names":["2.example.com"], "Requester":0}
   103  trailer`,
   104  			map[string][]time.Time{
   105  				"example.com":   {testTime},
   106  				"2.example.com": {testTime.Add(time.Hour), testTime.Add(2 * time.Hour)},
   107  				"3.example.com": {testTime.Add(time.Hour)},
   108  			},
   109  			testTime,
   110  			testTime.Add(2 * time.Hour),
   111  			"",
   112  		},
   113  	} {
   114  		t.Run(tc.name, func(t *testing.T) {
   115  			tmp, err := os.CreateTemp(os.TempDir(), "TestLoadIssuanceLog")
   116  			test.AssertNotError(t, err, "failed to create temporary log file")
   117  			defer os.Remove(tmp.Name())
   118  			_, err = tmp.Write([]byte(tc.loglines))
   119  			test.AssertNotError(t, err, "failed to write temporary log file")
   120  			err = tmp.Close()
   121  			test.AssertNotError(t, err, "failed to close temporary log file")
   122  
   123  			resMap, resEarliest, resLatest, resError := loadIssuanceLog(tmp.Name())
   124  			if tc.expErrStr != "" {
   125  				test.AssertError(t, resError, "loadIssuanceLog should have errored")
   126  				test.AssertContains(t, resError.Error(), tc.expErrStr)
   127  				return
   128  			}
   129  			test.AssertNotError(t, resError, "loadIssuanceLog shouldn't have errored")
   130  			test.AssertDeepEquals(t, resMap, tc.expMap)
   131  			test.AssertEquals(t, resEarliest, tc.expEarliest)
   132  			test.AssertEquals(t, resLatest, tc.expLatest)
   133  		})
   134  	}
   135  }
   136  
   137  func TestProcessCAALog(t *testing.T) {
   138  	for _, tc := range []struct {
   139  		name      string
   140  		loglines  string
   141  		issuances map[string][]time.Time
   142  		earliest  time.Time
   143  		latest    time.Time
   144  		tolerance time.Duration
   145  		expMap    map[string][]time.Time
   146  		expErrStr string
   147  	}{
   148  		{
   149  			"empty file",
   150  			"",
   151  			map[string][]time.Time{"example.com": {testTime}},
   152  			time.Time{},
   153  			time.Time{},
   154  			time.Second,
   155  			map[string][]time.Time{"example.com": {testTime}},
   156  			"",
   157  		},
   158  		{
   159  			"no matches",
   160  			"",
   161  			map[string][]time.Time{"example.com": {testTime}},
   162  			time.Time{},
   163  			time.Time{},
   164  			time.Second,
   165  			map[string][]time.Time{"example.com": {testTime}},
   166  			"",
   167  		},
   168  		{
   169  			"outside 8hr window",
   170  			`header
   171  0001-01-01T01:01:01.001001+00:00 Checked CAA records for example.com, [Present: true, ...
   172  filler
   173  0001-01-01T21:01:01.001001+00:00 Checked CAA records for example.com, [Present: true, ...
   174  trailer`,
   175  			map[string][]time.Time{"example.com": {testTime.Add(10 * time.Hour)}},
   176  			testTime,
   177  			testTime.Add(24 * time.Hour),
   178  			time.Second,
   179  			map[string][]time.Time{"example.com": {testTime.Add(10 * time.Hour)}},
   180  			"",
   181  		},
   182  		{
   183  			"outside earliest and latest",
   184  			`header
   185  0001-01-01T01:01:01.001001+00:00 Checked CAA records for example.com, [Present: true, ...
   186  filler
   187  0001-01-01T21:01:01.001001+00:00 Checked CAA records for example.com, [Present: true, ...
   188  trailer`,
   189  			map[string][]time.Time{"example.com": {testTime.Add(24 * time.Hour)}},
   190  			testTime.Add(10 * time.Hour),
   191  			testTime.Add(11 * time.Hour),
   192  			time.Second,
   193  			map[string][]time.Time{"example.com": {testTime.Add(24 * time.Hour)}},
   194  			"",
   195  		},
   196  		{
   197  			"present: false",
   198  			`header
   199  0001-01-01T01:01:01.001001+00:00 Checked CAA records for a.b.example.com, [Present: false, ...
   200  trailer`,
   201  			map[string][]time.Time{
   202  				"a.b.example.com": {testTime.Add(time.Hour)},
   203  				"b.example.com":   {testTime.Add(time.Hour)},
   204  				"example.com":     {testTime.Add(time.Hour)},
   205  				"other.com":       {testTime.Add(time.Hour)},
   206  			},
   207  			testTime,
   208  			testTime.Add(2 * time.Hour),
   209  			time.Second,
   210  			map[string][]time.Time{"other.com": {testTime.Add(time.Hour)}},
   211  			"",
   212  		},
   213  	} {
   214  		t.Run(tc.name, func(t *testing.T) {
   215  			fmt.Println(tc.name)
   216  			tmp, err := os.CreateTemp(os.TempDir(), "TestProcessCAALog")
   217  			test.AssertNotError(t, err, "failed to create temporary log file")
   218  			defer os.Remove(tmp.Name())
   219  			_, err = tmp.Write([]byte(tc.loglines))
   220  			test.AssertNotError(t, err, "failed to write temporary log file")
   221  			err = tmp.Close()
   222  			test.AssertNotError(t, err, "failed to close temporary log file")
   223  
   224  			resError := processCAALog(tmp.Name(), tc.issuances, tc.earliest, tc.latest, tc.tolerance)
   225  			if tc.expErrStr != "" {
   226  				test.AssertError(t, resError, "processCAALog should have errored")
   227  				test.AssertContains(t, resError.Error(), tc.expErrStr)
   228  				return
   229  			}
   230  			// Because processCAALog modifies its input map, we have to compare the
   231  			// testcase's input against the testcase's expectation.
   232  			test.AssertDeepEquals(t, tc.issuances, tc.expMap)
   233  		})
   234  	}
   235  }
   236  
   237  func TestRemoveCoveredTimestamps(t *testing.T) {
   238  	for _, tc := range []struct {
   239  		name       string
   240  		timestamps []time.Time
   241  		cover      time.Time
   242  		tolerance  time.Duration
   243  		expected   []time.Time
   244  	}{
   245  		{
   246  			"empty input",
   247  			[]time.Time{},
   248  			testTime,
   249  			time.Second,
   250  			[]time.Time{},
   251  		},
   252  		{
   253  			"normal functioning",
   254  			[]time.Time{testTime.Add(-1 * time.Hour), testTime.Add(5 * time.Hour), testTime.Add(10 * time.Hour)},
   255  			testTime,
   256  			time.Second,
   257  			[]time.Time{testTime.Add(-1 * time.Hour), testTime.Add(10 * time.Hour)},
   258  		},
   259  		{
   260  			"tolerance",
   261  			[]time.Time{testTime.Add(-1 * time.Second), testTime.Add(8*time.Hour + 1*time.Second)},
   262  			testTime,
   263  			time.Second,
   264  			[]time.Time{},
   265  		},
   266  		{
   267  			"intolerance",
   268  			[]time.Time{testTime.Add(-2 * time.Second), testTime.Add(8*time.Hour + 2*time.Second)},
   269  			testTime,
   270  			time.Second,
   271  			[]time.Time{testTime.Add(-2 * time.Second), testTime.Add(8*time.Hour + 2*time.Second)},
   272  		},
   273  	} {
   274  		t.Run(tc.name, func(t *testing.T) {
   275  			result := removeCoveredTimestamps(tc.timestamps, tc.cover, tc.tolerance)
   276  			test.AssertDeepEquals(t, result, tc.expected)
   277  		})
   278  	}
   279  }
   280  

View as plain text