Merge remote-tracking branch 'origin/master' into 500_error_on_login

This commit is contained in:
Robert Schäfer 2019-01-04 00:25:22 +01:00
commit fd206c2b1f
11 changed files with 1724 additions and 155 deletions

View File

@ -4,6 +4,7 @@ module.exports = {
"env": {
"es6": true,
"node": true,
"jest/globals": true
},
"rules": {
"indent": [
@ -14,5 +15,6 @@ module.exports = {
"error",
"single"
]
}
},
"plugins": ["jest"]
};

View File

@ -131,6 +131,9 @@ npm run db:reset
```
## Run Tests
**Beware**: We have no multiple database setup at the moment. We clean the database after each test, running the tests will wipe out all your data!
```bash
yarn run test
# -or-

View File

@ -11,9 +11,10 @@
"start": "node dist/",
"dev": "nodemon --exec babel-node src/index.js",
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/index.js",
"mocha": "mocha --require @babel/register src/**/*.test.js",
"lint": "eslint src --config .eslintrc.js",
"test": "nyc --reporter=text-lcov mocha --require @babel/register src/**/*.test.js",
"test": "nyc --reporter=text-lcov yarn run test:jest",
"test:jest": "$npm_package_config_no_auth run-p --race start test:cmd:jest",
"test:cmd:jest": "jest --forceExit",
"test:coverage": "nyc report --reporter=text-lcov > coverage.lcov",
"db:script:seed": "wait-on tcp:4001 && babel-node src/seed/seed-db.js",
"db:script:reset": "wait-on tcp:4001 && babel-node src/seed/reset-db.js",
@ -32,6 +33,7 @@
"cross-env": "~5.2.0",
"date-fns": "^2.0.0-alpha.26",
"dotenv": "~6.2.0",
"faker": "~4.1.0",
"graphql": "~0.13.0",
"graphql-custom-directives": "~0.2.14",
"graphql-iso-date": "~3.6.1",
@ -46,32 +48,36 @@
"neo4j-driver": "~1.7.2",
"neo4j-graphql-js": "~2.1.1",
"node-fetch": "~2.3.0",
"npm-run-all": "~4.1.5",
"passport": "~0.4.0",
"passport-jwt": "~4.0.0",
"sanitize-html": "~1.20.0",
"faker": "~4.1.0",
"slug": "~0.9.3",
"trunc-html": "~1.1.2",
"npm-run-all": "~4.1.5",
"wait-on": "~3.2.0"
},
"devDependencies": {
"@babel/cli": "~7.2.3",
"@babel/core": "~7.2.0",
"@babel/core": "^7.2.2",
"@babel/node": "~7.2.2",
"@babel/preset-env": "~7.2.3",
"@babel/register": "~7.0.0",
"apollo-server-testing": "~2.2.6",
"babel-core": "^7.0.0-0",
"babel-eslint": "~10.0.1",
"babel-jest": "^23.6.0",
"chai": "~4.2.0",
"eslint": "~5.11.1",
"eslint-config-standard": "~12.0.0",
"eslint-plugin-import": "~2.14.0",
"eslint-plugin-jest": "^22.1.2",
"eslint-plugin-node": "~8.0.0",
"eslint-plugin-promise": "~4.0.1",
"eslint-plugin-standard": "~4.0.0",
"mocha": "~5.2.0",
"graphql-request": "^1.8.2",
"jest": "^23.6.0",
"nodemon": "~1.18.9",
"nyc": "^13.1.0"
"nyc": "^13.1.0",
"supertest": "^3.3.0"
}
}

View File

@ -111,7 +111,7 @@ export const resolvers = {
return Object.assign(currentUser, {
token: generateJwt(currentUser)
})
} else throw new AuthenticationError('Incorrect username or password.')
} else throw new AuthenticationError('Incorrect email address or password.')
})
}
}

View File

@ -0,0 +1,78 @@
import { request } from 'graphql-request'
import createServer from './server'
import mocks from './mocks'
import { create, cleanDatabase } from './seed/factories'
import jwt from 'jsonwebtoken'
let getHost
let app
let port
beforeEach(async () => {
const server = createServer({ mocks })
app = await server.start({ port: 0 })
port = app.address().port
getHost = () => `http://127.0.0.1:${port}`
})
afterEach(async () => {
await app.close()
})
describe.only('login', () => {
const mutation = (params) => {
const { email, password } = params
return `
mutation {
login(email:"${email}", password:"${password}"){
token
}
}`
}
describe('given an existing user', () => {
beforeEach(async () => {
await create('user', {
email: 'test@example.org',
password: '1234'
})
})
afterEach(async () => {
await cleanDatabase()
})
describe('asking for a `token`', () => {
describe('with valid email/password combination', () => {
it('responds with a JWT token', async () => {
const data = await request(getHost(), mutation({ email: 'test@example.org', password: '1234' }))
const { token } = data.login
jwt.verify(token, process.env.JWT_SECRET, (err, data) => {
expect(data.email).toEqual('test@example.org')
expect(err).toBeNull()
})
})
})
describe('with a valid email but incorrect password', () => {
it('responds with "Incorrect email address or password."', async () => {
try {
await request(getHost(), mutation({ email: 'test@example.org', password: 'wrong' }))
} catch (error) {
expect(error.response.errors[0].message).toEqual('Incorrect email address or password.')
}
})
})
describe('with a non-existing email', () => {
it('responds with "Incorrect email address or password."', async () => {
try {
await request(getHost(), mutation({ email: 'non-existent@example.org', password: 'wrong' }))
} catch (error) {
expect(error.response.errors[0].message).toEqual('Incorrect email address or password.')
}
})
})
})
})
})

