decred.org/dcrdex@v1.0.5/server/db/driver/pg/system_online_test.go (about)

     1  //go:build pgonline
     2  
     3  package pg
     4  
     5  import (
     6  	"context"
     7  	"database/sql"
     8  	"fmt"
     9  	"os"
    10  	"testing"
    11  
    12  	"decred.org/dcrdex/dex"
    13  )
    14  
    15  const (
    16  	PGTestsHost   = "localhost" // "/run/postgresql" for UNIX socket
    17  	PGTestsPort   = "5432"      // "" for UNIX socket
    18  	PGTestsUser   = "dcrdex"    // "dcrdex" for full database rather than test data repo
    19  	PGTestsPass   = ""
    20  	PGTestsDBName = "dcrdex_mainnet_test"
    21  )
    22  
    23  var (
    24  	archie            *Archiver
    25  	mktInfo, mktInfo2 *dex.MarketInfo
    26  	numMarkets        int
    27  )
    28  
    29  func TestMain(m *testing.M) {
    30  	startLogger()
    31  
    32  	// Wrap openDB so that the cleanUp function may be deferred.
    33  	doIt := func() int {
    34  		// Not counted as coverage, must test Archiver constructor explicitly.
    35  		cleanUp, err := openDB()
    36  		defer cleanUp()
    37  		if err != nil {
    38  			panic(fmt.Sprintln("no db for testing:", err))
    39  		}
    40  
    41  		return m.Run()
    42  	}
    43  
    44  	os.Exit(doIt())
    45  }
    46  
    47  func openDB() (func() error, error) {
    48  	var err error
    49  	mktInfo, err = dex.NewMarketInfoFromSymbols("dcr", "btc", LotSize, RateStep, EpochDuration, 0, MarketBuyBuffer)
    50  	if err != nil {
    51  		return func() error { return nil }, fmt.Errorf("invalid market: %v", err)
    52  	}
    53  
    54  	mktInfo2, err = dex.NewMarketInfoFromSymbols("btc", "ltc", LotSize, RateStep, EpochDuration, 0, MarketBuyBuffer)
    55  	if err != nil {
    56  		return func() error { return nil }, fmt.Errorf("invalid market: %v", err)
    57  	}
    58  
    59  	AssetDCR = mktInfo.Base
    60  	AssetBTC = mktInfo.Quote
    61  	AssetLTC = mktInfo2.Quote
    62  
    63  	dbi := Config{
    64  		Host:         PGTestsHost,
    65  		Port:         PGTestsPort,
    66  		User:         PGTestsUser,
    67  		Pass:         PGTestsPass,
    68  		DBName:       PGTestsDBName,
    69  		ShowPGConfig: false,
    70  		QueryTimeout: 0, // zero to use the default
    71  		MarketCfg:    []*dex.MarketInfo{mktInfo, mktInfo2},
    72  	}
    73  
    74  	numMarkets = len(dbi.MarketCfg)
    75  
    76  	closeFn := func() error { return nil }
    77  
    78  	// Low-level db connection to kill any leftover tables.
    79  	db, err := connect(dbi.Host, dbi.Port, dbi.User, dbi.Pass, dbi.DBName)
    80  	if err != nil {
    81  		return closeFn, err
    82  	}
    83  	if err := nukeAll(db); err != nil {
    84  		return closeFn, fmt.Errorf("nukeAll: %v", err)
    85  	}
    86  	if err = db.Close(); err != nil {
    87  		return closeFn, fmt.Errorf("failed to close DB: %v", err)
    88  	}
    89  
    90  	ctx := context.Background()
    91  	archie, err = NewArchiver(ctx, &dbi)
    92  	if archie == nil {
    93  		return closeFn, err
    94  	}
    95  
    96  	closeFn = func() error {
    97  		if err := nukeAll(archie.db); err != nil {
    98  			log.Errorf("nukeAll: %v", err)
    99  		}
   100  		return archie.Close()
   101  	}
   102  
   103  	return closeFn, err
   104  }
   105  
   106  func detectMarkets(db *sql.DB) ([]string, error) {
   107  	// Identify all markets by matching schemas like dcr_btc with '___\____'.
   108  	rows, err := db.Query(`select nspname from pg_catalog.pg_namespace where nspname like '___\____';`)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	defer rows.Close()
   113  
   114  	var markets []string
   115  	for rows.Next() {
   116  		var market string
   117  		if err = rows.Scan(&market); err != nil {
   118  			rows.Close()
   119  			return nil, err
   120  		}
   121  		markets = append(markets, market)
   122  	}
   123  
   124  	if err = rows.Err(); err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	return markets, nil
   129  }
   130  
   131  // nukeAll removes all of the market schemas and the tables within them, as well
   132  // as all of the DEX tables in the public schema.
   133  // TODO: find a long term home for this once it is clear if and how it will be
   134  // used outside of tests.
   135  func nukeAll(db *sql.DB) error {
   136  	markets, err := detectMarkets(db)
   137  	if err != nil {
   138  		return fmt.Errorf("failed to detect markets: %v", err)
   139  	}
   140  
   141  	// Drop market schemas.
   142  	for i := range markets {
   143  		log.Tracef(`Dropping market %s schema...`, markets[i])
   144  		_, err = db.Exec(fmt.Sprintf("DROP SCHEMA %s CASCADE;", markets[i]))
   145  		if err != nil {
   146  			return err
   147  		}
   148  	}
   149  
   150  	// Drop tables in public schema.
   151  	dropPublic := func(stmts []tableStmt) error {
   152  		for i := range stmts {
   153  			tableName := "public." + stmts[i].name
   154  			log.Tracef(`Dropping DEX table %s...`, tableName)
   155  			if err = dropTable(db, tableName); err != nil {
   156  				return err
   157  			}
   158  		}
   159  		return nil
   160  	}
   161  
   162  	err = dropPublic(createDEXTableStatements)
   163  	if err != nil {
   164  		return err
   165  	}
   166  
   167  	return dropPublic(createAccountTableStatements)
   168  }
   169  
   170  func cleanTables(db *sql.DB) error {
   171  	err := nukeAll(db)
   172  	if err != nil {
   173  		return err
   174  	}
   175  	_, err = prepareTables(context.Background(), db, mktConfig())
   176  	return err
   177  }
   178  
   179  func Test_sqlExec(t *testing.T) {
   180  	// Expect to update 0 rows.
   181  	stmt := fmt.Sprintf(`UPDATE %s SET lot_size=1234 WHERE name='definitely not a market';`, marketsTableName)
   182  	N, err := sqlExec(archie.db, stmt)
   183  	if err != nil {
   184  		t.Fatal("sqlExec:", err)
   185  	}
   186  	if N != 0 {
   187  		t.Errorf("Should have updated 0 rows without error, got %d", N)
   188  	}
   189  
   190  	// Expect to update 1 rows.
   191  	stmt = fmt.Sprintf(`UPDATE %s SET lot_size=lot_size WHERE name=$1;`,
   192  		marketsTableName)
   193  	N, err = sqlExec(archie.db, stmt, mktInfo.Name)
   194  	if err != nil {
   195  		t.Fatal("sqlExec:", err)
   196  	}
   197  	if N != 1 {
   198  		t.Errorf("Should have updated 1 rows without error, got %d", N)
   199  	}
   200  
   201  	// Expect to update N=numMarkets rows.
   202  	stmt = fmt.Sprintf(`UPDATE %s SET lot_size=lot_size;`,
   203  		marketsTableName)
   204  	N, err = sqlExec(archie.db, stmt)
   205  	if err != nil {
   206  		t.Fatal("sqlExec:", err)
   207  	}
   208  	if N != int64(numMarkets) {
   209  		t.Errorf("Should have updated %d rows without error, got %d", numMarkets, N)
   210  	}
   211  }
   212  
   213  func Test_checkCurrentTimeZone(t *testing.T) {
   214  	currentTZ, err := checkCurrentTimeZone(archie.db)
   215  	if err != nil {
   216  		t.Fatal(err)
   217  	}
   218  	t.Logf("Set time zone: %v", currentTZ)
   219  }
   220  
   221  func Test_retrieveSysSettingsConfFile(t *testing.T) {
   222  	ss, err := retrieveSysSettingsConfFile(archie.db)
   223  	if err != nil && err != sql.ErrNoRows {
   224  		t.Errorf("Failed to retrieve system settings: %v", err)
   225  	}
   226  	t.Logf("\n%v", ss)
   227  }
   228  
   229  func Test_retrieveSysSettingsPerformance(t *testing.T) {
   230  	ss, err := retrieveSysSettingsPerformance(archie.db)
   231  	if err != nil {
   232  		t.Errorf("Failed to retrieve system settings: %v", err)
   233  	}
   234  	t.Logf("\n%v", ss)
   235  }
   236  
   237  func Test_retrieveSysSettingsServer(t *testing.T) {
   238  	ss, err := retrieveSysSettingsServer(archie.db)
   239  	if err != nil {
   240  		t.Errorf("Failed to retrieve system server: %v", err)
   241  	}
   242  	t.Logf("\n%v", ss)
   243  }
   244  
   245  func Test_retrievePGVersion(t *testing.T) {
   246  	ver, err := retrievePGVersion(archie.db)
   247  	if err != nil {
   248  		t.Errorf("Failed to retrieve postgres version: %v", err)
   249  	}
   250  	t.Logf("\n%s", ver)
   251  }