mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-12 23:35:58 +00:00
refactor(backend): test block & unblock user (#8879)
* queries * test block users & small refactor on the resolver * require 92% coverage (+2%) * update according to review * use cypher instead of neode
This commit is contained in:
parent
380d3401c0
commit
66b5e61c15
@ -18,7 +18,7 @@ module.exports = {
|
||||
],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
lines: 90,
|
||||
lines: 92,
|
||||
},
|
||||
},
|
||||
testMatch: ['**/src/**/?(*.)+(spec|test).ts?(x)'],
|
||||
|
||||
11
backend/src/graphql/queries/blockUser.ts
Normal file
11
backend/src/graphql/queries/blockUser.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const blockUser = gql`
|
||||
mutation ($id: ID!) {
|
||||
blockUser(id: $id) {
|
||||
id
|
||||
name
|
||||
isBlocked
|
||||
}
|
||||
}
|
||||
`
|
||||
11
backend/src/graphql/queries/blockedUsers.ts
Normal file
11
backend/src/graphql/queries/blockedUsers.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const blockedUsers = gql`
|
||||
query {
|
||||
blockedUsers {
|
||||
id
|
||||
name
|
||||
isBlocked
|
||||
}
|
||||
}
|
||||
`
|
||||
11
backend/src/graphql/queries/unblockUser.ts
Normal file
11
backend/src/graphql/queries/unblockUser.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const unblockUser = gql`
|
||||
mutation ($id: ID!) {
|
||||
unblockUser(id: $id) {
|
||||
id
|
||||
name
|
||||
isBlocked
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -35,36 +35,17 @@ export const getMutedUsers = async (context) => {
|
||||
return mutedUsers
|
||||
}
|
||||
|
||||
export const getBlockedUsers = async (context) => {
|
||||
const { neode } = context
|
||||
const userModel = neode.model('User')
|
||||
let blockedUsers = neode
|
||||
.query()
|
||||
.match('user', userModel)
|
||||
.where('user.id', context.user.id)
|
||||
.relationship(userModel.relationships().get('blocked'))
|
||||
.to('blocked', userModel)
|
||||
.return('blocked')
|
||||
blockedUsers = await blockedUsers.execute()
|
||||
blockedUsers = blockedUsers.records.map((r) => r.get('blocked').properties)
|
||||
return blockedUsers
|
||||
}
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
mutedUsers: async (_object, _args, context, _resolveInfo) => {
|
||||
try {
|
||||
return getMutedUsers(context)
|
||||
} catch (e) {
|
||||
throw new UserInputError(e.message)
|
||||
}
|
||||
},
|
||||
blockedUsers: async (_object, _args, context, _resolveInfo) => {
|
||||
try {
|
||||
return getBlockedUsers(context)
|
||||
} catch (e) {
|
||||
throw new UserInputError(e.message)
|
||||
}
|
||||
mutedUsers: async (_object, _args, context, _resolveInfo) => getMutedUsers(context),
|
||||
blockedUsers: async (_object, _args, context: Context, _resolveInfo) => {
|
||||
return (
|
||||
await context.database.query({
|
||||
query: `MATCH (user:User{ id: $user.id})-[:BLOCKED]->(blocked:User)
|
||||
RETURN blocked {.*}`,
|
||||
variables: { user: context.user },
|
||||
})
|
||||
).records.map((r) => r.get('blocked'))
|
||||
},
|
||||
User: async (object, args, context, resolveInfo) => {
|
||||
if (args.email) {
|
||||
@ -140,9 +121,11 @@ export default {
|
||||
return unBlockUserTransactionResponse.records.map((record) => record.get('blockedUser'))[0]
|
||||
})
|
||||
try {
|
||||
return await writeTxResultPromise
|
||||
} catch (error) {
|
||||
throw new UserInputError(error.message)
|
||||
const blockedUser = await writeTxResultPromise
|
||||
if (!blockedUser) {
|
||||
throw new UserInputError('Could not find User')
|
||||
}
|
||||
return blockedUser
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
@ -164,9 +147,13 @@ export default {
|
||||
return unBlockUserTransactionResponse.records.map((record) => record.get('blockedUser'))[0]
|
||||
})
|
||||
try {
|
||||
return await writeTxResultPromise
|
||||
} catch (error) {
|
||||
throw new UserInputError(error.message)
|
||||
const unblockedUser = await writeTxResultPromise
|
||||
if (!unblockedUser) {
|
||||
throw new Error('Could not find blocked User')
|
||||
}
|
||||
return unblockedUser
|
||||
} catch {
|
||||
throw new UserInputError('Could not find blocked User')
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
|
||||
406
backend/src/graphql/resolvers/users/blockedUsers.spec.ts
Normal file
406
backend/src/graphql/resolvers/users/blockedUsers.spec.ts
Normal file
@ -0,0 +1,406 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
import { cleanDatabase } from '@db/factories'
|
||||
import { blockedUsers } from '@graphql/queries/blockedUsers'
|
||||
import { blockUser } from '@graphql/queries/blockUser'
|
||||
import { unblockUser } from '@graphql/queries/unblockUser'
|
||||
import { createApolloTestSetup } from '@root/test/helpers'
|
||||
import type { ApolloTestSetup } from '@root/test/helpers'
|
||||
import type { Context } from '@src/context'
|
||||
|
||||
let currentUser
|
||||
let blockedUser
|
||||
|
||||
let authenticatedUser: Context['user']
|
||||
const context = () => ({ authenticatedUser })
|
||||
let mutate: ApolloTestSetup['mutate']
|
||||
let query: ApolloTestSetup['query']
|
||||
let database: ApolloTestSetup['database']
|
||||
let server: ApolloTestSetup['server']
|
||||
|
||||
beforeAll(async () => {
|
||||
await cleanDatabase()
|
||||
const apolloSetup = createApolloTestSetup({ context })
|
||||
mutate = apolloSetup.mutate
|
||||
query = apolloSetup.query
|
||||
database = apolloSetup.database
|
||||
server = apolloSetup.server
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDatabase()
|
||||
void server.stop()
|
||||
void database.driver.close()
|
||||
database.neode.close()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await cleanDatabase()
|
||||
})
|
||||
|
||||
describe('blockedUsers', () => {
|
||||
describe('unauthenticated', () => {
|
||||
beforeEach(() => {
|
||||
authenticatedUser = null
|
||||
})
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
await expect(query({ query: blockedUsers })).resolves.toMatchObject({
|
||||
data: { blockedUsers: null },
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated and given a blocked user', () => {
|
||||
beforeEach(async () => {
|
||||
currentUser = await database.neode.create('User', {
|
||||
name: 'Current User',
|
||||
id: 'u1',
|
||||
})
|
||||
blockedUser = await database.neode.create('User', {
|
||||
name: 'Blocked User',
|
||||
id: 'u2',
|
||||
})
|
||||
await currentUser.relateTo(blockedUser, 'blocked')
|
||||
authenticatedUser = await currentUser.toJson()
|
||||
})
|
||||
|
||||
it('returns a list of blocked users', async () => {
|
||||
await expect(query({ query: blockedUsers })).resolves.toMatchObject({
|
||||
data: {
|
||||
blockedUsers: [
|
||||
{
|
||||
name: 'Blocked User',
|
||||
id: 'u2',
|
||||
isBlocked: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('blockUser', () => {
|
||||
describe('unauthenticated', () => {
|
||||
beforeEach(() => {
|
||||
authenticatedUser = null
|
||||
})
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
await expect(mutate({ mutation: blockUser, variables: { id: 'u2' } })).resolves.toMatchObject(
|
||||
{
|
||||
data: { blockUser: null },
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
beforeEach(async () => {
|
||||
currentUser = await database.neode.create('User', {
|
||||
name: 'Current User',
|
||||
id: 'u1',
|
||||
})
|
||||
authenticatedUser = await currentUser.toJson()
|
||||
})
|
||||
|
||||
describe('block yourself', () => {
|
||||
it('returns null', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: blockUser, variables: { id: 'u1' } }),
|
||||
).resolves.toMatchObject({
|
||||
data: { blockUser: null },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('block not existing user', () => {
|
||||
it('throws an error', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: blockUser, variables: { id: 'u2' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [
|
||||
{
|
||||
message: 'Could not find User',
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('given a to-be-blocked user', () => {
|
||||
beforeEach(async () => {
|
||||
blockedUser = await database.neode.create('User', {
|
||||
name: 'Blocked User',
|
||||
id: 'u2',
|
||||
})
|
||||
})
|
||||
|
||||
it('blocks a user', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: blockUser, variables: { id: 'u2' } }),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
blockUser: { id: 'u2', name: 'Blocked User', isBlocked: true },
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('unfollows the user when blocking', async () => {
|
||||
await currentUser.relateTo(blockedUser, 'following')
|
||||
const queryUser = gql`
|
||||
query {
|
||||
User(id: "u2") {
|
||||
id
|
||||
isBlocked
|
||||
followedByCurrentUser
|
||||
}
|
||||
}
|
||||
`
|
||||
await expect(query({ query: queryUser })).resolves.toMatchObject({
|
||||
data: { User: [{ id: 'u2', isBlocked: false, followedByCurrentUser: true }] },
|
||||
})
|
||||
await mutate({ mutation: blockUser, variables: { id: 'u2' } })
|
||||
await expect(query({ query: queryUser })).resolves.toMatchObject({
|
||||
data: { User: [{ id: 'u2', isBlocked: true, followedByCurrentUser: false }] },
|
||||
})
|
||||
})
|
||||
|
||||
describe('given both the current user and the to-be-blocked user write a post', () => {
|
||||
let postQuery
|
||||
|
||||
beforeEach(async () => {
|
||||
const post1 = await database.neode.create('Post', {
|
||||
id: 'p12',
|
||||
title: 'A post written by the current user',
|
||||
})
|
||||
const post2 = await database.neode.create('Post', {
|
||||
id: 'p23',
|
||||
title: 'A post written by the blocked user',
|
||||
})
|
||||
await Promise.all([
|
||||
post1.relateTo(currentUser, 'author'),
|
||||
post2.relateTo(blockedUser, 'author'),
|
||||
])
|
||||
postQuery = gql`
|
||||
query {
|
||||
Post(orderBy: createdAt_asc) {
|
||||
id
|
||||
title
|
||||
author {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
const bothPostsAreInTheNewsfeed = async () => {
|
||||
await expect(query({ query: postQuery })).resolves.toMatchObject({
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
id: 'p12',
|
||||
title: 'A post written by the current user',
|
||||
author: {
|
||||
name: 'Current User',
|
||||
id: 'u1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'p23',
|
||||
title: 'A post written by the blocked user',
|
||||
author: {
|
||||
name: 'Blocked User',
|
||||
id: 'u2',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
describe('from the perspective of the current user', () => {
|
||||
it('both posts are in the newsfeed', bothPostsAreInTheNewsfeed)
|
||||
|
||||
describe('but if the current user blocks the other user', () => {
|
||||
beforeEach(async () => {
|
||||
await currentUser.relateTo(blockedUser, 'blocked')
|
||||
})
|
||||
|
||||
// TODO: clarify proper behaviour
|
||||
it("the blocked user's post still shows up in the newsfeed of the current user", async () => {
|
||||
await expect(query({ query: postQuery })).resolves.toMatchObject({
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
id: 'p12',
|
||||
title: 'A post written by the current user',
|
||||
author: {
|
||||
name: 'Current User',
|
||||
id: 'u1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'p23',
|
||||
title: 'A post written by the blocked user',
|
||||
author: {
|
||||
name: 'Blocked User',
|
||||
id: 'u2',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('from the perspective of the blocked user', () => {
|
||||
beforeEach(async () => {
|
||||
authenticatedUser = await blockedUser.toJson()
|
||||
})
|
||||
|
||||
it('both posts are in the newsfeed', bothPostsAreInTheNewsfeed)
|
||||
describe('but if the current user blocks the other user', () => {
|
||||
beforeEach(async () => {
|
||||
await currentUser.relateTo(blockedUser, 'blocked')
|
||||
})
|
||||
|
||||
it("the current user's post will show up in the newsfeed of the blocked user", async () => {
|
||||
await expect(query({ query: postQuery })).resolves.toMatchObject({
|
||||
data: {
|
||||
Post: expect.arrayContaining([
|
||||
{
|
||||
id: 'p23',
|
||||
title: 'A post written by the blocked user',
|
||||
author: { name: 'Blocked User', id: 'u2' },
|
||||
},
|
||||
{
|
||||
id: 'p12',
|
||||
title: 'A post written by the current user',
|
||||
author: { name: 'Current User', id: 'u1' },
|
||||
},
|
||||
]),
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('unblockUser', () => {
|
||||
describe('unauthenticated', () => {
|
||||
beforeEach(() => {
|
||||
authenticatedUser = null
|
||||
})
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: unblockUser, variables: { id: 'u2' } }),
|
||||
).resolves.toMatchObject({
|
||||
data: { unblockUser: null },
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
beforeEach(async () => {
|
||||
currentUser = await database.neode.create('User', {
|
||||
name: 'Current User',
|
||||
id: 'u1',
|
||||
})
|
||||
authenticatedUser = await currentUser.toJson()
|
||||
})
|
||||
|
||||
describe('unblock yourself', () => {
|
||||
it('returns null', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: unblockUser, variables: { id: 'u1' } }),
|
||||
).resolves.toMatchObject({
|
||||
data: { unblockUser: null },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('unblock not-existing user', () => {
|
||||
it('throws an error', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: unblockUser, variables: { id: 'lksjdflksfdj' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [
|
||||
{
|
||||
message: 'Could not find blocked User',
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('given another user', () => {
|
||||
beforeEach(async () => {
|
||||
blockedUser = await database.neode.create('User', {
|
||||
name: 'Blocked User',
|
||||
id: 'u2',
|
||||
})
|
||||
})
|
||||
|
||||
describe('unblocking a not yet blocked user', () => {
|
||||
it('throws an error', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: unblockUser, variables: { id: 'u2' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [
|
||||
{
|
||||
message: 'Could not find blocked User',
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('given a blocked user', () => {
|
||||
beforeEach(async () => {
|
||||
await currentUser.relateTo(blockedUser, 'blocked')
|
||||
})
|
||||
|
||||
it('unblocks a user', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: unblockUser, variables: { id: 'u2' } }),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
unblockUser: { id: 'u2', name: 'Blocked User', isBlocked: false },
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('unblocking twice', () => {
|
||||
it('throws an error on second unblock', async () => {
|
||||
await mutate({ mutation: unblockUser, variables: { id: 'u2' } })
|
||||
await expect(
|
||||
mutate({ mutation: unblockUser, variables: { id: 'u2' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [
|
||||
{
|
||||
message: 'Could not find blocked User',
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user