From c210c287fc20299d1766b7bf0bee883bae877bfc Mon Sep 17 00:00:00 2001 From: PDebus Date: Wed, 13 May 2020 21:59:44 -0700 Subject: [PATCH 01/12] Move registration with lego from the certificate model to the acme service --- acme/service.go | 7 ++++++- api/certificate.go | 25 +++++++++++++++++++++++++ model/certificate.go | 19 ------------------- service/acme.go | 12 ++++++++++++ 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/acme/service.go b/acme/service.go index 492d1a2..361e5f5 100644 --- a/acme/service.go +++ b/acme/service.go @@ -1,6 +1,10 @@ package acme -import "github.com/ImageWare/TLSential/model" +import ( + "github.com/ImageWare/TLSential/model" + "github.com/go-acme/lego/v3/lego" + lregistration "github.com/go-acme/lego/v3/registration" +) // Service implements the ability to trigger a new certificate request, or Renew // a certificate. Renewal presumes a certificate has already been issued. @@ -11,4 +15,5 @@ type Service interface { RequestRenew(id string) bool GetAutoRenewChannel() chan string GetIssueChannel() chan string + Register(*lego.Client) (*lregistration.Resource, error) } diff --git a/api/certificate.go b/api/certificate.go index 6624c3e..ab1a1cf 100644 --- a/api/certificate.go +++ b/api/certificate.go @@ -13,6 +13,9 @@ import ( "github.com/ImageWare/TLSential/certificate" "github.com/ImageWare/TLSential/model" + "github.com/go-acme/lego/v3/certcrypto" + "github.com/go-acme/lego/v3/lego" + "github.com/gorilla/mux" ) @@ -245,6 +248,28 @@ func (h *certHandler) Post() http.HandlerFunc { return } + config := lego.NewConfig(c) + + config.CADirURL = model.CADirURL + config.Certificate.KeyType = certcrypto.RSA2048 + + client, err := lego.NewClient(config) + + if err != nil { + log.Printf("api CertHandler POST, lego.NewClient(), %s", err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + reg, err := h.acme.Register(client) + + if err != nil { + log.Printf("api CertHandler POST, acme.Register(), %s", err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + c.ACMERegistration = reg + //TODO: Should probably decide valid range for client supplied RenewAt value //For instance we may not want them to be able to specify 0 or less, as that would //cause the cert to never auto renew. Although maybe thats a valid use case? diff --git a/model/certificate.go b/model/certificate.go index a018c24..0ed68f0 100644 --- a/model/certificate.go +++ b/model/certificate.go @@ -14,8 +14,6 @@ import ( "golang.org/x/net/idna" "github.com/ImageWare/TLSential/auth" - "github.com/go-acme/lego/v3/certcrypto" - "github.com/go-acme/lego/v3/lego" "github.com/go-acme/lego/v3/registration" "github.com/segmentio/ksuid" ) @@ -109,23 +107,6 @@ func NewCertificate(domains []string, email string) (*Certificate, error) { ACMEKey: privateKey, } - config := lego.NewConfig(c) - - config.CADirURL = CADirURL - config.Certificate.KeyType = certcrypto.RSA2048 - - client, err := lego.NewClient(config) - if err != nil { - return nil, err - } - - // TODO: Move this to acme Service so we can mock here - reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) - if err != nil { - return nil, err - } - c.ACMERegistration = reg - return c, nil } diff --git a/service/acme.go b/service/acme.go index e21766b..5a0d31e 100644 --- a/service/acme.go +++ b/service/acme.go @@ -11,6 +11,7 @@ import ( "github.com/go-acme/lego/v3/certcrypto" lcert "github.com/go-acme/lego/v3/certificate" "github.com/go-acme/lego/v3/lego" + lregistration "github.com/go-acme/lego/v3/registration" ) var certAutoRenewChan chan string @@ -221,6 +222,17 @@ func (s *acmeService) Renew(c *model.Certificate) { } +func (s *acmeService) Register(c *lego.Client) (*lregistration.Resource, error) { + + reg, err := c.Registration.Register(lregistration.RegisterOptions{TermsOfServiceAgreed: true}) + if err != nil { + return nil, err + } + + return reg, nil + +} + func getExpiry(c *model.Certificate) time.Time { x509Cert, err := certcrypto.ParsePEMCertificate(c.Certificate) if err != nil { From 1db260ff7e2796269077716635f039678bbb8f3e Mon Sep 17 00:00:00 2001 From: PDebus Date: Wed, 13 May 2020 22:28:06 -0700 Subject: [PATCH 02/12] Change to taking a lego.User object instead of a lego client. This should allow us to more easily mock the registration behavior for components using service.Acme --- acme/service.go | 3 +-- service/acme.go | 13 ++++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/acme/service.go b/acme/service.go index 361e5f5..ccc2c91 100644 --- a/acme/service.go +++ b/acme/service.go @@ -2,7 +2,6 @@ package acme import ( "github.com/ImageWare/TLSential/model" - "github.com/go-acme/lego/v3/lego" lregistration "github.com/go-acme/lego/v3/registration" ) @@ -15,5 +14,5 @@ type Service interface { RequestRenew(id string) bool GetAutoRenewChannel() chan string GetIssueChannel() chan string - Register(*lego.Client) (*lregistration.Resource, error) + Register(lregistration.User) (*lregistration.Resource, error) } diff --git a/service/acme.go b/service/acme.go index 5a0d31e..09c3fc0 100644 --- a/service/acme.go +++ b/service/acme.go @@ -222,7 +222,18 @@ func (s *acmeService) Renew(c *model.Certificate) { } -func (s *acmeService) Register(c *lego.Client) (*lregistration.Resource, error) { +func (s *acmeService) Register(u lregistration.User) (*lregistration.Resource, error) { + + config := lego.NewConfig(u) + + config.CADirURL = model.CADirURL + config.Certificate.KeyType = certcrypto.RSA2048 + + c, err := lego.NewClient(config) + + if err != nil { + return nil, err + } reg, err := c.Registration.Register(lregistration.RegisterOptions{TermsOfServiceAgreed: true}) if err != nil { From 06e22af4c0a420b7c175563c50e3ed1a6af27e21 Mon Sep 17 00:00:00 2001 From: PDebus Date: Wed, 13 May 2020 22:29:20 -0700 Subject: [PATCH 03/12] pass the cert to the acme.Service.Register function --- api/certificate.go | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/api/certificate.go b/api/certificate.go index ab1a1cf..d5999f2 100644 --- a/api/certificate.go +++ b/api/certificate.go @@ -13,9 +13,6 @@ import ( "github.com/ImageWare/TLSential/certificate" "github.com/ImageWare/TLSential/model" - "github.com/go-acme/lego/v3/certcrypto" - "github.com/go-acme/lego/v3/lego" - "github.com/gorilla/mux" ) @@ -248,20 +245,7 @@ func (h *certHandler) Post() http.HandlerFunc { return } - config := lego.NewConfig(c) - - config.CADirURL = model.CADirURL - config.Certificate.KeyType = certcrypto.RSA2048 - - client, err := lego.NewClient(config) - - if err != nil { - log.Printf("api CertHandler POST, lego.NewClient(), %s", err.Error()) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - reg, err := h.acme.Register(client) + reg, err := h.acme.Register(c) if err != nil { log.Printf("api CertHandler POST, acme.Register(), %s", err.Error()) From cba15c9abfb14c8deaf8e2c21f85cbe2426b4e1e Mon Sep 17 00:00:00 2001 From: PDebus Date: Wed, 13 May 2020 22:30:03 -0700 Subject: [PATCH 04/12] remove checking for acme registration as that isnt handled as part of model creation anymore --- model/certificate_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/model/certificate_test.go b/model/certificate_test.go index 0a05a63..6fc141f 100644 --- a/model/certificate_test.go +++ b/model/certificate_test.go @@ -121,11 +121,6 @@ func TestNewCertificate(t *testing.T) { t.Error("email mismatch") } - // TODO: Test ACMERegistration values, like Status, ToS, etc. - if c.ACMERegistration == nil { - t.Error("acme registration shouldn't be nil") - } - if c.ACMEKey == nil { t.Error("acme key should not be nil") } From ca008e4d7adadc5338b32caf000661964e2a7332 Mon Sep 17 00:00:00 2001 From: PDebus Date: Wed, 13 May 2020 22:31:11 -0700 Subject: [PATCH 05/12] Add acme tests that test the acme specific aspects of registration that used to be tested in certificate_test.go --- service/acme_test.go | 159 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 service/acme_test.go diff --git a/service/acme_test.go b/service/acme_test.go new file mode 100644 index 0000000..f013772 --- /dev/null +++ b/service/acme_test.go @@ -0,0 +1,159 @@ +package service + +import ( + "testing" + "time" + + "github.com/ImageWare/TLSential/model" +) + +type certTest struct { + testName string + domains []string + email string + expectedError string +} + +func testRegister(t *testing.T) { + certTests := []certTest{ + { + "happy path", + []string{"example.com", "example2.com"}, + "test@notexample.com", + "", + }, + { + "no domains", + []string{}, + "test@notexample.com", + model.ErrInvalidDomains.Error(), + }, + { + "email at example.com", + []string{"example.com"}, + "test@example.com", + "acme: error: 400 :: POST :: https://acme-v02.api.letsencrypt.org/acme/new-acct :: urn:ietf:params:acme:error:invalidEmail :: Error creating new account :: invalid contact domain. Contact emails @example.com are forbidden, url: ", + }, + { + "wildcard domain", + []string{"*.example.com"}, + "test@notexample.com", + "", + }, + { + "bad wildcard domain", + []string{"https://*.example.com"}, + "test@notexample.com", + model.ErrInvalidDomains.Error(), + }, + } + + for _, ct := range certTests { + t.Run(ct.testName, func(t *testing.T) { + c, err := model.NewCertificate(ct.domains, ct.email) + + if err == nil { + a := NewAcmeService(nil, nil) + reg, err := a.Register(c) + if err != nil { + c.ACMERegistration = reg + } + } + + if err == nil { + if ct.expectedError != "" { + t.Error("no error returned when expected") + } + } + + if err != nil { + if err.Error() != ct.expectedError { + t.Errorf("error mismatch: got %s, expected %s\n", err.Error(), ct.expectedError) + } + if c != nil { + t.Error("certificate should be nil on error") + } + return + } + + if c.ID == "" { + t.Error("certificate ID blank") + } + + if c.Secret == "" { + t.Error("certificate Secret blank") + } + + match := testEq(ct.domains, c.Domains) + if !match { + t.Error("given domains and certificate domains mismatch") + } + + if c.CommonName != ct.domains[0] { + t.Error("common name is not correct domain") + } + + if c.CertURL != "" { + t.Error("cert url should be blank") + } + if c.CertStableURL != "" { + t.Error("cert stable url should be blank") + } + + if len(c.PrivateKey) != 0 { + t.Error("private key should be empty") + } + if len(c.Certificate) != 0 { + t.Error("certificate should be empty") + } + if len(c.IssuerCertificate) != 0 { + t.Error("issuer certificate should be empty") + } + + if c.Issued != false { + t.Error("issued should be false") + } + + var blankTime time.Time + if !c.Expiry.Equal(blankTime) { + t.Error("expiry shouldn't have been set") + } + + if c.RenewAt != model.DefaultRenewAt { + t.Error("renew at not defuault") + } + + if c.LastError != nil { + t.Error("last error shouldn't be set") + } + + if c.ACMEEmail != ct.email { + t.Error("email mismatch") + } + + if c.ACMEKey == nil { + t.Error("acme key should not be nil") + } + }) + } +} + +func testEq(a, b []string) bool { + + // If one is nil, the other must also be nil. + if (a == nil) != (b == nil) { + return false + } + + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} From c1946a28bea7771bf1bca1effe3044083366553c Mon Sep 17 00:00:00 2001 From: PDebus Date: Wed, 13 May 2020 22:45:04 -0700 Subject: [PATCH 06/12] Remove registration test from certificate model test --- model/certificate_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/model/certificate_test.go b/model/certificate_test.go index 6fc141f..acca257 100644 --- a/model/certificate_test.go +++ b/model/certificate_test.go @@ -26,12 +26,6 @@ func TestNewCertificate(t *testing.T) { "test@notexample.com", ErrInvalidDomains.Error(), }, - { - "email at example.com", - []string{"example.com"}, - "test@example.com", - "acme: error: 400 :: POST :: https://acme-v02.api.letsencrypt.org/acme/new-acct :: urn:ietf:params:acme:error:invalidEmail :: Error creating new account :: invalid contact domain. Contact emails @example.com are forbidden, url: ", - }, { "wildcard domain", []string{"*.example.com"}, From 1e7d7a31bd6b3faa434e20060c76128656f6a5f7 Mon Sep 17 00:00:00 2001 From: PDebus Date: Wed, 13 May 2020 22:45:19 -0700 Subject: [PATCH 07/12] Clean up code --- service/acme.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/service/acme.go b/service/acme.go index 09c3fc0..6f10fff 100644 --- a/service/acme.go +++ b/service/acme.go @@ -236,11 +236,7 @@ func (s *acmeService) Register(u lregistration.User) (*lregistration.Resource, e } reg, err := c.Registration.Register(lregistration.RegisterOptions{TermsOfServiceAgreed: true}) - if err != nil { - return nil, err - } - - return reg, nil + return reg, err } From dc9d6265e41c96033f8475694e8c26ee2ef9bed3 Mon Sep 17 00:00:00 2001 From: PDebus Date: Wed, 13 May 2020 22:46:03 -0700 Subject: [PATCH 08/12] refactor test --- service/acme_test.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/service/acme_test.go b/service/acme_test.go index f013772..5cf75bd 100644 --- a/service/acme_test.go +++ b/service/acme_test.go @@ -14,7 +14,7 @@ type certTest struct { expectedError string } -func testRegister(t *testing.T) { +func TestRegister(t *testing.T) { certTests := []certTest{ { "happy path", @@ -50,14 +50,16 @@ func testRegister(t *testing.T) { for _, ct := range certTests { t.Run(ct.testName, func(t *testing.T) { + c, err := model.NewCertificate(ct.domains, ct.email) - if err == nil { - a := NewAcmeService(nil, nil) - reg, err := a.Register(c) - if err != nil { - c.ACMERegistration = reg - } + if err != nil { + t.Error("Error creating certificate", err) + } + a := NewAcmeService(nil, nil) + reg, err := a.Register(c) + if err != nil { + c.ACMERegistration = reg } if err == nil { From 2408f6f9a786a40353e5e1b7ef7e7bbf1cfe6e2c Mon Sep 17 00:00:00 2001 From: PDebus Date: Sat, 16 May 2020 18:35:21 -0700 Subject: [PATCH 09/12] Dont check for model creation issues in this test as that should only be in the model tests --- service/acme_test.go | 54 +++----------------------------------------- 1 file changed, 3 insertions(+), 51 deletions(-) diff --git a/service/acme_test.go b/service/acme_test.go index 5cf75bd..d2f83c2 100644 --- a/service/acme_test.go +++ b/service/acme_test.go @@ -2,7 +2,6 @@ package service import ( "testing" - "time" "github.com/ImageWare/TLSential/model" ) @@ -55,9 +54,11 @@ func TestRegister(t *testing.T) { if err != nil { t.Error("Error creating certificate", err) + return } a := NewAcmeService(nil, nil) reg, err := a.Register(c) + if err != nil { c.ACMERegistration = reg } @@ -65,6 +66,7 @@ func TestRegister(t *testing.T) { if err == nil { if ct.expectedError != "" { t.Error("no error returned when expected") + return } } @@ -72,59 +74,9 @@ func TestRegister(t *testing.T) { if err.Error() != ct.expectedError { t.Errorf("error mismatch: got %s, expected %s\n", err.Error(), ct.expectedError) } - if c != nil { - t.Error("certificate should be nil on error") - } return } - if c.ID == "" { - t.Error("certificate ID blank") - } - - if c.Secret == "" { - t.Error("certificate Secret blank") - } - - match := testEq(ct.domains, c.Domains) - if !match { - t.Error("given domains and certificate domains mismatch") - } - - if c.CommonName != ct.domains[0] { - t.Error("common name is not correct domain") - } - - if c.CertURL != "" { - t.Error("cert url should be blank") - } - if c.CertStableURL != "" { - t.Error("cert stable url should be blank") - } - - if len(c.PrivateKey) != 0 { - t.Error("private key should be empty") - } - if len(c.Certificate) != 0 { - t.Error("certificate should be empty") - } - if len(c.IssuerCertificate) != 0 { - t.Error("issuer certificate should be empty") - } - - if c.Issued != false { - t.Error("issued should be false") - } - - var blankTime time.Time - if !c.Expiry.Equal(blankTime) { - t.Error("expiry shouldn't have been set") - } - - if c.RenewAt != model.DefaultRenewAt { - t.Error("renew at not defuault") - } - if c.LastError != nil { t.Error("last error shouldn't be set") } From 33e6843afc2acfc55a51c2971522a577ad1ba03a Mon Sep 17 00:00:00 2001 From: PDebus Date: Sat, 16 May 2020 18:36:33 -0700 Subject: [PATCH 10/12] Remove model specific tests from the acme tests --- service/acme_test.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/service/acme_test.go b/service/acme_test.go index d2f83c2..bd7c0ce 100644 --- a/service/acme_test.go +++ b/service/acme_test.go @@ -21,12 +21,6 @@ func TestRegister(t *testing.T) { "test@notexample.com", "", }, - { - "no domains", - []string{}, - "test@notexample.com", - model.ErrInvalidDomains.Error(), - }, { "email at example.com", []string{"example.com"}, @@ -39,12 +33,6 @@ func TestRegister(t *testing.T) { "test@notexample.com", "", }, - { - "bad wildcard domain", - []string{"https://*.example.com"}, - "test@notexample.com", - model.ErrInvalidDomains.Error(), - }, } for _, ct := range certTests { From 423db7bb4436bcaa6306e0baa12379eed047a092 Mon Sep 17 00:00:00 2001 From: PDebus Date: Sat, 16 May 2020 21:24:36 -0700 Subject: [PATCH 11/12] Abstract the acme service from lego by having a configurable UserRegistrar. This also allows mocking the lego registration during testing --- service/acme.go | 37 +++++++++++++++++++--------- service/acme_test.go | 58 +++++++++++++++++++++----------------------- 2 files changed, 53 insertions(+), 42 deletions(-) diff --git a/service/acme.go b/service/acme.go index 6f10fff..3bb7d1e 100644 --- a/service/acme.go +++ b/service/acme.go @@ -20,8 +20,15 @@ var certIssueChan chan string type acmeService struct { certService cert.Service challService challenge_config.Service + registrar UserRegistrar } +type UserRegistrar interface { + Register(u lregistration.User) (*lregistration.Resource, error) +} + +type legoRegistrar struct{} + func CreateChannelsAndListeners(buffSize int, listeners int, cs cert.Service, as acme.Service) { certAutoRenewChan = make(chan string, buffSize) certIssueChan = make(chan string) @@ -57,8 +64,14 @@ func handleCertChannels(cs cert.Service, as acme.Service) { } } +//Create a new acme.Service with a default LEGO registrar func NewAcmeService(cts cert.Service, chs challenge_config.Service) acme.Service { - return &acmeService{certService: cts, challService: chs} + return NewAcmeServiceWithRegistrar(cts, chs, &legoRegistrar{}) +} + +//Create a new acme.Service that uses the supplied UserRegistrar. registrar must not be nil +func NewAcmeServiceWithRegistrar(cts cert.Service, chs challenge_config.Service, registrar UserRegistrar) acme.Service { + return &acmeService{certService: cts, challService: chs, registrar: registrar} } //RequestRenew will try to send to the CertAutoRenewChan channel, but won't block if the channel is full. @@ -223,7 +236,19 @@ func (s *acmeService) Renew(c *model.Certificate) { } func (s *acmeService) Register(u lregistration.User) (*lregistration.Resource, error) { + return s.registrar.Register(u) +} +func getExpiry(c *model.Certificate) time.Time { + x509Cert, err := certcrypto.ParsePEMCertificate(c.Certificate) + if err != nil { + log.Fatal(err) + } + + return x509Cert.NotAfter +} + +func (l *legoRegistrar) Register(u lregistration.User) (*lregistration.Resource, error) { config := lego.NewConfig(u) config.CADirURL = model.CADirURL @@ -237,14 +262,4 @@ func (s *acmeService) Register(u lregistration.User) (*lregistration.Resource, e reg, err := c.Registration.Register(lregistration.RegisterOptions{TermsOfServiceAgreed: true}) return reg, err - -} - -func getExpiry(c *model.Certificate) time.Time { - x509Cert, err := certcrypto.ParsePEMCertificate(c.Certificate) - if err != nil { - log.Fatal(err) - } - - return x509Cert.NotAfter } diff --git a/service/acme_test.go b/service/acme_test.go index bd7c0ce..dc74bec 100644 --- a/service/acme_test.go +++ b/service/acme_test.go @@ -1,37 +1,48 @@ package service import ( + "errors" "testing" + "github.com/ImageWare/TLSential/acme" "github.com/ImageWare/TLSential/model" + lregistration "github.com/go-acme/lego/v3/registration" ) type certTest struct { testName string domains []string email string + registrar UserRegistrar expectedError string } +type justReturnRegistrar struct { + resource *lregistration.Resource + err error +} + +func (r *justReturnRegistrar) Register(u lregistration.User) (*lregistration.Resource, error) { + return r.resource, r.err +} + func TestRegister(t *testing.T) { + passThruError := errors.New("This is the expected error") certTests := []certTest{ { "happy path", []string{"example.com", "example2.com"}, "test@notexample.com", + &justReturnRegistrar{nil, nil}, "", }, { - "email at example.com", - []string{"example.com"}, - "test@example.com", - "acme: error: 400 :: POST :: https://acme-v02.api.letsencrypt.org/acme/new-acct :: urn:ietf:params:acme:error:invalidEmail :: Error creating new account :: invalid contact domain. Contact emails @example.com are forbidden, url: ", - }, - { - "wildcard domain", - []string{"*.example.com"}, - "test@notexample.com", - "", + //This test makes sure the registrar is actually being called + "return error", + []string{"somestuff.com"}, + "test@aurl.com", + &justReturnRegistrar{nil, passThruError}, + passThruError.Error(), }, } @@ -44,7 +55,12 @@ func TestRegister(t *testing.T) { t.Error("Error creating certificate", err) return } - a := NewAcmeService(nil, nil) + var a acme.Service + if ct.registrar == nil { + a = NewAcmeService(nil, nil) + } else { + a = NewAcmeServiceWithRegistrar(nil, nil, ct.registrar) + } reg, err := a.Register(c) if err != nil { @@ -79,23 +95,3 @@ func TestRegister(t *testing.T) { }) } } - -func testEq(a, b []string) bool { - - // If one is nil, the other must also be nil. - if (a == nil) != (b == nil) { - return false - } - - if len(a) != len(b) { - return false - } - - for i := range a { - if a[i] != b[i] { - return false - } - } - - return true -} From 0a19e48802ab9c6618c34ae92a3b651c57986a34 Mon Sep 17 00:00:00 2001 From: PDebus Date: Sat, 16 May 2020 23:17:12 -0700 Subject: [PATCH 12/12] Add test for acme channels --- service/acme_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/service/acme_test.go b/service/acme_test.go index dc74bec..076fbe2 100644 --- a/service/acme_test.go +++ b/service/acme_test.go @@ -95,3 +95,32 @@ func TestRegister(t *testing.T) { }) } } + +func TestChannels(t *testing.T) { + t.Run("request_issue", func(t *testing.T) { + CreateChannelsAndListeners(1, 0, nil, nil) + + a := NewAcmeServiceWithRegistrar(nil, nil, nil) + + if !a.RequestRenew("id") { + t.Error("Should not have blocked yet") + return + } + + if a.RequestIssue("id2") { + t.Error("Should have blocked") + return + } + + select { + case id := <-a.GetAutoRenewChannel(): + if id != "id" { + t.Errorf("expected 'id' but got '%s'", id) + } + break + default: + t.Error("Could not read from channel") + } + }) + +}