diff --git a/integration/ModelFactory.ts b/integration/ModelFactory.ts index 628008b..1ca1208 100644 --- a/integration/ModelFactory.ts +++ b/integration/ModelFactory.ts @@ -1,13 +1,14 @@ import { Factory, Faker } from '../dist/lib/v1' import { User } from './models/User' import { Post } from './models/Post' +import { Comment } from './models/Comment' import { ObjectId } from 'bson' function createObjectId(): string { return new ObjectId().toHexString() } -Factory.define(User.className, (faker: Faker, attributes?: Object): Object => { +Factory.define(User, (faker: Faker, attributes?: Object): Object => { return Object.assign( { email: faker.email(), @@ -19,7 +20,7 @@ Factory.define(User.className, (faker: Faker, attributes?: Object): Object => { ) }) -Factory.define(Post.className, (faker: Faker, attributes?: Object): Object => { +Factory.define(Post, (faker: Faker, attributes?: Object): Object => { return Object.assign( { user_id: createObjectId(), @@ -30,3 +31,15 @@ Factory.define(Post.className, (faker: Faker, attributes?: Object): Object => { attributes ) }) + +Factory.define(Comment, (faker: Faker, attributes?: Object): Object => { + return Object.assign( + { + email: faker.email(), + name: faker.name(), + content: faker.paragraph(), + like: faker.natural() + }, + attributes + ) +}) diff --git a/integration/README.md b/integration/README.md new file mode 100644 index 0000000..5e8e1ff --- /dev/null +++ b/integration/README.md @@ -0,0 +1,17 @@ +## Integration + +There are 3 purposes for this directory + +1. **Integration test**: All files in this directory are written like "real-life-usage", it contains tests to ensure all features are working as expectations. +2. **Definition and Syntax test**: Because the `najs-eloquent` is implemented with Proxy and dynamic function names then we have to ensure that all classes have typed-definition correctly. +3. **Develop and debug**: All bugs which are reproducible in model-level should be written, fixed and tested in this directory + +The directory structure: + +* **models**: Definition of models which are use for integration/definition/syntax testing +* **mongodb**: Setup for mongodb/mongoose driver or environment +* **mysql**: Setup for mysql driver or environment +* **test**: Setup for mysql driver or environment +* * **features**: For integration test +* * **syntax**: For definition and syntax test, if a type-definitions is wrong it's never built successfully +* **ModelFactory.ts**: Factory definition diff --git a/integration/models/Comment.ts b/integration/models/Comment.ts new file mode 100644 index 0000000..d02bdec --- /dev/null +++ b/integration/models/Comment.ts @@ -0,0 +1,26 @@ +import { Eloquent, Mongoose } from '../../dist/lib/v1' + +export interface IComment { + user_id?: string + email?: string + name?: string + content: string + like: number +} + +export class Comment extends (Eloquent as Mongoose) { + static className: string = 'Comment' + protected static timestamps = true + protected static softDeletes = true + protected static schema = { + user_id: { type: String, required: false }, + email: { type: String, required: false }, + name: { type: String, required: false }, + content: { type: String, required: true }, + like: { type: Number, required: false } + } + + getClassName() { + return Comment.className + } +} diff --git a/integration/mongodb/index.ts b/integration/mongodb/index.ts index 41196ee..397e055 100644 --- a/integration/mongodb/index.ts +++ b/integration/mongodb/index.ts @@ -2,6 +2,7 @@ import '../ModelFactory' import { Eloquent, MongooseDriver, EloquentDriverProvider, MongooseProvider } from '../../dist/lib/v1' import { IUser, User } from '../models/User' import { IPost, Post } from '../models/Post' +import { IComment, Comment } from '../models/Comment' EloquentDriverProvider.register(MongooseDriver, 'mongoose', true) @@ -10,6 +11,7 @@ Eloquent.register(Post) export { IUser, User } export { IPost, Post } +export { IComment, Comment } export function init_mongoose(name: string): Promise { return new Promise(resolve => { diff --git a/integration/test/mongodb/Eloquent.test.ts b/integration/test/features/Eloquent.test.ts similarity index 99% rename from integration/test/mongodb/Eloquent.test.ts rename to integration/test/features/Eloquent.test.ts index 7d1909f..be3d3f9 100644 --- a/integration/test/mongodb/Eloquent.test.ts +++ b/integration/test/features/Eloquent.test.ts @@ -2,7 +2,7 @@ import { factory } from '../../../dist/lib/v1' import { User, init_mongoose, delete_collection } from '../../mongodb/index' const Moment = require('moment') -describe('Integration Test - Eloquent functions', function() { +describe('Integration Test - Eloquent model functions', function() { beforeAll(async function() { await init_mongoose('integration_eloquent') }) diff --git a/integration/test/features/EloquentCastedQuery.test.ts b/integration/test/features/EloquentCastedQuery.test.ts new file mode 100644 index 0000000..109b143 --- /dev/null +++ b/integration/test/features/EloquentCastedQuery.test.ts @@ -0,0 +1,339 @@ +import { QueryLog, factory } from '../../../dist/lib/v1' +import { Comment, init_mongoose, delete_collection } from '../../mongodb/index' + +describe('Integration Test - Eloquent Casted (3rd way) querying', function() { + beforeAll(async function() { + await init_mongoose('integration_eloquent_casted_query') + }) + + afterEach(async function() { + await delete_collection(['comments']) + }) + + const commentModel = new Comment() + + describe('.queryName()', function() { + it('starts new query with given name.', async function() { + const query = commentModel.queryName('You can name the query what ever you want') + expect(query['name']).toEqual('You can name the query what ever you want') + + QueryLog.enable() + await query.where('email', 'test').get() + const log = QueryLog.pull() + expect(log[0].query['name']).toEqual('You can name the query what ever you want') + QueryLog.disable() + }) + }) + + describe('.select()', function() { + it('set the columns or fields to be selected.', async function() { + await factory(Comment).create() + const comments = await commentModel.select('content', 'like').get() + for (const comment of comments) { + const result = comment.toObject() + expect(result['name']).toBeUndefined() + expect(result['email']).toBeUndefined() + } + }) + }) + + // describe('.distinct()', function() { + // it('sets the columns or fields to be applied distinct operation.', async function() { + // await factory(Comment) + // .times(2) + // .create({ like: 20 }) + + // QueryLog.enable() + // const comments = await commentModel + // .distinct('like') + // .where('like', 20) + // .all() + + // console.log(QueryLog.pull()) + // QueryLog.disable() + // console.log(comments) + + // for (const comment of comments) { + // const result = comment.toObject() + // console.log(result) + // } + // }) + // }) + + describe('.orderBy()', function() { + it('adds an "order by" clause to the query.', async function() { + await factory(Comment).create({ like: 50, name: 'a' }) + await factory(Comment).create({ like: 40, name: 'b' }) + await factory(Comment).create({ like: 30, name: 'c' }) + expect( + (await commentModel.orderBy('like').get()) + .map(function(comment) { + return comment.name + }) + .toArray() + ).toEqual(['c', 'b', 'a']) + expect( + (await commentModel.orderBy('like', 'desc').get()) + .map(function(comment) { + return comment.name + }) + .toArray() + ).toEqual(['a', 'b', 'c']) + }) + }) + + describe('.orderByAsc()', function() { + it('adds an "order by" clause to the query with direction ASC.', async function() { + await factory(Comment).create({ like: 50, name: 'a' }) + await factory(Comment).create({ like: 40, name: 'b' }) + await factory(Comment).create({ like: 30, name: 'c' }) + expect( + (await commentModel.orderByAsc('like').get()) + .map(function(comment) { + return comment.name + }) + .toArray() + ).toEqual(['c', 'b', 'a']) + expect( + (await commentModel.orderByAsc('name').get()) + .map(function(comment) { + return comment.name + }) + .toArray() + ).toEqual(['a', 'b', 'c']) + }) + }) + + describe('.orderByDesc()', function() { + it('adds an "order by" clause to the query with direction DESC.', async function() { + await factory(Comment).create({ like: 50, name: 'a' }) + await factory(Comment).create({ like: 40, name: 'b' }) + await factory(Comment).create({ like: 30, name: 'c' }) + expect( + (await commentModel.orderByDesc('like').get()) + .map(function(comment) { + return comment.name + }) + .toArray() + ).toEqual(['a', 'b', 'c']) + expect( + (await commentModel.orderByDesc('name').get()) + .map(function(comment) { + return comment.name + }) + .toArray() + ).toEqual(['c', 'b', 'a']) + }) + }) + + describe('.limit()', function() { + it('adds an "order by" clause to the query with direction DESC.', async function() { + await factory(Comment).create({ like: 50, name: 'a' }) + await factory(Comment).create({ like: 40, name: 'b' }) + await factory(Comment).create({ like: 30, name: 'c' }) + expect( + (await commentModel + .orderByDesc('like') + .limit(2) + .get()) + .map(function(comment) { + return comment.name + }) + .toArray() + ).toEqual(['a', 'b']) + expect( + (await commentModel + .orderByDesc('name') + .limit(1) + .get()) + .map(function(comment) { + return comment.name + }) + .toArray() + ).toEqual(['c']) + }) + }) + + describe('.where()', function() { + it('adds a basic where clause to the query.', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + await factory(Comment).create({ like: 40, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + const result = await commentModel + .where('like', '>', 10) + .where(function(query) { + return query.where('name', 'a').orWhere('name', 'b') + }) + .pluck('name', 'like') + expect(result).toEqual({ 50: 'a', 40: 'b' }) + }) + }) + + describe('.orWhere()', function() { + it('adds an "or where" clause to the query.', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + await factory(Comment).create({ like: 40, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + const result = await commentModel + .orWhere('like', 40) + .orWhere(function(query) { + return query.where('name', 'a').orWhere('name', 'b') + }) + .pluck('name', 'like') + expect(result).toEqual({ 50: 'a', 40: 'b' }) + }) + }) + + describe('.whereIn()', function() { + it('adds a "where in" clause to the query.', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + await factory(Comment).create({ like: 40, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + const result = await commentModel.whereIn('like', [30, 40]).pluck('name', 'like') + expect(result).toEqual({ 30: 'c', 40: 'b' }) + }) + }) + + describe('.orWhereIn()', function() { + it('adds an "or where in" clause to the query.', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + await factory(Comment).create({ like: 40, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + const result = await commentModel + .where('like', 30) + .orWhereIn('like', [40]) + .pluck('name', 'like') + expect(result).toEqual({ 30: 'c', 40: 'b' }) + }) + }) + + describe('.orWhereNotIn()', function() { + it('adds an "or where not in" clause to the query.', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + await factory(Comment).create({ like: 40, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + const result = await commentModel + .where('like', 30) + .orWhereNotIn('like', [40]) + .pluck('name', 'like') + expect(result).toEqual({ 30: 'c', 50: 'a' }) + }) + }) + + describe('.whereNull()', function() { + it('adds a "where null" clause to the query.', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + // tslint:disable-next-line + await factory(Comment).create({ like: null, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + + const result = await commentModel.whereNull('like').pluck('name', 'email') + expect(result).toEqual({ b: 'b' }) + }) + }) + + describe('.whereNotNull()', function() { + it('adds a "where not null" clause to the query.', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + // tslint:disable-next-line + await factory(Comment).create({ like: null, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + + const result = await commentModel.whereNotNull('like').pluck('name', 'email') + expect(result).toEqual({ a: 'a', c: 'c' }) + }) + }) + + describe('.orWhereNotNull()', function() { + it('adds an "or where null" clause to the query.', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + // tslint:disable-next-line + await factory(Comment).create({ like: null, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + + const result = await commentModel + .where('like', 50) + .orWhereNotNull('like') + .pluck('name', 'email') + expect(result).toEqual({ a: 'a', c: 'c' }) + }) + }) + + describe('.withTrashed()', function() { + it('considers all soft-deleted or not-deleted items.', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + await factory(Comment).create({ like: 40, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + await (await commentModel.where('like', 40).first()).delete() + + const result = await commentModel + .withTrashed() + .where('like', '>', 0) + .pluck('name', 'email') + expect(result).toEqual({ a: 'a', b: 'b', c: 'c' }) + }) + }) + + describe('.onlyTrashed()', function() { + it('considers soft-deleted items only.', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + await factory(Comment).create({ like: 40, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + await (await commentModel.where('like', 30).first()).delete() + await (await commentModel.where('like', 40).first()).delete() + + const result = await commentModel + .onlyTrashed() + .where('like', '>', 30) + .pluck('name', 'email') + expect(result).toEqual({ b: 'b' }) + }) + }) + + describe('.all()', function() { + it('executes the query and return a Collection', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + await factory(Comment).create({ like: 40, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + + const result = (await commentModel.all()).pluck('name', 'email').all() + expect(result).toEqual({ a: 'a', b: 'b', c: 'c' }) + }) + }) + + describe('.get()', function() { + it('executes the query and return a Collection', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + await factory(Comment).create({ like: 40, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + + const result = (await commentModel.get()).pluck('name', 'email').all() + expect(result).toEqual({ a: 'a', b: 'b', c: 'c' }) + }) + }) + + describe('.first()', function() { + it('executes the query and return a Collection', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + await factory(Comment).create({ like: 40, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + + const result = (await commentModel + .where('like', '>', 30) + .orderBy('like') + .first()).toObject() + expect(result.name).toEqual('b') + }) + }) + + describe('.count()', function() { + it('retrieves the "count" result of the query.', async function() { + await factory(Comment).create({ like: 50, name: 'a', email: 'a' }) + await factory(Comment).create({ like: 40, name: 'b', email: 'b' }) + await factory(Comment).create({ like: 30, name: 'c', email: 'c' }) + + const result = await commentModel.where('like', '<', 40).count() + expect(result).toEqual(1) + }) + }) +}) diff --git a/integration/test/mongodb/EloquentQuery.test.ts b/integration/test/features/EloquentQuery.test.ts similarity index 99% rename from integration/test/mongodb/EloquentQuery.test.ts rename to integration/test/features/EloquentQuery.test.ts index 1ebdc9a..3b6e6a4 100644 --- a/integration/test/mongodb/EloquentQuery.test.ts +++ b/integration/test/features/EloquentQuery.test.ts @@ -1,7 +1,7 @@ import { QueryLog, factory } from '../../../dist/lib/v1' import { User, init_mongoose, delete_collection } from '../../mongodb/index' -describe('Integration Test - Eloquent functions', function() { +describe('Integration Test - Eloquent (1st way) querying', function() { beforeAll(async function() { await init_mongoose('integration_eloquent_query') }) diff --git a/integration/test/mongodb/EloquentStaticQuery.test.ts b/integration/test/features/EloquentStaticQuery.test.ts similarity index 99% rename from integration/test/mongodb/EloquentStaticQuery.test.ts rename to integration/test/features/EloquentStaticQuery.test.ts index 81465de..8d1e60c 100644 --- a/integration/test/mongodb/EloquentStaticQuery.test.ts +++ b/integration/test/features/EloquentStaticQuery.test.ts @@ -1,7 +1,7 @@ import { QueryLog, factory } from '../../../dist/lib/v1' import { Post, init_mongoose, delete_collection } from '../../mongodb/index' -describe('Integration Test - Eloquent static query', function() { +describe('Integration Test - Eloquent Static (2nd way) querying', function() { beforeAll(async function() { await init_mongoose('integration_eloquent_static_query') }) diff --git a/integration/test/mongodb/Factory.test.ts b/integration/test/features/Factory.test.ts similarity index 96% rename from integration/test/mongodb/Factory.test.ts rename to integration/test/features/Factory.test.ts index a681c05..460d70c 100644 --- a/integration/test/mongodb/Factory.test.ts +++ b/integration/test/features/Factory.test.ts @@ -2,7 +2,7 @@ import 'jest' import { Faker, Factory } from '../../../dist/lib/v1' import { User, init_mongoose, delete_collection } from '../../mongodb/index' -describe('Integration Test - Factory', function() { +describe('Integration Test - Factory usage', function() { beforeAll(async function() { await init_mongoose('integration_factory') })