Skip to content

Commit d8d5eae

Browse files
committed
testing: run ORM tests against a SQL tenant
This code adds a test case that runs the ORM tests against a tenant if the cockroach version supports it.
1 parent 86db057 commit d8d5eae

File tree

1 file changed

+173
-107
lines changed

1 file changed

+173
-107
lines changed

testing/main_test.go

Lines changed: 173 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -43,49 +43,76 @@ var customURLSchemes = map[application]string{
4343
{language: "python", orm: "sqlalchemy"}: "cockroachdb",
4444
}
4545

46-
// initTestDatabase launches a test database as a subprocess.
47-
func initTestDatabase(t *testing.T, app application) (*sql.DB, *url.URL, string, func()) {
46+
type tenantServer interface {
47+
NewTenantServer() (testserver.TestServer, error)
48+
}
49+
50+
// newServer creates a new cockroachDB server.
51+
func newServer(t *testing.T) testserver.TestServer {
52+
t.Helper()
4853
ts, err := testserver.NewTestServer()
4954
if err != nil {
5055
t.Fatal(err)
5156
}
57+
return ts
58+
}
5259

53-
if err := ts.Start(); err != nil {
60+
// newTenant creates a new SQL Tenant pointed at the given TestServer. See
61+
// TestServer.NewTenantServer for more information.
62+
func newTenant(t *testing.T, ts testserver.TestServer) testserver.TestServer {
63+
t.Helper()
64+
tenant, err := ts.(tenantServer).NewTenantServer()
65+
if err != nil {
5466
t.Fatal(err)
5567
}
68+
return tenant
69+
}
5670

57-
url := ts.PGURL()
58-
if url == nil {
59-
t.Fatalf("url not found")
71+
// startServerWithApplication launches a test database as a subprocess.
72+
func startServerWithApplication(t *testing.T, ts testserver.TestServer, app application) (*sql.DB, *url.URL, func()) {
73+
t.Helper()
74+
if err := ts.Start(); err != nil {
75+
t.Fatal(err)
6076
}
61-
url.Path = app.dbName()
62-
63-
db, err := sql.Open("postgres", url.String())
77+
serverURL := ts.PGURL()
78+
if serverURL == nil {
79+
t.Fatal("url not found")
80+
}
81+
pgURL := *serverURL
82+
pgURL.Path = app.dbName()
83+
db, err := sql.Open("postgres", pgURL.String())
6484
if err != nil {
6585
t.Fatal(err)
6686
}
67-
68-
ts.WaitForInit(db)
69-
87+
if err := ts.WaitForInit(db); err != nil {
88+
t.Fatal(err)
89+
}
7090
// Create the database if it does not exist.
7191
if _, err := db.Exec("CREATE DATABASE IF NOT EXISTS " + app.dbName()); err != nil {
7292
t.Fatal(err)
7393
}
94+
if scheme, ok := customURLSchemes[app]; ok {
95+
pgURL.Scheme = scheme
96+
}
97+
return db, &pgURL, func() {
98+
_ = db.Close()
99+
ts.Stop()
100+
}
101+
}
74102

103+
func getVersionFromDB(t *testing.T, db *sql.DB) *version.Version {
104+
t.Helper()
75105
var crdbVersion string
76106
if err := db.QueryRow(
77107
`SELECT value FROM crdb_internal.node_build_info where field = 'Version'`,
78108
).Scan(&crdbVersion); err != nil {
79109
t.Fatal(err)
80110
}
81-
82-
if scheme, ok := customURLSchemes[app]; ok {
83-
url.Scheme = scheme
84-
}
85-
return db, url, crdbVersion, func() {
86-
_ = db.Close()
87-
ts.Stop()
111+
v, err := version.Parse(crdbVersion)
112+
if err != nil {
113+
t.Fatal(err)
88114
}
115+
return v
89116
}
90117

91118
// initORMApp launches an ORM application as a subprocess and returns a
@@ -162,6 +189,11 @@ var minRequiredVersionsByORMName = map[string]struct {
162189
},
163190
}
164191

192+
// minTenantVersion is the minimum version that supports creating SQL tenants
193+
// (i.e. the `cockroach mt start-sql command). Earlier versions cannot create
194+
// tenants.
195+
var minTenantVersion = version.MustParse("v20.2.0-alpha")
196+
165197
func testORM(
166198
t *testing.T, language, orm string, tableNames testTableNames, columnNames testColumnNames,
167199
) {
@@ -170,100 +202,134 @@ func testORM(
170202
orm: orm,
171203
}
172204

173-
db, dbURL, crdbVersion, stopDB := initTestDatabase(t, app)
174-
defer stopDB()
175-
176-
v, err := version.Parse(crdbVersion)
177-
if err != nil {
178-
t.Fatal(err)
205+
type testCase struct {
206+
name string
207+
db *sql.DB
208+
dbURL *url.URL
179209
}
180-
if info, ok := minRequiredVersionsByORMName[orm]; ok {
181-
if !v.AtLeast(info.v) {
182-
t.Skip(info.skipMsg)
210+
var testCases []testCase
211+
{
212+
ts := newServer(t)
213+
db, dbURL, stopDB := startServerWithApplication(t, ts, app)
214+
defer stopDB()
215+
216+
crdbVersion := getVersionFromDB(t, db)
217+
// Check that this ORM can be run with the given cockroach version.
218+
if info, ok := minRequiredVersionsByORMName[orm]; ok {
219+
if !crdbVersion.AtLeast(info.v) {
220+
t.Skip(info.skipMsg)
221+
}
183222
}
184-
}
185-
186-
td := testDriver{
187-
db: db,
188-
dbName: app.dbName(),
189-
tableNames: tableNames,
190-
columnNames: columnNames,
191-
}
192223

193-
t.Run("FirstRun", func(t *testing.T) {
194-
stopApp, err := initORMApp(app, dbURL)
195-
if err != nil {
196-
t.Fatal(err)
224+
testCases = []testCase{
225+
{
226+
name: "SystemTenant",
227+
db: db,
228+
dbURL: dbURL,
229+
},
197230
}
198-
defer func() {
199-
if err := stopApp(); err != nil {
200-
t.Fatal(err)
201-
}
202-
}()
203-
204-
// Test that the correct tables were generated.
205-
t.Run("GeneratedTables", td.TestGeneratedTables)
206-
207-
// Test that the correct columns in those tables were generated.
208-
t.Run("GeneratedColumns", parallelTestGroup{
209-
"CustomersTable": td.TestGeneratedCustomersTableColumns,
210-
"ProductsTable": td.TestGeneratedProductsTableColumns,
211-
"OrdersTable": td.TestGeneratedOrdersTableColumns,
212-
"OrderProductsTable": td.TestGeneratedOrderProductsTableColumns,
213-
}.T)
214-
215-
// Test that the tables begin empty.
216-
t.Run("EmptyTables", parallelTestGroup{
217-
"CustomersTable": td.TestCustomersEmpty,
218-
"ProductsTable": td.TestProductsTableEmpty,
219-
"OrdersTable": td.TestOrdersTableEmpty,
220-
"OrderProductsTable": td.TestOrderProductsTableEmpty,
221-
}.T)
222-
223-
// Test that the API returns empty sets for each collection.
224-
t.Run("RetrieveFromAPIBeforeCreation", parallelTestGroup{
225-
"Customers": td.TestRetrieveCustomersBeforeCreation,
226-
"Products": td.TestRetrieveProductsBeforeCreation,
227-
"Orders": td.TestRetrieveOrdersBeforeCreation,
228-
}.T)
229-
230-
// Test the creation of initial objects.
231-
t.Run("CreateCustomer", td.TestCreateCustomer)
232-
t.Run("CreateProduct", td.TestCreateProduct)
233-
234-
// Test that the API returns what we just created.
235-
t.Run("RetrieveFromAPIAfterInitialCreation", parallelTestGroup{
236-
"Customers": td.TestRetrieveCustomerAfterCreation,
237-
"Products": td.TestRetrieveProductAfterCreation,
238-
}.T)
239-
240-
// Test the creation of dependent objects.
241-
t.Run("CreateOrder", td.TestCreateOrder)
242-
243-
// Test that the API returns what we just created.
244-
t.Run("RetrieveFromAPIAfterDependentCreation", parallelTestGroup{
245-
"Order": td.TestRetrieveProductAfterCreation,
246-
}.T)
247-
})
248-
249-
t.Run("SecondRun", func(t *testing.T) {
250-
stopApp, err := initORMApp(app, dbURL)
251-
if err != nil {
252-
t.Fatal(err)
231+
232+
if crdbVersion.AtLeast(minTenantVersion) {
233+
// This cockroach version supports creating tenants, add a test case to
234+
// run a tenant server.
235+
tenant := newTenant(t, ts)
236+
db, dbURL, stopDB := startServerWithApplication(t, tenant, app)
237+
defer stopDB()
238+
testCases = append(testCases, testCase{
239+
name: "RegularTenant",
240+
db: db,
241+
dbURL: dbURL,
242+
})
243+
} else {
244+
t.Logf("not running tenant test case because minimum tenant version check was not satisfied (%s is < %s)", crdbVersion, minTenantVersion)
253245
}
254-
defer func() {
255-
if err := stopApp(); err != nil {
256-
t.Fatal(err)
246+
}
247+
248+
for _, tc := range testCases {
249+
t.Run(tc.name, func(t *testing.T) {
250+
td := testDriver{
251+
db: tc.db,
252+
dbName: app.dbName(),
253+
tableNames: tableNames,
254+
columnNames: columnNames,
257255
}
258-
}()
259-
260-
// Test that the API still returns all created objects.
261-
t.Run("RetrieveFromAPIAfterRestart", parallelTestGroup{
262-
"Customers": td.TestRetrieveCustomerAfterCreation,
263-
"Products": td.TestRetrieveProductAfterCreation,
264-
"Order": td.TestRetrieveProductAfterCreation,
265-
}.T)
266-
})
256+
257+
t.Run("FirstRun", func(t *testing.T) {
258+
stopApp, err := initORMApp(app, tc.dbURL)
259+
if err != nil {
260+
t.Fatal(err)
261+
}
262+
defer func() {
263+
if err := stopApp(); err != nil {
264+
t.Fatal(err)
265+
}
266+
}()
267+
268+
// Test that the correct tables were generated.
269+
t.Run("GeneratedTables", td.TestGeneratedTables)
270+
271+
// Test that the correct columns in those tables were generated.
272+
t.Run("GeneratedColumns", parallelTestGroup{
273+
"CustomersTable": td.TestGeneratedCustomersTableColumns,
274+
"ProductsTable": td.TestGeneratedProductsTableColumns,
275+
"OrdersTable": td.TestGeneratedOrdersTableColumns,
276+
"OrderProductsTable": td.TestGeneratedOrderProductsTableColumns,
277+
}.T)
278+
279+
// Test that the tables begin empty.
280+
t.Run("EmptyTables", parallelTestGroup{
281+
"CustomersTable": td.TestCustomersEmpty,
282+
"ProductsTable": td.TestProductsTableEmpty,
283+
"OrdersTable": td.TestOrdersTableEmpty,
284+
"OrderProductsTable": td.TestOrderProductsTableEmpty,
285+
}.T)
286+
287+
// Test that the API returns empty sets for each collection.
288+
t.Run("RetrieveFromAPIBeforeCreation", parallelTestGroup{
289+
"Customers": td.TestRetrieveCustomersBeforeCreation,
290+
"Products": td.TestRetrieveProductsBeforeCreation,
291+
"Orders": td.TestRetrieveOrdersBeforeCreation,
292+
}.T)
293+
294+
// Test the creation of initial objects.
295+
t.Run("CreateCustomer", td.TestCreateCustomer)
296+
t.Run("CreateProduct", td.TestCreateProduct)
297+
298+
// Test that the API returns what we just created.
299+
t.Run("RetrieveFromAPIAfterInitialCreation", parallelTestGroup{
300+
"Customers": td.TestRetrieveCustomerAfterCreation,
301+
"Products": td.TestRetrieveProductAfterCreation,
302+
}.T)
303+
304+
// Test the creation of dependent objects.
305+
t.Run("CreateOrder", td.TestCreateOrder)
306+
307+
// Test that the API returns what we just created.
308+
t.Run("RetrieveFromAPIAfterDependentCreation", parallelTestGroup{
309+
"Order": td.TestRetrieveProductAfterCreation,
310+
}.T)
311+
})
312+
313+
t.Run("SecondRun", func(t *testing.T) {
314+
stopApp, err := initORMApp(app, tc.dbURL)
315+
if err != nil {
316+
t.Fatal(err)
317+
}
318+
defer func() {
319+
if err := stopApp(); err != nil {
320+
t.Fatal(err)
321+
}
322+
}()
323+
324+
// Test that the API still returns all created objects.
325+
t.Run("RetrieveFromAPIAfterRestart", parallelTestGroup{
326+
"Customers": td.TestRetrieveCustomerAfterCreation,
327+
"Products": td.TestRetrieveProductAfterCreation,
328+
"Order": td.TestRetrieveProductAfterCreation,
329+
}.T)
330+
})
331+
})
332+
}
267333
}
268334

269335
func TestGORM(t *testing.T) {

0 commit comments

Comments
 (0)