github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/database/testing/fullsuite.go (about) 1 // Copyright 2022 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "context" 8 "database/sql" 9 "fmt" 10 "math/rand" 11 "net" 12 "strconv" 13 14 "github.com/juju/testing" 15 jc "github.com/juju/testing/checkers" 16 gc "gopkg.in/check.v1" 17 18 coredatabase "github.com/juju/juju/core/database" 19 "github.com/juju/juju/database/app" 20 ) 21 22 // DBSuite is used to provide a Dqlite-backed sql.DB reference to tests. 23 type DBSuite struct { 24 testing.IsolationSuite 25 26 dqlite *app.App 27 db *sql.DB 28 trackedDB coredatabase.TrackedDB 29 } 30 31 // SetUpSuite creates a new Dqlite application and waits for it to be ready. 32 func (s *DBSuite) SetUpSuite(c *gc.C) { 33 s.IsolationSuite.SetUpSuite(c) 34 35 dbPath := c.MkDir() 36 port := FindTCPPort(c) 37 38 url := fmt.Sprintf("%s:%d", "127.0.0.1", port) 39 c.Logf("Opening sqlite3 db with: %v", url) 40 41 var err error 42 s.dqlite, err = app.New(dbPath, app.WithAddress(url)) 43 c.Assert(err, jc.ErrorIsNil) 44 45 err = s.dqlite.Ready(context.TODO()) 46 c.Assert(err, jc.ErrorIsNil) 47 } 48 49 // TearDownSuite terminates the Dqlite node, releasing all resources. 50 func (s *DBSuite) TearDownSuite(c *gc.C) { 51 if s.dqlite != nil { 52 err := s.dqlite.Close() 53 c.Assert(err, jc.ErrorIsNil) 54 } 55 56 s.IsolationSuite.TearDownSuite(c) 57 } 58 59 // SetUpTest opens a new, randomly named database and 60 // makes it available for use by test the next test. 61 func (s *DBSuite) SetUpTest(c *gc.C) { 62 s.IsolationSuite.SetUpTest(c) 63 64 var err error 65 s.db, err = s.dqlite.Open(context.TODO(), strconv.Itoa(rand.Intn(10))) 66 c.Assert(err, jc.ErrorIsNil) 67 68 s.trackedDB = &trackedDB{ 69 db: s.db, 70 } 71 } 72 73 // TearDownTest closes the database opened in SetUpTest. 74 // TODO (manadart 2022-09-12): There is currently no avenue for dropping a DB. 75 func (s *DBSuite) TearDownTest(c *gc.C) { 76 if s.db != nil { 77 c.Logf("Closing DB") 78 err := s.db.Close() 79 c.Assert(err, jc.ErrorIsNil) 80 } 81 82 s.IsolationSuite.TearDownTest(c) 83 } 84 85 func (s *DBSuite) DB() *sql.DB { 86 return s.db 87 } 88 89 func (s *DBSuite) TrackedDB() coredatabase.TrackedDB { 90 return s.trackedDB 91 } 92 93 // FindTCPPort finds an unused TCP port and returns it. 94 // It is prone to racing, so the port should be used as soon as it is acquired 95 // to minimise the change of another process using it in the interim. 96 // The chances of this should be negligible during testing. 97 func FindTCPPort(c *gc.C) int { 98 l, err := net.Listen("tcp", ":0") 99 c.Assert(err, jc.ErrorIsNil) 100 c.Assert(l.Close(), jc.ErrorIsNil) 101 return l.Addr().(*net.TCPAddr).Port 102 }