Issues testing hapijs plugin - hapi.js

Here's my test
'use strict';
var assert = require('assert');
var sinon = require('sinon');
var proxyquire = require('proxyquire');
var Lab = require('lab');
var lab = exports.lab = Lab.script();
lab.experiment("src.mysql", function () {
var server = {
settings: {
app: {
mysql: {
connectionLimit: 10,
host: "none",
user: "me",
password: "nope",
database: "db"
}
}
},
expose: sinon.stub()
};
var mysql = sinon.stub();
var next = sinon.stub();
var plugin = proxyquire('../../src/mysql', {
mysql: mysql
});
lab.test("successful loads", function(done) {
plugin.register(server, {}, next, function(err) {
assert(err === 'hello');
});
done();
});
});
I'm not getting an error, but the test is passing, which is a false positive. Not sure what I am doing wrong

The latest version of hapi 8.x.x uses a new method for loading plugins, you should call server.register with arguments described here http://hapijs.com/api#serverregisterplugins-options-callback.

Related

Can not run multiple tests in a file

I'm building a GraphQL API and I want to test some resolvers and the database with jest.
Here is my helper file, where I set up the context and the Prisma Client for testing.
import { PrismaClient } from "#prisma/client";
import { ServerInfo } from "apollo-server";
import { execSync } from "child_process";
import getPort, { makeRange } from "get-port";
import { GraphQLClient } from "graphql-request";
import { nanoid } from "nanoid";
import { join } from "path";
import { Client } from "pg";
import { server } from "../api/server";
type TestContext = {
client: GraphQLClient;
db: PrismaClient;
};
export function createTestContext(): TestContext {
let ctx = {} as TestContext;
const graphqlCtx = graphqlTestContext();
const prismaCtx = prismaTestContext();
beforeEach(async () => {
const client = await graphqlCtx.before();
const db = await prismaCtx.before();
Object.assign(ctx, {
client,
db,
});
});
afterEach(async () => {
await graphqlCtx.after();
await prismaCtx.after();
});
return ctx;
}
function graphqlTestContext() {
let serverInstance: ServerInfo | null = null;
return {
async before() {
const port = await getPort({ port: makeRange(4000, 6000) });
serverInstance = await server.listen({ port });
return new GraphQLClient(`http://localhost:${port}`);
},
async after() {
serverInstance?.server.close();
},
};
}
function prismaTestContext() {
const prismaBinary = join(__dirname, "..", "node_modules", ".bin", "prisma");
let schema = "";
let databaseUrl = "";
let prismaClient: null | PrismaClient = null;
return {
async before() {
schema = `test_${nanoid()}`;
databaseUrl = `postgresql://user:123#localhost:5432/testing?schema=${schema}`;
process.env.DATABASE_URL = databaseUrl;
execSync(`${prismaBinary} migrate up --create-db --experimental`, {
env: {
...process.env,
DATABASE_URL: databaseUrl,
},
});
prismaClient = new PrismaClient();
return prismaClient;
},
async after() {
const client = new Client({
connectionString: databaseUrl,
});
await client.connect();
await client.query(`DROP SCHEMA IF EXISTS "${schema}" CASCADE`);
await client.end();
await prismaClient?.$disconnect();
},
};
}
My test file looks like this:
import { createTestContext } from "./__helpers";
const ctx = createTestContext();
it("register user", async () => {
const testUser = {
username: "Test",
email: "test#test.com",
password: "password",
};
const registerResult = await ctx.client.request(
`
mutation registerNewUser($username: String!, $email: String!, $password: String!) {
register(username: $username, email: $email, password: $password) {
user {
user_id
username
email
}
}
}
`,
{
username: testUser.username,
email: testUser.email,
password: testUser.password,
}
);
const resultUsername = registerResult.register.user.username;
const resultEmail = registerResult.register.user.email;
const resultUserID = registerResult.register.user.user_id;
expect(resultUsername).toBe(testUser.username);
expect(resultEmail).toBe(testUser.email);
expect(resultUserID).not.toBeNull;
const users = await ctx.db.user.findMany();
const savedUser = users[0];
expect(savedUser.username).toBe(testUser.username);
expect(savedUser.email).toBe(testUser.email);
expect(savedUser.user_id).toBe(resultUserID);
expect(savedUser.first_name).toBeNull;
expect(savedUser.last_name).toBeNull;
expect(savedUser.role).toBe("USER");
expect(savedUser.password).not.toBe(testUser.password);
});
it("all events", async () => {
const eventsResult = await ctx.client.request(
`
query {
allEvents {
event_id
title
description
}
}
`
);
expect(eventsResult.allEvents.length).toBe(0)
});
When I just run one file with one test in it, everything works. But when I run multiple tests in one file, the first one runs normal, but the ones after not. I receive this error:
The table `test_LjrcmbMjI4vLaDYM9-lvw.Event` does not exist in the current database.: {"response":{"errors":[{"message":"\nInvalid `prisma.event.findMany()` invocation:\n\n\n The table `test_LjrcmbMjI4vLaDYM9-lvw.Event` does not exist in the current database.","locations":[{"line":3,"column":7}],"path":["allEvents"],"extensions":{"code":"INTERNAL_SERVER_ERROR","exception":{"code":"P2021","clientVersion":"2.11.0","meta":{"table":"test_LjrcmbMjI4vLaDYM9-lvw.Event"}}}}],"data":null,"status":200},"request":{"query":"\n query {\n allEvents {\n event_id\n title\n description\n }\n }\n "}}
Also when I run two tests in separated files, on every second test run I get this error:
listen EADDRINUSE: address already in use :::4200
I did the nexus tutorial (Step 4, and 5), where they explained how to test, but somehow it doesn't work. So please help me.
https://nexusjs.org/docs/getting-started/tutorial
I have created a repo with parallel tests for the same here. The test environment setup is in the prisma folder and a similar helper is created in the tests folder.

