Adding tests for mocha and jest
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es6": true,
|
||||
"jest": true
|
||||
"jest": true,
|
||||
"mocha": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
|
||||
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
@@ -9,7 +9,9 @@
|
||||
"esbenp.prettier-vscode",
|
||||
"mechatroner.rainbow-csv",
|
||||
"humao.rest-client",
|
||||
"alexcvzz.vscode-sqlite"
|
||||
"alexcvzz.vscode-sqlite",
|
||||
"hbenl.vscode-mocha-test-adapter",
|
||||
"sdras.night-owl"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -2,5 +2,6 @@
|
||||
"files.exclude": {
|
||||
"**/.gitignore": true,
|
||||
"**/.vscode": true
|
||||
}
|
||||
},
|
||||
"cds.checkDevKitInstalled": false
|
||||
}
|
||||
|
||||
15
.vscode/tasks.json
vendored
Normal file
15
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "jest",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -37,6 +37,14 @@ cds watch bookshop
|
||||
|
||||
After that open this link in your browser: [http://localhost:4004](http://localhost:4004)
|
||||
|
||||
### Testing
|
||||
|
||||
Run the provided tests with [_jest_](http://jestjs.io) or [_mocha_](http://mochajs.org), e.g.:
|
||||
```sh
|
||||
npx jest
|
||||
```
|
||||
> While mocha is a bit smaller and faster, jest runs tests in parallel and isolation which allows to run all tests.
|
||||
|
||||
|
||||
## Get Support
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
namespace sap.capire.bookshop; //> important for reflection
|
||||
using from './db/schema';
|
||||
using from './srv/cat-service';
|
||||
using from './srv/admin-service';
|
||||
|
||||
@@ -8,15 +8,20 @@
|
||||
"@capire/bookshop": "file:bookshop",
|
||||
"@capire/common": "file:common",
|
||||
"@capire/fiori": "file:fiori",
|
||||
"@capire/media": "file:media",
|
||||
"@capire/orders": "file:orders",
|
||||
"@capire/reviews": "file:reviews"
|
||||
"@capire/reviews": "file:reviews",
|
||||
"@capire/tests": "file:test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-subset": "^1.6.0",
|
||||
"sqlite3": "^4"
|
||||
},
|
||||
"scripts": {
|
||||
"mocha": "npx mocha || echo",
|
||||
"jest": "npx jest"
|
||||
},
|
||||
"license": "SAP SAMPLE CODE LICENSE",
|
||||
"private": true
|
||||
}
|
||||
|
||||
95
test/bookshop.test.js
Normal file
95
test/bookshop.test.js
Normal file
@@ -0,0 +1,95 @@
|
||||
describe('@capire/bookshop', () => {
|
||||
|
||||
const { GET, POST, expect } = require('@capire/tests') .launch ('@capire/bookshop')
|
||||
|
||||
it ('should serve $metadata documents in v4', async () => {
|
||||
const {headers,status,data} = await GET `/browse/$metadata`
|
||||
expect (headers) .to.contain ({
|
||||
"content-type": 'application/xml',
|
||||
"odata-version": "4.0",
|
||||
})
|
||||
expect (data) .to.contain (
|
||||
'<EntitySet Name="Books" EntityType="CatalogService.Books">',
|
||||
)
|
||||
expect (data) .to.contain (
|
||||
'<Annotation Term="Common.Label" String="Currency"/>'
|
||||
)
|
||||
expect (status) .to.equal (200)
|
||||
})
|
||||
|
||||
|
||||
it ('should serve localized $metadata documents', async () => {
|
||||
const {data} = await GET `/browse/$metadata?sap-language=de`
|
||||
expect (data) .to.contain (
|
||||
'<Annotation Term="Common.Label" String="Währung"/>'
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
it ('should serve localized Books with $expanded currency', async () => {
|
||||
const {data} = await GET `/browse/Books?&$select=title,author&$expand=currency&sap-language=de`
|
||||
expect (data.value) .to.containSubset ([
|
||||
{
|
||||
ID:201, title: 'Sturmhöhe', author: 'Emily Brontë', currency: {
|
||||
code: 'GBP',
|
||||
descr: 'Britische Pfund',
|
||||
name: 'Pfund',
|
||||
symbol: '£'
|
||||
}
|
||||
},
|
||||
{
|
||||
ID:207, title: 'Jane Eyre', author: 'Charlotte Brontë', currency: {
|
||||
descr: 'Britische Pfund',
|
||||
}
|
||||
},
|
||||
{
|
||||
ID:251, title: 'The Raven', author: 'Edgar Allen Poe', currency: {
|
||||
code: 'USD',
|
||||
name: 'US-Dollar',
|
||||
symbol: '$'
|
||||
}
|
||||
},
|
||||
{
|
||||
ID:252, title: 'Eleonora', author: 'Edgar Allen Poe'
|
||||
},
|
||||
{
|
||||
ID:271, title: 'Catweazle', author: 'Richard Carpenter', currency: {
|
||||
code: 'EUR',
|
||||
name: 'Euro',
|
||||
symbol: '€'
|
||||
}
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
|
||||
it ('should serve localized Authors w/ $expanded book and currency', async () => {
|
||||
const {data} = await GET (`/admin/Authors/101?sap-language=de`
|
||||
+ `&$expand=books($select=title;$expand=currency($select=code,name,symbol))`
|
||||
+ `&$select=name`)
|
||||
expect (data) .to.eql ({
|
||||
'@odata.context': '$metadata#Authors(name,ID,books(title,ID,currency(code,name,symbol)))/$entity',
|
||||
ID:101, name: 'Emily Brontë', books: [
|
||||
{ ID:201, title: 'Sturmhöhe', currency: {
|
||||
code:'GBP', name: 'Pfund', symbol: '£'
|
||||
}}
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it ('should check on current stocks with $value', async () => {
|
||||
const { data } = await GET `/admin/Books/201/stock/$value`
|
||||
expect(data) .to.equal (12)
|
||||
})
|
||||
|
||||
|
||||
it ('should reject out-of-stock orders', ()=> {
|
||||
return expect (Promise.all ([
|
||||
POST ('/browse/submitOrder', { book: 201, amount: 5 }),
|
||||
POST ('/browse/submitOrder', { book: 201, amount: 5 }),
|
||||
POST ('/browse/submitOrder', { book: 201, amount: 5 }),
|
||||
])) .to.be.rejectedWith (/409 - 5 exceeds stock for book #201/)
|
||||
})
|
||||
|
||||
})
|
||||
531
test/cds.ql.test.js
Normal file
531
test/cds.ql.test.js
Normal file
@@ -0,0 +1,531 @@
|
||||
const { expect } = require('@capire/tests')
|
||||
const cds = require('@sap/cds')
|
||||
const CQL = ([cql]) => cds.parse.cql(cql)
|
||||
const Foo = { name: 'Foo' }
|
||||
const Books = { name: 'capire.bookshop.Books' }
|
||||
|
||||
const is_cds_333 = cds.version >= '3.33.3'
|
||||
if (!is_cds_333) {
|
||||
// Monky-patching v3.33.3 features in older releases
|
||||
const up = UPDATE('x').constructor.prototype
|
||||
up.with = up.set
|
||||
}
|
||||
|
||||
// while jest has 'test' as alias to 'it', mocha doesn't
|
||||
if (!global.test) global.test = it
|
||||
|
||||
describe('cds.ql', () => {
|
||||
//
|
||||
|
||||
describe(`BUGS + GAPS...`, () => {
|
||||
it.skip('should consistently handle *', () => {
|
||||
expect({
|
||||
SELECT: { from: { ref: ['Foo'] }, columns: ['*'] },
|
||||
})
|
||||
.to.eql(CQL`SELECT * from Foo`)
|
||||
.to.eql(CQL`SELECT from Foo{*}`)
|
||||
.to.eql(SELECT('*').from(Foo))
|
||||
.to.eql(SELECT.from(Foo, ['*']))
|
||||
})
|
||||
|
||||
it.skip('should correctly handle { ... and:{...} }', () => {
|
||||
expect(SELECT.from(Foo).where({ x: 1, and: { y: 2, or: { z: 2 } } })).to.eql({
|
||||
SELECT: {
|
||||
from: { ref: ['Foo'] },
|
||||
where: [
|
||||
{ ref: ['x'] },
|
||||
'=',
|
||||
{ val: 1 },
|
||||
'and',
|
||||
'(',
|
||||
{ ref: ['y'] },
|
||||
'=',
|
||||
{ val: 2 },
|
||||
'or',
|
||||
{ ref: ['z'] },
|
||||
'=',
|
||||
{ val: 3 },
|
||||
')',
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe(`SELECT...`, () => {
|
||||
test('from ( Foo )', () => {
|
||||
expect({
|
||||
SELECT: { from: { ref: ['Foo'] } },
|
||||
})
|
||||
.to.eql(CQL`SELECT from Foo`)
|
||||
.to.eql(SELECT.from(Foo))
|
||||
})
|
||||
|
||||
test('from ( ..., <key>)', () => {
|
||||
// Compiler
|
||||
expect(CQL`SELECT from Foo[11]`).to.eql({
|
||||
SELECT: {
|
||||
// REVISIT: add one:true?
|
||||
from: { ref: [{ id: 'Foo', where: [{ val: 11 }] }] },
|
||||
},
|
||||
})
|
||||
|
||||
expect(CQL`SELECT from Foo[ID=11]`).to.eql({
|
||||
SELECT: {
|
||||
// REVISIT: add one:true
|
||||
from: {
|
||||
ref: [{ id: 'Foo', where: [{ ref: ['ID'] }, '=', { val: 11 }] }],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Runtime ds.ql
|
||||
expect(SELECT.from(Foo, 11))
|
||||
.to.eql(SELECT.from(Foo, { ID: 11 }))
|
||||
.to.eql(SELECT.from(Foo).byKey(11))
|
||||
.to.eql(SELECT.from(Foo).byKey({ ID: 11 }))
|
||||
.to.eql(SELECT.one.from(Foo).where({ ID: 11 }))
|
||||
.to.eql({
|
||||
// REVISIT: should produce CQN as the ones above?
|
||||
SELECT: {
|
||||
one: true,
|
||||
from: { ref: ['Foo'] },
|
||||
where: [{ ref: ['ID'] }, '=', { val: 11 }],
|
||||
},
|
||||
})
|
||||
|
||||
expect(CQL`SELECT from Foo[11]{a}`).to.eql({
|
||||
SELECT: {
|
||||
// REVISIT: add one:true?
|
||||
from: { ref: [{ id: 'Foo', where: [{ val: 11 }] }] },
|
||||
columns: [{ ref: ['a'] }],
|
||||
},
|
||||
})
|
||||
|
||||
expect(SELECT.from(Foo, 11, ['a']))
|
||||
.to.eql(SELECT.from(Foo, 11, (foo) => foo.a))
|
||||
.to.eql({
|
||||
// REVISIT: should produce CQN as the ones above?
|
||||
SELECT: {
|
||||
one: true,
|
||||
from: { ref: ['Foo'] },
|
||||
columns: [{ ref: ['a'] }],
|
||||
where: [{ ref: ['ID'] }, '=', { val: 11 }],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('from ( ..., => {...})', () => {
|
||||
// single *, prefix and postfix, as array and function
|
||||
expect(CQL`SELECT * from Foo`).to.eql(CQL`SELECT from Foo{*}`)
|
||||
//> .to.eql... FIXME: see skipped 'should handle * correctly' below
|
||||
expect(SELECT('*').from(Foo))
|
||||
.to.eql(SELECT.from(Foo, ['*']))
|
||||
.to.eql(SELECT.from(Foo, (foo) => foo('*')))
|
||||
.to.eql(SELECT.from(Foo).columns('*'))
|
||||
.to.eql(SELECT.from(Foo).columns((foo) => foo('*')))
|
||||
.to.eql({
|
||||
SELECT: { from: { ref: ['Foo'] }, columns: [{ ref: ['*'] }] },
|
||||
})
|
||||
|
||||
// single column, prefix and postfix, as array and function
|
||||
expect(CQL`SELECT a from Foo`)
|
||||
expect(CQL`SELECT from Foo {a}`)
|
||||
.to.eql(SELECT.from(Foo, ['a']))
|
||||
.to.eql(SELECT.from(Foo, (foo) => foo.a))
|
||||
.to.eql({
|
||||
SELECT: { from: { ref: ['Foo'] }, columns: [{ ref: ['a'] }] },
|
||||
})
|
||||
|
||||
// multiple columns, prefix and postfix, as array and function
|
||||
expect(CQL`SELECT a,b as c from Foo`)
|
||||
expect(CQL`SELECT from Foo {a,b as c}`)
|
||||
.to.eql(SELECT.from(Foo, ['a', { b: 'c' }]))
|
||||
.to.eql(
|
||||
SELECT.from(Foo, (foo) => {
|
||||
foo.a, foo.b.as('c')
|
||||
})
|
||||
)
|
||||
.to.eql(SELECT.from(Foo).columns('a', { b: 'c' }))
|
||||
.to.eql(SELECT.from(Foo).columns(['a', { b: 'c' }]))
|
||||
.to.eql(
|
||||
SELECT.from(Foo).columns((foo) => {
|
||||
foo.a, foo.b.as('c')
|
||||
})
|
||||
)
|
||||
.to.eql({
|
||||
SELECT: {
|
||||
from: { ref: ['Foo'] },
|
||||
columns: [{ ref: ['a'] }, { ref: ['b'], as: 'c' }],
|
||||
},
|
||||
})
|
||||
|
||||
// multiple columns and *, prefix and postfix, as array and function
|
||||
expect(CQL`SELECT *,a,b from Foo`).to.eql(CQL`SELECT from Foo{*,a,b}`)
|
||||
//> .to.eql... FIXME: see skipped 'should handle * correctly' below
|
||||
expect(SELECT.from(Foo, ['a', 'b', '*']))
|
||||
.to.eql(SELECT.from(Foo).columns('a', 'b', '*'))
|
||||
.to.eql(SELECT.from(Foo).columns(['a', 'b', '*']))
|
||||
.to.eql(
|
||||
SELECT.from(Foo, (foo) => {
|
||||
foo.a, foo.b, foo('*')
|
||||
})
|
||||
)
|
||||
.to.eql({
|
||||
SELECT: {
|
||||
from: { ref: ['Foo'] },
|
||||
columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['*'] }],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
is_cds_333 &&
|
||||
test('from ( ..., => _.expand ( x=>{...}))', () => {
|
||||
// SELECT from Foo { *, x, bar.*, car{*}, boo { *, moo.zoo } }
|
||||
expect(
|
||||
SELECT.from(Foo, (foo) => {
|
||||
foo('*'),
|
||||
foo.x,
|
||||
foo.car('*'),
|
||||
foo.boo((b) => {
|
||||
b('*'), b.moo.zoo((x) => x.y.z)
|
||||
})
|
||||
})
|
||||
).to.eql({
|
||||
SELECT: {
|
||||
from: { ref: ['Foo'] },
|
||||
columns: [
|
||||
{ ref: ['*'] },
|
||||
{ ref: ['x'] },
|
||||
{ ref: ['car'], expand: ['*'] },
|
||||
{
|
||||
ref: ['boo'],
|
||||
expand: ['*', { ref: ['moo', 'zoo'], expand: [{ ref: ['y', 'z'] }] }],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
is_cds_333 &&
|
||||
test('from ( ..., => _.inline ( _=>{...}))', () => {
|
||||
// SELECT from Foo { *, x, bar.*, car{*}, boo { *, moo.zoo } }
|
||||
expect(
|
||||
SELECT.from(Foo, (foo) => {
|
||||
foo.bar('*'),
|
||||
foo.bar('.*'), //> leading dot indicates inline
|
||||
foo.boo((x) => x.moo.zoo),
|
||||
foo.boo((_) => _.moo.zoo) //> underscore arg name indicates inline
|
||||
})
|
||||
).to.eql({
|
||||
SELECT: {
|
||||
from: { ref: ['Foo'] },
|
||||
columns: [
|
||||
{ ref: ['bar'], expand: ['*'] },
|
||||
{ ref: ['bar'], inline: ['*'] },
|
||||
{ ref: ['boo'], expand: [{ ref: ['moo', 'zoo'] }] },
|
||||
{ ref: ['boo'], inline: [{ ref: ['moo', 'zoo'] }] },
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('one / distinct ...', () => {
|
||||
expect(SELECT.distinct.from(Foo).cqn)
|
||||
// .to.eql(CQL(`SELECT distinct from Foo`).SELECT)
|
||||
.to.eql(SELECT.distinct(Foo).cqn)
|
||||
.to.eql({ distinct: true, from: { ref: ['Foo'] } })
|
||||
|
||||
expect(SELECT.one.from(Foo).cqn)
|
||||
// .to.eql(CQL(`SELECT one from Foo`).SELECT)
|
||||
.to.eql(SELECT.one(Foo).cqn)
|
||||
.to.eql({ one: true, from: { ref: ['Foo'] } })
|
||||
|
||||
expect(SELECT.one('a').from(Foo).cqn)
|
||||
// .to.eql(CQL(`SELECT distinct a from Foo`).SELECT)
|
||||
.to.eql(SELECT.one(['a']).from(Foo).cqn)
|
||||
.to.eql(SELECT.one(Foo, ['a']).cqn)
|
||||
.to.eql(SELECT.one(Foo, (foo) => foo.a).cqn)
|
||||
.to.eql(SELECT.one.from(Foo, (foo) => foo.a).cqn)
|
||||
.to.eql(SELECT.one.from(Foo, ['a']).cqn)
|
||||
.to.eql({
|
||||
one: true,
|
||||
from: { ref: ['Foo'] },
|
||||
columns: [{ ref: ['a'] }],
|
||||
})
|
||||
// same for works distinct
|
||||
})
|
||||
|
||||
test('where ( ... cql | {x:y} )', () => {
|
||||
const args = [`foo`, "'bar'", 3]
|
||||
const ID = 11
|
||||
|
||||
// using simple predicate objects
|
||||
// (Note: this doesn't support paths in left-hand-sides, nor references in arrays)
|
||||
expect(
|
||||
SELECT.from(Foo).where({
|
||||
ID,
|
||||
args,
|
||||
and: { x: { like: '%x%' }, or: { y: { '>=': 9 } } },
|
||||
})
|
||||
).to.eql({
|
||||
SELECT: {
|
||||
from: { ref: ['Foo'] },
|
||||
where: [
|
||||
'(', //> this one is not required
|
||||
{ ref: ['ID'] },
|
||||
'=',
|
||||
{ val: ID },
|
||||
'and',
|
||||
{ ref: ['args'] },
|
||||
'in',
|
||||
{ val: args },
|
||||
'and',
|
||||
// '(', //> this one is missing, and that's changing the logic -> that's a BUG
|
||||
{ ref: ['x'] },
|
||||
'like',
|
||||
{ val: '%x%' },
|
||||
'or',
|
||||
{ ref: ['y'] },
|
||||
'>=',
|
||||
{ val: 9 },
|
||||
')',
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
// using CQL fragments -> uses cds.parse.expr
|
||||
expect(CQL`SELECT from Foo where ID=11 and x in ( foo, 'bar', 3)`)
|
||||
.to.eql(SELECT.from(Foo).where(`ID=`, ID, `and x in`, args))
|
||||
.to.eql(SELECT.from(Foo).where(`ID=${ID} and x in (${args})`))
|
||||
.to.eql({
|
||||
SELECT: {
|
||||
from: { ref: ['Foo'] },
|
||||
where: [
|
||||
{ ref: ['ID'] },
|
||||
'=',
|
||||
{ val: ID },
|
||||
'and',
|
||||
{ ref: ['x'] },
|
||||
'in',
|
||||
'(',
|
||||
{ ref: ['foo'] },
|
||||
',',
|
||||
{ val: 'bar' },
|
||||
',',
|
||||
{ val: 3 },
|
||||
')',
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
expect(CQL`SELECT from Foo where x=1 or y.z is null and (a>2 or b=3)`).to.eql(
|
||||
SELECT.from(Foo).where(`x=`, 1, `or y.z is null and (a>`, 2, `or b=`, 3, `)`)
|
||||
)
|
||||
|
||||
expect(CQL`SELECT from Foo where x between 1 and 9`).to.eql(
|
||||
SELECT.from(Foo).where(`x between`, 1, `and`, 9)
|
||||
)
|
||||
})
|
||||
|
||||
test('w/ sub selects', () => {
|
||||
// in where causes
|
||||
expect(CQL`SELECT from Foo where x in (SELECT y from Bar)`).to.eql(
|
||||
SELECT.from(Foo).where({ x: SELECT('y').from('Bar') })
|
||||
)
|
||||
// in classical semi joins
|
||||
expect(CQL`SELECT x from Foo where exists (SELECT 1 from Bar where y=x)`).to.eql(
|
||||
SELECT('x').from(Foo) .where ( `exists`,
|
||||
SELECT(1).from('Bar') .where ({ y: { ref: ['x'] } })
|
||||
) // prettier-ignore
|
||||
)
|
||||
// in select clauses
|
||||
cds.version >= '3.33.3' &&
|
||||
expect(CQL`SELECT from Foo { x, (SELECT y from Bar) as y }`)
|
||||
.to.eql(
|
||||
SELECT.from(Foo, (foo) => {
|
||||
foo.x, foo(SELECT.from('Bar', (b) => b.y)).as('y')
|
||||
})
|
||||
)
|
||||
.to.eql(
|
||||
SELECT.from(Foo, ['x', Object.assign(SELECT('y').from('Bar'), { as: 'y' })])
|
||||
)
|
||||
})
|
||||
|
||||
it('w/ plain SQL', () => {
|
||||
expect(SELECT.from(Books) + 'WHERE ...').to.eql(
|
||||
'SELECT * FROM capire_bookshop_Books WHERE ...'
|
||||
)
|
||||
})
|
||||
|
||||
//
|
||||
})
|
||||
|
||||
describe(`INSERT...`, () => {
|
||||
test('entries ({a,b}, ...)', () => {
|
||||
const entries = [{ foo: 1 }, { boo: 2 }]
|
||||
expect(INSERT(...entries).into(Foo))
|
||||
.to.eql(INSERT(entries).into(Foo))
|
||||
.to.eql(INSERT.into(Foo).entries(...entries))
|
||||
.to.eql(INSERT.into(Foo).entries(entries))
|
||||
.to.eql({
|
||||
INSERT: { into: 'Foo', entries },
|
||||
})
|
||||
})
|
||||
|
||||
test('rows ([1,2], ...)', () => {
|
||||
expect(
|
||||
INSERT.into(Foo)
|
||||
.columns('a', 'b')
|
||||
.rows([
|
||||
[1, 2],
|
||||
[3, 4],
|
||||
])
|
||||
)
|
||||
.to.eql(INSERT.into(Foo).columns('a', 'b').rows([1, 2], [3, 4]))
|
||||
.to.eql({
|
||||
INSERT: {
|
||||
into: 'Foo',
|
||||
columns: ['a', 'b'],
|
||||
rows: [
|
||||
[1, 2],
|
||||
[3, 4],
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('values (1,2)', () => {
|
||||
expect(INSERT.into(Foo).columns('a', 'b').values([1, 2]))
|
||||
.to.eql(INSERT.into(Foo).columns('a', 'b').values(1, 2))
|
||||
.to.eql({
|
||||
INSERT: { into: 'Foo', columns: ['a', 'b'], values: [1, 2] },
|
||||
})
|
||||
})
|
||||
|
||||
test('w/ plain SQL', () => {
|
||||
expect(INSERT.into(Books) + 'VALUES ...').to.eql(
|
||||
'INSERT INTO capire_bookshop_Books VALUES ...'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe(`UPDATE...`, () => {
|
||||
test('entity (..., <key>)', () => {
|
||||
expect(UPDATE(Books, 4711))
|
||||
.to.eql(UPDATE(Books, { ID: 4711 }))
|
||||
.to.eql(UPDATE(Books).byKey(4711))
|
||||
.to.eql(UPDATE(Books).byKey({ ID: 4711 }))
|
||||
.to.eql(UPDATE(Books).where({ ID: 4711 }))
|
||||
.to.eql(UPDATE(Books).where(`ID=`, 4711))
|
||||
.to.eql(UPDATE.entity(Books, 4711))
|
||||
.to.eql(UPDATE.entity(Books, { ID: 4711 }))
|
||||
// etc...
|
||||
.to.eql({
|
||||
UPDATE: {
|
||||
entity: 'capire.bookshop.Books',
|
||||
where: [{ ref: ['ID'] }, '=', { val: 4711 }],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
UPDATE.with allows to pass in plain data payloads, e.g. as obtained from REST clients.
|
||||
In addition, UPDATE.with supports specifying expressions, either in CQL fragements
|
||||
notation or as simple expression objects.
|
||||
*/
|
||||
test('with', () => {
|
||||
expect(UPDATE(Foo).with(`foo=11, bar = bar - 22`))
|
||||
.to.eql(UPDATE(Foo).with(`foo=`, 11, `bar-=`, 22))
|
||||
.to.eql(UPDATE(Foo).with({ foo: 11, bar: { '-=': 22 } }))
|
||||
.to.eql({
|
||||
UPDATE: {
|
||||
entity: 'Foo',
|
||||
with: {
|
||||
foo: { val: 11 },
|
||||
bar: { xpr: [{ ref: ['bar'] }, '-', { val: 22 }] },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// some more
|
||||
expect(UPDATE(Foo).with(`bar = coalesce(x,y), car = 'foo''s bar, car'`)).to.eql({
|
||||
UPDATE: {
|
||||
entity: 'Foo',
|
||||
with: {
|
||||
bar: { func: 'coalesce', args: [{ ref: ['x'] }, { ref: ['y'] }] },
|
||||
car: { val: "foo's bar, car" },
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
UPDATE.data allows to pass in plain data payloads, e.g. as obtained from REST clients.
|
||||
The passed in object can be modified subsequently, e.g. by adding or modifying values
|
||||
before the query is finally executed.
|
||||
*/
|
||||
test('data', () => {
|
||||
const o = {}
|
||||
const q = UPDATE(Foo).data(o).with(`bar-=`, 22)
|
||||
o.foo = 11
|
||||
expect(q).to.eql({
|
||||
UPDATE: {
|
||||
entity: 'Foo',
|
||||
data: { foo: 11 },
|
||||
with: {
|
||||
bar: { xpr: [{ ref: ['bar'] }, '-', { val: 22 }] },
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('w/ plain SQL', () => {
|
||||
expect(UPDATE(Books) + 'SET ...').to.eql('UPDATE capire_bookshop_Books SET ...')
|
||||
})
|
||||
})
|
||||
|
||||
describe(`DELETE...`, () => {
|
||||
test('from (..., <key>)', () => {
|
||||
expect(DELETE(Books, 4711))
|
||||
.to.eql(DELETE(Books, { ID: 4711 }))
|
||||
.to.eql(DELETE.from(Books, 4711))
|
||||
.to.eql(DELETE.from(Books, { ID: 4711 }))
|
||||
.to.eql(DELETE.from(Books).byKey(4711))
|
||||
.to.eql(DELETE.from(Books).byKey({ ID: 4711 }))
|
||||
.to.eql(DELETE.from(Books).where({ ID: 4711 }))
|
||||
.to.eql(DELETE.from(Books).where(`ID=`, 4711))
|
||||
.to.eql({
|
||||
DELETE: {
|
||||
from: 'capire.bookshop.Books',
|
||||
where: [{ ref: ['ID'] }, '=', { val: 4711 }],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('/w plain SQL', () => {
|
||||
expect(DELETE.from(Books) + 'WHERE ...').to.eql(
|
||||
'DELETE FROM capire_bookshop_Books WHERE ...'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe(`cds.ql etc...`, () => {
|
||||
it('queries marked for cds repl', () => {
|
||||
expect(UPDATE(Foo)._isQuery).to.be.true
|
||||
})
|
||||
|
||||
it('should keep null and undefined', () => {
|
||||
for (let each of [null, undefined]) {
|
||||
expect(SELECT.from(Foo).where({ ID: each })).to.eql({
|
||||
SELECT: {
|
||||
from: { ref: ['Foo'] },
|
||||
where: [{ ref: ['ID'] }, '=', { val: each }],
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
//
|
||||
})
|
||||
@@ -1,10 +1,10 @@
|
||||
const {expect} = require('@capire/tests')
|
||||
const cds = require ('@sap/cds')
|
||||
const {expect} = cds.require.chai
|
||||
|
||||
describe('reading/writing hierarchies', ()=>{
|
||||
describe('Hierarchical CodeLists', ()=>{
|
||||
|
||||
it ('should bootstrap sqlite in-memory db', async()=>{
|
||||
await cds.deploy (__dirname+'/../db') .to ('sqlite::memory:')
|
||||
it ('should bootstrap sqlite in-memory db...', async()=>{
|
||||
await cds.deploy ('@capire/bookshop') .to ('sqlite::memory:')
|
||||
expect (cds.db) .to.exist
|
||||
expect (cds.db.model) .to.exist
|
||||
})
|
||||
@@ -20,7 +20,7 @@ describe('reading/writing hierarchies', ()=>{
|
||||
{ ID:105, name:'Kitty Bat' } ]},
|
||||
{ ID:106, name:'Catwoman', children:[
|
||||
{ ID:107, name:'Catalina' } ]} ]},
|
||||
{ ID:108, name:'Catweazle' }
|
||||
{ ID:108, name:'Ca<tweazle' }
|
||||
]}
|
||||
)
|
||||
})
|
||||
@@ -45,7 +45,7 @@ describe('reading/writing hierarchies', ()=>{
|
||||
)
|
||||
})
|
||||
|
||||
it ('should read hierarchy of genres', async()=>{
|
||||
it ('should read deep hierarchy of genres', async()=>{
|
||||
const { Genres } = cds.entities
|
||||
expect (await
|
||||
|
||||
94
test/lib/helpers.js
Normal file
94
test/lib/helpers.js
Normal file
@@ -0,0 +1,94 @@
|
||||
/** For static usage w/o launching a server */
|
||||
module.exports = exports = {
|
||||
get chai() { return _chai() },
|
||||
get expect(){ return this.chai.expect },
|
||||
get assert(){ return this.chai.assert },
|
||||
}
|
||||
|
||||
// harmonizing jest and mocha
|
||||
if (global.test) { // it's jest
|
||||
global.before = global.beforeAll
|
||||
global.after = global.afterAll
|
||||
} else { // it's mocha
|
||||
global.beforeAll = global.before
|
||||
global.afterAll = global.after
|
||||
}
|
||||
|
||||
// lazy-loading chai
|
||||
function _chai(){
|
||||
const chai = exports.chai = require('chai')
|
||||
.use (require('chai-as-promised'))
|
||||
.use (require('chai-subset'))
|
||||
chai.should()
|
||||
return chai
|
||||
}
|
||||
|
||||
|
||||
/** Launching and testing a cds server */
|
||||
exports.launch = (project, args=['--in-memory?']) => {
|
||||
|
||||
const cds = require('@sap/cds')
|
||||
|
||||
if (!cds.utils.existsSync(project)) try {
|
||||
project = require('path').dirname (require.resolve(project+'/package.json'))
|
||||
} catch(e) {
|
||||
throw cds.error (`Cannot resolve project folder for '${project}'`)
|
||||
}
|
||||
|
||||
const axios = require('axios').default
|
||||
let baseURL //... be filled in below
|
||||
|
||||
// launch cds server...
|
||||
before (done => {
|
||||
|
||||
const console = global.console, logs=[]
|
||||
global.console = { __proto__: global.console, logs,
|
||||
time: ()=>{}, timeEnd: (...args)=> logs.push(args),
|
||||
debug: (...args)=> logs.push(args),
|
||||
log: (...args)=> logs.push(args),
|
||||
warn: (...args)=> logs.push(args),
|
||||
error: (...args)=> logs.push(args),
|
||||
dump(){ for (let each of logs) console.log (...each) },
|
||||
}
|
||||
|
||||
process.env.PORT = '0'
|
||||
const p = cds.exec ('run', project, ...args) // TODO w/ @sap/cds@3.33.3: , '--port', '0')
|
||||
if (p && 'catch' in p) p.catch (done)
|
||||
|
||||
cds.once('listening', ({ server, url }) => {
|
||||
after (done => {
|
||||
if (global.console !== console) global.console = console
|
||||
server.close (done)
|
||||
})
|
||||
baseURL = url
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
GET: (path) => axios.get (baseURL+path) .catch(_error),
|
||||
PUT: (path,data) => axios.put (baseURL+path,data) .catch(_error),
|
||||
POST: (path,data) => axios.post (baseURL+path,data) .catch(_error),
|
||||
PATCH: (path,data) => axios.patch (baseURL+path,data) .catch(_error),
|
||||
DELETE: (path) => axios.delete (baseURL+path) .catch(_error),
|
||||
|
||||
get: (path) => axios.get (baseURL+path) .catch(_error),
|
||||
put: (path,data) => axios.put (baseURL+path,data) .catch(_error),
|
||||
post: (path,data) => axios.post (baseURL+path,data) .catch(_error),
|
||||
patch: (path,data) => axios.patch (baseURL+path,data) .catch(_error),
|
||||
delete: (path) => axios.delete (baseURL+path) .catch(_error),
|
||||
|
||||
get chai(){ return _chai() },
|
||||
get expect(){ return this.chai.expect },
|
||||
get assert(){ return this.chai.assert },
|
||||
}
|
||||
|
||||
function _error (e) {
|
||||
if (!e.response) throw e
|
||||
if (!e.response.data) throw e
|
||||
if (!e.response.data.error) throw e
|
||||
const { code, message } = e.response.data.error
|
||||
throw new Error (`${code} - ${message}`)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
const _model = __dirname+'/..'
|
||||
const {expect} = require('@capire/tests')
|
||||
const cds = require ('@sap/cds')
|
||||
const {expect} = cds.require.chai
|
||||
const _model = '@capire/reviews'
|
||||
|
||||
describe('messaging tests', ()=>{
|
||||
|
||||
describe('Messaging', ()=>{
|
||||
|
||||
it ('should bootstrap sqlite in-memory db', async()=>{
|
||||
const db = await cds.deploy (_model) .to ('sqlite::memory:')
|
||||
@@ -10,7 +11,7 @@ describe('messaging tests', ()=>{
|
||||
})
|
||||
|
||||
let srv
|
||||
it ('should serve reviews services', async()=>{
|
||||
it ('should serve ReviewsService', async()=>{
|
||||
srv = await cds.serve('ReviewsService') .from (_model)
|
||||
expect (srv.name) .to.match (/ReviewsService/)
|
||||
})
|
||||
5
test/package.json
Normal file
5
test/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "@capire/tests",
|
||||
"version": "1.0.0",
|
||||
"main": "lib/helpers.js"
|
||||
}
|
||||
Reference in New Issue
Block a user