Add tests that admin can pin anyone's post/limit 1 pinned post

This commit is contained in:
mattwr18 2019-10-16 01:20:17 +02:00
parent 64f9d02c1a
commit 0007533b8c
3 changed files with 169 additions and 57 deletions

View File

@ -148,8 +148,8 @@ const permissions = shield(
SignupVerification: allow,
CreateInvitationCode: and(isAuthenticated, or(not(invitationLimitReached), isAdmin)),
UpdateUser: onlyYourself,
CreatePost: or(and(isAuthenticated, not(pinnedPost)), isAdmin),
UpdatePost: isAuthor,
CreatePost: isAuthenticated,
UpdatePost: or(and(isAuthor, not(pinnedPost)), isAdmin),
DeletePost: isAuthor,
report: isAuthenticated,
CreateSocialMedia: isAuthenticated,

View File

@ -75,29 +75,19 @@ export default {
},
Mutation: {
CreatePost: async (_parent, params, context, _resolveInfo) => {
const { categoryIds, pinned } = params
delete params.pinned
const { categoryIds } = params
delete params.categoryIds
params = await fileUpload(params, { file: 'imageUpload', url: 'image' })
params.id = params.id || uuid()
let post
let createPostCypher = `CREATE (post:Post {params})
const createPostCypher = `CREATE (post:Post {params})
SET post.createdAt = toString(datetime())
SET post.updatedAt = toString(datetime())
WITH post
MATCH (author:User {id: $userId})
MERGE (post)<-[:WROTE]-(author)
WITH post, author`
if (pinned) {
createPostCypher += `
MERGE (post)<-[:PINNED]-(author)
WITH post
`
}
createPostCypher += `
WITH post, author
UNWIND $categoryIds AS categoryId
MATCH (category:Category {id: categoryId})
MERGE (post)-[:CATEGORIZED]->(category)
@ -123,14 +113,16 @@ export default {
return post
},
UpdatePost: async (_parent, params, context, _resolveInfo) => {
const { categoryIds } = params
const { categoryIds, pinned } = params
const { id: userId } = context.user
delete params.pinned
delete params.categoryIds
params = await fileUpload(params, { file: 'imageUpload', url: 'image' })
const session = context.driver.session()
let updatePostCypher = `MATCH (post:Post {id: $params.id})
SET post += $params
SET post.updatedAt = toString(datetime())
WITH post
`
if (categoryIds && categoryIds.length) {
@ -143,15 +135,31 @@ export default {
await session.run(cypherDeletePreviousRelations, { params })
updatePostCypher += `
WITH post
UNWIND $categoryIds AS categoryId
MATCH (category:Category {id: categoryId})
MERGE (post)-[:CATEGORIZED]->(category)
WITH post
`
}
if (pinned) {
const cypherDeletePreviousRelations = `
MATCH ()-[previousRelations:PINNED]->(post:Post)
DELETE previousRelations
RETURN post
`
await session.run(cypherDeletePreviousRelations)
updatePostCypher += `
MATCH (user:User {id: $userId}) WHERE user.role = 'admin'
MERGE (user)-[:PINNED]->(post)
WITH post
`
}
updatePostCypher += `RETURN post`
const updatePostVariables = { categoryIds, params }
const updatePostVariables = { categoryIds, params, userId }
const transactionRes = await session.run(updatePostCypher, updatePostVariables)
const [post] = transactionRes.records.map(record => {

View File

@ -17,13 +17,7 @@ const categoryIds = ['cat9', 'cat4', 'cat15']
let variables
const createPostMutation = gql`
mutation(
$id: ID
$title: String!
$content: String!
$language: String
$categoryIds: [ID]
) {
mutation($id: ID, $title: String!, $content: String!, $language: String, $categoryIds: [ID]) {
CreatePost(
id: $id
title: $title
@ -380,9 +374,25 @@ describe('UpdatePost', () => {
let author, newlyCreatedPost
const updatePostMutation = gql`
mutation($id: ID!, $title: String!, $content: String!, $categoryIds: [ID], $pinned: Boolean) {
UpdatePost(id: $id, title: $title, content: $content, categoryIds: $categoryIds, pinned: $pinned) {
UpdatePost(
id: $id
title: $title
content: $content
categoryIds: $categoryIds
pinned: $pinned
) {
id
title
content
author {
name
slug
}
pinnedBy {
id
name
role
}
categories {
id
}
@ -568,13 +578,13 @@ describe('UpdatePost', () => {
})
})
})
describe('pinned posts', () => {
beforeEach(async () => {
variables = { ...variables, categoryIds: ['cat9', 'cat27', 'cat15'], pinned: true }
variables = { ...variables, pinned: true }
})
describe('users cannot pin posts on update', () => {
it.only('throws authorization error', async () => {
describe('users cannot pin posts', () => {
it('throws authorization error', async () => {
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorised!' }],
data: { UpdatePost: null },
@ -582,7 +592,7 @@ describe('UpdatePost', () => {
})
})
describe('moderator cannot create pinned posts', () => {
describe('moderator cannot pin posts', () => {
let moderator
beforeEach(async () => {
moderator = await user.update({ role: 'moderator', updatedAt: new Date().toISOString() })
@ -590,14 +600,14 @@ describe('UpdatePost', () => {
})
it('throws authorization error', async () => {
await expect(mutate({ mutation: createPostMutation, variables })).resolves.toMatchObject({
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorised!' }],
data: { CreatePost: null },
data: { UpdatePost: null },
})
})
})
describe('admin can create pinned posts', () => {
describe('admin can pin posts', () => {
let admin
beforeEach(async () => {
admin = await user.update({
@ -605,37 +615,131 @@ describe('UpdatePost', () => {
name: 'Admin',
updatedAt: new Date().toISOString(),
})
authenticatedUser = await admin.toJson()
variables = {
...variables,
title: 'pinned post',
content: 'this is super important for the community and we promise not to have too many',
content: 'this is super important for the community',
}
authenticatedUser = await admin.toJson()
})
it('throws authorization error', async () => {
const expected = {
data: {
CreatePost: {
title: 'pinned post',
content:
'this is super important for the community and we promise not to have too many',
author: {
name: 'Admin',
},
pinnedBy: {
id: 'current-user',
name: 'Admin',
role: 'admin',
describe('post created by them', () => {
beforeEach(async () => {
await factory.create('Post', {
id: 'created-and-pinned-by-same-admin',
author: admin,
})
})
it('responds with the updated Post', async () => {
variables = { ...variables, id: 'created-and-pinned-by-same-admin' }
const expected = {
data: {
UpdatePost: {
title: 'pinned post',
content: 'this is super important for the community',
author: {
name: 'Admin',
},
pinnedBy: {
id: 'current-user',
name: 'Admin',
role: 'admin',
},
},
},
},
errors: undefined,
}
errors: undefined,
}
await expect(mutate({ mutation: createPostMutation, variables })).resolves.toMatchObject(
expected,
)
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
expected,
)
})
})
describe('post created by another admin', () => {
let otherAdmin
beforeEach(async () => {
otherAdmin = await factory.create('User', {
role: 'admin',
name: 'otherAdmin',
})
authenticatedUser = await otherAdmin.toJson()
await factory.create('Post', {
id: 'created-by-one-admin-pinned-by-different-one',
author: otherAdmin,
})
})
it('responds with the updated Post', async () => {
authenticatedUser = await admin.toJson()
variables = { ...variables, id: 'created-by-one-admin-pinned-by-different-one' }
const expected = {
data: {
UpdatePost: {
title: 'pinned post',
content: 'this is super important for the community',
author: {
name: 'otherAdmin',
},
pinnedBy: {
id: 'current-user',
name: 'Admin',
role: 'admin',
},
},
},
errors: undefined,
}
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
expected,
)
})
})
describe('post created by another user', () => {
it('responds with the updated Post', async () => {
const expected = {
data: {
UpdatePost: {
title: 'pinned post',
content: 'this is super important for the community',
author: {
slug: 'the-author',
},
pinnedBy: {
id: 'current-user',
name: 'Admin',
role: 'admin',
},
},
},
errors: undefined,
}
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
expected,
)
})
})
describe('removes other pinned post', () => {
beforeEach(async () => {
await factory.create('Post', {
id: 'only-pinned-post',
author: admin,
})
})
it('leaves only one pinned post at a time', async () => {
expect.assertions(1)
await mutate({ mutation: updatePostMutation, variables })
variables = { ...variables, id: 'only-pinned-post' }
await mutate({ mutation: updatePostMutation, variables })
const pinnedPosts = await neode.cypher(`MATCH ()-[:PINNED]->(post:Post) RETURN post`)
expect(pinnedPosts.records).toHaveLength(1)
})
})
})
})