diff --git a/sqlite/test/queries-without-models.test.js b/sqlite/test/queries-without-models.test.js index b27ed3265..56fd1abfb 100644 --- a/sqlite/test/queries-without-models.test.js +++ b/sqlite/test/queries-without-models.test.js @@ -1,5 +1,5 @@ const impl = require.resolve('../index') -const cds = require('@sap/cds') +const cds = require('../../test/cds.js') const { expect } = cds.test // eslint-disable-next-line no-global-assign diff --git a/sqlite/test/service.js b/sqlite/test/service.js new file mode 100644 index 000000000..d024518fe --- /dev/null +++ b/sqlite/test/service.js @@ -0,0 +1,8 @@ +const path = require('node:path') + +module.exports = { + "impl": "@cap-js/sqlite", + "credentials": { + "database": path.resolve(__dirname,"../../test/db.sqlite") + } +} diff --git a/sqlite/test/service.json b/sqlite/test/service.json deleted file mode 100644 index 38460c681..000000000 --- a/sqlite/test/service.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "impl": "@cap-js/sqlite" -} diff --git a/test/cds.js b/test/cds.js index 73e88e032..a4ec44fb8 100644 --- a/test/cds.js +++ b/test/cds.js @@ -51,74 +51,6 @@ cds.test = Object.setPrototypeOf(function () { let ret = cdsTest(...arguments) - global.beforeAll(async () => { - // Setup isolation after cds has prepare the project (e.g. cds.model) - if (ret.data._autoIsolation) { - await ret.data.isolate() - } - }) - - let isolate = null - - ret.data.isolate = - ret.data.isolate || - async function (db) { - if (!db) db = await cds.connect.to('db') - - // If database driver supports database and tenant isolation run test in isolation - if (typeof db.database === 'function' && typeof db.tenant === 'function') { - const { createHash } = require('crypto') - const hash = createHash('sha1') - const isolateName = (require.main.filename || 'test_tenant') + isolateCounter++ - hash.update(isolateName) - ret.data.isolation = isolate = { - // Create one database for each overall test execution - database: process.env.TRAVIS_JOB_ID || process.env.GITHUB_RUN_ID || require('os').userInfo().username || 'test_db', - // Create one tenant for each test suite - tenant: 'T' + hash.digest('hex'), - } - - // Create new database isolation - await db.database(isolate) - - // Create new tenant isolation in database - await db.tenant(isolate) - - ret.credentials = db.options.credentials - } - } - - ret.data.autoIsolation = - ret.data.autoIsolation || - function (enabled) { - this._autoIsolation = enabled - return this - } - ret.data.autoIsolation(true) - - global.beforeAll(async () => { - if (ret.data._autoIsolation && !ret.data._deployed) { - ret.data._deployed = cds.deploy(cds.options.from[0]) - await ret.data._deployed - } - }) - - global.afterAll(async () => { - // Clean database connection pool - await cds.db?.disconnect?.() - - if (isolate) { - await cds.db?.tenant?.(isolate, true) - } - - // Clean cache - delete cds.services._pending.db - delete cds.services.db - delete cds.db - delete cds.model - global.cds.resolve.cache = {} - }) - ret.expect = cdsTest.expect return ret }, cdsTest.constructor.prototype) diff --git a/test/compliance/CREATE.test.js b/test/compliance/CREATE.test.js index ae276ace7..4c5d1c23e 100644 --- a/test/compliance/CREATE.test.js +++ b/test/compliance/CREATE.test.js @@ -166,21 +166,8 @@ const dataTest = async function (entity, table, type, obj) { } } -describe('CREATE', () => { - // TODO: reference to ./definitions.test.js - - // Set cds.root before requiring cds.Service as it resolves and caches package.json - // Call default cds.test API - const { data } = cds.test(__dirname + '/resources') - // Prevent deployment - /* skipping deploy causes issues with running all compliance tests in a single suite - cds.deploy = () => ({ - to:() => {return cds.db || cds.connect('db')}, - then:() => {return cds.db || cds.connect('db')} - }) - // */ - data.autoIsolation(true) - data._deployed = true // Skip automatic deployment +describe.skip('CREATE', () => { + cds.test(__dirname + '/resources') // Load model before test suite to generate test suite from model definition const model = cds.load(__dirname + '/resources/db', { sync: true }) diff --git a/test/compliance/DELETE.test.js b/test/compliance/DELETE.test.js index f5ce0cd46..600210901 100644 --- a/test/compliance/DELETE.test.js +++ b/test/compliance/DELETE.test.js @@ -6,9 +6,7 @@ const RootPWithKeys = 'complex.RootPWithKeys' const ChildPWithWhere = 'complex.ChildPWithWhere' describe('DELETE', () => { - const { data, expect } = cds.test(__dirname + '/resources') - data.autoIsolation(true) - data.autoReset() + const { expect } = cds.test(__dirname + '/resources') describe('from', () => { describe('deep', () => { @@ -47,6 +45,10 @@ describe('DELETE', () => { expect(insertsResp[0].affectedRows).to.be.eq(1) }) + after(async () => { + await DELETE.from(Root).where({ ID: [5, 6, 7, 8, 9] }) + }) + test('on root with keys', async () => { const deepDelete = await cds.run(DELETE.from(RootPWithKeys).where({ ID: 5 })) expect(deepDelete).to.be.eq(1) @@ -77,8 +79,8 @@ describe('DELETE', () => { test('ref', async () => { const { Authors } = cds.entities('complex.associations') await INSERT.into(Authors).entries(new Array(9).fill().map((e, i) => ({ ID: 100 + i, name: 'name' + i }))) - const changes = await cds.run(DELETE.from(Authors)) - expect(changes | 0).to.be.eq(10, 'Ensure that all rows are affected') // 1 from csv, 9 newly added + const changes = await cds.run(DELETE.from(Authors).where(`ID BETWEEN 100 AND 109`)) + expect(changes | 0).to.be.eq(9, 'Ensure that all rows are affected') }) }) diff --git a/test/compliance/INSERT.test.js b/test/compliance/INSERT.test.js index 1947b8ac9..094ad7a7b 100644 --- a/test/compliance/INSERT.test.js +++ b/test/compliance/INSERT.test.js @@ -3,8 +3,7 @@ const { text } = require('stream/consumers') const { PassThrough, Readable } = require('stream') describe('INSERT', () => { - const { data, expect } = cds.test(__dirname + '/resources') - data.autoIsolation(true) + const { expect } = cds.test(__dirname + '/resources') describe('into', () => { test.skip('missing', () => { @@ -13,19 +12,32 @@ describe('INSERT', () => { }) describe('entries', () => { + const list = [] + const where = [{ ref: ['uuid'] }, 'in', { list }] let genCount = 0 const gen = function* () { - for (var i = 0; i < 100; i++) - yield { uuid: cds.utils.uuid() } + for (var i = 0; i < 100; i++) { + const row = { uuid: cds.utils.uuid() } + list.push({ val: row.uuid }) + yield row + } genCount += i } + after(async () => { + const { uuid } = cds.entities('basic.literals') + const { Order } = cds.entities('complex.keywords') + + await DELETE.from(uuid).where(where) + await DELETE.from(Order).where({ ID: 1 }) + }) + test('array', async () => { const { uuid } = cds.entities('basic.literals') await INSERT([...gen()]).into(uuid) - const result = await SELECT.from(uuid) + const result = await SELECT.from(uuid).where(where) expect(result.length).to.eq(genCount) }) @@ -34,7 +46,7 @@ describe('INSERT', () => { await INSERT(gen()).into(uuid) - const result = await SELECT.from(uuid) + const result = await SELECT.from(uuid).where(where) expect(result.length).to.eq(genCount) }) @@ -43,7 +55,7 @@ describe('INSERT', () => { await INSERT(Readable.from(gen())).into(uuid) - const result = await SELECT.from(uuid) + const result = await SELECT.from(uuid).where(where) expect(result.length).to.eq(genCount) }) @@ -63,7 +75,7 @@ describe('INSERT', () => { await INSERT(Readable.from(raw(gen()), { objectMode: false })).into(uuid) - const result = await SELECT.from(uuid) + const result = await SELECT.from(uuid).where(where) expect(result.length).to.eq(genCount) }) @@ -143,7 +155,7 @@ describe('INSERT', () => { ], } await INSERT(data).into(Order) - const select = await cds.run(cds.ql`SELECT from ${Order} { ID, alter { * } } where exists alter`) + const select = await cds.run(cds.ql`SELECT from ${Order} { ID, alter { * } } where ID = ${1} and exists alter`) expect(select[0]).to.deep.eql(data) }) }) @@ -163,6 +175,13 @@ describe('INSERT', () => { }) describe('from', () => { + after(async () => { + const { Alter, ASC } = cds.entities('complex.keywords') + + await DELETE.from(ASC).where({ ID: 1 }) + await DELETE.from(Alter).where({ ID: 1 }) + }) + test('smart quoting', async () => { const { Alter, ASC } = cds.entities('complex.keywords') // fill other table first @@ -179,6 +198,10 @@ describe('INSERT', () => { }) }) + after(async () => { + await DELETE.from('complex.associations.Books').where({ ID: 5 }) + }) + test('InsertResult', async () => { const insert = INSERT.into('complex.associations.Books').entries({ ID: 5 }) const affectedRows = await cds.db.run(insert) @@ -188,15 +211,19 @@ describe('INSERT', () => { expect(affectedRows).not.to.include({ _affectedRows: 1 }) // lastInsertRowid not available on postgres }) + after(async () => { + await DELETE.from('basic.common.dollar_now_default').where({ ID: [5, 6] }) + }) + test('default $now adds current tx timestamp in correct format', async () => { await cds.tx(async tx => { // the statements are run explicitly in sequential order to ensure current_timestamp would create different timestamps - await tx.run(INSERT.into('basic.common.dollar_now_default').entries({ id: 5 })) - await tx.run(INSERT.into('basic.common.dollar_now_default').entries({ id: 6 })) + await tx.run(INSERT.into('basic.common.dollar_now_default').entries({ ID: 5 })) + await tx.run(INSERT.into('basic.common.dollar_now_default').entries({ ID: 6 })) }) const result = await SELECT.from('basic.common.dollar_now_default') - + expect(result.length).to.eq(2) expect(result[0].date).to.match(/^\d{4}-\d{2}-\d{2}$/) expect(result[0].date).to.eq(result[1].date) diff --git a/test/compliance/SELECT.test.js b/test/compliance/SELECT.test.js index 45c40d292..292a8eabe 100644 --- a/test/compliance/SELECT.test.js +++ b/test/compliance/SELECT.test.js @@ -110,20 +110,20 @@ describe('SELECT', () => { test('select func', async () => { const { string } = cds.entities('basic.projection') - const cqn = cds.ql`SELECT count() FROM ${string}` + const cqn = cds.ql`SELECT count() FROM ${string} WHERE string in (${'yes'},${'no'})` const res = await cds.run(cqn) assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') - assert.strictEqual(res[0].count, 3, 'Ensure that the function is applied') + assert.strictEqual(res[0].count, 2, 'Ensure that the function is applied') }) test('select funcs', async () => { const { string } = cds.entities('basic.projection') - const cqn = cds.ql`SELECT min(string),max(string),count() FROM ${string}` + const cqn = cds.ql`SELECT min(string),max(string),count() FROM ${string} WHERE string in (${'yes'},${'no'})` const res = await cds.run(cqn) assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') assert.strictEqual(res[0].min, 'no', 'Ensure that the function is applied') assert.strictEqual(res[0].max, 'yes', 'Ensure that the function is applied') - assert.strictEqual(res[0].count, 3, 'Ensure that the function is applied') + assert.strictEqual(res[0].count, 2, 'Ensure that the function is applied') }) test('select funcs (duplicates)', async () => { @@ -134,10 +134,10 @@ describe('SELECT', () => { test('select func alias', async () => { const { string } = cds.entities('basic.projection') - const cqn = cds.ql`SELECT count() as count_renamed FROM ${string}` + const cqn = cds.ql`SELECT count() as count_renamed FROM ${string} where string in (${'yes'},${'no'})` const res = await cds.run(cqn) assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') - assert.strictEqual(res[0].count_renamed, 3, 'Ensure that the function is applied and aliased') + assert.strictEqual(res[0].count_renamed, 2, 'Ensure that the function is applied and aliased') }) test('select funcs alias', async () => { @@ -148,7 +148,8 @@ describe('SELECT', () => { count(1) as count_one, count(string) as count_string, count(char) as count_char - FROM ${string}` + FROM ${string} + WHERE string in (${'yes'},${'no'}) or string is null` const res = await cds.run(cqn) assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') assert.strictEqual(res[0].count_star, 3, 'Ensure that the function is applied and aliased') @@ -173,7 +174,10 @@ describe('SELECT', () => { test('select xpr', async () => { // REVISIT: Make HANAService ANSI SQL compliant by wrapping compare expressions into case statements for columns const { string } = cds.entities('basic.projection') - const cqn = cds.ql`SELECT (${'yes'} = string) as xpr : cds.Boolean FROM ${string} order by string` + const cqn = cds.ql`SELECT (${'yes'} = string) as xpr : cds.Boolean + FROM ${string} + WHERE string is null or string = ${'yes'} or string = ${'no'} + ORDER BY string` const res = await cds.run(cqn) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') assert.equal(res[0].xpr, null) @@ -183,37 +187,34 @@ describe('SELECT', () => { test('select calculation', async () => { const { string } = cds.entities('basic.projection') - const cqn = cds.ql`SELECT (string || string) as string FROM ${string}` + const cqn = cds.ql`SELECT (string || string) as string FROM ${string} WHERE string in (${'yes'},${'no'})` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual(res.length, 2, 'Ensure that all rows are coming back') }) test('select sub select', async () => { const { string } = cds.entities('basic.projection') - const cqn = cds.ql`SELECT (SELECT string FROM ${string} as sub WHERE sub.string = root.string) as string FROM ${string} as root` + const cqn = cds.ql`SELECT (SELECT string FROM ${string} as sub WHERE sub.string = root.string) as string FROM ${string} as root WHERE string in (${'yes'},${'no'})` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual(res.length, 2, 'Ensure that all rows are coming back') }) test('select 200 columns', async () => { const { string } = cds.entities('basic.projection') - const cqn = SELECT(new Array(200).fill().map((_, i) => ({ as: `${i}`, val: i }))).from(string) + const cqn = SELECT.one(new Array(200).fill().map((_, i) => ({ as: `${i}`, val: i }))).from(string) const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') - assert.equal(Object.keys(res[0]).length, cqn.SELECT.columns.length) + assert.equal(Object.keys(res).length, cqn.SELECT.columns.length) }) const nulls = length => new Array(length).fill().map((_, i) => ({ as: `null${i}`, val: null })) test('select 200 null columns', async () => { const { string } = cds.entities('basic.projection') - const cqn = SELECT(nulls(200)).from(string) + const cqn = SELECT.one(nulls(200)).from(string) const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') // ensure that all null values are returned - assert.strictEqual(Object.keys(res[0]).length, 200) - res[0] + assert.strictEqual(Object.keys(res).length, 200) cqn.SELECT.columns.forEach((c) => { - assert.strictEqual(res[0][c.as], null) + assert.strictEqual(res[c.as], null) }) }) @@ -267,7 +268,7 @@ describe('SELECT', () => { const { string } = cds.entities('basic.literals') const cqn = cds.ql`SELECT FROM ${string} excluding { string }` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.notEqual(res.length, 0, 'Ensure that rows are coming back') assert.strictEqual('string' in res[0], false, 'Ensure that excluded columns are missing') }) @@ -275,7 +276,7 @@ describe('SELECT', () => { const { string } = cds.entities('basic.literals') const cqn = cds.ql`SELECT FROM ${string} { * } excluding { string }` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.notEqual(res.length, 0, 'Ensure that rows are coming back') assert.strictEqual('string' in res[0], false, 'Ensure that excluded columns are missing') }) @@ -283,7 +284,7 @@ describe('SELECT', () => { const { string } = cds.entities('basic.literals') const cqn = cds.ql`SELECT FROM ${string} { *, ${'extra'} } excluding { string }` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.notEqual(res.length, 0, 'Ensure that rows are coming back') assert.strictEqual('string' in res[0], false, 'Ensure that excluded columns are missing') assert.strictEqual('extra' in res[0], true, 'Ensure that specific columns are included') }) @@ -292,7 +293,7 @@ describe('SELECT', () => { const { string } = cds.entities('basic.literals') const cqn = cds.ql`SELECT FROM ${string} { string, char } excluding { string }` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.notEqual(res.length, 0, 'Ensure that rows are coming back') assert.strictEqual('string' in res[0], true, 'Ensure that specific columns are included') }) }) @@ -303,7 +304,7 @@ describe('SELECT', () => { const cqn = cds.ql`SELECT bool FROM ${globals}` cqn.SELECT.where = [] const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.notEqual(res.length, 0, 'Ensure that rows are coming back') }) test('compare with DateTime column', async () => { @@ -614,37 +615,37 @@ describe('SELECT', () => { describe('groupby', () => { test('single ref', async () => { const { string } = cds.entities('basic.literals') - const cqn = cds.ql`SELECT string FROM ${string} GROUP BY string` + const cqn = cds.ql`SELECT string FROM ${string} WHERE string in (${'yes'},${'no'}) GROUP BY string` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual(res.length, 2, 'Ensure that all rows are coming back') }) test('multiple refs', async () => { const { string } = cds.entities('basic.literals') - const cqn = cds.ql`SELECT string FROM ${string} GROUP BY string, char` + const cqn = cds.ql`SELECT string FROM ${string} WHERE string in (${'yes'},${'no'}) GROUP BY string, char` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual(res.length, 2, 'Ensure that all rows are coming back') }) test('static val', async () => { const { string } = cds.entities('basic.literals') - const cqn = cds.ql`SELECT string FROM ${string} GROUP BY string,${'1'}` + const cqn = cds.ql`SELECT string FROM ${string} WHERE string in (${'yes'},${'no'}) GROUP BY string,${'1'}` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual(res.length, 2, 'Ensure that all rows are coming back') }) test('func', async () => { const { string } = cds.entities('basic.literals') - const cqn = cds.ql`SELECT string FROM ${string} GROUP BY string,now()` + const cqn = cds.ql`SELECT string FROM ${string} WHERE string in (${'yes'},${'no'}) GROUP BY string,now()` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual(res.length, 2, 'Ensure that all rows are coming back') }) test('func', async () => { const { string } = cds.entities('basic.literals') - const cqn = cds.ql`SELECT string FROM ${string} GROUP BY string,now()` + const cqn = cds.ql`SELECT string FROM ${string} WHERE string in (${'yes'},${'no'}) GROUP BY string,now()` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual(res.length, 2, 'Ensure that all rows are coming back') }) test('navigation with duplicate identifier in path', async () => { @@ -708,7 +709,6 @@ describe('SELECT', () => { const { string } = cds.entities('basic.literals') const cqn = cds.ql`SELECT string FROM ${string} ORDER BY string asc` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') const sorted = [...res].sort((a, b) => _localeSort(a.string, b.string)) assert.deepEqual(res, sorted, 'Ensure that all rows are in the correct order') }) @@ -717,7 +717,6 @@ describe('SELECT', () => { const { string } = cds.entities('basic.literals') const cqn = cds.ql`SELECT string FROM ${string} ORDER BY string desc` const res = await cds.run(cqn) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') const sorted = [...res].sort((a, b) => _localeSort(b.string, a.string)) assert.deepEqual(res, sorted, 'Ensure that all rows are in the correct order') }) diff --git a/test/compliance/UPDATE.test.js b/test/compliance/UPDATE.test.js index 8c90cb110..500a2130f 100644 --- a/test/compliance/UPDATE.test.js +++ b/test/compliance/UPDATE.test.js @@ -1,11 +1,31 @@ const cds = require('../cds.js') const Books = 'complex.associations.Books' const BooksUnique = 'complex.uniques.Books' +const PagesUnique = 'complex.uniques.Pages' describe('UPDATE', () => { - const { data, expect } = cds.test(__dirname + '/resources') - data.autoIsolation(true) - data.autoReset() + const { expect } = cds.test(__dirname + '/resources') + + const uniques = { + number: { integer64: '201503001904' }, + string: { medium: 'UPDATE.test.js' }, + } + + before(async () => { + const { string, number } = cds.entities('basic.literals') + + await INSERT({ string: 'initial', ...uniques.string }).into(string) + await INSERT({ integer32: 0, ...uniques.number }).into(number) + }) + + after(async () => { + const { string, number } = cds.entities('basic.literals') + const { Order } = cds.entities('complex.keywords') + + await DELETE.from(number).where(uniques.number) + await DELETE.from(string).where(uniques.string) + await DELETE.from(Order).where({ ID: [2015, 300, 1904] }) + }) describe('entity', () => { test('string', async () => { @@ -18,44 +38,43 @@ describe('UPDATE', () => { describe('data', () => { test('string', async () => { const { string } = cds.entities('basic.literals') - await UPDATE(string).data({ string: 'updated' }) - const result = await SELECT.one.from(string) + await UPDATE(string).data({ string: 'updated' }).where(uniques.string) + const result = await SELECT.one.from(string).where(uniques.string) expect(result.string).to.equal('updated') }) test('number', async () => { const { number } = cds.entities('basic.literals') - await INSERT({ integer32: 0 }).into(number) - await UPDATE(number).data({ integer32: 3 }) - const result = await SELECT.one.from(number) + await UPDATE(number).data({ integer32: 3 }).where(uniques.number) + const result = await SELECT.one.from(number).where(uniques.number) expect(result.integer32).to.equal(3) }) test('smart quoting', async () => { const { Order } = cds.entities('complex.keywords') const data = { - ID: 1, + ID: 2015, alter: [ { - ID: 42, + ID: 300, number: null, - order_ID: 1, + order_ID: 2015, }, { - ID: 43, + ID: 1904, number: null, - order_ID: 1, + order_ID: 2015, }, ], } await INSERT(data).into(Order) - const select = await cds.run(cds.ql`SELECT from ${Order} { ID, alter { * } } where exists alter`) + const select = await cds.run(cds.ql`SELECT from ${Order} { ID, alter { * } } where ID=2015 and exists alter`) expect(select[0]).to.deep.eql(data) data.alter.forEach(e => (e.number = 99)) // change data - await UPDATE.entity(Order).with(data).where('exists alter') + await UPDATE.entity(Order).with(data).where('ID=2015 and exists alter') - const selectAfterChange = await cds.run(cds.ql`SELECT from ${Order} { ID, alter { * } } where exists alter`) + const selectAfterChange = await cds.run(cds.ql`SELECT from ${Order} { ID, alter { * } } where ID=2015 and exists alter`) expect(selectAfterChange[0]).to.deep.eql(data) }) }) @@ -63,30 +82,30 @@ describe('UPDATE', () => { describe('with', () => { test('val', async () => { const { string } = cds.entities('basic.literals') - await UPDATE(string).with({ string: { val: 'updated' } }) - const result = await SELECT.one.from(string) + await UPDATE(string).with({ string: { val: 'updated' } }).where(uniques.string) + const result = await SELECT.one.from(string).where(uniques.string) expect(result.string).to.equal('updated') }) test('xpr', async () => { const { number } = cds.entities('basic.literals') - await INSERT({ integer32: 1 }).into(number) - await UPDATE(number).with({ integer32: { xpr: [{ ref: ['integer32'] }, '+', { val: 2 }] } }) - const result = await SELECT.one.from(number) + await UPDATE(number).data({ integer32: 1 }).where(uniques.number) + await UPDATE(number).with({ integer32: { xpr: [{ ref: ['integer32'] }, '+', { val: 2 }] } }).where(uniques.number) + const result = await SELECT.one.from(number).where(uniques.number) expect(result.integer32).to.equal(3) }) test('func', async () => { const { string } = cds.entities('basic.literals') - await UPDATE(string).with({ string: { func: 'concat', args: [{ val: 'a' }, { val: 'b' }] } }) - const result = await SELECT.one.from(string) + await UPDATE(string).with({ string: { func: 'concat', args: [{ val: 'a' }, { val: 'b' }] } }).where(uniques.string) + const result = await SELECT.one.from(string).where(uniques.string) expect(result.string).to.equal('ab') }) test('non existing values', async () => { const { string } = cds.entities('basic.literals') try { - await UPDATE(string).with({ nonExisting: { val: 'not updated' } }) + await UPDATE(string).with({ nonExisting: { val: 'not updated' } }).where(uniques.string) // should not get here expect(0).to.be(1) } catch (error) { @@ -101,30 +120,38 @@ describe('UPDATE', () => { const { string } = cds.entities('basic.literals') await UPDATE(string) .data({ string: 'updated' }) - .with({ medium: { func: 'concat', args: [{ val: 'a' }, { val: 'b' }] } }) - const result = await SELECT.one.from(string) + .with({ short: { func: 'concat', args: [{ val: 'a' }, { val: 'b' }] } }) + .where(uniques.string) + const result = await SELECT.one.from(string).where(uniques.string) expect(result.string).to.equal('updated') - expect(result.medium).to.equal('ab') + expect(result.short).to.equal('ab') }) test('number', async () => { const { number } = cds.entities('basic.literals') - await INSERT({ integer32: 0 }).into(number) + await UPDATE(number) + .data({ integer32: 0 }) + .where(uniques.number) await UPDATE(number) .data({ integer32: 1 }) - .with({ integer64: { xpr: [{ ref: ['integer32'] }, '+', { val: 2 }] } }) - const result = await SELECT.one.from(number) + .with({ integer16: { xpr: [{ ref: ['integer32'] }, '+', { val: 2 }] } }) + .where(uniques.number) + const result = await SELECT.one.from(number).where(uniques.number) expect(result.integer32).to.equal(1) - expect(result.integer64).to.equal('2') + expect(result.integer16).to.equal(2) }) }) describe('where', () => { + after(async () => { + await DELETE.from(Books).where({ ID: [2015, 300] }) + }) + test('flat with or on key', async () => { const insert = await cds.run( INSERT.into(Books).entries([ - { ID: 5, title: 'foo' }, - { ID: 6, title: 'bar' }, + { ID: 2015, title: 'foo' }, + { ID: 300, title: 'bar' }, ]), ) expect(insert.affectedRows).to.equal(2) @@ -132,26 +159,31 @@ describe('UPDATE', () => { const update = await cds.run( UPDATE.entity(Books) .set({ title: 'foo' }) - .where({ ID: 5, or: { ID: 6 } }), + .where({ ID: 2015, or: { ID: 300 } }), ) expect(update).to.equal(2) }) }) describe('uniques in deep updates', () => { + after(async () => { + await DELETE.from(PagesUnique).where({ ID: [300, 1904, 1503, 201503] }) + await DELETE.from(BooksUnique).where({ ID: 2015 }) + }) + test('2nd level unique constraints', async () => { // number must be unique for each book const data = { - ID: 1, + ID: 2015, title: 'foo', pages: [ // Set both numbers to the same value to be conflicting - { ID: 1, number: 0 }, - { ID: 2, number: 0 }, + { ID: 300, number: 0 }, + { ID: 1904, number: 0 }, ], } - await DELETE.from(BooksUnique).where(`ID=${1}`) + await DELETE.from(BooksUnique).where(`ID=${2015}`) await expect(INSERT(data).into(BooksUnique)).rejected // Update the numbers to be non conflicting @@ -160,21 +192,14 @@ describe('UPDATE', () => { await INSERT(data).into(BooksUnique) // Create new entries with conflicting numbers - data.pages[0].ID = 3 - data.pages[1].ID = 4 - await UPDATE(BooksUnique).data(data) // first, old entries are deleted, so no violation + data.pages[0].ID = 1503 + data.pages[1].ID = 19 + await UPDATE(BooksUnique).data(data).where(`ID = ${data.ID}`) // first, old entries are deleted, so no violation - data.pages[0].ID = 5 + data.pages[0].ID = 201503 data.pages[0].number = 1 // would fail without the update below first data.pages[1].number = 999 - await UPDATE(BooksUnique).data(data) + await UPDATE(BooksUnique).data(data).where(`ID = ${data.ID}`) }) }) - - test('affected rows', async () => { - const { count } = await SELECT.one`count(*)`.from('complex.associations.Books') - - const affectedRows = await UPDATE.entity('complex.associations.Books').data({ title: 'Book' }) - expect(affectedRows).to.be.eq(count) - }) }) diff --git a/test/compliance/UPSERT.test.js b/test/compliance/UPSERT.test.js index 02977cc44..34250bc2f 100644 --- a/test/compliance/UPSERT.test.js +++ b/test/compliance/UPSERT.test.js @@ -1,23 +1,38 @@ const cds = require('../cds.js') describe('UPSERT', () => { - const { data, expect } = cds.test(__dirname + '/resources') - data.autoIsolation(true) + const { expect } = cds.test(__dirname + '/resources') + const uniques = { + keys: { ID: 304110 }, + ASC: { ID: 304110 }, + Books: { ID: 304110 }, + } + + after(async () => { + const { keys } = cds.entities('basic.common') + const { ASC } = cds.entities('complex.keywords') + const { Books } = cds.entities('complex.associations') + + await DELETE.from(keys).where(uniques.keys) + await DELETE.from(ASC).where(uniques.ASC) + await DELETE.from(Books).where(uniques.Books) + }) describe('into', () => { test('Apply default for keys before join to existing data', async () => { const { keys } = cds.entities('basic.common') + // HXE cannot handle the default key logic when using @sap/hana-client - await INSERT([{ id: 0, data: 'insert' }, { id: 0, default: 'overwritten', data: 'insert' }]).into(keys) + await INSERT([{ ...uniques.keys, data: 'insert' }, { ...uniques.keys, default: 'overwritten', data: 'insert' }]).into(keys) const insert = await SELECT.from(keys) - await UPSERT([{ id: 0, data: 'upsert' }, { id: 0, default: 'overwritten', data: 'upsert' }]).into(keys) + await UPSERT([{ ...uniques.keys, data: 'upsert' }, { ...uniques.keys, default: 'overwritten', data: 'upsert' }]).into(keys) const upsert = await SELECT.from(keys) for (let i = 0; i < insert.length; i++) { const ins = insert[i] const ups = upsert[i] - expect(ups.id).to.eq(ins.id) + expect(ups.ID).to.eq(ins.ID) expect(ups.default).to.eq(ins.default) expect(ins.data).to.eq('insert') expect(ups.data).to.eq('upsert') @@ -28,10 +43,10 @@ describe('UPSERT', () => { describe('entries', () => { test('smart quoting', async () => { const { ASC } = cds.entities('complex.keywords') - await UPSERT.into(ASC).entries({ ID: 42, select: 4711 }) - await UPSERT.into(ASC).entries({ ID: 42, alias: 9 }) - const select = await SELECT.one.from(ASC).where('ID = 42') - expect(select).to.eql({ ID: 42, select: 4711, alias: 9 }) + await UPSERT.into(ASC).entries({ ...uniques.ASC, select: 4711 }) + await UPSERT.into(ASC).entries({ ...uniques.ASC, alias: 9 }) + const select = await SELECT.one.from(ASC).where(`ID = ${uniques.ASC.ID}`) + expect(select).to.eql({ ...uniques.ASC, select: 4711, alias: 9 }) }) }) @@ -47,9 +62,9 @@ describe('UPSERT', () => { const { ASC } = cds.entities('complex.keywords') await UPSERT.into(ASC) .columns(['ID', 'select']) - .rows([[42, 4711]]) - let select = await SELECT.one.from(ASC, ['ID', 'select']).where('ID = 42') - expect(select).to.eql({ ID: 42, select: 4711 }) + .rows([[uniques.ASC.ID, 4711]]) + let select = await SELECT.one.from(ASC, ['ID', 'select']).where(`ID = ${uniques.ASC.ID}`) + expect(select).to.eql({ ...uniques.ASC, select: 4711 }) }) }) }) @@ -61,7 +76,8 @@ describe('UPSERT', () => { }) test('affected row', async () => { - const affectedRows = await UPSERT.into('complex.associations.Books').entries({ ID: 9999999, title: 'Book' }) + const { Books } = cds.entities('complex.associations') + const affectedRows = await UPSERT.into(Books).entries({ ...uniques.Books, title: 'Book' }) expect(affectedRows).to.be.eq(1) }) }) diff --git a/test/compliance/functions.test.js b/test/compliance/functions.test.js index 2f3050604..9bc5ad9b8 100644 --- a/test/compliance/functions.test.js +++ b/test/compliance/functions.test.js @@ -2,7 +2,6 @@ const cds = require('../cds.js') describe('functions', () => { const { expect, data } = cds.test(__dirname + '/resources') - data.autoIsolation(true) describe('ABAP_ALPHANUM', () => { test.skip('missing', () => { diff --git a/test/compliance/resources/db/basic/common.cds b/test/compliance/resources/db/basic/common.cds index 00c055dab..096491a22 100644 --- a/test/compliance/resources/db/basic/common.cds +++ b/test/compliance/resources/db/basic/common.cds @@ -44,7 +44,7 @@ entity ![default] : _cuid { } entity dollar_now_default { - key id : Integer; + key ID : Integer; date : Date default $now; time : Time default $now; dateTime : DateTime default $now; @@ -52,7 +52,7 @@ entity dollar_now_default { } entity keys { - key id : Integer; + key ID : Integer; key default : String default 'defaulted'; data : String; } diff --git a/test/deploy.js b/test/deploy.js index 06495b764..24e91351a 100644 --- a/test/deploy.js +++ b/test/deploy.js @@ -1,3 +1,9 @@ -var { deploy } = require('@cap-js/postgres') -// eslint-disable-next-line no-console -deploy('*').to('db').catch(console.error) +const cds = require('./cds.js') + +describe('deploy', () => { + cds.test(__dirname,'index.cds') + + test('execute', async () => { + return cds.deploy(cds.options.from).to('db') + }) +}) \ No newline at end of file diff --git a/test/index.cds b/test/index.cds new file mode 100644 index 000000000..7c083124c --- /dev/null +++ b/test/index.cds @@ -0,0 +1,15 @@ +// bookshop +using from './bookshop/srv/admin-service'; +using from './bookshop/srv/cat-service'; +using from './bookshop/srv/draft-enabled-service'; +using from './bookshop/srv/genres'; +using from './bookshop/srv/tree-service'; + +// sflight +using from './sflight/srv/analytics-service'; +using from './sflight/srv/travel-service'; +using from './sflight/app/services'; + +// compliance +using from './compliance/resources/db'; +using from './compliance/resources/srv';