View File

@ -1,4 +1,4 @@
import server from './server'
import createServer from './server'
const serverConfig = {
port: process.env.GRAPHQL_PORT || 4000
@ -8,6 +8,7 @@ const serverConfig = {
// }
}
const server = createServer()
server.start(serverConfig, options => {
/* eslint-disable-next-line no-console */
console.log(`Server ready at ${process.env.GRAPHQL_URI} 🚀`)

View File

@ -1,16 +1,14 @@
import { describe, it } from 'mocha'
describe('query', () => {
describe('statistics', () => {
describe('authenticated user', () => {
describe('read', () => {
it('is forbidden')
xit('is forbidden', () => {})
})
})
describe('admin', () => {
describe('read', () => {
it('is permitted')
xit('is permitted', () => {})
})
})
})

View File

@ -0,0 +1,44 @@
import ApolloClient from 'apollo-client'
import gql from 'graphql-tag'
import dotenv from 'dotenv'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import neo4j from '../../bootstrap/neo4j'
import { query } from '../../graphql-schema'
import fetch from 'node-fetch'
dotenv.config()
if (process.env.NODE_ENV === 'production') {
throw new Error('YOU CAN`T RUN FACTORIES IN PRODUCTION MODE')
}
const client = new ApolloClient({
link: new HttpLink({ uri: process.env.GRAPHQL_URI, fetch }),
cache: new InMemoryCache()
})
const driver = neo4j().getDriver()
const session = driver.session()
const builders = {
'user': require('./users.js').default
}
const buildMutation = (model, parameters) => {
return builders[model](parameters)
}
const create = async (model, parameters) => {
await client.mutate({ mutation: gql(buildMutation(model, parameters)) })
}
const cleanDatabase = async () => {
await query('MATCH (n) DETACH DELETE n', session)
}
export {
create,
buildMutation,
cleanDatabase
}

View File

@ -0,0 +1,30 @@
import faker from 'faker'
export default function (params) {
const {
name = faker.name.findName(),
email = faker.internet.email(),
password = '1234',
avatar = faker.internet.avatar()
} = params
return `
mutation {
u1: CreateUser(
id: "u1",
name: "${name}",
password: "${password}",
email: "${email}",
avatar: "${avatar}",
role: admin,
disabled: false,
deleted: false) {
id
name
email
avatar
role
}
}
`
}

View File

@ -22,10 +22,6 @@ let schema = makeExecutableSchema({
const driver = neo4j().getDriver()
const MOCK = (process.env.MOCK === 'true')
/* eslint-disable-next-line no-console */
console.log('MOCK:', MOCK)
schema = augmentSchema(schema, {
query: {
exclude: ['Statistics', 'LoggedInUser']
@ -36,31 +32,35 @@ schema = augmentSchema(schema, {
})
schema = applyScalars(applyDirectives(schema))
const server = new GraphQLServer({
context: async (req) => {
const payload = {
driver,
user: null,
req: req.request
}
try {
const token = payload.req.headers.authorization.replace('Bearer ', '')
payload.user = await jwt.verify(token, process.env.JWT_SECRET)
} catch (err) {
// nothing
}
const createServer = (options) => {
const defaults = {
context: async (req) => {
const payload = {
driver,
user: null,
req: req.request
}
try {
const token = payload.req.headers.authorization.replace('Bearer ', '')
payload.user = await jwt.verify(token, process.env.JWT_SECRET)
} catch (err) {
// nothing
}
return payload
},
schema: schema,
tracing: true,
middlewares: middleware(schema),
mocks: MOCK ? mocks : false
})
return payload
},
schema: schema,
tracing: true,
middlewares: middleware(schema),
mocks: (process.env.MOCK === 'true') ? mocks : false
}
const server = new GraphQLServer(Object.assign({}, defaults, options))
passport.use('jwt', jwtStrategy())
server.express.use(passport.initialize())
passport.use('jwt', jwtStrategy())
server.express.use(passport.initialize())
server.express.post('/graphql', passport.authenticate(['jwt'], { session: false }))
server.express.post('/graphql', passport.authenticate(['jwt'], { session: false }))
return server
}
export default server
export default createServer

1633
yarn.lock

File diff suppressed because it is too large Load Diff