self-signed certificate configuration in Selenium Standalone and webdriverio

How does one set in the configuration to accept insecure self-signed certificates.
I'm using Selenium Standalone and webdriverio.
https://github.com/vvo/selenium-standalone
https://github.com/webdriverio/webdriverio
I cannot read anywhere how to do this.
I'm suing the code below:
const assert = require('assert');
const { promisify } = require('util');
const exec = promisify(require('child_process').exec);
const selenium = require('selenium-standalone');
const webdriverio = require('webdriverio');
selenium.installAsync = promisify(selenium.install);
selenium.startAsync = promisify(selenium.start);
let browser;
let seleniumChild;
before(async function () {
this.timeout(10 * 1000);
try {
// Remove any previous hanging sessions
await exec('pkill -f selenium-standalone');
} catch (error) {
if (error.cmd !== 'pkill -f selenium-standalone') {
console.error(error);
process.exit(1);
}
}
await selenium.installAsync({});
seleniumChild = await selenium.startAsync({});
const options = {
desiredCapabilities: {
browserName: 'chrome',
},
port: 4444,
};
browser = webdriverio.remote(options);
await browser.init();
await browser.url('http://google.com');
const title = await browser.getTitle();
console.log('Title ->', title);
await browser.end();
});
describe('test', function () {
it('test', async function () {
assert.ok(true);
});
});
Since it's starting a Selenium server, I'm expecting to be able to specify this through capabilities:
Did you tried using:
"acceptSslCerts": "true"
More on this topic you can find on the Selenium github page.

WebdriverIO - Get browser logs

I want to get the browser logs (console.logs) from chrome with WebdriverIO's log functionality but all I get is, that the function log is not a function.
var WebdriverIO = require('webdriverio');
var chai = require('chai');
var _ = require('lodash');
var chaiAsPromised = require('chai-as-promised');
var expect = chai.expect;
chai.use(chaiAsPromised);
var browser = {
host: '127.0.0.1',
port: 4444,
desiredCapabilities: {
browserName : 'chrome',
chromeOptions: {
args: [
'no-sandbox',
'use-fake-device-for-media-stream',
'use-fake-ui-for-media-stream',
'mute-audio',
]
},
loggingPrefs: {
'driver': 'INFO',
'browser': 'INFO'
}
},
};
var matrix = WebdriverIO.multiremote({
browserA: browser,
browserB: browser,
});
chaiAsPromised.transferPromiseness = matrix.transferPromiseness;
var browserA = matrix.select('browserA');
var browserB = matrix.select('browserB');
it('should initialize browsers', function() {
return matrix.init();
});
it('should open two browsers', function(done) {
browserA.url('https://127.0.0.1:3000/');
browserB.url('https://127.0.0.1:3000/');
matrix.timeouts('implicit', 15000);
matrix.sync().call(done);
});
it('should return logs', function(done) {
browserA
.log('browser', function(err, msg, a) {
console.log(msg);
})
.call(done);
});
Does someone know how to use this function properly? It also doesnt work when I just use one browser and not creating a Matrix like I did in the code snippet.
You are using the API wrong. You can not pass in a callback to the log method. The command returns a promise (if you use a standalone script like that) so you need to do:
browserA
.log('browser').then(function(msg) {
console.log(msg);
})

