mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 18:47:20 -04:00 
			
		
		
		
	* Initial concept for pluggable storage (sans tests and docs) * Add TLS storage docs, test harness, and minor clean up from code review * Fix issue with caddymain's temporary moveStorage * Formatting improvement on struct array literal by removing struct name * Pluggable storage changes: * Change storage interface to persist all site or user data in one call * Add lock/unlock calls for renewal and cert obtaining * Key fields on composite literals
		
			
				
	
	
		
			271 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			271 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package storagetest provides utilities to assist in testing caddytls.Storage
 | |
| // implementations.
 | |
| package storagetest
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"github.com/mholt/caddy/caddytls"
 | |
| 	"testing"
 | |
| )
 | |
| 
 | |
| // StorageTest is a test harness that contains tests to execute all exposed
 | |
| // parts of a Storage implementation.
 | |
| type StorageTest struct {
 | |
| 	// Storage is the implementation to use during tests. This must be
 | |
| 	// present.
 | |
| 	caddytls.Storage
 | |
| 
 | |
| 	// PreTest, if present, is called before every test. Any error returned
 | |
| 	// is returned from the test and the test does not continue.
 | |
| 	PreTest func() error
 | |
| 
 | |
| 	// PostTest, if present, is executed after every test via defer which
 | |
| 	// means it executes even on failure of the test (but not on failure of
 | |
| 	// PreTest).
 | |
| 	PostTest func()
 | |
| 
 | |
| 	// AfterUserEmailStore, if present, is invoked during
 | |
| 	// TestMostRecentUserEmail after each storage just in case anything
 | |
| 	// needs to be mocked.
 | |
| 	AfterUserEmailStore func(email string) error
 | |
| }
 | |
| 
 | |
| // TestFunc holds information about a test.
 | |
| type TestFunc struct {
 | |
| 	// Name is the friendly name of the test.
 | |
| 	Name string
 | |
| 
 | |
| 	// Fn is the function that is invoked for the test.
 | |
| 	Fn func() error
 | |
| }
 | |
| 
 | |
| // runPreTest runs the PreTest function if present.
 | |
