@@ -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+
165197func 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
269335func TestGORM (t * testing.T ) {
0 commit comments