how to unit test passport local strategy? (sinon, proxyquire)

I have some logic inside my local login strategy for passport that I want to unit test with stubs/mocks (because it calls an external API), but I can't seem to get into the function to test it.
Here's my test.js file:
var expect = require('chai').expect;
var sinon = require('sinon');
var proxyquire = require('proxyquire');
require('../config/passport.js');
describe('it should get user account from the API', function () {
it('should be able to access passport authenticate', function(){
var reqUserObject = {
body: { user_name: 'fakeymcfakeypants', password: '123Skidoo' }
}
var requestPromiseStub = sinon.stub();
requestPromiseStub.onCall(0).returns(Promise.resolve('{"userId": 138}'))
.onCall(1).returns(Promise.resolve('{"userName": "fakeymcfakeypants", "status": 0}'))
var passportTest = proxyquire('passport', {
'request-promise': requestPromiseStub
});
var passportStub = sinon.stub(passportTest, "authenticate");
var response = passportStub.calledWith('localLogin', reqUserObject);
console.log(response);
expect.response.to.be.true;
});
});
And the setup for the function in config/passport.js:
var rp = require('request-promise');
var passport = require("passport");
var LocalStrategy = require('passport-local').Strategy;
module.exports = function (passport, LocalStrategy) {
passport.use('localLogin', new LocalStrategy({
usernameField : 'user[user_name]',
passwordField : 'user[password]'},
function(username, password, done) {
//logic blah blah blah here, uses two request-promise calls
return done(null, username);
})
)
}
As far as I can tell, the passport.authenticate method is not being called (it always returns false). If I remove proxyquire and just require passport & config/passport.js 'normally' the response is also false.
I know this is a bit complicated, so any other suggestions as to how to test this would be greatly appreciated!
EDITING TO ADD CURRENT CODE:
var expect = require('chai').expect;
var chai = require('chai');
var sinon = require('sinon');
var proxyquire = require('proxyquire');
var passport = require('passport');
var sinonChai = require('sinon-chai');
var async = require('async');
chai.use(sinonChai);
describe('canary test: passport', function (){
it('should pass this canary test', function(){
expect(true).to.be.true;
});
});
describe('it should get user account from the API', function () {
var authSpy;
var requestPromiseStub;
var passportResponse;
var userResponse;
beforeEach(function () {
this.sandbox = sinon.sandbox.create()
authSpy = sinon.spy(passport, 'authenticate');
})
afterEach(function () {
this.sandbox.restore()
})
it('should be able to access passport authenticate', function(){
var mockReq = {
body: {
username: 'fakeymcfakeypants',
password: '123Skidoo'
},
logIn: function () {}
};
var mockRes = {
};
requestPromiseStub = sinon.stub();
next = sinon.stub();
requestPromiseStub.onCall(0).returns(Promise.resolve({userId: 138, statusCode: 200}))
.onCall(1).returns(Promise.resolve({userName: 'fakeymcfakeypants', status : 0}))
var overrides = {
'request-promise': requestPromiseStub,
'authenticate': {authenticate: authSpy}
};
proxyquire('../config/passport.js', overrides)();
//added 'next' here as authenticate expects it: https://github.com/jaredhanson/passport/blob/master/lib/middleware/authenticate.js#L81
//passport should return either a username, or false, not sure how to access that?
passport.authenticate('localLogin')(mockReq, mockRes, next);
// if I comment out the 'logIn' function above and make an explicit function here I can see the username being returned, but of course it's inside the function closure:
passport.authenticate('localLogin', function(err, user){
// I can see here that the username is correct:
console.log(user)
})(mockReq, mockRes, next);
expect(requestPromiseStub).to.have.been.called;
});
});
I'm pretty sure I'm overlooking something really simple & dumb, but I can't seem to get normal callbacky wrappers to work with the syntax of passport.authenticate. :(
config/passport.js
var rp = require('request-promise');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
module.exports = function () {
passport.use('local', new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
},
function (username, password, done) {
console.log('logic blah blah blah here, uses two request-promise calls');
return done(null, username);
}));
};
test.js
var chai = require('chai');
var expect = require('chai').expect;
var sinon = require('sinon');
var proxyquire = require('proxyquire');
var passport = require('passport');
var sinonChai = require('sinon-chai');
chai.use(sinonChai);
describe('it should get user account from the API', function () {
it('should be able to access passport authenticate', function () {
// configure request and response
var mockReq = {
body: {
username: 'johndoe',
password: 'secret'
},
logIn: function () {}
};
var mockRes = {};
// configure request-promise
var requestPromiseStub = sinon.stub();
requestPromiseStub
.onCall(0).returns(Promise.resolve({
userId: 138
}))
.onCall(1).returns(Promise.resolve({
userName: 'johndoe',
status: 0
}));
var overrides = {
'request-promise': requestPromiseStub
};
proxyquire('./passport.js', overrides)();
passport.authenticate('local')(mockReq, mockRes);
// ASSERTS HERE
//expect(requestPromiseStub).to.have.been.called();
});
});

