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  }