Skip to content

Commit a10acf5

Browse files
committed
Setup (and fix) tests to run against Postgres as well as MySQL. Issue #59
1 parent 731b370 commit a10acf5

File tree

11 files changed

+207
-81
lines changed

11 files changed

+207
-81
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,40 @@ the first place
7878
- `DB_HOST`: `localhost`
7979
- `DB_NAME`: `circle_test`
8080
- `DB_USER`: `ubuntu`
81+
- You may use `docker`/`docker-compose` to create MySql and Postgres containers to test against
82+
- `docker-compose up -d`
83+
- Start containers named `js-data-sql-mysql` and `js-data-sql-pg`
84+
- MySQL
85+
- Environment variables
86+
- `DB_CLIENT` = `mysql`
87+
- `DB_USER` = `root`
88+
- `DB_HOST` = `IP of docker-machine if not localhost`
89+
- Populate schema
90+
- `DB_CLIENT=mysql DB_USER=root npm run migrate-db`
91+
- Also set `DB_HOST` if different from `localhost`
92+
- Run tests
93+
- `npm run mocha-mysql`
94+
- Set `DB_HOST` if different from `localhost`
95+
- Run cli
96+
- `docker exec -it js-data-sql-mysql mysql circle_test`
97+
- Postgres
98+
- Environment variables
99+
- `DB_CLIENT` = `pg`
100+
- `DB_USER` = `ubuntu`
101+
- `DB_HOST` = `IP of docker-machine if not localhost`
102+
- Populate schema
103+
- `DB_CLIENT=pg npm run migrate-db`
104+
- Also set `DB_HOST` if different from `localhost`
105+
- Run tests
106+
- `npm run mocha-pg`
107+
- Also set `DB_HOST` if different from `localhost`
108+
- `docker exec -it js-data-sql-pg psql -U ubuntu -d circle_test`
109+
- Run cli
110+
- All databases
111+
- Run all tests against MySQL and Postgres
112+
- `npm run mocha-all`
113+
- Also set `DB_HOST` if different from `localhost`
114+
81115
1. Your code will be linted and checked for formatting, the tests will be run
82116
1. The `dist/` folder & files will be generated, do NOT commit `dist/*`! They
83117
will be committed when a release is cut.

circle.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ machine:
44

55
database:
66
override:
7-
- mysql -u ubuntu circle_test < test/setup.sql
7+
- DB_CLIENT=mysql npm run migrate-db
8+
- DB_CLIENT=pg npm run migrate-db
89
test:
910
override:
1011
- npm run ci

docker-compose.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
mysql:
2+
image: mysql:5.6.28
3+
container_name: 'js-data-sql-mysql'
4+
environment:
5+
- MYSQL_DATABASE=circle_test
6+
# - MYSQL_USER=ubuntu
7+
# - MYSQL_PASSWORD=
8+
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
9+
ports:
10+
- "3306:3306"
11+
12+
postgres:
13+
image: postgres:9.4.5
14+
container_name: 'js-data-sql-pg'
15+
ports:
16+
- "5432:5432"
17+
environment:
18+
- POSTGRES_DB=circle_test
19+
- POSTGRES_USER=ubuntu
20+