Invalidate session with custom authenticator

Using ember-cli 0.1.2 and ember-cli-simple-auth 0.7.0, I need to invalidate the session both on client and server. As explained here I need to do something similar to the authenticate method making an ajax request to the server and ensuring its success before emptying the session:
import Ember from 'ember';
import Base from "simple-auth/authenticators/base";
var CustomAuthenticator = Base.extend({
tokenEndpoint: 'http://127.0.0.1:3000/api/v1/auth/login',
restore: function(data) {
},
authenticate: function(credentials) {
var _this = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
Ember.$.ajax({
url: _this.tokenEndpoint,
type: 'POST',
data: JSON.stringify({ email: credentials.identification, password: credentials.password }),
contentType: 'application/json'
}).then(function(response) {
Ember.run(function() {
resolve({ token: response.token });
});
}, function(xhr, status, error) {
var response = JSON.parse(xhr.responseText);
Ember.run(function() {
reject(response.error);
});
});
});
},
invalidate: function() {
var _this = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
Ember.$.ajax({
url: _this.tokenEndpoint,
type: 'DELETE'
}).then(function(response) {
resolve();
}, function(xhr, status, error) {
var response = JSON.parse(xhr.responseText);
Ember.run(function() {
reject(response.error);
});
});
});
}
// invalidate: function() {
// var _this = this;
// return new Ember.RSVP.Promise(function(resolve) {
// Ember.$.ajax({ url: _this.tokenEndpoint, type: 'DELETE' }).always(function() {
// resolve();
// });
// });
// }
});
export default {
name : 'authentication',
before : 'simple-auth',
initialize : function(container) {
container.register('authenticator:custom', CustomAuthenticator);
}
};
My logout API endpoint need the token (in the headers). How do I pass it? I read this but my authorizer seems ignoring it and I got a 401:
import Ember from 'ember';
import Base from 'simple-auth/authorizers/base';
var CustomAuthorizer = Base.extend({
authorize: function(jqXHR, requestOptions){
Ember.debug("AUTHORIZING!");
}
});
export default {
name : 'authorization',
before : 'simple-auth',
initialize : function(container) {
container.register('authorizer:custom', CustomAuthorizer);
}
};
My environment.js:
/* jshint node: true */
module.exports = function(environment) {
var ENV = {
modulePrefix: 'wishhhh',
environment: environment,
baseURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
}
};
// TODO: disabled because of https://github.com/stefanpenner/ember-cli/issues/2174
ENV.contentSecurityPolicyHeader = 'Disabled-Content-Security-Policy'
ENV['simple-auth'] = {
authorizer: 'authorizer:custom',
// crossOriginWhitelist: ['http://localhost:3000']
crossOriginWhitelist: ['*']
}
if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
ENV.APP.LOG_ACTIVE_GENERATION = true;
// ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
ENV.APP.LOG_VIEW_LOOKUPS = true;
}
if (environment === 'test') {
// Testem prefers this...
ENV.baseURL = '/';
ENV.locationType = 'auto';
// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
}
if (environment === 'production') {
}
return ENV;
};
The following is the Ember inspector output when, eventually, I try to logout:
Did you actually configure Ember Simple Auth to use your custom authorizer? In that case it should authorize the session invalidation request automatically.
Alternatively you could add the token in the authenticator's invalidate method which gets passed the session's contents.
Thanks to marcoow, I found out that it was actually a problem with every request not only the logout one. My authorizer never got called. Problem was environment setup of crossOriginWhitelist which, in order to work with my dev API, I had to set to ['http://127.0.0.1:3000']. Neither ['http://localhost:3000'] nor [*] worked.