func TestGetAndProcessCerts(t *testing.T) { saDbMap, err := sa.NewDbMap(saDbConnStr) test.AssertNotError(t, err, "Couldn't connect to database") paDbMap, err := sa.NewDbMap(paDbConnStr) test.AssertNotError(t, err, "Couldn't connect to policy database") fc := clock.NewFake() checker := newChecker(saDbMap, paDbMap, fc, false) sa, err := sa.NewSQLStorageAuthority(saDbMap, fc) test.AssertNotError(t, err, "Couldn't create SA to insert certificates") saCleanUp := test.ResetTestDatabase(t, saDbMap.Db) paCleanUp := test.ResetTestDatabase(t, paDbMap.Db) defer func() { saCleanUp() paCleanUp() }() testKey, _ := rsa.GenerateKey(rand.Reader, 1024) // Problems // Expiry period is too long rawCert := x509.Certificate{ Subject: pkix.Name{ CommonName: "not-blacklisted.com", }, BasicConstraintsValid: true, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, } reg, err := sa.NewRegistration(core.Registration{ Key: satest.GoodJWK(), }) test.AssertNotError(t, err, "Couldn't create registration") for i := int64(0); i < 5; i++ { rawCert.SerialNumber = big.NewInt(i) certDER, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") _, err = sa.AddCertificate(certDER, reg.ID) test.AssertNotError(t, err, "Couldn't add certificate") } err = checker.getCerts() test.AssertNotError(t, err, "Failed to retrieve certificates") test.AssertEquals(t, len(checker.certs), 5) wg := new(sync.WaitGroup) wg.Add(1) checker.processCerts(wg) test.AssertEquals(t, checker.issuedReport.BadCerts, int64(5)) test.AssertEquals(t, len(checker.issuedReport.Entries), 5) }
func setup(t *testing.T, nagTimes []time.Duration) *testCtx { dbMap, err := sa.NewDbMap(dbConnStr) if err != nil { t.Fatalf("Couldn't connect the database: %s", err) } fc := clock.NewFake() ssa, err := sa.NewSQLStorageAuthority(dbMap, fc) if err != nil { t.Fatalf("unable to create SQLStorageAuthority: %s", err) } cleanUp := test.ResetTestDatabase(t, dbMap.Db) stats, _ := statsd.NewNoopClient(nil) mc := &mockMail{} m := &mailer{ log: blog.GetAuditLogger(), stats: stats, mailer: mc, emailTemplate: tmpl, dbMap: dbMap, rs: ssa, nagTimes: nagTimes, limit: 100, clk: fc, } return &testCtx{ dbMap: dbMap, ssa: ssa, mc: mc, fc: fc, m: m, cleanUp: cleanUp, } }
func paImpl(t *testing.T) (*PolicyAuthorityImpl, func()) { dbMap, err := sa.NewDbMap(dbConnStr) test.AssertNotError(t, err, "Could not construct dbMap") pa, err := NewPolicyAuthorityImpl(dbMap, false) test.AssertNotError(t, err, "Couldn't create PADB") cleanUp := test.ResetTestDatabase(t, dbMap.Db) return pa, cleanUp }
// initSA constructs a SQLStorageAuthority and a clean up function // that should be defer'ed to the end of the test. func initSA(t *testing.T) (*SQLStorageAuthority, func()) { dbMap, err := NewDbMap(dbConnStr) if err != nil { t.Fatalf("Failed to create dbMap: %s", err) } sa, err := NewSQLStorageAuthority(dbMap) if err != nil { t.Fatalf("Failed to create SA: %s", err) } cleanUp := test.ResetTestDatabase(t, dbMap.Db) return sa, cleanUp }
// This is an unfortunate bit of tech debt that is being taken on in // order to get the more important change of using MySQL/MariaDB in // all of our tests working without SQLite. We already had issues with // the RA here getting a real CertificateAuthority instead of a // CertificateAuthorityClient, so this is only marginally worse. // TODO(Issue #628): use a CAClient fake instead of a CAImpl instance func caDBImpl(t *testing.T) (core.CertificateAuthorityDatabase, func()) { dbMap, err := sa.NewDbMap(caDBConnStr) if err != nil { t.Fatalf("Could not construct dbMap: %s", err) } cadb, err := ca.NewCertificateAuthorityDatabaseImpl(dbMap) if err != nil { t.Fatalf("Could not construct CA DB: %s", err) } cleanUp := test.ResetTestDatabase(t, dbMap.Db) return cadb, cleanUp }
// initSA constructs a SQLStorageAuthority and a clean up function // that should be defer'ed to the end of the test. func initSA(t *testing.T) (*SQLStorageAuthority, clock.FakeClock, func()) { dbMap, err := NewDbMap(dbConnStr) if err != nil { t.Fatalf("Failed to create dbMap: %s", err) } fc := clock.NewFake() fc.Add(1 * time.Hour) sa, err := NewSQLStorageAuthority(dbMap, fc) if err != nil { t.Fatalf("Failed to create SA: %s", err) } cleanUp := test.ResetTestDatabase(t, dbMap.Db) return sa, fc, cleanUp }
// This is an unfortunate bit of tech debt that is being taken on in // order to get the more important change of using MySQL/MariaDB in // all of our tests working without SQLite. We already had issues with // the RA here getting a real CertificateAuthority instead of a // CertificateAuthorityClient, so this is only marginally worse. // TODO(Issue #628): use a CAClient fake instead of a CAImpl instance func caDBImpl(t *testing.T) (core.CertificateAuthorityDatabase, func()) { dbMap, err := sa.NewDbMap(caDBConnStr) if err != nil { t.Fatalf("Could not construct dbMap: %s", err) } cadb, err := ca.NewCertificateAuthorityDatabaseImpl(dbMap) if err != nil { t.Fatalf("Could not construct CA DB: %s", err) } cleanUp := test.ResetTestDatabase(t, dbMap.Db) // This row is required to exist for caDBImpl to work // correctly. We can no longer use // dbMap.Insert(&SerialNumber{...}) for this because gorp will // ignore the ID and insert a new row at a new autoincrement id. // TODO(jmhodges): gen ids flickr-style, no row needed a head of time _, err = dbMap.Db.Exec("insert into serialNumber (id, number, lastUpdated) VALUES (?, ?, ?)", 1, 1, time.Now()) if err != nil { t.Fatalf("unable to create the serial number row: %s", err) } return cadb, cleanUp }
func TestFindExpiringCertificates(t *testing.T) { dbMap, err := sa.NewDbMap(dbConnStr) if err != nil { t.Fatalf("Couldn't connect the database: %s", err) } cleanUp := test.ResetTestDatabase(t, dbMap.Db) ssa, err := sa.NewSQLStorageAuthority(dbMap) if err != nil { t.Fatalf("unable to create SQLStorageAuthority: %s", err) } defer cleanUp() tmpl, err := template.New("expiry-email").Parse(testTmpl) test.AssertNotError(t, err, "Couldn't parse test email template") stats, _ := statsd.NewNoopClient(nil) mc := mockMail{} m := mailer{ log: blog.GetAuditLogger(), stats: stats, mailer: &mc, emailTemplate: tmpl, dbMap: dbMap, rs: ssa, nagTimes: []time.Duration{time.Hour * 24, time.Hour * 24 * 4, time.Hour * 24 * 7}, limit: 100, } log.Clear() err = m.findExpiringCertificates() test.AssertNotError(t, err, "Failed on no certificates") test.AssertEquals(t, len(log.GetAllMatching("Searching for certificates that expire between.*")), 3) // Add some expiring certificates and registrations emailA, _ := core.ParseAcmeURL("mailto:[email protected]") emailB, _ := core.ParseAcmeURL("mailto:[email protected]") var keyA jose.JsonWebKey var keyB jose.JsonWebKey err = json.Unmarshal(jsonKeyA, &keyA) test.AssertNotError(t, err, "Failed to unmarshal public JWK") err = json.Unmarshal(jsonKeyB, &keyB) test.AssertNotError(t, err, "Failed to unmarshal public JWK") regA := core.Registration{ ID: 1, Contact: []*core.AcmeURL{ emailA, }, Key: keyA, } regB := core.Registration{ ID: 2, Contact: []*core.AcmeURL{ emailB, }, Key: keyB, } regA, err = ssa.NewRegistration(regA) if err != nil { t.Fatalf("Couldn't store regA: %s", err) } regB, err = ssa.NewRegistration(regB) if err != nil { t.Fatalf("Couldn't store regB: %s", err) } rawCertA := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy A", }, NotAfter: time.Now().AddDate(0, 0, 1), DNSNames: []string{"example-a.com"}, SerialNumber: big.NewInt(1337), } certDerA, _ := x509.CreateCertificate(rand.Reader, &rawCertA, &rawCertA, &testKey.PublicKey, &testKey) certA := &core.Certificate{ RegistrationID: regA.ID, Status: core.StatusValid, Serial: "001", Expires: time.Now().AddDate(0, 0, 1), DER: certDerA, } // Already sent a nag but too long ago certStatusA := &core.CertificateStatus{Serial: "001", LastExpirationNagSent: time.Now().Add(-time.Hour * 24 * 3)} rawCertB := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy B", }, NotAfter: time.Now().AddDate(0, 0, 3), DNSNames: []string{"example-b.com"}, SerialNumber: big.NewInt(1337), } certDerB, _ := x509.CreateCertificate(rand.Reader, &rawCertB, &rawCertB, &testKey.PublicKey, &testKey) certB := &core.Certificate{ RegistrationID: regA.ID, Status: core.StatusValid, Serial: "002", Expires: time.Now().AddDate(0, 0, 3), DER: certDerB, } // Already sent a nag for this period certStatusB := &core.CertificateStatus{Serial: "002", LastExpirationNagSent: time.Now().Add(-time.Hour * 24 * 3)} rawCertC := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy C", }, NotAfter: time.Now().AddDate(0, 0, 7), DNSNames: []string{"example-c.com"}, SerialNumber: big.NewInt(1337), } certDerC, _ := x509.CreateCertificate(rand.Reader, &rawCertC, &rawCertC, &testKey.PublicKey, &testKey) certC := &core.Certificate{ RegistrationID: regB.ID, Status: core.StatusValid, Serial: "003", Expires: time.Now().AddDate(0, 0, 7), DER: certDerC, } certStatusC := &core.CertificateStatus{Serial: "003"} err = dbMap.Insert(certA) test.AssertNotError(t, err, "Couldn't add certA") err = dbMap.Insert(certB) test.AssertNotError(t, err, "Couldn't add certB") err = dbMap.Insert(certC) test.AssertNotError(t, err, "Couldn't add certC") err = dbMap.Insert(certStatusA) test.AssertNotError(t, err, "Couldn't add certStatusA") err = dbMap.Insert(certStatusB) test.AssertNotError(t, err, "Couldn't add certStatusB") err = dbMap.Insert(certStatusC) test.AssertNotError(t, err, "Couldn't add certStatusC") log.Clear() err = m.findExpiringCertificates() test.AssertNotError(t, err, "Failed to find expiring certs") // Should get 001 and 003 test.AssertEquals(t, len(mc.Messages), 2) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example-a.com is going to expire in 1 days (%s)`, rawCertA.NotAfter.UTC().Format("2006-01-02 15:04:05 -0700 MST")), mc.Messages[0]) test.AssertEquals(t, fmt.Sprintf(`hi, cert for DNS names example-c.com is going to expire in 7 days (%s)`, rawCertC.NotAfter.UTC().Format("2006-01-02 15:04:05 -0700 MST")), mc.Messages[1]) // A consecutive run shouldn't find anything mc.Clear() log.Clear() err = m.findExpiringCertificates() test.AssertNotError(t, err, "Failed to find expiring certs") test.AssertEquals(t, len(mc.Messages), 0) }
func setup(t *testing.T) *testCtx { // Create an SA dbMap, err := sa.NewDbMap(saDBConnStr) if err != nil { t.Fatalf("Failed to create dbMap: %s", err) } fc := clock.NewFake() fc.Add(1 * time.Hour) ssa, err := sa.NewSQLStorageAuthority(dbMap, fc) if err != nil { t.Fatalf("Failed to create SA: %s", err) } saDBCleanUp := test.ResetTestDatabase(t, dbMap.Db) cadb, caDBCleanUp := caDBImpl(t) paDbMap, err := sa.NewDbMap(paDBConnStr) test.AssertNotError(t, err, "Could not construct dbMap") pa, err := policy.NewPolicyAuthorityImpl(paDbMap, false) test.AssertNotError(t, err, "Couldn't create PADB") paDBCleanUp := test.ResetTestDatabase(t, paDbMap.Db) cleanUp := func() { saDBCleanUp() caDBCleanUp() paDBCleanUp() } // TODO(jmhodges): use of this pkg here is a bug caused by using a real SA reg := satest.CreateWorkingRegistration(t, ssa) // Create a CA caConfig := cmd.CAConfig{ Profile: profileName, SerialPrefix: 17, Key: cmd.KeyConfig{ File: caKeyFile, }, Expiry: "8760h", LifespanOCSP: "45m", MaxNames: 2, CFSSL: cfsslConfig.Config{ Signing: &cfsslConfig.Signing{ Profiles: map[string]*cfsslConfig.SigningProfile{ profileName: &cfsslConfig.SigningProfile{ Usage: []string{"server auth"}, CA: false, IssuerURL: []string{"http://not-example.com/issuer-url"}, OCSP: "http://not-example.com/ocsp", CRL: "http://not-example.com/crl", Policies: []cfsslConfig.CertificatePolicy{ cfsslConfig.CertificatePolicy{ ID: cfsslConfig.OID(asn1.ObjectIdentifier{2, 23, 140, 1, 2, 1}), }, }, ExpiryString: "8760h", Backdate: time.Hour, CSRWhitelist: &cfsslConfig.CSRWhitelist{ PublicKeyAlgorithm: true, PublicKey: true, SignatureAlgorithm: true, }, }, }, Default: &cfsslConfig.SigningProfile{ ExpiryString: "8760h", }, }, OCSP: &ocspConfig.Config{ CACertFile: caCertFile, ResponderCertFile: caCertFile, KeyFile: caKeyFile, }, }, } return &testCtx{cadb, ssa, caConfig, reg, pa, fc, cleanUp} }
func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAuthority, *RegistrationAuthorityImpl, func()) { err := json.Unmarshal(AccountKeyJSONA, &AccountKeyA) test.AssertNotError(t, err, "Failed to unmarshal public JWK") err = json.Unmarshal(AccountKeyJSONB, &AccountKeyB) test.AssertNotError(t, err, "Failed to unmarshal public JWK") err = json.Unmarshal(AccountKeyJSONC, &AccountKeyC) test.AssertNotError(t, err, "Failed to unmarshal public JWK") err = json.Unmarshal(AccountPrivateKeyJSON, &AccountPrivateKey) test.AssertNotError(t, err, "Failed to unmarshal private JWK") err = json.Unmarshal(ShortKeyJSON, &ShortKey) test.AssertNotError(t, err, "Failed to unmarshall JWK") dbMap, err := sa.NewDbMap(saDBConnStr) if err != nil { t.Fatalf("Failed to create dbMap: %s", err) } ssa, err := sa.NewSQLStorageAuthority(dbMap) if err != nil { t.Fatalf("Failed to create SA: %s", err) } saDBCleanUp := test.ResetTestDatabase(t, dbMap.Db) va := &DummyValidationAuthority{} // PEM files in certificate-authority_test.go caKeyPEM, _ := pem.Decode([]byte(CAkeyPEM)) caKey, _ := x509.ParsePKCS1PrivateKey(caKeyPEM.Bytes) caCertPEM, _ := pem.Decode([]byte(CAcertPEM)) caCert, _ := x509.ParseCertificate(caCertPEM.Bytes) basicPolicy := &cfsslConfig.Signing{ Default: &cfsslConfig.SigningProfile{ Usage: []string{"server auth", "client auth"}, Expiry: 1 * time.Hour, CSRWhitelist: &cfsslConfig.CSRWhitelist{ PublicKey: true, PublicKeyAlgorithm: true, SignatureAlgorithm: true, DNSNames: true, }, }, } signer, _ := local.NewSigner(caKey, caCert, x509.SHA256WithRSA, basicPolicy) ocspSigner, _ := ocsp.NewSigner(caCert, caCert, caKey, time.Hour) pa := policy.NewPolicyAuthorityImpl() cadb, caDBCleanUp := caDBImpl(t) ca := ca.CertificateAuthorityImpl{ Signer: signer, OCSPSigner: ocspSigner, SA: ssa, PA: pa, DB: cadb, ValidityPeriod: time.Hour * 2190, NotAfter: time.Now().Add(time.Hour * 8761), MaxKeySize: 4096, } cleanUp := func() { saDBCleanUp() caDBCleanUp() } csrDER, _ := hex.DecodeString(CSRhex) ExampleCSR, _ = x509.ParseCertificateRequest(csrDER) Registration, _ = ssa.NewRegistration(core.Registration{Key: AccountKeyA}) ra := NewRegistrationAuthorityImpl() ra.SA = ssa ra.VA = va ra.CA = &ca ra.PA = pa ra.AuthzBase = "http://acme.invalid/authz/" ra.MaxKeySize = 4096 ra.DNSResolver = &mocks.MockDNS{} AuthzInitial.RegistrationID = Registration.ID AuthzUpdated = AuthzInitial AuthzFinal = AuthzUpdated AuthzFinal.Status = "valid" exp := time.Now().Add(365 * 24 * time.Hour) AuthzFinal.Expires = &exp AuthzFinal.Challenges[0].Status = "valid" return va, ssa, &ra, cleanUp }
func TestCheckCert(t *testing.T) { saDbMap, err := sa.NewDbMap(saDbConnStr) test.AssertNotError(t, err, "Couldn't connect to database") saCleanup := test.ResetTestDatabase(t, saDbMap.Db) paDbMap, err := sa.NewDbMap(paDbConnStr) test.AssertNotError(t, err, "Couldn't connect to policy database") paCleanup := test.ResetTestDatabase(t, paDbMap.Db) defer func() { saCleanup() paCleanup() }() testKey, _ := rsa.GenerateKey(rand.Reader, 1024) fc := clock.NewFake() fc.Add(time.Hour * 24 * 90) checker := newChecker(saDbMap, paDbMap, fc, false) issued := checker.clock.Now().Add(-time.Hour * 24 * 45) goodExpiry := issued.Add(checkPeriod) serial := big.NewInt(1337) // Problems // Expiry period is too long // Basic Constraints aren't set // Wrong key usage (none) rawCert := x509.Certificate{ Subject: pkix.Name{ CommonName: "example.com", }, NotBefore: issued, NotAfter: goodExpiry.AddDate(0, 0, 1), // Period too long DNSNames: []string{"example-a.com"}, SerialNumber: serial, BasicConstraintsValid: false, } brokenCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") // Problems // Digest doesn't match // Serial doesn't match // Expiry doesn't match // Issued doesn't match cert := core.Certificate{ DER: brokenCertDer, Issued: issued.Add(12 * time.Hour), Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match } problems := checker.checkCert(cert) fmt.Println(strings.Join(problems, "\n")) test.AssertEquals(t, len(problems), 7) // Fix the problems rawCert.Subject.CommonName = "example-a.com" rawCert.NotAfter = goodExpiry rawCert.BasicConstraintsValid = true rawCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} goodCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") parsed, err := x509.ParseCertificate(goodCertDer) test.AssertNotError(t, err, "Couldn't parse created certificate") cert.Serial = core.SerialToString(serial) cert.Digest = core.Fingerprint256(goodCertDer) cert.DER = goodCertDer cert.Expires = parsed.NotAfter cert.Issued = parsed.NotBefore problems = checker.checkCert(cert) test.AssertEquals(t, len(problems), 0) }
func TestCheckCert(t *testing.T) { saDbMap, err := sa.NewDbMap(saDbConnStr) test.AssertNotError(t, err, "Couldn't connect to database") saCleanup := test.ResetTestDatabase(t, saDbMap.Db) paDbMap, err := sa.NewDbMap(paDbConnStr) test.AssertNotError(t, err, "Couldn't connect to policy database") paCleanup := test.ResetTestDatabase(t, paDbMap.Db) defer func() { saCleanup() paCleanup() }() testKey, _ := rsa.GenerateKey(rand.Reader, 1024) fc := clock.NewFake() fc.Add(time.Hour * 24 * 90) checker := newChecker(saDbMap, paDbMap, fc, false) issued := checker.clock.Now().Add(-time.Hour * 24 * 45) goodExpiry := issued.Add(checkPeriod) serial := big.NewInt(1337) // Problems // Expiry period is too long // Basic Constraints aren't set // Wrong key usage (none) rawCert := x509.Certificate{ Subject: pkix.Name{ CommonName: "example.com", }, NotBefore: issued, NotAfter: goodExpiry.AddDate(0, 0, 1), // Period too long DNSNames: []string{"example-a.com"}, SerialNumber: serial, BasicConstraintsValid: false, } brokenCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") // Problems // Digest doesn't match // Serial doesn't match // Expiry doesn't match // Issued doesn't match cert := core.Certificate{ Serial: "8485f2687eba29ad455ae4e31c8679206fec", DER: brokenCertDer, Issued: issued.Add(12 * time.Hour), Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match } problems := checker.checkCert(cert) problemsMap := map[string]int{ "Stored digest doesn't match certificate digest": 1, "Stored serial doesn't match certificate serial": 1, "Stored expiration doesn't match certificate NotAfter": 1, "Certificate doesn't have basic constraints set": 1, "Certificate has a validity period longer than 2160h0m0s": 1, "Stored issuance date is outside of 6 hour window of certificate NotBefore": 1, "Certificate has incorrect key usage extensions": 1, } test.AssertEquals(t, len(problems), 7) for _, p := range problems { _, ok := problemsMap[p] if !ok { t.Errorf("Expected problem '%s' but didn't find it.", p) } delete(problemsMap, p) } for k, _ := range problemsMap { t.Errorf("Found unexpected problem '%s'.", k) } // Same settings as above, but the stored serial number in the DB is invalid. cert.Serial = "not valid" problems = checker.checkCert(cert) foundInvalidSerialProblem := false for _, p := range problems { if p == "Stored serial is invalid" { foundInvalidSerialProblem = true } } test.Assert(t, foundInvalidSerialProblem, "Invalid certificate serial number in DB did not trigger problem.") // Fix the problems rawCert.Subject.CommonName = "example-a.com" rawCert.NotAfter = goodExpiry rawCert.BasicConstraintsValid = true rawCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} goodCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") parsed, err := x509.ParseCertificate(goodCertDer) test.AssertNotError(t, err, "Couldn't parse created certificate") cert.Serial = core.SerialToString(serial) cert.Digest = core.Fingerprint256(goodCertDer) cert.DER = goodCertDer cert.Expires = parsed.NotAfter cert.Issued = parsed.NotBefore problems = checker.checkCert(cert) test.AssertEquals(t, len(problems), 0) }
func TestLifetimeOfACert(t *testing.T) { dbMap, err := sa.NewDbMap(dbConnStr) if err != nil { t.Fatalf("Couldn't connect the database: %s", err) } cleanUp := test.ResetTestDatabase(t, dbMap.Db) ssa, err := sa.NewSQLStorageAuthority(dbMap) if err != nil { t.Fatalf("unable to create SQLStorageAuthority: %s", err) } defer cleanUp() stats, _ := statsd.NewNoopClient(nil) mc := mockMail{} fc := clock.NewFake() m := mailer{ log: blog.GetAuditLogger(), stats: stats, mailer: &mc, emailTemplate: tmpl, dbMap: dbMap, rs: ssa, nagTimes: []time.Duration{time.Hour * 24, time.Hour * 24 * 4, time.Hour * 24 * 7}, limit: 100, clk: fc, } var keyA jose.JsonWebKey err = json.Unmarshal(jsonKeyA, &keyA) test.AssertNotError(t, err, "Failed to unmarshal public JWK") emailA, _ := core.ParseAcmeURL("mailto:[email protected]") regA := core.Registration{ ID: 1, Contact: []*core.AcmeURL{ emailA, }, Key: keyA, } regA, err = ssa.NewRegistration(regA) if err != nil { t.Fatalf("Couldn't store regA: %s", err) } rawCertA := x509.Certificate{ Subject: pkix.Name{ CommonName: "happy A", }, NotAfter: fc.Now(), DNSNames: []string{"example-a.com"}, SerialNumber: big.NewInt(1337), } certDerA, _ := x509.CreateCertificate(rand.Reader, &rawCertA, &rawCertA, &testKey.PublicKey, &testKey) certA := &core.Certificate{ RegistrationID: regA.ID, Status: core.StatusValid, Serial: "001", Expires: rawCertA.NotAfter, DER: certDerA, } certStatusA := &core.CertificateStatus{ Serial: "001", } err = dbMap.Insert(certA) test.AssertNotError(t, err, "unable to insert Certificate") err = dbMap.Insert(certStatusA) test.AssertNotError(t, err, "unable to insert CertificateStatus") type lifeTest struct { timeLeft time.Duration numMsgs int context string } tests := []lifeTest{ { timeLeft: 8 * 24 * time.Hour, // 8 days before expiration numMsgs: 0, context: "Expected no emails sent because we are more than 7 days out.", }, { 7 * 24 * time.Hour, // 7 days before 1, "Sent 1 for 7 day notice.", }, { 5 * 24 * time.Hour, 1, "The 7 day email was already sent.", }, { 3 * 24 * time.Hour, // 3 days before, the mailer wasn't run the day before 2, "Sent 1 for the 7 day notice, and 1 for the 4 day notice.", }, { 1 * 24 * time.Hour, 3, "Sent 1 for the 7 day notice, 1 for the 4 day notice, and 1 for the 1 day notice.", }, { 12 * time.Hour, 3, "The 1 day before email was already sent.", }, { -2 * 24 * time.Hour, // 2 days after expiration 3, "No expiration warning emails are sent after expiration", }, } for _, tt := range tests { fc.Add(-tt.timeLeft) err = m.findExpiringCertificates() test.AssertNotError(t, err, "error calling findExpiringCertificates") if len(mc.Messages) != tt.numMsgs { t.Errorf(tt.context+" number of messages: expected %d, got %d", tt.numMsgs, len(mc.Messages)) } fc.Add(tt.timeLeft) } }