test.group('Context - Share', (group) => { group.after(() => { delete process.env.ENV_SILENT }) group.beforeEach(() => { ioc.restore() RouteStore.clear() }) test('share request with view', async (assert) => { const Route = use('Route') Route.get('/', function ({ view }) { return view._locals.request.url() }) const { text } = await supertest(appUrl).get('/').expect(200) assert.equal(text, '/') }) test('call {is} method from view locals', async (assert) => { const Route = use('Route') Route.get('/', function ({ view }) { return view._locals.is('/') }) const { text } = await supertest(appUrl).get('/').expect(200) assert.equal(text, 'true') }) })
test.group('Sanitizations | To Int', () => { test('should convert string representation of integer to integer', function (assert) { const sanitized = sanitizations.toInt('20') assert.equal(sanitized, 20) }) test('should convert string representation of float to integer', function (assert) { const sanitized = sanitizations.toInt('20.40') assert.equal(sanitized, 20) }) test('should convert boolean to NaN', function (assert) { const sanitized = sanitizations.toInt(null) assert.deepEqual(sanitized, NaN) }) test('should convert string to NaN', function (assert) { const sanitized = sanitizations.toInt('hello') assert.deepEqual(sanitized, NaN) }) test('use different radix', function (assert) { const sanitized = sanitizations.toInt('10', [5]) assert.deepEqual(sanitized, 5) }) })
test.group('Sanitizations | trim', () => { test('remove whitespace from both sides of a given string.', (assert) => { assert.equal(sanitizations.trim(' joe '), 'joe') }) test('return same value when it\'s not a string', (assert) => { assert.equal(sanitizations.trim(22), 22) }) })
test.group('Sanitizations | stripLinks', () => { test('remove anchor tags from a string', (assert) => { assert.equal(sanitizations.stripLinks('<a href=""> google.com </a>'), 'google.com') }) test('return same value when it\'s not a string', (assert) => { assert.equal(sanitizations.stripLinks(22), 22) }) })
test.group('Sanitizations | escape', () => { test('escape HTML string', (assert) => { assert.equal(sanitizations.escape('<div> hello </div>'), '<div> hello </div>') }) test('return same value when it\'s not a string', (assert) => { assert.equal(sanitizations.escape(22), 22) }) })
test.group('Sanitizations | Singular', () => { test('should pluralize a given string', function (assert) { const sanitized = sanitizations.singular('people') assert.equal(sanitized, 'person') }) test('return exact value when not a string', function (assert) { const sanitized = sanitizations.singular(null) assert.isNull(sanitized) }) })
test.group('View', (group) => { group.before(async () => { await pify(fs.ensureDir)(path.join(__dirname, 'resources/views')) }) group.after(async () => { await pify(fs.remove)(path.join(__dirname, 'resources')) }) group.beforeEach(() => { this.helpers = new Helpers(path.join(__dirname, './')) }) test('configure edge', (assert) => { const view = new View(this.helpers) assert.equal(view.engine._loader._viewsPath, path.join(__dirname, './resources/views')) assert.equal(view.engine._loader._presentersPath, path.join(__dirname, './resources/presenters')) assert.equal(view.engine._options.cache, false) }) test('configure edge with cache enabled', (assert) => { const view = new View(this.helpers, true) assert.equal(view.engine._options.cache, true) }) test('call edge methods directly on the engine', (assert) => { const view = new View(this.helpers, true) assert.equal(view.engine.renderString('{{ 2 + 2 }}').trim(), '4') }) test('define globals', (assert) => { const view = new View(this.helpers, true) const fn = function () {} view.global('myGlobal', fn) assert.equal(view.engine._globals.myGlobal, fn) }) test('render string', (assert) => { const view = new View(this.helpers, true) assert.equal(view.renderString('{{ 2 + 2 }}').trim(), '4') }) test('share locals', (assert) => { const view = new View(this.helpers, true) assert.equal(view.share({age: 22}).renderString('{{ age }}').trim(), '22') }) test('render a view', async (assert) => { await pify(fs.writeFile)(path.join(__dirname, 'resources/views/hello.edge'), 'Hello {{ name }}') const view = new View(this.helpers, true) assert.equal(view.render('hello', { name: 'virk' }).trim(), 'Hello virk') }) test('get access to the base presenter', async (assert) => { const view = new View(this.helpers, true) assert.isDefined(view.BasePresenter) }) })
test.group('Views tags', () => { test('convert inline svg to svg source', (assert) => { const view = new View(this.helpers) Tags(view, this.helpers) const fooSvg = path.join(__dirname, './public', 'logo.svg') const template = `@inlineSvg('${fooSvg}')` assert.equal(view.renderString(template).trim(), '<svg id="logo"></svg>') }) test('throw exception with correct lineno when file is not found', (assert) => { assert.plan(3) const view = new View(this.helpers) Tags(view, this.helpers) const fooSvg = path.join(__dirname, './public', 'foo.svg') const template = ` <h2> Hello </h2> @inlineSvg('${fooSvg}') ` try { view.renderString(template).trim() } catch ({ code, lineno, charno }) { assert.equal(code, 'ENOENT') assert.equal(lineno, 2) assert.equal(charno, 1) } }) test('load file when path is not absolute', (assert) => { const view = new View(this.helpers) Tags(view, this.helpers) const template = `@inlineSvg('logo.svg')` assert.equal(view.renderString(template).trim(), '<svg id="logo"></svg>') }) test('prefix svg extension if missing', (assert) => { const view = new View(this.helpers) Tags(view, this.helpers) const template = `@inlineSvg('logo')` assert.equal(view.renderString(template).trim(), '<svg id="logo"></svg>') }) })
test.group('Sanitizations | To Date', () => { test('should convert a date to date object', function (assert) { const sanitized = sanitizations.toDate('2015-10-20') assert.deepEqual(sanitized, new Date('2015-10-20')) }) test('do not convert existing date objects', function (assert) { const date = new Date() const sanitized = sanitizations.toDate(date) assert.deepEqual(sanitized, date) }) test('should return null when is not a valid date', function (assert) { const sanitized = sanitizations.toDate('hello') assert.equal(sanitized, null) }) })
test.group('Sanitizations | Normalize email', () => { test('normalize email by lowercasing the domain', (assert) => { assert.equal(sanitizations.normalizeEmail('*****@*****.**'), '*****@*****.**') }) test('remove dots from gmail email address', (assert) => { assert.equal(sanitizations.normalizeEmail('*****@*****.**'), '*****@*****.**') }) test('should return original value email is incorrect', function (assert) { assert.equal(sanitizations.normalizeEmail(1111), 1111) }) test('return email as it is when invalid', function (assert) { assert.equal(sanitizations.normalizeEmail('*****@*****.**'), '*****@*****.**') }) })
test.group('Relations Parser', () => { test('parse individual relationship string', (assert) => { const parsed = RelationsParser.parseRelation('posts') assert.deepEqual(parsed, { name: 'posts', nested: null, callback: null }) }) test('parse relationship string with callback', (assert) => { const fn = function () {} const parsed = RelationsParser.parseRelation('posts', fn) assert.deepEqual(parsed, { name: 'posts', nested: null, callback: fn }) }) test('parse nested relationship string', (assert) => { const parsed = RelationsParser.parseRelation('posts.comments') assert.deepEqual(parsed, { name: 'posts', nested: { comments: null }, callback: null }) }) test('parse nested relationship with callback', (assert) => { const fn = function () {} const parsed = RelationsParser.parseRelation('posts.comments', fn) assert.deepEqual(parsed, { name: 'posts', nested: { comments: fn }, callback: null }) }) test('parse deeply nested relationship', (assert) => { const parsed = RelationsParser.parseRelation('posts.comments.votes') assert.deepEqual(parsed, { name: 'posts', nested: { 'comments.votes': null }, callback: null }) }) test('parse deeply nested relationship with callback', (assert) => { const fn = function () {} const parsed = RelationsParser.parseRelation('posts.comments.votes', fn) assert.deepEqual(parsed, { name: 'posts', nested: { 'comments.votes': fn }, callback: null }) }) test('parse multiple nested relations', (assert) => { const parsed = RelationsParser.parseRelations({ 'posts.comments': null, 'posts.likes': null }) assert.deepEqual(parsed, { posts: { name: 'posts', callback: null, nested: { comments: null, likes: null } } }) }) test('parse multiple nested relations when first relation doesn\t have nesting', (assert) => { const parsed = RelationsParser.parseRelations({ 'posts': null, 'posts.comments': null, 'posts.likes': null }) assert.deepEqual(parsed, { posts: { name: 'posts', callback: null, nested: { comments: null, likes: null } } }) }) test('parse multiple nested relations with callback', (assert) => { const fn = function () {} const parsed = RelationsParser.parseRelations({ 'posts.comments': null, 'posts.likes': null, posts: fn }) assert.deepEqual(parsed, { posts: { name: 'posts', callback: fn, nested: { comments: null, likes: null } } }) }) })
test.group('Route | Group', () => { test('group routes', (assert) => { const route = new Route('/', function () {}) const group = new RouteGroup([route]) assert.deepEqual(group._routes, [route]) }) test('add middleware to route via group', (assert) => { const route = new Route('/', function () {}) const group = new RouteGroup([route]) group.middleware(['foo']) assert.deepEqual(route._middleware, ['foo']) }) test('add formats to route via group', (assert) => { const route = new Route('/', function () {}) const userRoute = new Route('/user', function () {}) const group = new RouteGroup([route, userRoute]) group.formats(['json'], true) assert.ok(route._regexp.test('/.json')) assert.ok(userRoute._regexp.test('/user.json')) }) test('prefix route via group', (assert) => { const route = new Route('/', function () {}) const userRoute = new Route('/user', function () {}) const group = new RouteGroup([route, userRoute]) group.prefix('api') assert.ok(route._regexp.test('/api')) assert.ok(userRoute._regexp.test('/api/user')) }) test('define domain via group', (assert) => { const route = new Route('/', function () {}) const postsRoute = new Route('/posts', function () {}) const group = new RouteGroup([route, postsRoute]) group.domain('blog.adonisjs.com') assert.ok(route._domain.test('blog.adonisjs.com')) assert.ok(postsRoute._domain.test('blog.adonisjs.com')) }) })
test.group('Sanitizations | Strip tags', () => { test('should remove tags from a given string', function (assert) { const para = `Click <a href="http://google.com">here</a> to search and visit <a href="http://adonisjs.com">AdonisJs</a>` const sanitized = sanitizations.stripTags(para, []) assert.equal(sanitized, 'Click here to search and visit AdonisJs') }) test('whitelist tags', function (assert) { const para = `Click <a href="http://google.com">here</a> to search and visit <a href="http://adonisjs.com">AdonisJs</a>` const sanitized = sanitizations.stripTags(para, ['a']) assert.equal(sanitized, 'Click <a href="http://google.com">here</a> to search and visit <a href="http://adonisjs.com">AdonisJs</a>') }) test('should actual value when value is not a string', function (assert) { const para = 11 const sanitized = sanitizations.stripTags(para) assert.equal(sanitized, para) }) })
test.group('Sanitizations | toNull', () => { test('convert empty string to null', function (assert) { assert.isNull(sanitizations.toNull('')) }) test('convert empty string with multiple spaces to null', function (assert) { assert.isNull(sanitizations.toNull(' ')) }) test('convert undefined to null', function (assert) { assert.isNull(sanitizations.toNull()) }) test('convert null to null', function (assert) { assert.isNull(sanitizations.toNull(null)) }) test('return original value when actual value is empty array', function (assert) { assert.isArray(sanitizations.toNull([])) }) })
test.group('Sanitizations | Slug', () => { test('should convert a value to a slug', function (assert) { const sanitized = sanitizations.slug('learn jquery in 30minutes') assert.equal(sanitized, 'learn-jquery-in-30-minutes') }) test('should convert a weired value to a slug', function (assert) { const sanitized = sanitizations.slug('weird[case') assert.equal(sanitized, 'weird-case') }) test('should convert a dot seperate value to a slug', function (assert) { const sanitized = sanitizations.slug('dot.case') assert.equal(sanitized, 'dot-case') }) test('should convert a weird character to a slug', function (assert) { const sanitized = sanitizations.slug('tôi tên là đức tạ') assert.equal(sanitized, 'toi-ten-la-duc-ta') }) test('return exact value when its a number', function (assert) { const sanitized = sanitizations.slug(12) assert.equal(sanitized, 12) }) test('return exact value when value is null', function (assert) { const sanitized = sanitizations.slug(null) assert.isNull(sanitized) }) })
test.group('Sanitizations | To Boolean', () => { test('should convert string false to boolean false', function (assert) { const sanitized = sanitizations.toBoolean('false') assert.equal(sanitized, false) }) test('should convert string 0 to boolean false', function (assert) { const sanitized = sanitizations.toBoolean('0') assert.equal(sanitized, false) }) test('should convert boolean false to boolean false', function (assert) { const sanitized = sanitizations.toBoolean(false) assert.equal(sanitized, false) }) test('should convert integer 0 to boolean false', function (assert) { const sanitized = sanitizations.toBoolean(0) assert.equal(sanitized, false) }) test('should convert empty string to boolean false', function (assert) { const sanitized = sanitizations.toBoolean('') assert.equal(sanitized, false) }) test('should convert any string to boolean true', function (assert) { const sanitized = sanitizations.toBoolean('hello') assert.equal(sanitized, true) }) })
test.group('Middleware | Guest', (group) => { groupSetup.databaseHook(group) groupSetup.hashHook(group) group.before(async () => { await setup() }) group.beforeEach(() => { this.server = http.createServer() }) test('throw exception when trying to reach a guest route and user is logged in', async (assert) => { await ioc.use('App/Models/User').create({ email: '*****@*****.**', password: '******' }) this.server.on('request', (req, res) => { const Context = ioc.use('Adonis/Src/HttpContext') const ctx = new Context() ctx.request = helpers.getRequest(req) ctx.response = helpers.getResponse(req, res) ctx.session = helpers.getSession(req, res) const guestMiddleware = ioc.use('Adonis/Middleware/AllowGuestOnly') guestMiddleware .handle(ctx, function () {}) .then((status) => { res.writeHead(200) res.write('skipped') res.end() }) .catch(({ status, message }) => { res.writeHead(status || 500) res.write(message) res.end() }) }) const { text } = await supertest(this.server).get('/').set('Cookie', 'adonis-auth=1').expect(403) assert.equal(text, `E_GUEST_ONLY: Only guest user can access the route GET /`) }) })
test.group('Route | Group', () => { test('group routes', (assert) => { const route = new Route('/', function () {}) const group = new RouteGroup([route]) assert.deepEqual(group._routes, [route]) }) test('add middleware to route via group', (assert) => { const route = new Route('/', function () {}) const group = new RouteGroup([route]) group.middleware(['foo']) assert.deepEqual(route.middlewareList, ['foo']) }) test('add formats to route via group', (assert) => { const route = new Route('/', function () {}) const userRoute = new Route('/user', function () {}) const group = new RouteGroup([route, userRoute]) group.formats(['json'], true) assert.ok(route._regexp.test('/.json')) assert.ok(userRoute._regexp.test('/user.json')) }) test('prefix route via group', (assert) => { const route = new Route('/', function () {}) const userRoute = new Route('/user', function () {}) const group = new RouteGroup([route, userRoute]) group.prefix('api') assert.ok(route._regexp.test('/api')) assert.ok(userRoute._regexp.test('/api/user')) }) test('namespace route via group', (assert) => { const route = new Route('/', 'IndexController') const userRoute = new Route('/user', 'UserController') const group = new RouteGroup([route, userRoute]) group.namespace('Admin') assert.equal(route.handler, 'Admin/IndexController') assert.equal(userRoute.handler, 'Admin/UserController') }) test('define domain via group', (assert) => { const route = new Route('/', function () {}) const postsRoute = new Route('/posts', function () {}) const group = new RouteGroup([route, postsRoute]) group.domain('blog.adonisjs.com') assert.ok(route.forDomain.test('blog.adonisjs.com')) assert.ok(postsRoute.forDomain.test('blog.adonisjs.com')) }) })
test.group('Response', (group) => { group.beforeEach(() => { RouteStore.clear() }) test('send raw string as response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.send('hello world') response.end() }) const res = await supertest(server).get('/').expect('Content-Type', /plain/).expect(200) assert.equal(res.text, 'hello world') }) test('send json object as response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.send({name: 'virk'}) response.end() }) const res = await supertest(server).get('/').expect('Content-Type', /json/).expect(200) assert.deepEqual(res.body, {name: 'virk'}) }) test('send number as response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.send(22) response.end() }) const res = await supertest(server).get('/').expect('Content-Type', /plain/).expect(200) assert.equal(res.text, 22) }) test('send boolean as response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.send(true) response.end() }) const res = await supertest(server).get('/').expect('Content-Type', /plain/).expect(200) assert.equal(res.text, 'true') }) test('send html as response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.send('<h2> Hello world </h2>') response.end() }) const res = await supertest(server).get('/').expect('Content-Type', /html/).expect(200) assert.equal(res.text, '<h2> Hello world </h2>') }) test('set http response status', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.status(304).send('hello') response.end() }) await supertest(server).get('/').expect(304) }) test('set http request header', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.header('Link', ['<http://localhost/>', '<http://localhost:3000/>']) response.end() }) await supertest(server).get('/').expect('link', '<http://localhost/>, <http://localhost:3000/>') }) test('only set the header when does not exists', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.safeHeader('Content-Type', 'application/json') response.safeHeader('Content-Type', 'text/plain') response.send('') response.end() }) await supertest(server).get('/').expect('Content-Type', /json/) }) test('remove response header', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.header('Link', ['<http://localhost/>', '<http://localhost:3000/>']) response.removeHeader('link') response.end() }) const res = await supertest(server).get('/') assert.notProperty(res.headers, 'link') }) test('get value for existing header', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.header('Link', ['<http://localhost/>', '<http://localhost:3000/>']) response.send(response.getHeader('link')) response.end() }) const res = await supertest(server).get('/').expect(200) assert.deepEqual(res.body, ['<http://localhost/>', '<http://localhost:3000/>']) }) test('download file', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.download(path.join(__dirname, '../../package.json')) }) const res = await supertest(server).get('/').expect(200) assert.isObject(res.body) assert.equal(res.body.name, '@adonisjs/framework') }) test('send 404 when file does not exists', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.download(path.join(__dirname, '../../logo.svg')) }) await supertest(server).get('/').expect(404) }) test('force download the file by setting content-disposition', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.attachment(path.join(__dirname, '../../package.json')) }) const res = await supertest(server).get('/').expect('Content-Disposition', /filename="package.json"/) assert.isObject(res.body) assert.equal(res.body.name, '@adonisjs/framework') }) test('force download the file with different file name', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.attachment(path.join(__dirname, '../../package.json'), 'adonis.json') }) const res = await supertest(server).get('/').expect('Content-Disposition', /filename="adonis.json"/) assert.isObject(res.body) assert.equal(res.body.name, '@adonisjs/framework') }) test('set the location http header', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.location('http://adonisjs.com') response.end() }) await supertest(server).get('/').expect('Location', 'http://adonisjs.com') }) test('redirect the request', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.redirect('http://adonisjs.com') response.end() }) await supertest(server).get('/').expect('Location', 'http://adonisjs.com').expect(302) }) test('set content-type based on type', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.type('html').send({username: '******'}) response.end() }) await supertest(server).get('/').expect('Content-Type', /html/).expect(200) }) test('send content as json with content-type explicitly set to text/javascript', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.jsonp({username: '******'}) response.end() }) await supertest(server).get('/').expect('Content-Type', /javascript/).expect(200) }) test('use the request query param callback for jsonp response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.jsonp({username: '******'}) response.end() }) const res = await supertest(server).get('/?callback=exec').expect(200) assert.equal(res.text, `/**/ typeof exec === 'function' && exec({"username":"******"});`) }) test('use the explicit callbackFn over request query param', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.jsonp({username: '******'}, 'eval') response.end() }) const res = await supertest(server).get('/?callback=exec').expect(200) assert.equal(res.text, `/**/ typeof eval === 'function' && eval({"username":"******"});`) }) test('set 401 as the status via unauthorized method', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.unauthorized('Login First') response.end() }) const res = await supertest(server).get('/').expect(401) assert.equal(res.text, 'Login First') }) test('save cookie to the browser', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.cookie('cart_total', '20') response.send('') response.end() }) const { headers } = await supertest(server).get('/').expect(200) assert.equal(headers['set-cookie'][0], 'cart_total=20') }) test('encrypt cookie when secret is defined', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() config.set('app.appKey', SECRET) const response = new Response(new Request(req, res), config) response.cookie('cart_total', '20') response.send('') response.end() }) const { headers } = await supertest(server).get('/').expect(200) const encrypter = simpleEncryptor({ key: SECRET, hmac: false }) assert.strictEqual( sig.unsign( encrypter.decrypt(querystring.unescape(headers['set-cookie'][0].replace('cart_total=', ''))).replace('s:', ''), SECRET ), '20' ) }) test('send plain cookie even if secret is defined', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() config.set('app.appKey', SECRET) const response = new Response(new Request(req, res), config) response.plainCookie('cart_total', '20') response.send('') response.end() }) const { headers } = await supertest(server).get('/').expect(200) assert.equal(headers['set-cookie'][0], 'cart_total=20') }) test('send vary header', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() const response = new Response(new Request(req, res), config) response.vary('Origin') response.send('') response.end() }) const { headers } = await supertest(server).get('/').expect(200) assert.equal(headers.vary, 'Origin') }) test('clear existing cookie by setting expiry in past', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() const response = new Response(new Request(req, res), config) response.clearCookie('cart') response.send('') response.end() }) const { headers } = await supertest(server).get('/').expect(200) assert.equal(headers['set-cookie'][0], 'cart=; Expires=Thu, 01 Jan 1970 00:00:00 GMT') }) test('redirect to a registered route', async (assert) => { RouteManager.get('users', function () {}).as('listUsers') const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.route('listUsers') response.end() }) await supertest(server).get('/').expect('Location', '/users').expect(302) }) test('redirect to a registered route by passing controller method', async (assert) => { RouteManager.get('users', 'UserController.index') const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.route('UserController.index') response.end() }) await supertest(server).get('/').expect('Location', '/users').expect(302) }) test('redirect to the string when unable to resolve route', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.route('UserController.index') response.end() }) await supertest(server).get('/').expect('Location', 'UserController.index').expect(302) }) test('redirect to dynamic route', async (assert) => { RouteManager.get('users/:id', 'UserController.show') const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.route('UserController.show', { id: 2 }) response.end() }) await supertest(server).get('/').expect('Location', '/users/2').expect(302) }) test('redirect with params', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.redirect('users', true) response.end() }) await supertest(server).get('/?name=virk&age=22').expect('Location', 'users?name=virk&age=22').expect(302) }) test('throw abort exception when abortIf expression is truthy', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) try { response.abortIf(true, 500, 'Aborted') } catch (error) { response.status(error.status) response.send(error.body) } response.end() }) const response = await supertest(server) .get('/?name=virk&age=22') .expect(500) assert.equal(response.error.text, 'Aborted') }) test('throw abort exception with default body and status', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) try { response.abortIf(true) } catch (error) { response.status(error.status) response.send(error.body) } response.end() }) const response = await supertest(server) .get('/?name=virk&age=22') .expect(400) assert.equal(response.error.text, 'Request aborted') }) test('evaluate expression when expression is a function', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) try { response.abortIf(function () { return true }) } catch (error) { response.status(error.status) response.send(error.body) } response.end() }) const response = await supertest(server) .get('/?name=virk&age=22') .expect(400) assert.equal(response.error.text, 'Request aborted') }) test('do not throw exception when expression is not truthy', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) try { response.abortIf(function () { return false }) } catch (error) { response.status(error.status) response.send(error.body) } response.end() }) await supertest(server) .get('/?name=virk&age=22') .expect(204) }) test('throw abort exception when abortUnless expression is falsy', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) try { response.abortUnless(false, 500, 'Aborted') } catch (error) { response.status(error.status) response.send(error.body) } response.end() }) const response = await supertest(server) .get('/?name=virk&age=22') .expect(500) assert.equal(response.error.text, 'Aborted') }) test('throw abort exception with default body and status', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) try { response.abortUnless(false) } catch (error) { response.status(error.status) response.send(error.body) } response.end() }) const response = await supertest(server) .get('/?name=virk&age=22') .expect(400) assert.equal(response.error.text, 'Request aborted') }) test('evaluate expression when expression is a function', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) try { response.abortUnless(function () { return false }) } catch (error) { response.status(error.status) response.send(error.body) } response.end() }) const response = await supertest(server) .get('/?name=virk&age=22') .expect(400) assert.equal(response.error.text, 'Request aborted') }) test('do not throw exception when expression is not falsy', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) try { response.abortUnless(function () { return true }) } catch (error) { response.status(error.status) response.send(error.body) } response.end() }) await supertest(server) .get('/?name=virk&age=22') .expect(204) }) test('do not set etag when set as false', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.send('hello world', false) response.end() }) const res = await supertest(server).get('/').expect(200) assert.notProperty(res.headers, 'etag') }) test('pull etag from config file', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() config.set('app.http.etag', false) const response = new Response(new Request(req, res), config) response.send('hello world') response.end() }) const res = await supertest(server).get('/').expect(200) assert.notProperty(res.headers, 'etag') }) test('fallback etag to true', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() config.set('app.http.etag', true) const response = new Response(new Request(req, res), config) response.send('hello world') response.end() }) const res = await supertest(server).get('/').expect(200) assert.property(res.headers, 'etag') }) test('do not set etag when set as false in explicit mode', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.implicitEnd = false response.send('hello world', false) }) const res = await supertest(server).get('/').expect(200) assert.notProperty(res.headers, 'etag') }) test('pull etag from config file in explicit mode', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() config.set('app.http.etag', false) const response = new Response(new Request(req, res), config) response.implicitEnd = false response.send('hello world') }) const res = await supertest(server).get('/').expect(200) assert.notProperty(res.headers, 'etag') }) test('fallback etag to true in explicit mode', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() config.set('app.http.etag', true) const response = new Response(new Request(req, res), config) response.implicitEnd = false response.send('hello world') }) const res = await supertest(server).get('/').expect(200) assert.property(res.headers, 'etag') }) test('send 204 when response body is null', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.send(null) response.end() }) await supertest(server).get('/').expect(204) }) test('send empty response for HEAD request', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.send({ username: '******' }) response.end() }) const { body } = await supertest(server).head('/') assert.deepEqual(body, {}) }) test('set response body empty when request is fresh', async (assert) => { const server = http.createServer((req, res) => { const resourceEtag = etag('hello world') req.headers['if-none-match'] = resourceEtag const config = new Config() config.set('app.http.etag', true) const response = new Response(new Request(req, res), config) response.send('hello world') response.end() }) await supertest(server).get('/').expect(304) }) test('return exact response when etag is disabled', async (assert) => { const server = http.createServer((req, res) => { const resourceEtag = etag('hello world') req.headers['if-none-match'] = resourceEtag const response = new Response(new Request(req, res), new Config()) response.send('hello world') response.end() }) await supertest(server).get('/').expect(200) }) test('do not generate etag for intentional 400 status code', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() config.set('app.http.etag', true) const response = new Response(new Request(req, res), config) response.notFound('Not found') response.end() }) const { headers } = await supertest(server).get('/').expect(404) assert.notProperty(headers, 'etag') }) test('generate etag when using description methods', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.ok('Received', true) response.end() }) const { headers } = await supertest(server).get('/').expect(200) assert.property(headers, 'etag') }) test('do not generate etag for POST request', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.ok('Received', true) response.end() }) const { headers } = await supertest(server).post('/').expect(200) assert.notProperty(headers, 'etag') }) test('do not generate etag for PUT request', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.ok('Received', true) response.end() }) const { headers } = await supertest(server).put('/').expect(200) assert.notProperty(headers, 'etag') }) test('generate etag using json method', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(new Request(req, res), new Config()) response.json({ username: '******' }, true) response.end() }) const { headers } = await supertest(server).get('/').expect('content-type', 'application/json; charset=utf-8').expect(200) assert.property(headers, 'etag') }) })
test.group('Scheduler', (group) => { /** * @param {String} name * @return {Helpers} */ function getHelpers (name) { ioc.fake('Adonis/Src/Helpers', () => new Helpers(path.join(__dirname, `./projects/${name}`))) return ioc.use('Adonis/Src/Helpers') } group.before(() => { ioc.fake('Task', () => require('./../src/Scheduler/Task')) ioc.fake('Adonis/Src/Logger', () => new Logger()) }) test('Should instantiate correctly', (assert) => { const Helpers = getHelpers('good') const scheduler = new Scheduler(Helpers) assert.equal(scheduler.tasksPath, path.join(Helpers.appRoot(), 'app', 'Tasks')) assert.deepEqual(scheduler.registeredTasks, []) }) test('Should run with good tasks', async (assert) => { const Helpers = getHelpers('good') const scheduler = new Scheduler(Helpers) await scheduler.run() assert.isArray(scheduler.registeredTasks) assert.equal(scheduler.registeredTasks.length, 1) }) test('Should ignore invalid task file types', async (assert) => { const Helpers = getHelpers('badFile') const scheduler = new Scheduler(Helpers) try { await scheduler.run() assert.isTrue(true) } catch (e) { assert.isTrue(false) } }) test('Should fail to run gracefully if task has no schedule property', async (assert) => { const Helpers = getHelpers('noSchedule') const scheduler = new Scheduler(Helpers) try { await scheduler.run() assert.isTrue(false) } catch (e) { assert.isTrue(true) } }) test('Should fail to run gracefully if task has no handler', async (assert) => { const Helpers = getHelpers('noHandler') const scheduler = new Scheduler(Helpers) try { await scheduler.run() assert.isTrue(false) } catch (e) { assert.isTrue(true) } }) test('Should fail to run gracefully if no task dir exists', async (assert) => { const Helpers = getHelpers('noTasksDir') const scheduler = new Scheduler(Helpers) try { await scheduler.run() assert.isTrue(false) } catch (e) { assert.isTrue(true) } }) test('Should fail to run gracefully if task dir is empty', async (assert) => { const Helpers = getHelpers('noTasks') const scheduler = new Scheduler(Helpers) try { await scheduler.run() assert.isTrue(false) } catch (e) { assert.isTrue(true) } }) })
test.group('Seed Database', (group) => { group.before(async () => { ioc.bind('Adonis/Src/Config', () => { const config = new Config() config.set('database', { connection: 'testing', testing: helpers.getConfig() }) return config }) ioc.bind('Adonis/Src/Helpers', () => { return new Helpers(path.join(__dirname)) }) await fs.ensureDir(path.join(__dirname, 'database/seeds')) await registrar .providers([ path.join(__dirname, '../../providers/LucidProvider'), path.join(__dirname, '../../providers/MigrationsProvider') ]).registerAndBoot() await fs.ensureDir(path.join(__dirname, '../unit/tmp')) await helpers.createTables(ioc.use('Database')) setupResolver() }) group.afterEach(async () => { ace.commands = {} }) group.after(async () => { await helpers.dropTables(ioc.use('Database')) ioc.use('Database').close() try { await fs.remove(path.join(__dirname, '../unit/tmp')) await fs.remove(path.join(__dirname, 'database')) } catch (error) { if (process.platform !== 'win32' || error.code !== 'EBUSY') { throw error } } }).timeout(0) test('skip when there are no seed files', async (assert) => { ace.addCommand(Seed) const result = await ace.call('seed') assert.equal(result, 'Nothing to seed') }) test('run seeds in sequence', async (assert) => { ace.addCommand(Seed) const g = global || GLOBAL g.stack = [] await fs.outputFile(path.join(__dirname, 'database/seeds/bar.js'), ` class Seed { run () { return new Promise((resolve) => { setTimeout(() => { (global || GLOBAL).stack.push('bar') resolve() }, 10) }) } } module.exports = Seed `) await fs.outputFile(path.join(__dirname, 'database/seeds/baz.js'), ` class Seed { run () { (global || GLOBAL).stack.push('baz') } } module.exports = Seed `) await ace.call('seed') assert.deepEqual(global.stack, ['bar', 'baz']) }) test('run only selected files', async (assert) => { ace.addCommand(Seed) const g = global || GLOBAL g.stack = [] await fs.outputFile(path.join(__dirname, 'database/seeds/bar.js'), ` class Seed { run () { return new Promise((resolve) => { setTimeout(() => { (global || GLOBAL).stack.push('bar') resolve() }, 10) }) } } module.exports = Seed `) await fs.outputFile(path.join(__dirname, 'database/seeds/foo.js'), ` class Seed { run () { (global || GLOBAL).stack.push('foo') } } module.exports = Seed `) await ace.call('seed', {}, { files: 'foo.js' }) assert.deepEqual(global.stack, ['foo']) }) })
test.group('Relations | Has Many Through - Belongs To Many', (group) => { group.before(async () => { ioc.singleton('Adonis/Src/Database', function () { const config = new Config() config.set('database', { connection: 'testing', testing: helpers.getConfig() }) return new DatabaseManager(config) }) ioc.alias('Adonis/Src/Database', 'Database') await fs.ensureDir(path.join(__dirname, './tmp')) await helpers.createTables(ioc.use('Adonis/Src/Database')) }) group.afterEach(async () => { await ioc.use('Adonis/Src/Database').table('categories').truncate() await ioc.use('Adonis/Src/Database').table('sections').truncate() await ioc.use('Adonis/Src/Database').table('posts').truncate() await ioc.use('Adonis/Src/Database').table('post_section').truncate() }) group.after(async () => { await helpers.dropTables(ioc.use('Adonis/Src/Database')) ioc.use('Database').close() try { await fs.remove(path.join(__dirname, './tmp')) } catch (error) { if (process.platform !== 'win32' || error.code !== 'EBUSY') { throw error } } }).timeout(0) test('create correct query', (assert) => { class Post extends Model { } class Section extends Model { posts () { return this.belongsToMany(Post) } } class Category extends Model { posts () { return this.manyThrough(Section, 'posts') } } Category._bootIfNotBooted() Section._bootIfNotBooted() Post._bootIfNotBooted() const category = new Category() category.id = 1 category.$persisted = true const query = category.posts().toSQL() assert.equal(query.sql, helpers.formatQuery('select "posts".* from "posts" inner join "post_section" on "posts"."id" = "post_section"."post_id" inner join "sections" on "sections"."id" = "post_section"."section_id" where "sections"."category_id" = ?')) }) test('selected related rows', async (assert) => { class Post extends Model { } class Section extends Model { posts () { return this.belongsToMany(Post) } } class Category extends Model { posts () { return this.manyThrough(Section, 'posts') } } Category._bootIfNotBooted() Section._bootIfNotBooted() Post._bootIfNotBooted() await ioc.use('Database').table('categories').insert([{ name: 'Sql' }, { name: 'Javascript' }]) await ioc.use('Database').table('sections').insert([ { name: 'Loops', category_id: 2 }, { name: 'Conditionals', category_id: 2 } ]) await ioc.use('Database').table('posts').insert({ title: 'For each loop' }) await ioc.use('Database').table('post_section').insert({ post_id: 1, section_id: 1 }) const js = await Category.find(2) const posts = await js.posts().fetch() assert.equal(posts.size(), 1) assert.equal(posts.rows[0].title, 'For each loop') }) test('eagerload related rows', async (assert) => { class Post extends Model { } class Section extends Model { posts () { return this.belongsToMany(Post) } } class Category extends Model { posts () { return this.manyThrough(Section, 'posts') } } Category._bootIfNotBooted() Section._bootIfNotBooted() Post._bootIfNotBooted() await ioc.use('Database').table('categories').insert([{ name: 'Sql' }, { name: 'Javascript' }]) await ioc.use('Database').table('sections').insert([ { name: 'Loops', category_id: 2 }, { name: 'Conditionals', category_id: 2 }, { name: 'Transactions', category_id: 1 } ]) await ioc.use('Database').table('posts').insert([{ title: 'For each loop' }, { title: 'Transactions 101' }]) await ioc.use('Database').table('post_section').insert([ { post_id: 1, section_id: 1 }, { post_id: 2, section_id: 3 } ]) const categories = await Category.query().with('posts').orderBy('id', 'asc').fetch() assert.instanceOf(categories, VanillaSerializer) assert.equal(categories.size(), 2) assert.equal(categories.last().getRelated('posts').size(), 1) assert.equal(categories.last().getRelated('posts').toJSON()[0].title, 'For each loop') assert.equal(categories.first().getRelated('posts').size(), 1) assert.equal(categories.first().getRelated('posts').toJSON()[0].title, 'Transactions 101') }) test('add constraints when eager loading', async (assert) => { class Post extends Model { } class Section extends Model { posts () { return this.belongsToMany(Post) } } class Category extends Model { posts () { return this.manyThrough(Section, 'posts') } } Category._bootIfNotBooted() Section._bootIfNotBooted() Post._bootIfNotBooted() let postsQuery = null Post.onQuery((query) => (postsQuery = query)) await ioc.use('Database').table('categories').insert([{ name: 'Sql' }, { name: 'Javascript' }]) await ioc.use('Database').table('sections').insert([ { name: 'Loops', category_id: 2, is_active: true }, { name: 'Conditionals', category_id: 2, is_active: true }, { name: 'Transactions', category_id: 1 } ]) await ioc.use('Database').table('posts').insert([{ title: 'For each loop' }, { title: 'Transactions 101' }]) await ioc.use('Database').table('post_section').insert([ { post_id: 1, section_id: 1 }, { post_id: 2, section_id: 3 } ]) const categories = await Category.query().with('posts', (builder) => { builder.where('sections.is_active', true) }).orderBy('id', 'asc').fetch() assert.equal(postsQuery.sql, helpers.formatQuery('select "posts".*, "sections"."category_id" as "through_category_id" from "posts" inner join "post_section" on "posts"."id" = "post_section"."post_id" inner join "sections" on "sections"."id" = "post_section"."section_id" where "sections"."is_active" = ? and "sections"."category_id" in (?, ?)')) assert.instanceOf(categories, VanillaSerializer) assert.equal(categories.size(), 2) assert.equal(categories.last().getRelated('posts').size(), 1) assert.equal(categories.last().getRelated('posts').toJSON()[0].title, 'For each loop') assert.equal(categories.first().getRelated('posts').size(), 0) }) test('fetch related row via IoC container binding', async (assert) => { class Post extends Model { } class User extends Model { posts () { return this.hasMany('App/Models/Post') } } ioc.fake('App/Models/Post', () => Post) ioc.fake('App/Models/User', () => User) class Country extends Model { posts () { return this.manyThrough('App/Models/User', 'posts') } } User._bootIfNotBooted() Country._bootIfNotBooted() Post._bootIfNotBooted() await ioc.use('Database').table('countries').insert({ name: 'India', id: 2 }) await ioc.use('Database').table('users').insert({ country_id: 2, id: 20, username: '******' }) await ioc.use('Database').table('posts').insert({ user_id: 20, title: 'Adonis 101' }) const country = await Country.find(2) const posts = await country.posts().fetch() assert.instanceOf(posts, VanillaSerializer) assert.equal(posts.size(), 1) assert.deepEqual(posts.toJSON(), [ { id: 1, user_id: 20, title: 'Adonis 101', created_at: null, updated_at: null, deleted_at: null } ]) }) })
test.group('Migration Rollback', (group) => { group.before(async () => { ioc.bind('Adonis/Src/Config', () => { const config = new Config() config.set('database', { connection: 'testing', testing: helpers.getConfig() }) return config }) ioc.bind('Adonis/Src/Helpers', () => { return new Helpers(path.join(__dirname)) }) await fs.ensureDir(path.join(__dirname, 'database/migrations')) await registrar .providers([ path.join(__dirname, '../../providers/LucidProvider'), path.join(__dirname, '../../providers/MigrationsProvider') ]).registerAndBoot() await fs.ensureDir(path.join(__dirname, '../unit/tmp')) await helpers.createTables(ioc.use('Database')) setupResolver() }) group.afterEach(async () => { ace.commands = {} await ioc.use('Database').table('adonis_schema').truncate() await ioc.use('Database').schema.dropTableIfExists('schema_users') }) group.after(async () => { await helpers.dropTables(ioc.use('Database')) await ioc.use('Database').schema.dropTableIfExists('adonis_schema') ioc.use('Database').close() try { await fs.remove(path.join(__dirname, '../unit/tmp')) await fs.remove(path.join(__dirname, 'database')) } catch (error) { if (process.platform !== 'win32' || error.code !== 'EBUSY') { throw error } } }).timeout(0) test('skip when there is nothing to rollback', async (assert) => { ace.addCommand(MigrationRollback) const result = await ace.call('migration:rollback') assert.deepEqual(result, { migrated: [], status: 'skipped', queries: undefined }) }) test('rollback migrations by requiring all schema files', async (assert) => { ace.addCommand(MigrationRun) ace.addCommand(MigrationRollback) await fs.writeFile(path.join(__dirname, 'database/migrations/User.js'), ` const Schema = use('Schema') class User extends Schema { up () { this.createTable('schema_users', (table) => { table.increments() table.string('username') }) } down () { this.drop('schema_users') } } module.exports = User `) await ace.call('migration:run') const result = await ace.call('migration:rollback') assert.deepEqual(result, { migrated: ['User'], status: 'completed', queries: undefined }) const migrations = await ioc.use('Database').table('adonis_schema') assert.lengthOf(migrations, 0) }) test('log queries when asked to log', async (assert) => { ace.addCommand(MigrationRun) ace.addCommand(MigrationRollback) await fs.writeFile(path.join(__dirname, 'database/migrations/User.js'), ` const Schema = use('Schema') class User extends Schema { up () { this.createTable('schema_users', (table) => { table.increments() table.string('username') }) } down () { this.drop('schema_users') } } module.exports = User `) await ace.call('migration:run') const result = await ace.call('migration:rollback', {}, { log: true }) assert.isArray(result.queries) }) })
test.group('Indicative', () => { test('should be able to call validate with inbuilt rules', async (assert) => { assert.plan(1) const data = {} const rules = { username: '******' } try { await indicative.validate(data, rules) } catch (errors) { assert.deepEqual(errors, [ { validation: 'required', field: 'username', message: 'required validation failed on username' } ]) } }) test('should be able to call validateAll with inbuilt rules', async (assert) => { assert.plan(1) const data = {} const rules = { username: '******', email: 'required' } try { await indicative.validateAll(data, rules) } catch (errors) { assert.deepEqual(errors, [ { validation: 'required', field: 'username', message: 'required validation failed on username' }, { validation: 'required', field: 'email', message: 'required validation failed on email' } ]) } }) test('should be able to call raw validation methods', async (assert) => { assert.isTrue(indicative.is.existy('hello')) }) test('should be able to use sanitize method', async (assert) => { const data = { email: '*****@*****.**' } const rules = { email: 'normalize_email' } assert.deepEqual(indicative.sanitize(data, rules), { email: '*****@*****.**' }) }) test('should be able to call raw sanitization methods', async (assert) => { assert.equal(indicative.sanitizor.normalizeEmail('*****@*****.**'), '*****@*****.**') }) test('should be able to call rule method', async (assert) => { assert.deepEqual(indicative.rule('foo', 1), { name: 'foo', args: [1] }) }) test('should be able to access formatters', async (assert) => { assert.property(indicative.formatters, 'Vanilla') }) test('export configure method and update defaults', async (assert) => { assert.plan(1) indicative.configure({ EXISTY_STRICT: true }) const data = { email: null } const rules = { username: '******' } try { await indicative.validate(data, rules) } catch (errors) { assert.deepEqual(errors, [ { validation: 'required', field: 'username', message: 'required validation failed on username' } ]) } indicative.configure({ EXISTY_STRICT: indicative.configure.DEFAULTS.EXISTY_STRICT }) }) test('add new validation rules', async (assert) => { assert.plan(1) indicative.validations.time = function () { return new Promise((resolve, reject) => { resolve('validation passed') }) } const data = { call: '10:20' } const rules = { call: 'time' } const result = await indicative.validate(data, rules) assert.deepEqual(result, data) }) test('add new sanitization rules', async (assert) => { assert.plan(1) indicative.sanitizor.stringToNull = function () { return null } const data = { username: '' } const rules = { username: '******' } const result = indicative.sanitize(data, rules) assert.deepEqual(result, { username: null }) }) test('use jsonapi formatter', async (assert) => { assert.plan(1) const data = {} const rules = { username: '******' } try { await indicative.validate(data, rules, null, indicative.formatters.JsonApi) } catch (errors) { assert.deepEqual(errors, { errors: [ { title: 'required', source: { pointer: 'username' }, detail: 'required validation failed on username' } ] }) } }) test('validate array expressions', async (assert) => { assert.plan(1) const data = { names: [22] } const rules = { 'names.*': 'string' } try { await indicative.validate(data, rules, null) } catch (errors) { assert.deepEqual(errors, [ { validation: 'string', field: 'names.0', message: 'string validation failed on names.0' } ]) } }) })
test.group('Encryption', () => { test('throw exception app key is missing', (assert) => { const fn = () => new Encryption(new Config()) assert.throw(fn, 'E_MISSING_APP_KEY: Make sure to define appKey inside config/app.js file before using Encryption provider') }) test('throw exception when key is smaller than 16 digits', (assert) => { const config = new Config() config.set('app.appKey', 'foo') const fn = () => new Encryption(config) assert.throw(fn, 'key must be at least 16 characters long') }) test('encrypt string using valid key', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) assert.isDefined(encryption.encrypt('hello world')) }) test('encrypt object using valid key', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) assert.isDefined(encryption.encrypt({name: 'virk'})) }) test('encrypt boolean using valid key', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) assert.isDefined(encryption.encrypt(true)) }) test('encrypt number using valid key', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) assert.isDefined(encryption.encrypt(20)) }) test('encrypt date using valid key', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) assert.isDefined(encryption.encrypt(new Date())) }) test('decrypt string using valid key', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) const encryptedValue = encryption.encrypt('hello world') assert.equal(encryption.decrypt(encryptedValue), 'hello world') }) test('decrypt object using valid key', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) const encryptedValue = encryption.encrypt({name: 'virk'}) assert.deepEqual(encryption.decrypt(encryptedValue), {name: 'virk'}) }) test('decrypt boolean using valid key', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) const encryptedValue = encryption.encrypt(true) assert.equal(encryption.decrypt(encryptedValue), true) }) test('decrypt number using valid key', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) const encryptedValue = encryption.encrypt(20) assert.equal(encryption.decrypt(encryptedValue), 20) }) test('decrypt date using valid key', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) const date = new Date().toString() const encryptedValue = encryption.encrypt(date) assert.deepEqual(encryption.decrypt(encryptedValue), date) }) test('base64 encode a string', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) const encodedValue = encryption.base64Encode('hello world') assert.isDefined(encodedValue) }) test('base64 decode a string', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) const encodedValue = encryption.base64Encode('hello world') assert.equal(encryption.base64Decode(encodedValue), 'hello world') }) test('base64 decode a buffer', (assert) => { const config = new Config() config.set('app.appKey', getAppKey()) const encryption = new Encryption(config) const buff = Buffer.from('hello world') assert.equal(encryption.base64Decode(buff), 'hello world') }) })
test.group('Response', () => { test('send raw string as response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.send('hello world') response.end() }) const res = await supertest(server).get('/').expect('Content-Type', /plain/).expect(200) assert.equal(res.text, 'hello world') }) test('send json object as response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.send({name: 'virk'}) response.end() }) const res = await supertest(server).get('/').expect('Content-Type', /json/).expect(200) assert.deepEqual(res.body, {name: 'virk'}) }) test('send number as response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.send(22) response.end() }) const res = await supertest(server).get('/').expect('Content-Type', /plain/).expect(200) assert.equal(res.text, 22) }) test('send boolean as response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.send(true) response.end() }) const res = await supertest(server).get('/').expect('Content-Type', /plain/).expect(200) assert.equal(res.text, 'true') }) test('send html as response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.send('<h2> Hello world </h2>') response.end() }) const res = await supertest(server).get('/').expect('Content-Type', /html/).expect(200) assert.equal(res.text, '<h2> Hello world </h2>') }) test('set http response status', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.status(304).send('hello') response.end() }) await supertest(server).get('/').expect(304) }) test('set http request header', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.header('Link', ['<http://localhost/>', '<http://localhost:3000/>']) response.end() }) await supertest(server).get('/').expect('link', '<http://localhost/>, <http://localhost:3000/>') }) test('only set the header when does not exists', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.safeHeader('Content-Type', 'application/json') response.safeHeader('Content-Type', 'text/plain') response.send('') response.end() }) await supertest(server).get('/').expect('Content-Type', /json/) }) test('remove response header', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.header('Link', ['<http://localhost/>', '<http://localhost:3000/>']) response.removeHeader('link') response.end() }) const res = await supertest(server).get('/') assert.notProperty(res.headers, 'link') }) test('get value for existing header', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.header('Link', ['<http://localhost/>', '<http://localhost:3000/>']) response.send(response.getHeader('link')) response.end() }) const res = await supertest(server).get('/').expect(200) assert.deepEqual(res.body, ['<http://localhost/>', '<http://localhost:3000/>']) }) test('download file', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.download(path.join(__dirname, '../../package.json')) }) const res = await supertest(server).get('/').expect(200) assert.isObject(res.body) assert.equal(res.body.name, '@adonisjs/framework') }) test('send 404 when file does not exists', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.download(path.join(__dirname, '../../logo.svg')) }) await supertest(server).get('/').expect(404) }) test('force download the file by setting content-disposition', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.attachment(path.join(__dirname, '../../package.json')) }) const res = await supertest(server).get('/').expect('Content-Disposition', /filename="package.json"/) assert.isObject(res.body) assert.equal(res.body.name, '@adonisjs/framework') }) test('force download the file with different file name', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.attachment(path.join(__dirname, '../../package.json'), 'adonis.json') }) const res = await supertest(server).get('/').expect('Content-Disposition', /filename="adonis.json"/) assert.isObject(res.body) assert.equal(res.body.name, '@adonisjs/framework') }) test('set the location http header', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.location('http://adonisjs.com') response.end() }) await supertest(server).get('/').expect('Location', 'http://adonisjs.com') }) test('redirect the request', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.redirect('http://adonisjs.com') response.end() }) await supertest(server).get('/').expect('Location', 'http://adonisjs.com').expect(302) }) test('set content-type based on type', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.type('html').send({username: '******'}) response.end() }) await supertest(server).get('/').expect('Content-Type', /html/).expect(200) }) test('send content as json with content-type explicitly set to text/javascript', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.jsonp({username: '******'}) response.end() }) await supertest(server).get('/').expect('Content-Type', /javascript/).expect(200) }) test('use the request query param callback for jsonp response', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.jsonp({username: '******'}) response.end() }) const res = await supertest(server).get('/?callback=exec').expect(200) assert.equal(res.text, `/**/ typeof exec === 'function' && exec({"username":"******"});`) }) test('use the explicit callbackFn over request query param', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.jsonp({username: '******'}, 'eval') response.end() }) const res = await supertest(server).get('/?callback=exec').expect(200) assert.equal(res.text, `/**/ typeof eval === 'function' && eval({"username":"******"});`) }) test('set 401 as the status via unauthorized method', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.unauthorized('Login First') response.end() }) const res = await supertest(server).get('/').expect(401) assert.equal(res.text, 'Login First') }) test('save cookie to the browser', async (assert) => { const server = http.createServer((req, res) => { const response = new Response(req, res, new Config()) response.cookie('cart_total', '20') response.send('') response.end() }) const { headers } = await supertest(server).get('/').expect(200) assert.equal(headers['set-cookie'][0], 'cart_total=20') }) test('encrypt cookie when secret is defined', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() config.set('app.secret', SECRET) const response = new Response(req, res, config) response.cookie('cart_total', '20') response.send('') response.end() }) const { headers } = await supertest(server).get('/').expect(200) const encrypter = simpleEncryptor({ key: SECRET, hmac: false }) assert.strictEqual( sig.unsign( encrypter.decrypt(querystring.unescape(headers['set-cookie'][0].replace('cart_total=', ''))).replace('s:', ''), SECRET ), '20' ) }) test('send plain cookie even if secret is defined', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() config.set('app.secret', SECRET) const response = new Response(req, res, config) response.plainCookie('cart_total', '20') response.send('') response.end() }) const { headers } = await supertest(server).get('/').expect(200) assert.equal(headers['set-cookie'][0], 'cart_total=20') }) test('send vary header', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() const response = new Response(req, res, config) response.vary('Origin') response.send('') response.end() }) const { headers } = await supertest(server).get('/').expect(200) assert.equal(headers.vary, 'Origin') }) test('clear existing cookie by setting expiry in past', async (assert) => { const server = http.createServer((req, res) => { const config = new Config() const response = new Response(req, res, config) response.clearCookie('cart') response.send('') response.end() }) const { headers } = await supertest(server).get('/').expect(200) assert.equal(headers['set-cookie'][0], 'cart=; Expires=Thu, 01 Jan 1970 00:00:00 GMT') }) })
test.group('Schemes - Jwt', (group) => { setup.databaseHook(group) setup.hashHook(group) test('throw exception when unable to validate credentials', async (assert) => { assert.plan(1) const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', scheme: 'jwt' } const lucid = new LucidSerializer() lucid.setConfig(config) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) try { await jwt.validate('*****@*****.**', 'secret') } catch ({ message }) { assert.equal(message, 'E_USER_NOT_FOUND: Cannot find user with email as foo@bar.com') } }) test('throw exception when password mismatches', async (assert) => { assert.plan(1) const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', scheme: 'jwt' } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) try { await jwt.validate('*****@*****.**', 'supersecret') } catch ({ message }) { assert.equal(message, 'E_PASSWORD_MISMATCH: Cannot verify user password') } }) test('return true when able to validate credentials', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******' } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const validated = await jwt.validate('*****@*****.**', 'secret') assert.isTrue(validated) }) test('generate token for user', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const tokenPayload = await jwt.generate(user) const payload = await verifyToken(tokenPayload.token) assert.property(tokenPayload, 'token') assert.isNull(tokenPayload.refreshToken) assert.equal(tokenPayload.type, 'bearer') assert.equal(payload.uid, 1) }) test('generate token for user and attach user to it', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const { token } = await jwt.generate(user, true) const payload = await verifyToken(token) assert.equal(payload.uid, 1) assert.equal(payload.data.id, 1) assert.equal(payload.data.email, '*****@*****.**') assert.isUndefined(payload.data.password) }) test('generate token for user and attach custom data to it', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const { token } = await jwt.generate(user, { isAdmin: true }) const payload = await verifyToken(token) assert.equal(payload.uid, 1) assert.equal(payload.data.isAdmin, true) }) test('set jwt options', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET, issuer: 'adonisjs' } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const { token } = await jwt.generate(user) const payload = await verifyToken(token) assert.equal(payload.iss, 'adonisjs') }) test('generate jwt token with refresh token', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET, issuer: 'adonisjs' } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const { refreshToken } = await jwt.withRefreshToken().generate(user) assert.isDefined(refreshToken) const userTokens = await user.tokens().fetch() assert.equal(userTokens.size(), 1) assert.equal(`e${userTokens.first().token}`, refreshToken) assert.equal(userTokens.first().is_revoked, 0) assert.equal(userTokens.first().type, 'jwt_refresh_token') }) test('verify user token from header', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const token = await generateToken({ uid: 1 }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header (key) { return `Bearer ${token}` } } }) const isLoggedIn = await jwt.check() assert.isTrue(isLoggedIn) assert.instanceOf(jwt.user, User) assert.equal(jwt.jwtPayload.uid, jwt.user.id) }) test('throw exception when jwt token is invalid', async (assert) => { assert.plan(2) const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header (key) { return 'Bearer 20' } } }) try { await jwt.check() } catch ({ name, message }) { assert.equal(message, 'E_INVALID_JWT_TOKEN: jwt malformed') assert.equal(name, 'InvalidJwtToken') } }) test('throw exception when user doesn\'t exist', async (assert) => { assert.plan(2) const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const token = await generateToken({ uid: 1 }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header (key) { return `Bearer ${token}` } } }) try { await jwt.check() } catch ({ name, message }) { assert.equal(message, 'E_INVALID_JWT_TOKEN: The Jwt token is invalid') assert.equal(name, 'InvalidJwtToken') } }) test('throw exception when options mismatches', async (assert) => { assert.plan(2) const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET, issuer: 'adonisjs' } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const token = await generateToken({ uid: 1 }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header (key) { return `Bearer ${token}` } } }) try { await jwt.check() } catch ({ name, message }) { assert.equal(message, 'E_INVALID_JWT_TOKEN: jwt issuer invalid. expected: adonisjs') assert.equal(name, 'InvalidJwtToken') } }) test('throw exception when token has been expired', async (assert) => { assert.plan(2) const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const token = await generateToken({ uid: 1 }, { expiresIn: 1 }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header (key) { return `Bearer ${token}` } } }) await helpers.sleep(1000) try { await jwt.check() } catch ({ name, message }) { assert.equal(message, 'E_JWT_TOKEN_EXPIRED: The jwt token has been expired. Generate a new one to continue') assert.equal(name, 'ExpiredJwtToken') } }).timeout(0) test('return user when token is correct', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const token = await generateToken({ uid: 1 }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header (key) { return `Bearer ${token}` } } }) const user = await jwt.getUser() assert.instanceOf(user, User) }) test('find user for a refresh token', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) await user.tokens().create({ token: '20', is_revoked: false, type: 'jwt_refresh_token' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const { token } = await jwt.generateForRefreshToken('20', false, { expiresIn: '1m' }) assert.isDefined(token) const payload = await verifyToken(token) assert.equal(payload.uid, 1) assert.isDefined(payload.exp) const firstToken = await user.tokens().where('token', '20').first() assert.equal(firstToken.is_revoked, 0) }) test('on generating token with refreshToken remove the old one', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) await user.tokens().create({ token: '20', is_revoked: false, type: 'jwt_refresh_token' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const { token, refreshToken } = await jwt.newRefreshToken().generateForRefreshToken('20') assert.isDefined(token) const payload = await verifyToken(token) assert.equal(payload.uid, 1) assert.notEqual(refreshToken, '20') const firstToken = await user.tokens().where('token', '20').first() assert.isNull(firstToken) }) test('generate token using credentials', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const { token, refreshToken } = await jwt.attempt('*****@*****.**', 'secret') assert.isDefined(token) const payload = await verifyToken(token) assert.equal(payload.uid, 1) assert.notEqual(refreshToken, '20') }) test('throw exception when user is not found for a refreshToken', async (assert) => { assert.plan(2) const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) try { await jwt.generateForRefreshToken('20') } catch ({ name, message }) { assert.equal(name, 'InvalidRefreshToken') assert.equal(message, 'E_INVALID_JWT_REFRESH_TOKEN: Invalid refresh token 20') } }) test('throw exception when jwtSecret is missing', async (assert) => { assert.plan(2) const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: {} } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) try { await jwt.generate({}) } catch ({ name, message }) { assert.equal(name, 'RuntimeException') assert.match(message, /^E_INCOMPLETE_CONFIG: Make sure to define secret on jwt inside config\/auth.js/) } }) test('throw exception when user doesn\'t have an id', async (assert) => { assert.plan(2) const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: 'SECRET' } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) try { await jwt.generate({}) } catch ({ name, message }) { assert.equal(name, 'RuntimeException') assert.match(message, /^E_RUNTIME_ERROR: Primary key value is missing for user/) } }) test('calling check twice should return true', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const token = await generateToken({ uid: 1 }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header (key) { return `Bearer ${token}` } } }) await jwt.check() jwt._verifyToken = function () { throw new Error('Unexpected call') } await jwt.check() }) test('read token from request input', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const token = await generateToken({ uid: 1 }, { expiresIn: 1 }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header () { return null }, input () { return token } } }) const isLogged = await jwt.check() assert.isTrue(isLogged) }) test('parse token only when token is bearer type', async (assert) => { assert.plan(1) const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const token = await generateToken({ uid: 1 }, { expiresIn: 1 }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header () { return token } } }) try { await jwt.check() } catch ({ message }) { assert.equal(message, 'E_INVALID_JWT_TOKEN: jwt must be provided') } }) test('pass runtime options to generate token', async (assert) => { assert.plan(1) const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const { token } = await jwt.generate(user, {}, { issuer: 'adonisjs' }) const { iss } = await verifyToken(token) assert.equal(iss, 'adonisjs') }) test('throw exception when calling login()', async (assert) => { assert.plan(1) const User = helpers.getUserModel() const config = { model: User, uid: 'email', scheme: 'jwt', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) try { jwt.login(user) } catch ({ message }) { assert.match(message, /^E_INVALID_METHOD: login method is not implemented by jwt scheme/) } }) test('list refresh tokens', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const payload = await jwt.withRefreshToken().generate(user) const tokensList = await jwt.listTokensForUser(user) assert.equal(tokensList.length, 1) assert.equal(tokensList[0].token, payload.refreshToken) }) test('return empty array when tokens doesn\'t exists', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const tokensList = await jwt.listTokens(user) assert.lengthOf(tokensList, 0) }) test('return empty array when user has not been defined', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) await jwt.withRefreshToken().generate(user) const tokensList = await jwt.listTokens() assert.lengthOf(tokensList, 0) }) test('return encrypted tokens via database serializer', async (assert) => { const User = helpers.getUserModel() const config = { primaryKey: 'id', table: 'users', tokensTable: 'tokens', uid: 'email', foreignKey: 'user_id', password: '******', options: { secret: SECRET } } const database = new DatabaseSerializer(ioc.use('Hash')) database.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, database) const payload = await jwt.withRefreshToken().generate(user) const tokensList = await jwt.listTokensForUser({ id: 1 }, 'jwt_refresh_token') assert.deepEqual(tokensList[0].token, payload.refreshToken) }) test('login as client', async (assert) => { assert.plan(2) const config = { primaryKey: 'id', table: 'users', tokensTable: 'tokens', uid: 'email', foreignKey: 'user_id', password: '******', options: { secret: SECRET } } const database = new DatabaseSerializer(ioc.use('Hash')) database.setConfig(config) const jwt = new Jwt(Encryption) jwt.setOptions(config, database) const headerFn = function (key, value) { assert.equal(key, 'authorization') assert.include(value, 'Bearer') } await jwt.clientLogin(headerFn, null, { id: 1 }) }) test('revoke tokens for a given user', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) await user.tokens().create({ type: 'jwt_refresh_token', token: '22', is_revoked: false }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) await jwt.revokeTokensForUser(user, ['22']) const token = await user.tokens().first() assert.equal(token.is_revoked, true) assert.equal(token.token, '22') }) test('revoke all tokens for the current user', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const payload = await jwt.withRefreshToken().generate(user) jwt.setCtx({ request: { header (key) { return `Bearer ${payload.token}` } } }) await jwt.check() await jwt.revokeTokens() const token = await user.tokens().first() assert.equal(token.is_revoked, true) assert.equal(`e${token.token}`, payload.refreshToken) }) test('delete tokens instead of revoking it', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) const user = await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) const payload = await jwt.generate(user) jwt.setCtx({ request: { header (key) { return `Bearer ${payload.token}` } } }) await jwt.check() await jwt.revokeTokens(null, true) const token = await user.tokens().first() assert.isNull(token) }) test('set user property when token exists', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const token = await generateToken({ uid: 1 }, { expiresIn: 1 }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header () { return `Bearer ${token}` } } }) const isLogged = await jwt.loginIfCan() assert.isTrue(isLogged) assert.instanceOf(jwt.user, User) }) test('silently ignore when token header is missing', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header () { return null }, input () { return null } } }) const isLogged = await jwt.loginIfCan() assert.isFalse(isLogged) assert.isNull(jwt.user) }) test('silently ignore when token is invalid', async (assert) => { const User = helpers.getUserModel() const config = { model: User, uid: 'email', password: '******', options: { secret: SECRET } } const lucid = new LucidSerializer(ioc.use('Hash')) lucid.setConfig(config) await User.create({ email: '*****@*****.**', password: '******' }) const jwt = new Jwt(Encryption) jwt.setOptions(config, lucid) jwt.setCtx({ request: { header () { return '30' } } }) const isLogged = await jwt.loginIfCan() assert.isFalse(isLogged) assert.isNull(jwt.user) }) })
test.group('Relations | Has Many Through - Has Many ', (group) => { group.before(async () => { ioc.singleton('Adonis/Src/Database', function () { const config = new Config() config.set('database', { connection: 'testing', testing: helpers.getConfig() }) return new DatabaseManager(config) }) ioc.alias('Adonis/Src/Database', 'Database') await fs.ensureDir(path.join(__dirname, './tmp')) await helpers.createTables(ioc.use('Adonis/Src/Database')) }) group.afterEach(async () => { ioc.restore() await ioc.use('Adonis/Src/Database').table('countries').truncate() await ioc.use('Adonis/Src/Database').table('users').truncate() await ioc.use('Adonis/Src/Database').table('posts').truncate() }) group.after(async () => { await helpers.dropTables(ioc.use('Adonis/Src/Database')) ioc.use('Database').close() try { await fs.remove(path.join(__dirname, './tmp')) } catch (error) { if (process.platform !== 'win32' || error.code !== 'EBUSY') { throw error } } }).timeout(0) test('create correct query', (assert) => { class Post extends Model { } class User extends Model { posts () { return this.hasMany(Post) } } class Country extends Model { posts () { return this.manyThrough(User, 'posts') } } User._bootIfNotBooted() Country._bootIfNotBooted() Post._bootIfNotBooted() const country = new Country() country.id = 1 country.$persisted = true const query = country.posts().toSQL() assert.equal(query.sql, helpers.formatQuery('select "posts".* from "posts" inner join "users" on "users"."id" = "posts"."user_id" where "users"."country_id" = ?')) }) test('define through fields', (assert) => { class Post extends Model { } class User extends Model { posts () { return this.hasMany(Post) } } class Country extends Model { posts () { return this.manyThrough(User, 'posts') } } User._bootIfNotBooted() Country._bootIfNotBooted() Post._bootIfNotBooted() const country = new Country() country.id = 1 country.$persisted = true const query = country.posts().selectThrough('username').toSQL() assert.equal(query.sql, helpers.formatQuery('select "posts".*, "users"."username" as "through_username" from "posts" inner join "users" on "users"."id" = "posts"."user_id" where "users"."country_id" = ?')) }) test('define related fields', (assert) => { class Post extends Model { } class User extends Model { posts () { return this.hasMany(Post) } } class Country extends Model { posts () { return this.manyThrough(User, 'posts') } } User._bootIfNotBooted() Country._bootIfNotBooted() Post._bootIfNotBooted() const country = new Country() country.id = 1 country.$persisted = true const query = country.posts().selectRelated('title').select('name').toSQL() assert.equal(query.sql, helpers.formatQuery('select "countries"."name", "posts"."title" from "posts" inner join "users" on "users"."id" = "posts"."user_id" where "users"."country_id" = ?')) }) test('fetch related row', async (assert) => { class Post extends Model { } class User extends Model { posts () { return this.hasMany(Post) } } class Country extends Model { posts () { return this.manyThrough(User, 'posts') } } User._bootIfNotBooted() Country._bootIfNotBooted() Post._bootIfNotBooted() await ioc.use('Database').table('countries').insert({ name: 'India', id: 2 }) await ioc.use('Database').table('users').insert({ country_id: 2, id: 20, username: '******' }) await ioc.use('Database').table('posts').insert({ user_id: 20, title: 'Adonis 101' }) const country = await Country.find(2) const posts = await country.posts().fetch() assert.instanceOf(posts, VanillaSerializer) assert.equal(posts.size(), 1) assert.deepEqual(posts.toJSON(), [ { id: 1, user_id: 20, title: 'Adonis 101', created_at: null, updated_at: null, deleted_at: null } ]) }) test('eagerload related rows', async (assert) => { class Post extends Model { } class User extends Model { posts () { return this.hasMany(Post) } } class Country extends Model { posts () { return this.manyThrough(User, 'posts') } } User._bootIfNotBooted() Country._bootIfNotBooted() Post._bootIfNotBooted() await ioc.use('Database').table('countries').insert({ name: 'India', id: 2 }) await ioc.use('Database').table('users').insert({ country_id: 2, id: 20, username: '******' }) await ioc.use('Database').table('posts').insert({ user_id: 20, title: 'Adonis 101' }) const countries = await Country.query().with('posts', (builder) => { builder.selectThrough('id') }).fetch() assert.equal(countries.size(), 1) const country = countries.first() assert.instanceOf(country.getRelated('posts'), VanillaSerializer) assert.equal(country.getRelated('posts').size(), 1) assert.equal(country.getRelated('posts').toJSON()[0].__meta__.through_country_id, country.id) }) test('limit parent rows based upon child rows', async (assert) => { class Post extends Model { } class User extends Model { posts () { return this.hasMany(Post) } } class Country extends Model { posts () { return this.manyThrough(User, 'posts') } } User._bootIfNotBooted() Country._bootIfNotBooted() Post._bootIfNotBooted() let countryQuery = null Country.onQuery((query) => (countryQuery = query)) await ioc.use('Database').table('countries').insert({ name: 'India', id: 2 }) await Country.query().has('posts').fetch() assert.equal(countryQuery.sql, helpers.formatQuery('select * from "countries" where exists (select * from "posts" inner join "users" on "users"."id" = "posts"."user_id" where countries.id = users.country_id)')) }) test('limit parent rows based upon child rows count', async (assert) => { class Post extends Model { } class User extends Model { posts () { return this.hasMany(Post) } } class Country extends Model { posts () { return this.manyThrough(User, 'posts') } } User._bootIfNotBooted() Country._bootIfNotBooted() Post._bootIfNotBooted() let countryQuery = null Country.onQuery((query) => (countryQuery = query)) await ioc.use('Database').table('countries').insert({ name: 'India', id: 2 }) await Country.query().has('posts', '>', 1).fetch() assert.equal(countryQuery.sql, helpers.formatQuery('select * from "countries" where (select count(*) from "posts" inner join "users" on "users"."id" = "posts"."user_id" where countries.id = users.country_id) > ?')) }) test('fetch only filtered parent rows', async (assert) => { class Post extends Model { } class User extends Model { posts () { return this.hasMany(Post) } } class Country extends Model { posts () { return this.manyThrough(User, 'posts') } } User._bootIfNotBooted() Country._bootIfNotBooted() Post._bootIfNotBooted() let countryQuery = null Country.onQuery((query) => (countryQuery = query)) await ioc.use('Database').table('countries').insert([{ name: 'India', id: 2 }, { name: 'UK', id: 3 }]) await ioc.use('Database').table('users').insert([{ country_id: 2, id: 20, username: '******' }, { country_id: 3, username: '******' }]) await ioc.use('Database').table('posts').insert({ user_id: 20, title: 'Adonis 101' }) const countries = await Country.query().has('posts').fetch() assert.equal(countries.size(), 1) assert.equal(countries.first().name, 'India') assert.equal(countryQuery.sql, helpers.formatQuery('select * from "countries" where exists (select * from "posts" inner join "users" on "users"."id" = "posts"."user_id" where countries.id = users.country_id)')) }) test('paginate only filtered parent rows', async (assert) => { class Post extends Model { } class User extends Model { posts () { return this.hasMany(Post) } } class Country extends Model { posts () { return this.manyThrough(User, 'posts') } } User._bootIfNotBooted() Country._bootIfNotBooted() Post._bootIfNotBooted() let countryQuery = null Country.onQuery((query) => (countryQuery = query)) await ioc.use('Database').table('countries').insert([{ name: 'India', id: 2 }, { name: 'UK', id: 3 }]) await ioc.use('Database').table('users').insert([{ country_id: 2, id: 20, username: '******' }, { country_id: 3, username: '******' }]) await ioc.use('Database').table('posts').insert({ user_id: 20, title: 'Adonis 101' }) const countries = await Country.query().has('posts').paginate() assert.equal(countries.size(), 1) assert.equal(countries.first().name, 'India') assert.equal(countryQuery.sql, helpers.formatQuery('select * from "countries" where exists (select * from "posts" inner join "users" on "users"."id" = "posts"."user_id" where countries.id = users.country_id) limit ?')) }) })
test.group('Relations | Has Many', (group) => { group.before(async () => { ioc.singleton('Adonis/Src/Database', function () { const config = new Config() config.set('database', { connection: 'testing', testing: helpers.getConfig() }) return new DatabaseManager(config) }) ioc.alias('Adonis/Src/Database', 'Database') await fs.ensureDir(path.join(__dirname, './tmp')) await helpers.createTables(ioc.use('Adonis/Src/Database')) }) group.afterEach(async () => { ioc.restore() await ioc.use('Adonis/Src/Database').table('users').truncate() await ioc.use('Adonis/Src/Database').table('cars').truncate() await ioc.use('Adonis/Src/Database').table('parts').truncate() }) group.after(async () => { await helpers.dropTables(ioc.use('Adonis/Src/Database')) ioc.use('Database').close() try { await fs.remove(path.join(__dirname, './tmp')) } catch (error) { if (process.platform !== 'win32' || error.code !== 'EBUSY') { throw error } } }).timeout(0) test('get instance of has many when calling to relation method', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null Car.onQuery((query) => (carQuery = query)) await ioc.use('Database').table('users').insert({ username: '******' }) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'merc', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' } ]) const user = await User.find(1) const cars = await user.cars().fetch() assert.instanceOf(cars, VanillaSerializer) assert.equal(cars.size(), 2) assert.equal(carQuery.sql, helpers.formatQuery('select * from "cars" where "user_id" = ?')) assert.deepEqual(carQuery.bindings, helpers.formatBindings([1])) }) test('get first instance of related model', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null Car.onQuery((query) => (carQuery = query)) await ioc.use('Database').table('users').insert({ username: '******' }) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'merc', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' } ]) const user = await User.find(1) const car = await user.cars().first() assert.instanceOf(car, Car) assert.equal(car.name, 'merc') assert.equal(carQuery.sql, helpers.formatQuery('select * from "cars" where "user_id" = ? limit ?')) assert.deepEqual(carQuery.bindings, helpers.formatBindings([1, 1])) }) test('eagerload relation', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null Car.onQuery((query) => (carQuery = query)) await ioc.use('Database').table('users').insert({ username: '******' }) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'merc', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' } ]) const user = await User.query().with('cars').first() assert.instanceOf(user.getRelated('cars'), VanillaSerializer) assert.equal(user.getRelated('cars').size(), 2) assert.deepEqual(user.getRelated('cars').rows.map((car) => car.$parent), ['User', 'User']) assert.equal(carQuery.sql, helpers.formatQuery('select * from "cars" where "user_id" = ?')) assert.deepEqual(carQuery.bindings, helpers.formatBindings([1])) }) test('add constraints when eagerloading', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null Car.onQuery((query) => (carQuery = query)) await ioc.use('Database').table('users').insert({ username: '******' }) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'merc', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' } ]) const users = await User.query().with('cars', (builder) => { builder.where('model', '>', '2000') }).fetch() const user = users.first() assert.equal(user.getRelated('cars').size(), 1) assert.equal(user.getRelated('cars').rows[0].name, 'audi') assert.equal(carQuery.sql, helpers.formatQuery('select * from "cars" where "model" > ? and "user_id" in (?)')) assert.deepEqual(carQuery.bindings, helpers.formatBindings(['2000', 1])) }) test('return serailizer instance when nothing exists', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null Car.onQuery((query) => (carQuery = query)) await ioc.use('Database').table('users').insert({ username: '******' }) const users = await User.query().with('cars').fetch() const user = users.first() assert.equal(user.getRelated('cars').size(), 0) assert.equal(carQuery.sql, helpers.formatQuery('select * from "cars" where "user_id" in (?)')) assert.deepEqual(carQuery.bindings, helpers.formatBindings([1])) }) test('calling toJSON should build right json structure', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() await ioc.use('Database').table('users').insert([{ username: '******' }, { username: '******' }]) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'merc', model: '1990' }, { user_id: 2, name: 'audi', model: '2001' } ]) const users = await User.query().with('cars').fetch() const json = users.toJSON() assert.equal(json[0].cars[0].name, 'merc') assert.equal(json[1].cars[0].name, 'audi') }) test('calling toJSON should build right json structure', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() await ioc.use('Database').table('users').insert([{ username: '******' }, { username: '******' }]) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'merc', model: '1990' }, { user_id: 2, name: 'audi', model: '2001' } ]) const users = await User.query().with('cars').fetch() const json = users.toJSON() assert.equal(json[0].cars[0].name, 'merc') assert.equal(json[1].cars[0].name, 'audi') }) test('calling toJSON should build right json structure', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() await ioc.use('Database').table('users').insert([{ username: '******' }, { username: '******' }]) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'merc', model: '1990' }, { user_id: 2, name: 'audi', model: '2001' } ]) const users = await User.query().with('cars').fetch() const json = users.toJSON() assert.equal(json[0].cars[0].name, 'merc') assert.equal(json[1].cars[0].name, 'audi') }) test('should work with nested relations', async (assert) => { class Part extends Model { } class Car extends Model { parts () { return this.hasMany(Part) } } class User extends Model { cars () { return this.hasMany(Car) } } Part._bootIfNotBooted() Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null let partQuery = null Car.onQuery((query) => (carQuery = query)) Part.onQuery((query) => (partQuery = query)) await ioc.use('Database').table('users').insert({ username: '******' }) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'mercedes', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' } ]) await ioc.use('Database').table('parts').insert([ { car_id: 1, part_name: 'wheels' }, { car_id: 1, part_name: 'engine' }, { car_id: 2, part_name: 'wheels' }, { car_id: 2, part_name: 'engine' } ]) const user = await User.query().with('cars.parts').first() assert.equal(user.getRelated('cars').size(), 2) assert.equal(user.getRelated('cars').first().getRelated('parts').size(), 2) assert.equal(user.getRelated('cars').last().getRelated('parts').size(), 2) assert.equal(carQuery.sql, helpers.formatQuery('select * from "cars" where "user_id" = ?')) assert.equal(partQuery.sql, helpers.formatQuery('select * from "parts" where "car_id" in (?, ?)')) }) test('add query constraint to nested query', async (assert) => { class Part extends Model { } class Car extends Model { parts () { return this.hasMany(Part) } } class User extends Model { cars () { return this.hasMany(Car) } } Part._bootIfNotBooted() Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null let partQuery = null Car.onQuery((query) => (carQuery = query)) Part.onQuery((query) => (partQuery = query)) await ioc.use('Database').table('users').insert({ username: '******' }) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'mercedes', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' } ]) await ioc.use('Database').table('parts').insert([ { car_id: 1, part_name: 'wheels' }, { car_id: 1, part_name: 'engine' }, { car_id: 2, part_name: 'wheels' }, { car_id: 2, part_name: 'engine' } ]) const user = await User.query().with('cars.parts', (builder) => builder.where('part_name', 'engine')).first() assert.equal(user.getRelated('cars').size(), 2) assert.equal(user.getRelated('cars').first().getRelated('parts').size(), 1) assert.equal(user.getRelated('cars').last().getRelated('parts').size(), 1) assert.equal(carQuery.sql, helpers.formatQuery('select * from "cars" where "user_id" = ?')) assert.equal(partQuery.sql, helpers.formatQuery('select * from "parts" where "part_name" = ? and "car_id" in (?, ?)')) }) test('add query constraint to child and grand child query', async (assert) => { class Part extends Model { } class Car extends Model { parts () { return this.hasMany(Part) } } class User extends Model { cars () { return this.hasMany(Car) } } Part._bootIfNotBooted() Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null let partQuery = null Car.onQuery((query) => (carQuery = query)) Part.onQuery((query) => (partQuery = query)) await ioc.use('Database').table('users').insert({ username: '******' }) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'mercedes', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' } ]) await ioc.use('Database').table('parts').insert([ { car_id: 1, part_name: 'wheels' }, { car_id: 1, part_name: 'engine' }, { car_id: 2, part_name: 'wheels' }, { car_id: 2, part_name: 'engine' } ]) const user = await User.query().with('cars', (builder) => { builder.where('name', 'audi').with('parts', (builder) => builder.where('part_name', 'engine')) }).first() assert.equal(user.getRelated('cars').size(), 1) assert.equal(user.getRelated('cars').first().getRelated('parts').size(), 1) assert.equal(carQuery.sql, helpers.formatQuery('select * from "cars" where "name" = ? and "user_id" = ?')) assert.equal(partQuery.sql, helpers.formatQuery('select * from "parts" where "part_name" = ? and "car_id" in (?)')) }) test('get relation count', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let userQuery = null User.onQuery((query) => (userQuery = query)) await ioc.use('Database').table('users').insert({ username: '******' }) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'mercedes', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' } ]) const user = await User.query().withCount('cars').first() assert.deepEqual(user.$sideLoaded, { cars_count: helpers.formatNumber(2) }) assert.equal(userQuery.sql, helpers.formatQuery('select *, (select count(*) from "cars" where users.id = cars.user_id) as "cars_count" from "users" limit ?')) }) test('filter parent based upon child', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let userQuery = null User.onQuery((query) => (userQuery = query)) await ioc.use('Database').table('users').insert([{ username: '******' }, { username: '******' }]) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'mercedes', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' } ]) const users = await User.query().has('cars').fetch() assert.equal(users.size(), 1) assert.equal(userQuery.sql, helpers.formatQuery('select * from "users" where exists (select * from "cars" where users.id = cars.user_id)')) }) test('define minimum count via has', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let userQuery = null User.onQuery((query) => (userQuery = query)) await ioc.use('Database').table('users').insert([{ username: '******' }, { username: '******' }]) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'mercedes', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' }, { user_id: 2, name: 'audi', model: '2001' } ]) const users = await User.query().has('cars', '>=', 2).fetch() assert.equal(users.size(), 1) assert.equal(userQuery.sql, helpers.formatQuery('select * from "users" where (select count(*) from "cars" where users.id = cars.user_id) >= ?')) }) test('add additional constraints via where has', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let userQuery = null User.onQuery((query) => (userQuery = query)) await ioc.use('Database').table('users').insert([{ username: '******' }, { username: '******' }]) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'mercedes', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' }, { user_id: 2, name: 'audi', model: '2001' } ]) const users = await User.query().whereHas('cars', (builder) => { return builder.where('name', 'audi') }).fetch() assert.equal(users.size(), 2) assert.equal(userQuery.sql, helpers.formatQuery('select * from "users" where exists (select * from "cars" where "name" = ? and users.id = cars.user_id)')) }) test('add additional constraints and count constraints at same time', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let userQuery = null User.onQuery((query) => (userQuery = query)) await ioc.use('Database').table('users').insert([{ username: '******' }, { username: '******' }]) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'mercedes', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' }, { user_id: 2, name: 'audi', model: '2001' } ]) const users = await User.query().whereHas('cars', (builder) => { return builder.where('name', 'audi') }, '>', 1).fetch() assert.equal(users.size(), 0) assert.equal(userQuery.sql, helpers.formatQuery('select * from "users" where (select count(*) from "cars" where "name" = ? and users.id = cars.user_id) > ?')) }) test('add orWhereHas clause', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let userQuery = null User.onQuery((query) => (userQuery = query)) await ioc.use('Database').table('users').insert([{ username: '******' }, { username: '******' }]) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'mercedes', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' }, { user_id: 2, name: 'audi', model: '2001' } ]) const users = await User.query().whereHas('cars', (builder) => { return builder.where('name', 'audi') }, '>', 1).orWhereHas('cars', (builder) => builder.where('name', 'mercedes')).fetch() assert.equal(users.size(), 1) assert.equal(userQuery.sql, helpers.formatQuery('select * from "users" where (select count(*) from "cars" where "name" = ? and users.id = cars.user_id) > ? or exists (select * from "cars" where "name" = ? and users.id = cars.user_id)')) }) test('paginate records', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() await ioc.use('Database').table('users').insert([{ username: '******' }, { username: '******' }]) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'mercedes', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' }, { user_id: 2, name: 'audi', model: '2001' } ]) const users = await User.query().with('cars').paginate() assert.equal(users.size(), 2) assert.deepEqual(users.pages, { total: helpers.formatNumber(2), perPage: 20, page: 1, lastPage: 1 }) }) test('convert paginated records to json', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() await ioc.use('Database').table('users').insert([{ username: '******' }, { username: '******' }]) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'mercedes', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' }, { user_id: 2, name: 'audi', model: '2001' } ]) const users = await User.query().with('cars').paginate() const json = users.toJSON() assert.deepEqual(json.total, helpers.formatNumber(2)) assert.deepEqual(json.perPage, 20) assert.deepEqual(json.page, 1) assert.deepEqual(json.lastPage, 1) assert.isArray(json.data) assert.isArray(json.data[0].cars) assert.isArray(json.data[1].cars) }) test('save related model instance', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() const user = new User() user.username = '******' await user.save() const mercedes = new Car() mercedes.name = 'mercedes' mercedes.model = '1992' await user.cars().save(mercedes) assert.equal(mercedes.user_id, user.id) assert.isTrue(mercedes.$persisted) assert.isFalse(mercedes.isNew) }) test('create related model instance', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() const user = new User() user.username = '******' await user.save() const mercedes = await user.cars().create({ name: 'mercedes', model: '1992' }) assert.equal(mercedes.user_id, 1) assert.equal(mercedes.user_id, user.id) assert.isTrue(mercedes.$persisted) assert.isFalse(mercedes.isNew) }) test('persist parent model when isNew', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() const user = new User() user.username = '******' const mercedes = await user.cars().create({ name: 'mercedes', model: '1992' }) assert.equal(mercedes.user_id, 1) assert.equal(mercedes.user_id, user.id) assert.isTrue(mercedes.$persisted) assert.isFalse(mercedes.isNew) assert.isTrue(user.$persisted) assert.isFalse(user.isNew) }) test('persist parent model when isNew while calling save', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() const user = new User() user.username = '******' const mercedes = new Car() mercedes.name = 'mercedes' mercedes.model = '1992' await user.cars().save(mercedes) assert.equal(mercedes.user_id, 1) assert.equal(mercedes.user_id, user.id) assert.isTrue(mercedes.$persisted) assert.isFalse(mercedes.isNew) assert.isTrue(user.$persisted) assert.isFalse(user.isNew) }) test('saveMany of related instances', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() const user = new User() user.username = '******' const mercedes = new Car() mercedes.name = 'mercedes' mercedes.model = '1992' const ferrari = new Car() ferrari.name = 'ferrari' ferrari.model = '2002' await user.cars().saveMany([mercedes, ferrari]) assert.equal(mercedes.user_id, 1) assert.equal(mercedes.user_id, user.id) assert.equal(ferrari.user_id, user.id) assert.isTrue(mercedes.$persisted) assert.isFalse(ferrari.isNew) assert.isTrue(ferrari.$persisted) assert.isFalse(mercedes.isNew) assert.isTrue(user.$persisted) assert.isFalse(user.isNew) }) test('createMany of related instances', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() const user = new User() user.username = '******' const [mercedes, ferrari] = await user.cars().createMany([{ name: 'mercedes', model: '1992' }, { name: 'ferrari', model: '2002' }]) assert.equal(mercedes.user_id, 1) assert.equal(mercedes.user_id, user.id) assert.equal(ferrari.user_id, user.id) assert.isTrue(mercedes.$persisted) assert.isFalse(ferrari.isNew) assert.isTrue(ferrari.$persisted) assert.isFalse(mercedes.isNew) assert.isTrue(user.$persisted) assert.isFalse(user.isNew) }) test('delete related rows', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null Car.onQuery((query) => (carQuery = query)) const user = new User() user.username = '******' await user.cars().createMany([{ name: 'mercedes', model: '1992' }, { name: 'ferrari', model: '2002' }]) await user.cars().delete() const cars = await ioc.use('Database').table('cars') assert.lengthOf(cars, 0) assert.equal(carQuery.sql, helpers.formatQuery('delete from "cars" where "user_id" = ?')) }) test('add constraints to delete query', async (assert) => { class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null Car.onQuery((query) => (carQuery = query)) const user = new User() user.username = '******' await user.cars().createMany([{ name: 'mercedes', model: '1992' }, { name: 'ferrari', model: '2002' }]) await user.cars().where('name', 'mercedes').delete() const cars = await ioc.use('Database').table('cars') assert.lengthOf(cars, 1) assert.equal(cars[0].name, 'ferrari') assert.equal(carQuery.sql, helpers.formatQuery('delete from "cars" where "name" = ? and "user_id" = ?')) }) test('throw exception when createMany doesn\'t receives an array', async (assert) => { assert.plan(1) class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() const user = new User() user.username = '******' try { await user.cars().createMany({ name: 'mercedes', model: '1992' }) } catch ({ message }) { assert.equal(message, 'E_INVALID_PARAMETER: hasMany.createMany expects an array of values instead received object') } }) test('throw exception when saveMany doesn\'t receives an array', async (assert) => { assert.plan(1) class Car extends Model { } class User extends Model { cars () { return this.hasMany(Car) } } Car._bootIfNotBooted() User._bootIfNotBooted() const user = new User() user.username = '******' try { await user.cars().saveMany(new Car()) } catch ({ message }) { assert.equal(message, 'E_INVALID_PARAMETER: hasMany.saveMany expects an array of related model instances instead received object') } }) test('get first instance of related model via IoC container', async (assert) => { class Car extends Model { } ioc.fake('App/Models/Car', () => Car) class User extends Model { cars () { return this.hasMany('App/Models/Car') } } Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null Car.onQuery((query) => (carQuery = query)) await ioc.use('Database').table('users').insert({ username: '******' }) await ioc.use('Database').table('cars').insert([ { user_id: 1, name: 'merc', model: '1990' }, { user_id: 1, name: 'audi', model: '2001' } ]) const user = await User.find(1) const car = await user.cars().first() assert.instanceOf(car, Car) assert.equal(car.name, 'merc') assert.equal(carQuery.sql, helpers.formatQuery('select * from "cars" where "user_id" = ? limit ?')) assert.deepEqual(carQuery.bindings, helpers.formatBindings([1, 1])) }) test('bind custom callback for eagerload query', async (assert) => { class Car extends Model { } ioc.fake('App/Models/Car', () => Car) class User extends Model { cars () { return this.hasMany('App/Models/Car') } } Car._bootIfNotBooted() User._bootIfNotBooted() let carQuery = null Car.onQuery((query) => (carQuery = query)) await ioc.use('Database').table('users').insert({ username: '******' }) await User.query().with('cars', (builder) => { builder.eagerLoadQuery((query, fk, values) => { query.whereIn(fk, values).where('model', 'BMW') }) }).fetch() assert.equal(carQuery.sql, helpers.formatQuery('select * from "cars" where "user_id" in (?) and "model" = ?')) assert.deepEqual(carQuery.bindings, helpers.formatBindings([1, 'BMW'])) }) })
test.group('Relations | Has Many Through - Belongs To', (group) => { group.before(async () => { ioc.singleton('Adonis/Src/Database', function () { const config = new Config() config.set('database', { connection: 'testing', testing: helpers.getConfig() }) return new DatabaseManager(config) }) ioc.alias('Adonis/Src/Database', 'Database') await fs.ensureDir(path.join(__dirname, './tmp')) await helpers.createTables(ioc.use('Adonis/Src/Database')) }) group.afterEach(async () => { await ioc.use('Adonis/Src/Database').table('countries').truncate() await ioc.use('Adonis/Src/Database').table('users').truncate() await ioc.use('Adonis/Src/Database').table('profiles').truncate() }) group.after(async () => { await helpers.dropTables(ioc.use('Adonis/Src/Database')) try { await fs.remove(path.join(__dirname, './tmp')) } catch (error) { if (process.platform !== 'win32' || error.code !== 'EBUSY') { throw error } } }).timeout(0) test('create correct query', (assert) => { class User extends Model { } class Profile extends Model { user () { return this.belongsTo(User) } } class Country extends Model { users () { return this.manyThrough(Profile, 'user') } } User._bootIfNotBooted() Country._bootIfNotBooted() Profile._bootIfNotBooted() const country = new Country() country.id = 1 country.$persisted = true const query = country.users().toSQL() assert.equal(query.sql, helpers.formatQuery('select "users".* from "users" inner join "profiles" on "profiles"."user_id" = "users"."id" where "profiles"."country_id" = ?')) }) test('select related rows', async (assert) => { class User extends Model { } class Profile extends Model { user () { return this.belongsTo(User) } } class Country extends Model { users () { return this.manyThrough(Profile, 'user') } } User._bootIfNotBooted() Country._bootIfNotBooted() Profile._bootIfNotBooted() await ioc.use('Database').table('countries').insert([{ name: 'India', id: 2 }, { name: 'Uk', id: 3 }]) await ioc.use('Database').table('users').insert([ { username: '******' }, { username: '******' } ]) await ioc.use('Database').table('profiles').insert([ { user_id: 1, profile_name: 'Virk', country_id: 2 }, { user_id: 1, profile_name: 'Virk', country_id: 3 }, { user_id: 2, profile_name: 'Nikk', country_id: 2 } ]) const india = await Country.find(2) const indianUsers = await india.users().fetch() assert.equal(indianUsers.size(), 2) assert.deepEqual(indianUsers.rows.map((user) => user.username), ['virk', 'nikk']) const uk = await Country.find(3) const ukUsers = await uk.users().fetch() assert.equal(ukUsers.size(), 1) assert.deepEqual(ukUsers.rows.map((user) => user.username), ['virk']) }) test('eagerload related rows', async (assert) => { class User extends Model { } class Profile extends Model { user () { return this.belongsTo(User) } } class Country extends Model { users () { return this.manyThrough(Profile, 'user') } } User._bootIfNotBooted() Country._bootIfNotBooted() Profile._bootIfNotBooted() await ioc.use('Database').table('countries').insert([{ name: 'India', id: 2 }, { name: 'Uk', id: 3 }]) await ioc.use('Database').table('users').insert([ { username: '******' }, { username: '******' } ]) await ioc.use('Database').table('profiles').insert([ { user_id: 1, profile_name: 'Virk', country_id: 2 }, { user_id: 1, profile_name: 'Virk', country_id: 3 }, { user_id: 2, profile_name: 'Nikk', country_id: 2 } ]) const countries = await Country.query().with('users').orderBy('id', 'asc').fetch() assert.equal(countries.size(), 2) assert.equal(countries.first().getRelated('users').size(), 2) assert.equal(countries.last().getRelated('users').size(), 1) }) })