mocha.start.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,16 @@ for (var key in globals) {
2929
test.globals(testGlobals);
3030

3131
var config = {
32-
client: 'mysql',
32+
client: process.env.DB_CLIENT || 'mysql',
3333
connection: {
3434
host: process.env.DB_HOST || 'localhost',
3535
user: process.env.DB_USER || process.env.C9_USER || 'ubuntu',
3636
database: process.env.DB_NAME || (process.env.C9_USER ? 'c9' : 'circle_test')
3737
},
38+
pool: {
39+
min: 0,
40+
max: 10
41+
},
3842
debug: process.env.DEBUG || false
3943
};
4044

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,16 @@
2828
"scripts": {
2929
"lint": "standard src/index.js",
3030
"build": "webpack --config webpack.config.js --progress --colors",
31-
"mocha": "mocha --timeout 20000 --reporter dot mocha.start.js test/*.spec.js",
31+
"mocha-mysql": "DB_CLIENT=mysql DB_USER=root mocha --timeout 20000 --reporter dot mocha.start.js test/*.spec.js",
32+
"mocha-pg": "DB_CLIENT=pg mocha --timeout 20000 --reporter dot mocha.start.js test/*.spec.js",
33+
"mocha-all": "npm run mocha-mysql && npm run mocha-pg",
3234
"cover": "istanbul cover --hook-run-in-context node_modules/mocha/bin/_mocha -- --timeout 20000 --reporter dot mocha.start.js test/*.spec.js",
3335
"test": "npm run lint && npm run build && npm run cover",
34-
"ci": "npm run test && cat coverage/lcov.info | coveralls || true && cat ./coverage/lcov.info | codacy-coverage || true"
36+
"ci": "npm run test && cat coverage/lcov.info | coveralls || true && cat ./coverage/lcov.info | codacy-coverage || true",
37+
38+
"create-migration": "knex --cwd=test migrate:make",
39+
"migrate-db": "knex --cwd=test migrate:latest",
40+
"rollback-db": "knex --cwd=test migrate:rollback"
3541
},
3642
"standard": {
3743
"parser": "babel-eslint"

src/index.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -314,17 +314,17 @@ class DSSqlAdapter {
314314
}
315315
} else if (relation.type === 'hasMany') {
316316
// Perform `WHERE EXISTS` subquery for hasMany property
317-
let table = getTable(localResourceConfig)
318-
let localId = `${table}.${localResourceConfig.idAttribute}`
319-
320-
let relationTable = getTable(relationResourceConfig)
321-
let foreignId = `${relationTable}.${relation.foreignKey}`
322-
323317
let existsParams = {
324-
[foreignId]: {'===': knex.raw(localId)},
325318
[parts[0]]: criteria
326319
};
327-
query.whereExists(this.filterQuery(relationResourceConfig, existsParams, options));
320+
let subQuery = this.filterQuery(relationResourceConfig, existsParams, options)
321+
.whereRaw('??.??=??.??', [
322+
getTable(relationResourceConfig),
323+
relation.foreignKey,
324+
getTable(localResourceConfig),
325+
localResourceConfig.idAttribute
326+
])
327+
query.whereExists(subQuery);
328328
criteria = null; // criteria handled by EXISTS subquery
329329
}
330330

test/filterQuery.spec.js

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,100 @@ describe('DSSqlAdapter#filterQuery', function () {
22

33
it('should use built-in query if no custom query provided', function* () {
44
var filterQuery = adapter.filterQuery(User);
5-
assert.equal(filterQuery.toString(), 'select `user`.* from `user`')
5+
var expectedQuery = adapter.query
6+
.from('user')
7+
.select('user.*')
8+
9+
assert.equal(filterQuery.toString(), expectedQuery.toString())
610
});
711

812
it('should use custom query if passed as params (second parameter)', function* () {
913
var query = adapter.query.from('test');
1014
var filterQuery = adapter.filterQuery(User, query);
11-
assert.equal(filterQuery.toString(), 'select * from `test`')
15+
var expectedQuery = adapter.query
16+
.from('test')
17+
18+
assert.equal(filterQuery.toString(), expectedQuery.toString())
1219
});
1320

1421
it('should use custom query if passed as options.query', function* () {
1522
var query = adapter.query.from('test');
1623
var filterQuery = adapter.filterQuery(User, null, { query });
17-
assert.equal(filterQuery.toString(), 'select * from `test`')
24+
var expectedQuery = adapter.query
25+
.from('test')
26+
27+
assert.equal(filterQuery.toString(), expectedQuery.toString())
1828
});
1929

2030
it('should apply where from params to custom query', function* () {
2131
var query = adapter.query.from('test');
2232
var filterQuery = adapter.filterQuery(User, { name: 'Sean' }, { query });
23-
assert.equal(filterQuery.toString(), 'select * from `test` where `name` = \'Sean\'')
33+
var expectedQuery = adapter.query
34+
.from('test')
35+
.where({name: 'Sean'})
36+
37+
assert.equal(filterQuery.toString(), expectedQuery.toString())
2438
});
2539

2640
it('should apply limit from params to custom query', function* () {
2741
var query = adapter.query.from('test');
2842
var filterQuery = adapter.filterQuery(User, { limit: 2 }, { query });
29-
assert.equal(filterQuery.toString(), 'select * from `test` limit 2')
43+
var expectedQuery = adapter.query
44+
.from('test')
45+
.limit(2)
46+
47+
assert.equal(filterQuery.toString(), expectedQuery.toString())
3048
});
3149

3250
it('should apply order from params to custom query', function* () {
3351
var query = adapter.query.from('test');
3452
var filterQuery = adapter.filterQuery(User, { orderBy: 'name' }, { query });
35-
assert.equal(filterQuery.toString(), 'select * from `test` order by `name` asc')
53+
var expectedQuery = adapter.query
54+
.from('test')
55+
.orderBy('name', 'asc')
56+
57+
assert.equal(filterQuery.toString(), expectedQuery.toString())
3658
});
3759

3860
it('should convert == null to IS NULL', function* () {
3961
var query = adapter.query.from('test');
4062
var filterQuery = adapter.filterQuery(User, { name: { '==' : null } }, { query });
41-
assert.equal(filterQuery.toString(), 'select * from `test` where `name` is null')
63+
var expectedQuery = adapter.query
64+
.from('test')
65+
.whereNull('name')
66+
67+
assert.equal(filterQuery.toString(), expectedQuery.toString())
4268
});
4369

4470
it('should convert != null to IS NOT NULL', function* () {
4571
var query = adapter.query.from('test');
4672
var filterQuery = adapter.filterQuery(User, { name: { '!=' : null } }, { query });
47-
assert.equal(filterQuery.toString(), 'select * from `test` where `name` is not null')
73+
var expectedQuery = adapter.query
74+
.from('test')
75+
.whereNotNull('name')
76+
77+
assert.equal(filterQuery.toString(), expectedQuery.toString())
4878
});
4979

5080
it('should convert |== null to OR field IS NULL', function* () {
5181
var query = adapter.query.from('test');
5282
var filterQuery = adapter.filterQuery(User, { name: 'Sean', age: { '|==' : null } }, { query });
53-
assert.equal(filterQuery.toString(), 'select * from `test` where `name` = \'Sean\' or `age` is null')
83+
var expectedQuery = adapter.query
84+
.from('test')
85+
.where('name', 'Sean')
86+
.orWhereNull('age')
87+
88+
assert.equal(filterQuery.toString(), expectedQuery.toString())
5489
});
5590

5691
it('should convert |!= null to OR field IS NOT NULL', function* () {
5792
var query = adapter.query.from('test');
5893
var filterQuery = adapter.filterQuery(User, { name: 'Sean', age: { '|!=' : null } }, { query });
59-
assert.equal(filterQuery.toString(), 'select * from `test` where `name` = \'Sean\' or `age` is not null')
94+
var expectedQuery = adapter.query
95+
.from('test')
96+
.where('name', 'Sean')
97+
.orWhereNotNull('age')
98+
99+
assert.equal(filterQuery.toString(), expectedQuery.toString())
60100
});
61101
});

test/findAll.spec.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ describe('DSSqlAdapter#findAll', function () {
6060
let post6 = yield adapter.create(Post, {userId: user3.id, content: 'bar'});
6161
let post7 = yield adapter.create(Post, {userId: user3.id, content: 'baz'});
6262

63-
let users = yield adapter.findAll(User, {where: {'post.content': {'==': 'foo'} }});
63+
let users = yield adapter.findAll(User, {where: {'post.content': {'==': 'foo'} }, orderBy: 'name'});
6464
assert.equal(users.length, 2);
65-
assert.equal(users[0].name, 'Sean');
66-
assert.equal(users[1].name, 'Jason');
65+
assert.equal(users[0].name, 'Jason');
66+
assert.equal(users[1].name, 'Sean');
6767
});
6868

6969
it("should filter using a hasMany relation's localField", function* () {
@@ -80,10 +80,10 @@ describe('DSSqlAdapter#findAll', function () {
8080
let post6 = yield adapter.create(Post, {userId: user3.id, content: 'bar'});
8181
let post7 = yield adapter.create(Post, {userId: user3.id, content: 'baz'});
8282

83-
let users = yield adapter.findAll(User, {where: {'posts.content': {'==': 'foo'} }});
83+
let users = yield adapter.findAll(User, {where: {'posts.content': {'==': 'foo'} }, orderBy: 'name'});
8484
assert.equal(users.length, 2);
85-
assert.equal(users[0].name, 'Sean');
86-
assert.equal(users[1].name, 'Jason');
85+
assert.equal(users[0].name, 'Jason');
86+
assert.equal(users[1].name, 'Sean');
8787
});
8888

8989
describe('near', function () {

test/knexfile.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports = {
2+
client: process.env.DB_CLIENT || 'mysql',
3+
connection: {
4+
host: process.env.DB_HOST || 'localhost',
5+
database: process.env.DB_NAME || 'circle_test',
6+
user: process.env.DB_USER || 'ubuntu'
7+
},
8+
migrations: {
9+
tableName: 'migrations'
10+
},
11+
debug: process.env.DEBUG || false
12+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
2+
exports.up = function(knex, Promise) {
3+
4+
return knex.schema
5+
.createTable('profile', function (table) {
6+
table.increments('id').primary();
7+
table.string('email');
8+
})
9+
10+
.createTable('address', function (table) {
11+
table.increments('id').primary();
12+
table.string('name');
13+
table.decimal('latitude', 10, 7)
14+
table.decimal('longitude', 10, 7)
15+
})
16+
17+
.createTable('user', function (table) {
18+
table.increments('id').primary();
19+
table.string('name');
20+
table.integer('age').unsigned();
21+
22+
table.integer('profileId')
23+
.unsigned()
24+
.references('profile.id');
25+
26+
table.integer('addressId')
27+
.unsigned()
28+
.references('address.id');
29+
})
30+
31+
.createTable('post', function (table) {
32+
table.increments('id').primary();
33+
table.text('content');
34+
35+
table.integer('userId')
36+
.unsigned()
37+
.references('user.id');
38+
})
39+
40+
.createTable('comment', function (table) {
41+
table.increments('id').primary();
42+
table.text('content');
43+
44+
table.integer('userId')
45+
.unsigned()
46+
.references('user.id');
47+
48+
table.integer('postId')
49+
.unsigned()
50+
.references('post.id');
51+
})
52+
};
53+
54+
exports.down = function(knex, Promise) {
55+
return knex.schema
56+
.dropTableIfExists('comment')
57+
.dropTableIfExists('post')
58+
.dropTableIfExists('user')
59+
.dropTableIfExists('address')
60+
.dropTableIfExists('profile')
61+
;
62+
};

0 commit comments

Comments
 (0)