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 }