Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit596fdcb

Browse files
authored
test: refactor dbtestutil to record database creation (#19843)
relates tocoder/internal#927Refactors dbtestutil to use a `Broker` struct to create test databases. Additionally uses a `coder_testing` database to record test databases that are created and when they are dropped.This is in preparation for the PR above this in the stack which adds a "cleaner" subprocess that cleans out any databases that were left when the test process ends.
1 parente13fcaf commit596fdcb

File tree

3 files changed

+255
-133
lines changed

3 files changed

+255
-133
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package dbtestutil
2+
3+
import (
4+
"database/sql"
5+
_"embed"
6+
"fmt"
7+
"os"
8+
"sync"
9+
"time"
10+
11+
"github.com/google/uuid"
12+
"github.com/lib/pq"
13+
"golang.org/x/xerrors"
14+
15+
"github.com/coder/coder/v2/cryptorand"
16+
)
17+
18+
constCoderTestingDBName="coder_testing"
19+
20+
//go:embed coder_testing.sql
21+
varcoderTestingSQLInitstring
22+
23+
typeBrokerstruct {
24+
sync.Mutex
25+
uuid uuid.UUID
26+
coderTestingDB*sql.DB
27+
refCountint
28+
}
29+
30+
func (b*Broker)Create(tTBSubset,opts...OpenOption) (ConnectionParams,error) {
31+
iferr:=b.init(t);err!=nil {
32+
returnConnectionParams{},err
33+
}
34+
openOptions:=OpenOptions{}
35+
for_,opt:=rangeopts {
36+
opt(&openOptions)
37+
}
38+
39+
var (
40+
username=defaultConnectionParams.Username
41+
password=defaultConnectionParams.Password
42+
host=defaultConnectionParams.Host
43+
port=defaultConnectionParams.Port
44+
)
45+
46+
// Use a time-based prefix to make it easier to find the database
47+
// when debugging.
48+
now:=time.Now().Format("test_2006_01_02_15_04_05")
49+
dbSuffix,err:=cryptorand.StringCharset(cryptorand.Lower,10)
50+
iferr!=nil {
51+
returnConnectionParams{},xerrors.Errorf("generate db suffix: %w",err)
52+
}
53+
dbName:=now+"_"+dbSuffix
54+
55+
// TODO: add package and test name
56+
_,err=b.coderTestingDB.Exec(
57+
"INSERT INTO test_databases (name, process_uuid) VALUES ($1, $2)",dbName,b.uuid)
58+
iferr!=nil {
59+
returnConnectionParams{},xerrors.Errorf("insert test_database row: %w",err)
60+
}
61+
62+
// if empty createDatabaseFromTemplate will create a new template db
63+
templateDBName:=os.Getenv("DB_FROM")
64+
ifopenOptions.DBFrom!=nil {
65+
templateDBName=*openOptions.DBFrom
66+
}
67+
iferr=createDatabaseFromTemplate(t,defaultConnectionParams,b.coderTestingDB,dbName,templateDBName);err!=nil {
68+
returnConnectionParams{},xerrors.Errorf("create database: %w",err)
69+
}
70+
71+
testDBParams:=ConnectionParams{
72+
Username:username,
73+
Password:password,
74+
Host:host,
75+
Port:port,
76+
DBName:dbName,
77+
}
78+
79+
// Optionally log the DSN to help connect to the test database.
80+
ifopenOptions.LogDSN {
81+
_,_=fmt.Fprintf(os.Stderr,"Connect to the database for %s using: psql '%s'\n",t.Name(),testDBParams.DSN())
82+
}
83+
t.Cleanup(b.clean(t,dbName))
84+
returntestDBParams,nil
85+
}
86+
87+
func (b*Broker)clean(tTBSubset,dbNamestring)func() {
88+
returnfunc() {
89+
_,err:=b.coderTestingDB.Exec("DROP DATABASE "+dbName+";")
90+
iferr!=nil {
91+
t.Logf("failed to clean up database %q: %s\n",dbName,err.Error())
92+
return
93+
}
94+
_,err=b.coderTestingDB.Exec("UPDATE test_databases SET dropped_at = CURRENT_TIMESTAMP WHERE name = $1",dbName)
95+
iferr!=nil {
96+
t.Logf("failed to mark test database '%s' dropped: %s\n",dbName,err.Error())
97+
}
98+
}
99+
}
100+
101+
func (b*Broker)init(tTBSubset)error {
102+
b.Lock()
103+
deferb.Unlock()
104+
b.refCount++
105+
t.Cleanup(b.decRef)
106+
ifb.coderTestingDB!=nil {
107+
// already initialized
108+
returnnil
109+
}
110+
111+
connectionParamsInitOnce.Do(func() {
112+
errDefaultConnectionParamsInit=initDefaultConnection(t)
113+
})
114+
iferrDefaultConnectionParamsInit!=nil {
115+
returnxerrors.Errorf("init default connection params: %w",errDefaultConnectionParamsInit)
116+
}
117+
coderTestingParams:=defaultConnectionParams
118+
coderTestingParams.DBName=CoderTestingDBName
119+
coderTestingDB,err:=sql.Open("postgres",coderTestingParams.DSN())
120+
iferr!=nil {
121+
returnxerrors.Errorf("open postgres connection: %w",err)
122+
}
123+
124+
// creating the db can succeed even if the database doesn't exist. Ping it to find out.
125+
err=coderTestingDB.Ping()
126+
varpqErr*pq.Error
127+
ifxerrors.As(err,&pqErr)&&pqErr.Code=="3D000" {
128+
// database does not exist.
129+
ifcloseErr:=coderTestingDB.Close();closeErr!=nil {
130+
returnxerrors.Errorf("close postgres connection: %w",closeErr)
131+
}
132+
err=createCoderTestingDB(t)
133+
iferr!=nil {
134+
returnxerrors.Errorf("create coder testing db: %w",err)
135+
}
136+
coderTestingDB,err=sql.Open("postgres",coderTestingParams.DSN())
137+
iferr!=nil {
138+
returnxerrors.Errorf("open postgres connection: %w",err)
139+
}
140+
}elseiferr!=nil {
141+
_=coderTestingDB.Close()
142+
returnxerrors.Errorf("ping '%s' database: %w",CoderTestingDBName,err)
143+
}
144+
b.coderTestingDB=coderTestingDB
145+
b.uuid=uuid.New()
146+
returnnil
147+
}
148+
149+
funccreateCoderTestingDB(tTBSubset)error {
150+
db,err:=sql.Open("postgres",defaultConnectionParams.DSN())
151+
iferr!=nil {
152+
returnxerrors.Errorf("open postgres connection: %w",err)
153+
}
154+
deferfunc() {
155+
_=db.Close()
156+
}()
157+
err=createAndInitDatabase(t,defaultConnectionParams,db,CoderTestingDBName,func(testDB*sql.DB)error {
158+
_,err:=testDB.Exec(coderTestingSQLInit)
159+
returnerr
160+
})
161+
iferr!=nil {
162+
returnxerrors.Errorf("create coder testing db: %w",err)
163+
}
164+
returnnil
165+
}
166+
167+
func (b*Broker)decRef() {
168+
b.Lock()
169+
deferb.Unlock()
170+
b.refCount--
171+
ifb.refCount==0 {
172+
// ensures we don't leave go routines around for GoLeak to find.
173+
_=b.coderTestingDB.Close()
174+
b.coderTestingDB=nil
175+
}
176+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CREATETABLEIF NOT EXISTS test_databases (
2+
nametextPRIMARY KEY,
3+
created_attimestamp with time zoneNOT NULL DEFAULTCURRENT_TIMESTAMP,
4+
dropped_attimestamp with time zone,-- null means it hasn't been dropped
5+
process_uuid uuidNOT NULL
6+
);
7+
8+
CREATEINDEXIF NOT EXISTS test_databases_process_uuidON test_databases (process_uuid, dropped_at);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp