github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/index/postgres/postgres_test.go (about)

     1  /*
     2  Copyright 2012 The Camlistore Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package postgres_test
    18  
    19  import (
    20  	"database/sql"
    21  	"errors"
    22  	"fmt"
    23  	"log"
    24  	"strings"
    25  	"sync"
    26  	"testing"
    27  
    28  	"camlistore.org/pkg/index"
    29  	"camlistore.org/pkg/index/indextest"
    30  	"camlistore.org/pkg/osutil"
    31  	"camlistore.org/pkg/sorted"
    32  	"camlistore.org/pkg/sorted/kvtest"
    33  	"camlistore.org/pkg/sorted/postgres"
    34  	"camlistore.org/pkg/test"
    35  
    36  	_ "camlistore.org/third_party/github.com/lib/pq"
    37  )
    38  
    39  var (
    40  	once        sync.Once
    41  	dbAvailable bool
    42  	rootdb      *sql.DB
    43  )
    44  
    45  func checkDB() {
    46  	var err error
    47  	rootdb, err = sql.Open("postgres", "user=postgres password=postgres host=localhost dbname=postgres")
    48  	if err != nil {
    49  		log.Printf("Could not open postgres rootdb: %v", err)
    50  		return
    51  	}
    52  	var n int
    53  	err = rootdb.QueryRow("SELECT COUNT(*) FROM user").Scan(&n)
    54  	if err == nil {
    55  		dbAvailable = true
    56  	}
    57  }
    58  
    59  func skipOrFailIfNoPostgreSQL(t *testing.T) {
    60  	once.Do(checkDB)
    61  	if !dbAvailable {
    62  		err := errors.New("Not running; start a postgres daemon on the standard port (5432) with password 'postgres' for postgres user")
    63  		test.DependencyErrorOrSkip(t)
    64  		t.Fatalf("PostGreSQL not available locally for testing: %v", err)
    65  	}
    66  }
    67  
    68  func do(db *sql.DB, sql string) {
    69  	_, err := db.Exec(sql)
    70  	if err == nil {
    71  		return
    72  	}
    73  	log.Fatalf("Error %v running SQL: %s", err, sql)
    74  }
    75  
    76  func doQuery(db *sql.DB, sql string) {
    77  	r, err := db.Query(sql)
    78  	if err == nil {
    79  		r.Close()
    80  		return
    81  	}
    82  	log.Fatalf("Error %v running SQL query: %s", err, sql)
    83  }
    84  
    85  // closeAllSessions connects to the "postgres" DB on cfg.Host, and closes all connections to cfg.Database.
    86  func closeAllSessions(cfg postgres.Config) error {
    87  	conninfo := fmt.Sprintf("user=%s dbname=postgres host=%s password=%s sslmode=%s",
    88  		cfg.User, cfg.Host, cfg.Password, cfg.SSLMode)
    89  	rootdb, err := sql.Open("postgres", conninfo)
    90  	if err != nil {
    91  		return fmt.Errorf("Could not open root db: %v", err)
    92  	}
    93  	defer rootdb.Close()
    94  	query := `
    95  SELECT
    96      pg_terminate_backend(pg_stat_activity.pid)
    97  FROM
    98      pg_stat_activity
    99  WHERE
   100  	datname = '` + cfg.Database +
   101  		`' AND pid <> pg_backend_pid()`
   102  
   103  	// They changed procpid to pid in 9.2
   104  	if version(rootdb) < "9.2" {
   105  		query = strings.Replace(query, ".pid", ".procpid", 1)
   106  		query = strings.Replace(query, "AND pid", "AND procpid", 1)
   107  	}
   108  	r, err := rootdb.Query(query)
   109  	if err != nil {
   110  		return fmt.Errorf("Error running SQL query\n %v\n: %s", query, err)
   111  	}
   112  	r.Close()
   113  	return nil
   114  }
   115  
   116  func version(db *sql.DB) string {
   117  	version := ""
   118  	if err := db.QueryRow("SELECT version()").Scan(&version); err != nil {
   119  		log.Fatalf("Could not get PostgreSQL version: %v", err)
   120  	}
   121  	fields := strings.Fields(version)
   122  	if len(fields) < 2 {
   123  		log.Fatalf("Could not get PostgreSQL version because bogus answer: %q", version)
   124  	}
   125  	return fields[1]
   126  }
   127  
   128  func newSorted(t *testing.T) (kv sorted.KeyValue, clean func()) {
   129  	skipOrFailIfNoPostgreSQL(t)
   130  	dbname := "camlitest_" + osutil.Username()
   131  	if err := closeAllSessions(postgres.Config{
   132  		User:     "postgres",
   133  		Password: "postgres",
   134  		SSLMode:  "require",
   135  		Database: dbname,
   136  		Host:     "localhost",
   137  	}); err != nil {
   138  		t.Fatalf("Could not close all old sessions to %q: %v", dbname, err)
   139  	}
   140  	do(rootdb, "DROP DATABASE IF EXISTS "+dbname)
   141  	do(rootdb, "CREATE DATABASE "+dbname+" LC_COLLATE = 'C' TEMPLATE = template0")
   142  
   143  	testdb, err := sql.Open("postgres", "user=postgres password=postgres host=localhost sslmode=require dbname="+dbname)
   144  	if err != nil {
   145  		t.Fatalf("opening test database: " + err.Error())
   146  	}
   147  	for _, tableSql := range postgres.SQLCreateTables() {
   148  		do(testdb, tableSql)
   149  	}
   150  	for _, statement := range postgres.SQLDefineReplace() {
   151  		do(testdb, statement)
   152  	}
   153  	doQuery(testdb, fmt.Sprintf(`SELECT replaceintometa('version', '%d')`, postgres.SchemaVersion()))
   154  
   155  	kv, err = postgres.NewKeyValue(postgres.Config{
   156  		Host:     "localhost",
   157  		Database: dbname,
   158  		User:     "postgres",
   159  		Password: "postgres",
   160  		SSLMode:  "require",
   161  	})
   162  	if err != nil {
   163  		t.Fatal(err)
   164  	}
   165  	return kv, func() {
   166  		kv.Close()
   167  	}
   168  }
   169  
   170  func TestSortedKV(t *testing.T) {
   171  	kv, clean := newSorted(t)
   172  	defer clean()
   173  	kvtest.TestSorted(t, kv)
   174  }
   175  
   176  type postgresTester struct{}
   177  
   178  func (postgresTester) test(t *testing.T, tfn func(*testing.T, func() *index.Index)) {
   179  	var mu sync.Mutex // guards cleanups
   180  	var cleanups []func()
   181  	defer func() {
   182  		mu.Lock() // never unlocked
   183  		for _, fn := range cleanups {
   184  			fn()
   185  		}
   186  	}()
   187  	makeIndex := func() *index.Index {
   188  		s, cleanup := newSorted(t)
   189  		mu.Lock()
   190  		cleanups = append(cleanups, cleanup)
   191  		mu.Unlock()
   192  		return index.New(s)
   193  	}
   194  	tfn(t, makeIndex)
   195  }
   196  
   197  func TestIndex_Postgres(t *testing.T) {
   198  	if testing.Short() {
   199  		t.Logf("skipping test in short mode")
   200  		return
   201  	}
   202  	postgresTester{}.test(t, indextest.Index)
   203  }
   204  
   205  func TestPathsOfSignerTarget_Postgres(t *testing.T) {
   206  	if testing.Short() {
   207  		t.Logf("skipping test in short mode")
   208  		return
   209  	}
   210  	postgresTester{}.test(t, indextest.PathsOfSignerTarget)
   211  }
   212  
   213  func TestFiles_Postgres(t *testing.T) {
   214  	if testing.Short() {
   215  		t.Logf("skipping test in short mode")
   216  		return
   217  	}
   218  	postgresTester{}.test(t, indextest.Files)
   219  }
   220  
   221  func TestEdgesTo_Postgres(t *testing.T) {
   222  	if testing.Short() {
   223  		t.Logf("skipping test in short mode")
   224  		return
   225  	}
   226  	postgresTester{}.test(t, indextest.EdgesTo)
   227  }
   228  
   229  func TestDelete_Postgres(t *testing.T) {
   230  	postgresTester{}.test(t, indextest.Delete)
   231  }