| func (s *StorageTest) runPreTest() error {
 | |
| 	if s.PreTest != nil {
 | |
| 		return s.PreTest()
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // runPostTest runs the PostTest function if present.
 | |
| func (s *StorageTest) runPostTest() {
 | |
| 	if s.PostTest != nil {
 | |
| 		s.PostTest()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // AllFuncs returns all test functions that are part of this harness.
 | |
| func (s *StorageTest) AllFuncs() []TestFunc {
 | |
| 	return []TestFunc{
 | |
| 		{"TestSiteInfoExists", s.TestSiteExists},
 | |
| 		{"TestSite", s.TestSite},
 | |
| 		{"TestUser", s.TestUser},
 | |
| 		{"TestMostRecentUserEmail", s.TestMostRecentUserEmail},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Test executes the entire harness using the testing package. Failures are
 | |
| // reported via T.Fatal. If eagerFail is true, the first failure causes all
 | |
| // testing to stop immediately.
 | |
| func (s *StorageTest) Test(t *testing.T, eagerFail bool) {
 | |
| 	if errs := s.TestAll(eagerFail); len(errs) > 0 {
 | |
| 		ifaces := make([]interface{}, len(errs))
 | |
| 		for i, err := range errs {
 | |
| 			ifaces[i] = err
 | |
| 		}
 | |
| 		t.Fatal(ifaces...)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TestAll executes the entire harness and returns the results as an array of
 | |
| // errors. If eagerFail is true, the first failure causes all testing to stop
 | |
| // immediately.
 | |
| func (s *StorageTest) TestAll(eagerFail bool) (errs []error) {
 | |
| 	for _, fn := range s.AllFuncs() {
 | |
| 		if err := fn.Fn(); err != nil {
 | |
| 			errs = append(errs, fmt.Errorf("%v failed: %v", fn.Name, err))
 | |
| 			if eagerFail {
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| var simpleSiteData = &caddytls.SiteData{
 | |
| 	Cert: []byte("foo"),
 | |
| 	Key:  []byte("bar"),
 | |
| 	Meta: []byte("baz"),
 | |
| }
 | |
| var simpleSiteDataAlt = &caddytls.SiteData{
 | |
| 	Cert: []byte("qux"),
 | |
| 	Key:  []byte("quux"),
 | |
| 	Meta: []byte("corge"),
 | |
| }
 | |
| 
 | |
| // TestSiteExists tests Storage.SiteExists.
 | |
| func (s *StorageTest) TestSiteExists() error {
 | |
| 	if err := s.runPreTest(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer s.runPostTest()
 | |
| 
 | |
| 	// Should not exist at first
 | |
| 	if s.SiteExists("example.com") {
 | |
| 		return errors.New("Site should not exist")
 | |
| 	}
 | |
| 
 | |
| 	// Should exist after we store it
 | |
| 	if err := s.StoreSite("example.com", simpleSiteData); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if !s.SiteExists("example.com") {
 | |
| 		return errors.New("Expected site to exist")
 | |
| 	}
 | |
| 
 | |
| 	// Site should no longer exist after we delete it
 | |
| 	if err := s.DeleteSite("example.com"); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if s.SiteExists("example.com") {
 | |
| 		return errors.New("Site should not exist after delete")
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // TestSite tests Storage.LoadSite, Storage.StoreSite, and Storage.DeleteSite.
 | |
| func (s *StorageTest) TestSite() error {
 | |
| 	if err := s.runPreTest(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer s.runPostTest()
 | |
| 
 | |
| 	// Should be a not-found error at first
 | |
| 	if _, err := s.LoadSite("example.com"); err != caddytls.ErrStorageNotFound {
 | |
| 		return fmt.Errorf("Expected ErrStorageNotFound from load, got: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Delete should also be a not-found error at first
 | |
| 	if err := s.DeleteSite("example.com"); err != caddytls.ErrStorageNotFound {
 | |
| 		return fmt.Errorf("Expected ErrStorageNotFound from delete, got: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Should store successfully and then load just fine
 | |
| 	if err := s.StoreSite("example.com", simpleSiteData); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if siteData, err := s.LoadSite("example.com"); err != nil {
 | |
| 		return err
 | |
| 	} else if !bytes.Equal(siteData.Cert, simpleSiteData.Cert) {
 | |
| 		return errors.New("Unexpected cert returned after store")
 | |
| 	} else if !bytes.Equal(siteData.Key, simpleSiteData.Key) {
 | |
| 		return errors.New("Unexpected key returned after store")
 | |
| 	} else if !bytes.Equal(siteData.Meta, simpleSiteData.Meta) {
 | |
| 		return errors.New("Unexpected meta returned after store")
 | |
| 	}
 | |
| 
 | |
| 	// Overwrite should work just fine
 | |
| 	if err := s.StoreSite("example.com", simpleSiteDataAlt); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if siteData, err := s.LoadSite("example.com"); err != nil {
 | |
| 		return err
 | |
| 	} else if !bytes.Equal(siteData.Cert, simpleSiteDataAlt.Cert) {
 | |
| 		return errors.New("Unexpected cert returned after overwrite")
 | |
| 	}
 | |
| 
 | |
| 	// It should delete fine and then not be there
 | |
| 	if err := s.DeleteSite("example.com"); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if _, err := s.LoadSite("example.com"); err != caddytls.ErrStorageNotFound {
 | |
| 		return fmt.Errorf("Expected ErrStorageNotFound after delete, got: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| var simpleUserData = &caddytls.UserData{
 | |
| 	Reg: []byte("foo"),
 | |
| 	Key: []byte("bar"),
 | |
| }
 | |
| var simpleUserDataAlt = &caddytls.UserData{
 | |
| 	Reg: []byte("baz"),
 | |
| 	Key: []byte("qux"),
 | |
| }
 | |
| 
 | |
| // TestUser tests Storage.LoadUser and Storage.StoreUser.
 | |
| func (s *StorageTest) TestUser() error {
 | |
| 	if err := s.runPreTest(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer s.runPostTest()
 | |
| 
 | |
| 	// Should be a not-found error at first
 | |
| 	if _, err := s.LoadUser("foo@example.com"); err != caddytls.ErrStorageNotFound {
 | |
| 		return fmt.Errorf("Expected ErrStorageNotFound from load, got: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Should store successfully and then load just fine
 | |
| 	if err := s.StoreUser("foo@example.com", simpleUserData); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if userData, err := s.LoadUser("foo@example.com"); err != nil {
 | |
| 		return err
 | |
| 	} else if !bytes.Equal(userData.Reg, simpleUserData.Reg) {
 | |
| 		return errors.New("Unexpected reg returned after store")
 | |
| 	} else if !bytes.Equal(userData.Key, simpleUserData.Key) {
 | |
| 		return errors.New("Unexpected key returned after store")
 | |
| 	}
 | |
| 
 | |
| 	// Overwrite should work just fine
 | |
| 	if err := s.StoreUser("foo@example.com", simpleUserDataAlt); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if userData, err := s.LoadUser("foo@example.com"); err != nil {
 | |
| 		return err
 | |
| 	} else if !bytes.Equal(userData.Reg, simpleUserDataAlt.Reg) {
 | |
| 		return errors.New("Unexpected reg returned after overwrite")
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // TestMostRecentUserEmail tests Storage.MostRecentUserEmail.
 | |
| func (s *StorageTest) TestMostRecentUserEmail() error {
 | |
| 	if err := s.runPreTest(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer s.runPostTest()
 | |
| 
 | |
| 	// Should be empty on first run
 | |
| 	if e := s.MostRecentUserEmail(); e != "" {
 | |
| 		return fmt.Errorf("Expected empty most recent user on first run, got: %v", e)
 | |
| 	}
 | |
| 
 | |
| 	// If we store user, then that one should be returned
 | |
| 	if err := s.StoreUser("foo1@example.com", simpleUserData); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if s.AfterUserEmailStore != nil {
 | |
| 		s.AfterUserEmailStore("foo1@example.com")
 | |
| 	}
 | |
| 	if e := s.MostRecentUserEmail(); e != "foo1@example.com" {
 | |
| 		return fmt.Errorf("Unexpected most recent email after first store: %v", e)
 | |
| 	}
 | |
| 
 | |
| 	// If we store another user, then that one should be returned
 | |
| 	if err := s.StoreUser("foo2@example.com", simpleUserDataAlt); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if s.AfterUserEmailStore != nil {
 | |
| 		s.AfterUserEmailStore("foo2@example.com")
 | |
| 	}
 | |
| 	if e := s.MostRecentUserEmail(); e != "foo2@example.com" {
 | |
| 		return fmt.Errorf("Unexpected most recent email after user key: %v", e)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 |