Skip to content

Commit 8cc62a3

Browse files
committed
Clean up compliance test suite and switch to using hdb
1 parent 3e1948a commit 8cc62a3

24 files changed

+165
-384
lines changed

hana/lib/HANAService.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ class HANAService extends SQLService {
901901
const extraction = managed.map(c => c.extract)
902902

903903
const sql = `WITH SRC AS (SELECT ? AS JSON FROM DUMMY UNION ALL SELECT TO_NCLOB(NULL) AS JSON FROM DUMMY)
904-
SELECT ${mixing} FROM JSON_TABLE(SRC.JSON, '$' COLUMNS(${extraction})) AS NEW LEFT JOIN ${this.quote(entity)} AS OLD ON ${keyCompare}`
904+
SELECT ${mixing} FROM JSON_TABLE(SRC.JSON, '$' COLUMNS(${extraction}) ERROR ON ERROR) AS NEW LEFT JOIN ${this.quote(entity)} AS OLD ON ${keyCompare}`
905905

906906
return (this.sql = `UPSERT ${this.quote(entity)} (${this.columns.map(c => this.quote(c))}) ${sql}`)
907907
}

hana/test/hana-functions.test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ describe('HANA native functions', () => {
1010
from: {ref: ['DUMMY']},
1111
columns: [{func: 'CURRENT_UTCTIMESTAMP', as: 'NO'}]
1212
}}
13-
13+
1414
const res = await cds.run(cqn)
15-
16-
expect(res.NO.match(/\.(\d\d\d)0000/)).not.to.be.null // default 3
15+
16+
expect(res.NO.match(/\.(\d\d\d)0{0,4}/)).not.to.be.null // default 3
1717
})
1818

1919
// HXE does not allow args

hana/test/service.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ module.exports = {
99
"encrypt": true,
1010
"sslValidateCertificate": false,
1111
"disableCloudRedirect": true,
12-
"driver": "hana-client"
12+
"driver": "hdb"
1313
}
1414
}

sqlite/test/general/stream.test.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,12 @@ describe('streaming', () => {
187187
const { Images } = cds.entities('test')
188188
const { data: stream } = await SELECT.one.from(Images).columns('data').where({ ID: 1 })
189189

190-
const changes = await UPDATE(Images).with({ data2: stream }).where({ ID: 3 })
191-
expect(changes).to.equal(1)
190+
const insert = async () => {
191+
const changes = await UPDATE(Images).with({ data2: stream }).where({ ID: 3 })
192+
expect(changes).to.equal(1)
193+
}
194+
if(cds.db.pools._factory.options.max > 1) await cds.tx(insert) // Stream over multiple transaction for `hdb` limitation
195+
else await insert()
192196

193197
const [{ data2: stream_ }] = await SELECT.from(Images).columns('data2').where({ ID: 3 })
194198
await checkSize(stream_)

test/compliance/DELETE.test.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ describe('DELETE', () => {
7676

7777
test('ref', async () => {
7878
const { Authors } = cds.entities('complex.associations')
79-
await INSERT.into(Authors).entries(new Array(9).fill().map((e,i) => ({ ID: 100+i, name: 'name'+i})))
79+
await INSERT.into(Authors).entries(new Array(9).fill().map((e, i) => ({ ID: 100 + i, name: 'name' + i })))
8080
const changes = await cds.run(DELETE.from(Authors))
8181
expect(changes | 0).to.be.eq(10, 'Ensure that all rows are affected') // 1 from csv, 9 newly added
8282
})
@@ -87,4 +87,9 @@ describe('DELETE', () => {
8787
throw new Error('not supported')
8888
})
8989
})
90+
91+
test('affected rows', async () => {
92+
const affectedRows = await DELETE.from('complex.associations.Books').where('ID = 4712')
93+
expect(affectedRows).to.be.eq(0)
94+
})
9095
})

test/compliance/DROP.test.js

-9
This file was deleted.

test/compliance/INSERT.test.js

+40-11
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,26 @@ describe('INSERT', () => {
1111
})
1212

1313
describe('entries', () => {
14-
test.skip('missing', () => {
15-
throw new Error('not supported')
14+
test('smart quoting', async () => {
15+
const { Order } = cds.entities('complex.keywords')
16+
const data = {
17+
ID: 1,
18+
alter: [
19+
{
20+
ID: 42,
21+
number: null,
22+
order_ID: 1,
23+
},
24+
{
25+
ID: 43,
26+
number: null,
27+
order_ID: 1,
28+
},
29+
],
30+
}
31+
await INSERT(data).into(Order)
32+
const select = await cds.run(cds.ql`SELECT from ${Order} { ID, alter { * } } where exists alter`)
33+
expect(select[0]).to.deep.eql(data)
1634
})
1735
})
1836

@@ -31,17 +49,28 @@ describe('INSERT', () => {
3149
})
3250

3351
describe('as', () => {
34-
test.skip('missing', () => {
35-
throw new Error('not supported')
52+
test('smart quoting', async () => {
53+
const { Alter, ASC } = cds.entities('complex.keywords')
54+
// fill other table first
55+
await cds.run(INSERT({ ID: 1, alias: 42 }).into(ASC))
56+
await INSERT.into(Alter)
57+
.columns(['ID', 'number'])
58+
.as(
59+
SELECT.from(ASC)
60+
.columns(['ID', 'alias'])
61+
.where({ ref: ['alias'] }, '=', { val: 42 }),
62+
)
63+
const select = await SELECT.from(Alter).where('number = 42')
64+
expect(select[0]).to.eql({ ID: 1, number: 42, order_ID: null })
3665
})
3766
})
3867

39-
describe('default values', () => {
40-
test('default values are generated', async () => {
41-
const { 'basic.literals.defaults': entity } = cds.db.entities
42-
await cds.run(INSERT.into(entity).entries({ID: 1}))
43-
const result = await cds.run(SELECT.from(entity, 1))
44-
expect(result).to.deep.eq({ ID: 1, boolean: false, integer: 0, nulls: null, string: ''})
45-
})
68+
test('InsertResult', async () => {
69+
const insert = INSERT.into('complex.associations.Books').entries({ ID: 5 })
70+
const affectedRows = await cds.db.run(insert)
71+
// affectedRows is an InsertResult, so we need to do lose comparison here, as strict will not work due to InsertResult
72+
expect(affectedRows == 1).to.be.eq(true)
73+
// InsertResult
74+
expect(affectedRows).not.to.include({ _affectedRows: 1 }) // lastInsertRowid not available on postgres
4675
})
4776
})

test/compliance/SELECT.test.js

+45-32
Original file line numberDiff line numberDiff line change
@@ -296,14 +296,13 @@ describe('SELECT', () => {
296296

297297
test('compare with DateTime column', async () => {
298298
const { dateTime: entity } = cds.entities('basic.literals')
299-
const dateTime = '1970-02-02T10:09:34Z'
300-
const timestamp = dateTime.slice(0, -1) + '.000Z'
301-
await DELETE.from(entity)
302-
await INSERT({ dateTime }).into(entity)
303-
const dateTimeMatches = await SELECT('dateTime').from(entity).where(`dateTime = `, dateTime)
304-
assert.strictEqual(dateTimeMatches.length, 1, 'Ensure that the dateTime column matches the dateTime value')
305-
const timestampMatches = await SELECT('dateTime').from(entity).where(`dateTime = `, timestamp)
306-
assert.strictEqual(timestampMatches.length, 1, 'Ensure that the dateTime column matches the timestamp value')
299+
const sel = SELECT('dateTime').from(entity)
300+
const [{ dateTime }] = await sel.clone()
301+
const timestamp = new Date(dateTime)
302+
303+
expect(await sel.clone().where(`dateTime = `, dateTime)).length(1)
304+
expect(await sel.clone().where(`dateTime = `, timestamp)).length(1)
305+
expect(await sel.clone().where(`dateTime = `, timestamp.toISOString())).length(1)
307306
})
308307

309308
test('combine expr with nested functions and other compare', async () => {
@@ -526,7 +525,7 @@ describe('SELECT', () => {
526525
let res
527526
try {
528527
res = await tx.run(query)
529-
} catch (err) {
528+
} catch (err) {
530529
if (tx.dbc.server.major < 4) return // not not is not supported by older HANA versions
531530
throw err
532531
}
@@ -617,7 +616,7 @@ describe('SELECT', () => {
617616

618617
test('static val', async () => {
619618
const { string } = cds.entities('basic.literals')
620-
const cqn = cds.ql`SELECT string FROM ${string} GROUP BY string,${1}`
619+
const cqn = cds.ql`SELECT string FROM ${string} GROUP BY string,${'1'}`
621620
const res = await cds.run(cqn)
622621
assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back')
623622
})
@@ -756,31 +755,39 @@ describe('SELECT', () => {
756755
const isSQLite = () => cds.db.options.impl === '@cap-js/sqlite'
757756

758757
const setMax = max => {
759-
let oldMax
758+
let oldMax, oldTimeout
760759
beforeAll(async () => {
761-
if (isSQLite()) return
760+
const options = cds.db.pools._factory.options
761+
oldMax = options.max
762+
oldTimeout = options.acquireTimeoutMillis
763+
764+
if (isSQLite()) {
765+
oldTimeout = cds.db.pools._factory.options.acquireTimeoutMillis
766+
cds.db.pools.undefined._config.acquireTimeoutMillis =
767+
cds.db.pools._factory.options.acquireTimeoutMillis = 1000
768+
return
769+
}
762770
await cds.db.disconnect()
763-
oldMax = cds.db.pools._factory.options.max
764-
cds.db.pools._factory.options.max = max
771+
772+
options.max = max
773+
options.acquireTimeoutMillis = 1000
765774
})
766775

767776
afterAll(async () => {
768-
if (isSQLite()) return
769-
cds.db.pools._factory.options.max = oldMax
770-
})
771-
}
777+
const options = cds.db.pools._factory.options
772778

773-
let oldTimeout
774-
beforeAll(async () => {
775-
oldTimeout = cds.db.pools._factory.options.acquireTimeoutMillis
776-
cds.db.pools.undefined._config.acquireTimeoutMillis =
777-
cds.db.pools._factory.options.acquireTimeoutMillis = 1000
778-
})
779+
if (isSQLite()) {
780+
oldTimeout = cds.db.pools._factory.options.acquireTimeoutMillis
781+
cds.db.pools.undefined._config.acquireTimeoutMillis =
782+
cds.db.pools._factory.options.acquireTimeoutMillis = 1000
783+
return
784+
}
785+
await cds.db.disconnect()
779786

780-
afterAll(() => {
781-
cds.db.pools.undefined._config.acquireTimeoutMillis =
782-
cds.db.pools._factory.options.acquireTimeoutMillis = oldTimeout
783-
})
787+
options.max = oldMax
788+
options.acquireTimeoutMillis = oldTimeout
789+
})
790+
}
784791

785792
describe('pool max = 1', () => {
786793
setMax(1)
@@ -1524,20 +1531,26 @@ describe('SELECT', () => {
15241531
)
15251532
})
15261533

1534+
const os = require('os')
15271535
for (let type of ['ref', 'val', 'func', 'xpr', 'list', 'SELECT']) {
15281536
describe(`${type}: ${unified[type].length}`, () => {
15291537
test('execute', async () => {
1530-
// const batchCount = Math.min(os.availableParallelism() - 1, cds.db.factory.options.max || 1)
1531-
const batches = new Array(1).fill('')
1538+
const batchCount = Math.min(os.availableParallelism() - 1, cds.db.factory.options.max || 1)
1539+
const batches = new Array(batchCount).fill('')
15321540
const iterator = typeof unified[type] === 'function' ? unified[type]() : unified[type][Symbol.iterator]()
15331541

15341542
const { [targetName]: target } = cds.entities
1535-
await Promise.all(batches.map(() => cds.tx(async (tx) => {
1543+
await Promise.all(batches.map((_, i) => cds.tx(async (tx) => {
15361544
for (const t of iterator) {
15371545
// limit(0) still validates that the query is valid, but improves test execution time
15381546
await tx.run(SELECT([t]).from(target).limit(0))
15391547
}
1540-
})))
1548+
})
1549+
.catch(err => {
1550+
if (err.name === 'TimeoutError') return
1551+
throw err
1552+
}))
1553+
)
15411554
})
15421555
})
15431556
}

test/compliance/UPDATE.test.js

+35
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,34 @@ describe('UPDATE', () => {
3030
const result = await SELECT.one.from(number)
3131
expect(result.integer32).to.equal(3)
3232
})
33+
34+
test('smart quoting', async () => {
35+
const { Order } = cds.entities('complex.keywords')
36+
const data = {
37+
ID: 1,
38+
alter: [
39+
{
40+
ID: 42,
41+
number: null,
42+
order_ID: 1,
43+
},
44+
{
45+
ID: 43,
46+
number: null,
47+
order_ID: 1,
48+
},
49+
],
50+
}
51+
await INSERT(data).into(Order)
52+
const select = await cds.run(cds.ql`SELECT from ${Order} { ID, alter { * } } where exists alter`)
53+
expect(select[0]).to.deep.eql(data)
54+
55+
data.alter.forEach(e => (e.number = 99)) // change data
56+
await UPDATE.entity(Order).with(data).where('exists alter')
57+
58+
const selectAfterChange = await cds.run(cds.ql`SELECT from ${Order} { ID, alter { * } } where exists alter`)
59+
expect(selectAfterChange[0]).to.deep.eql(data)
60+
})
3361
})
3462

3563
describe('with', () => {
@@ -142,4 +170,11 @@ describe('UPDATE', () => {
142170
await UPDATE(BooksUnique).data(data)
143171
})
144172
})
173+
174+
test('affected rows', async () => {
175+
const { count } = await SELECT.one`count(*)`.from('complex.associations.Books')
176+
177+
const affectedRows = await UPDATE.entity('complex.associations.Books').data({ title: 'Book' })
178+
expect(affectedRows).to.be.eq(count)
179+
})
145180
})

test/compliance/UPSERT.test.js

+21-7
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ describe('UPSERT', () => {
77
describe('into', () => {
88
test('Apply default for keys before join to existing data', async () => {
99
const { keys } = cds.entities('basic.common')
10-
// HXE cannot handle the default key logic
11-
await INSERT([/*{ id: 0, data: 'insert' },*/ { id: 0, default: 'overwritten', data: 'insert' }]).into(keys)
10+
// HXE cannot handle the default key logic when using @sap/hana-client
11+
await INSERT([{ id: 0, data: 'insert' }, { id: 0, default: 'overwritten', data: 'insert' }]).into(keys)
1212
const insert = await SELECT.from(keys)
1313

14-
await UPSERT([/*{ id: 0, data: 'upsert' },*/ { id: 0, default: 'overwritten', data: 'upsert' }]).into(keys)
14+
await UPSERT([{ id: 0, data: 'upsert' }, { id: 0, default: 'overwritten', data: 'upsert' }]).into(keys)
1515
const upsert = await SELECT.from(keys)
1616

1717
for (let i = 0; i < insert.length; i++) {
@@ -26,8 +26,12 @@ describe('UPSERT', () => {
2626
})
2727

2828
describe('entries', () => {
29-
test.skip('missing', () => {
30-
throw new Error('not supported')
29+
test('smart quoting', async () => {
30+
const { ASC } = cds.entities('complex.keywords')
31+
await UPSERT.into(ASC).entries({ ID: 42, select: 4711 })
32+
await UPSERT.into(ASC).entries({ ID: 42, alias: 9 })
33+
select = await SELECT.one.from(ASC).where('ID = 42')
34+
expect(select).to.eql({ ID: 42, select: 4711, alias: 9 })
3135
})
3236
})
3337

@@ -39,8 +43,13 @@ describe('UPSERT', () => {
3943
})
4044

4145
describe('rows', () => {
42-
test.skip('missing', () => {
43-
throw new Error('not supported')
46+
test('smart quoting', async () => {
47+
const { ASC } = cds.entities('complex.keywords')
48+
await UPSERT.into(ASC)
49+
.columns(['ID', 'select'])
50+
.rows([[42, 4711]])
51+
let select = await SELECT.one.from(ASC, ['ID', 'select']).where('ID = 42')
52+
expect(select).to.eql({ ID: 42, select: 4711 })
4453
})
4554
})
4655
})
@@ -50,4 +59,9 @@ describe('UPSERT', () => {
5059
throw new Error('not supported')
5160
})
5261
})
62+
63+
test('affected row', async () => {
64+
const affectedRows = await UPSERT.into('complex.associations.Books').entries({ ID: 9999999, title: 'Book' })
65+
expect(affectedRows).to.be.eq(1)
66+
})
5367
})

0 commit comments

Comments
 (0)