refactor(backend): types for neo4j & neode (#8409)

* type for neo4j and neode

* fix build

* remove flakyness

* wait for neode to install schema

* remove flakyness

* explain why we wait for a non-promise
This commit is contained in:
Ulf Gebhardt 2025-04-25 10:04:58 +02:00 committed by GitHub
parent 2369d13ca2
commit 507179738a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
81 changed files with 236 additions and 180 deletions

View File

@ -12,6 +12,6 @@ import { trophies, verification } from './seed/badges'
await trophies() await trophies()
await verification() await verification()
} finally { } finally {
await neode.close() neode.close()
} }
})() })()

View File

@ -39,7 +39,7 @@ export const cleanDatabase = async ({ withMigrations } = { withMigrations: false
return transaction.run(clean) return transaction.run(clean)
}) })
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -11,7 +11,7 @@ import { getDriver, getNeode } from '@db/neo4j'
class Store { class Store {
async init(errFn) { async init(errFn) {
const neode = getNeode() const neode = getNeode()
const session = neode.driver.session() const session = neode.session()
const txFreshIndicesConstrains = session.writeTransaction(async (txc) => { const txFreshIndicesConstrains = session.writeTransaction(async (txc) => {
// drop all indices and constraints // drop all indices and constraints
await txc.run('CALL apoc.schema.assert({},{},true)') await txc.run('CALL apoc.schema.assert({},{},true)')
@ -38,6 +38,9 @@ class Store {
// we need to have all constraints and indexes defined here. They can not be properly migrated // we need to have all constraints and indexes defined here. They can not be properly migrated
await txFreshIndicesConstrains await txFreshIndicesConstrains
// You have to wait for the schema to install, else the constraints will not be present.
// This is a type error of the library
// eslint-disable-next-line @typescript-eslint/await-thenable
await getNeode().schema.install() await getNeode().schema.install()
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('Successfully created database indices and constraints!') console.log('Successfully created database indices and constraints!')
@ -46,8 +49,8 @@ class Store {
console.log(error) // eslint-disable-line no-console console.log(error) // eslint-disable-line no-console
errFn(error) errFn(error)
} finally { } finally {
session.close() await session.close()
neode.driver.close() neode.close()
} }
} }
@ -76,7 +79,7 @@ class Store {
console.log(error) // eslint-disable-line no-console console.log(error) // eslint-disable-line no-console
next(error) next(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -112,7 +115,7 @@ class Store {
console.log(error) // eslint-disable-line no-console console.log(error) // eslint-disable-line no-console
next(error) next(error)
} finally { } finally {
session.close() await session.close()
} }
} }
} }

View File

@ -27,7 +27,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -48,6 +48,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -25,7 +25,8 @@ export const description = `
` `
export function up(next) { export function up(next) {
const driver = getDriver() const driver = getDriver()
const rxSession = driver.rxSession() // eslint-disable-next-line @typescript-eslint/no-explicit-any
const rxSession = driver.rxSession() as any
rxSession rxSession
.beginTransaction() .beginTransaction()
.pipe( .pipe(

View File

@ -19,7 +19,8 @@ export const description = `
` `
export function up(next) { export function up(next) {
const driver = getDriver() const driver = getDriver()
const rxSession = driver.rxSession() // eslint-disable-next-line @typescript-eslint/no-explicit-any
const rxSession = driver.rxSession() as any
rxSession rxSession
.beginTransaction() .beginTransaction()
.pipe( .pipe(

View File

@ -37,20 +37,20 @@ export async function up(_next) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('rolled back') console.log('rolled back')
} finally { } finally {
session.close() await session.close()
} }
} }
export function down(next) { export async function down(next) {
const driver = getDriver() const driver = getDriver()
const session = driver.session() const session = driver.session()
try { try {
// Rollback your migration here. // Rollback your migration here.
next() // next()
// eslint-disable-next-line no-catch-all/no-catch-all // eslint-disable-next-line no-catch-all/no-catch-all
} catch (err) { } catch (err) {
next(err) next(err)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -39,7 +39,7 @@ export async function up(next) {
throw new Error(error) throw new Error(error)
} }
} finally { } finally {
session.close() await session.close()
} }
} }
@ -66,6 +66,6 @@ export async function down(next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -40,7 +40,7 @@ export async function up(next) {
throw new Error(error) throw new Error(error)
} }
} finally { } finally {
session.close() await session.close()
} }
} }
@ -64,6 +64,6 @@ export async function down(next) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('rolled back') console.log('rolled back')
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -92,7 +92,7 @@ export async function up(next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -114,6 +114,6 @@ export async function down(next) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('rolled back') console.log('rolled back')
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -66,7 +66,7 @@ export async function up() {
console.log('Created image nodes from all user avatars and post images.') console.log('Created image nodes from all user avatars and post images.')
printSummaries(stats) printSummaries(stats)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -104,6 +104,6 @@ export async function down() {
console.log('UNDO: Split images from users and posts.') console.log('UNDO: Split images from users and posts.')
printSummaries(stats) printSummaries(stats)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -28,7 +28,7 @@ export async function up(next) {
`) `)
try { try {
// Implement your migration here. // Implement your migration here.
const users = await updateDeletedUserAttributes.records.map((record) => record.get('user')) const users = updateDeletedUserAttributes.records.map((record) => record.get('user'))
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(users) console.log(users)
await transaction.commit() await transaction.commit()
@ -41,7 +41,7 @@ export async function up(next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -30,7 +30,7 @@ export async function up(next) {
`) `)
try { try {
// Implement your migration here. // Implement your migration here.
const posts = await updateDeletedPostsAttributes.records.map((record) => record.get('post')) const posts = updateDeletedPostsAttributes.records.map((record) => record.get('post'))
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(posts) console.log(posts)
await transaction.commit() await transaction.commit()
@ -43,7 +43,7 @@ export async function up(next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -62,7 +62,7 @@ export async function up(next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -27,7 +27,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -50,6 +50,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -27,7 +27,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -50,6 +50,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -43,7 +43,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -68,6 +68,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -33,7 +33,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -60,6 +60,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -42,7 +42,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -74,6 +74,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -50,7 +50,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -75,6 +75,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -30,7 +30,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -54,6 +54,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -30,7 +30,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -54,6 +54,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-base-to-string */
/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/restrict-template-expressions */
@ -26,11 +27,11 @@ export async function up(_next) {
`) `)
for (const event of events.records) { for (const event of events.records) {
let [id, eventStart, eventEnd] = event let [id, eventStart, eventEnd] = event
let date = new Date(eventStart) let date = new Date(eventStart as string)
date.setHours(date.getHours() - 1) date.setHours(date.getHours() - 1)
eventStart = date.toISOString() eventStart = date.toISOString()
if (eventEnd) { if (eventEnd) {
date = new Date(eventEnd) date = new Date(eventEnd as string)
date.setHours(date.getHours() - 1) date.setHours(date.getHours() - 1)
eventEnd = date.toISOString() eventEnd = date.toISOString()
} }
@ -50,7 +51,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -69,6 +70,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -37,7 +37,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -62,6 +62,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -37,7 +37,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -63,6 +63,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -38,7 +38,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -69,6 +69,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -30,7 +30,7 @@ export async function up(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }
@ -52,6 +52,6 @@ export async function down(_next) {
console.log('rolled back') console.log('rolled back')
throw new Error(error) throw new Error(error)
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -4,13 +4,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable import/no-named-as-default-member */ /* eslint-disable import/no-named-as-default-member */
import neo4j from 'neo4j-driver' import neo4j, { Driver } from 'neo4j-driver'
import Neode from 'neode' import Neode from 'neode'
import CONFIG from '@config/index' import CONFIG from '@config/index'
import models from '@models/index' import models from '@models/index'
let driver let driver: Driver
const defaultOptions = { const defaultOptions = {
uri: CONFIG.NEO4J_URI, uri: CONFIG.NEO4J_URI,
username: CONFIG.NEO4J_USERNAME, username: CONFIG.NEO4J_USERNAME,
@ -25,7 +25,7 @@ export function getDriver(options = {}) {
return driver return driver
} }
let neodeInstance let neodeInstance: Neode
export function getNeode(options = {}) { export function getNeode(options = {}) {
if (!neodeInstance) { if (!neodeInstance) {
const { uri, username, password } = { ...defaultOptions, ...options } const { uri, username, password } = { ...defaultOptions, ...options }

View File

@ -1585,7 +1585,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
/* eslint-disable-next-line no-console */ /* eslint-disable-next-line no-console */
console.log('Seeded Data...') console.log('Seeded Data...')
await driver.close() await driver.close()
await neode.close() neode.close()
process.exit(0) process.exit(0)
// eslint-disable-next-line no-catch-all/no-catch-all // eslint-disable-next-line no-catch-all/no-catch-all
} catch (err) { } catch (err) {

View File

@ -3,6 +3,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getDriver, getNeode } from '@db/neo4j' import { getDriver, getNeode } from '@db/neo4j'
import User from '@models/User'
import decode from './decode' import decode from './decode'
import encode from './encode' import encode from './encode'
@ -16,7 +17,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543
@ -86,26 +87,28 @@ describe('decode', () => {
}) })
it('sets `lastActiveAt`', async () => { it('sets `lastActiveAt`', async () => {
let user = await neode.first('User', { id: 'u3' }) let user = await neode.first<typeof User>('User', { id: 'u3' }, undefined)
await expect(user.toJson()).resolves.not.toHaveProperty('lastActiveAt') await expect(user.toJson()).resolves.not.toHaveProperty('lastActiveAt')
await decode(driver, validAuthorizationHeader) await decode(driver, validAuthorizationHeader)
user = await neode.first('User', { id: 'u3' }) user = await neode.first<typeof User>('User', { id: 'u3' }, undefined)
await expect(user.toJson()).resolves.toMatchObject({ await expect(user.toJson()).resolves.toMatchObject({
lastActiveAt: expect.any(String), lastActiveAt: expect.any(String),
}) })
}) })
it('updates `lastActiveAt` for every authenticated request', async () => { it('updates `lastActiveAt` for every authenticated request', async () => {
let user = await neode.first('User', { id: 'u3' }) let user = await neode.first('User', { id: 'u3' }, undefined)
await user.update({ await user.update({
updatedAt: new Date().toISOString(), // eslint-disable-next-line @typescript-eslint/no-explicit-any
lastActiveAt: '2019-10-03T23:33:08.598Z', updatedAt: new Date().toISOString() as any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
lastActiveAt: '2019-10-03T23:33:08.598Z' as any,
}) })
await expect(user.toJson()).resolves.toMatchObject({ await expect(user.toJson()).resolves.toMatchObject({
lastActiveAt: '2019-10-03T23:33:08.598Z', lastActiveAt: '2019-10-03T23:33:08.598Z',
}) })
await decode(driver, validAuthorizationHeader) await decode(driver, validAuthorizationHeader)
user = await neode.first('User', { id: 'u3' }) user = await neode.first<typeof User>('User', { id: 'u3' }, undefined)
await expect(user.toJson()).resolves.toMatchObject({ await expect(user.toJson()).resolves.toMatchObject({
// should be a different time by now ;) // should be a different time by now ;)
lastActiveAt: expect.not.stringContaining('2019-10-03T23:33'), lastActiveAt: expect.not.stringContaining('2019-10-03T23:33'),

View File

@ -55,22 +55,15 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {
hashtagingUser = await neode.create( hashtagingUser = await neode.create('User', {
'User', id: 'you',
{ name: 'Al Capone',
id: 'you', slug: 'al-capone',
name: 'Al Capone', })
slug: 'al-capone',
},
{
password: '1234',
email: 'test@example.org',
},
)
await neode.create('Category', { await neode.create('Category', {
id: 'cat9', id: 'cat9',
name: 'Democracy & Politics', name: 'Democracy & Politics',

View File

@ -32,7 +32,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
const createPostMutation = gql` const createPostMutation = gql`

View File

@ -120,7 +120,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('emails sent for notifications', () => { describe('emails sent for notifications', () => {

View File

@ -94,7 +94,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('following users notifications', () => { describe('following users notifications', () => {

View File

@ -116,7 +116,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('mentions in groups', () => { describe('mentions in groups', () => {

View File

@ -102,7 +102,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('notifications for users that observe a post', () => { describe('notifications for users that observe a post', () => {

View File

@ -62,7 +62,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
afterEach(async () => { afterEach(async () => {

View File

@ -118,7 +118,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('notify group members of new posts in group', () => { describe('notify group members of new posts in group', () => {

View File

@ -88,7 +88,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -28,7 +28,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -33,7 +33,7 @@ describe('authorization', () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -8,6 +8,7 @@ import { rule, shield, deny, allow, or, and } from 'graphql-shield'
import CONFIG from '@config/index' import CONFIG from '@config/index'
import { getNeode } from '@db/neo4j' import { getNeode } from '@db/neo4j'
import SocialMedia from '@models/SocialMedia'
import { validateInviteCode } from '@schema/resolvers/transactions/inviteCodes' import { validateInviteCode } from '@schema/resolvers/transactions/inviteCodes'
const debug = !!CONFIG.DEBUG const debug = !!CONFIG.DEBUG
@ -48,15 +49,16 @@ const isMySocialMedia = rule({
if (!user) { if (!user) {
return false return false
} }
let socialMedia = await neode.find('SocialMedia', args.id) const socialMedia = await neode.find<typeof SocialMedia>('SocialMedia', args.id)
// Did we find a social media node? // Did we find a social media node?
if (!socialMedia) { if (!socialMedia) {
return false return false
} }
socialMedia = await socialMedia.toJson() // whats this for? const socialMediaJson = await socialMedia.toJson() // whats this for?
// Is it my social media entry? // Is it my social media entry?
return socialMedia.ownedBy.node.id === user.id // eslint-disable-next-line @typescript-eslint/no-explicit-any
return (socialMediaJson.ownedBy as any).node.id === user.id
}) })
const isAllowedToChangeGroupSettings = rule({ const isAllowedToChangeGroupSettings = rule({

View File

@ -42,7 +42,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -202,7 +202,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('softDeleteMiddleware', () => { describe('softDeleteMiddleware', () => {

View File

@ -46,7 +46,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('middleware/userInteractions', () => { describe('middleware/userInteractions', () => {

View File

@ -79,7 +79,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
@ -15,7 +16,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -33,7 +33,7 @@ describe('Badges', () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -30,7 +30,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -42,7 +42,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('donations', () => { describe('donations', () => {

View File

@ -36,7 +36,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {
@ -110,11 +110,14 @@ describe('AddEmailAddress', () => {
it('connects `UnverifiedEmailAddress` to the authenticated user', async () => { it('connects `UnverifiedEmailAddress` to the authenticated user', async () => {
await mutate({ mutation, variables }) await mutate({ mutation, variables })
const result = await neode.cypher(` const result = await neode.cypher(
`
MATCH(u:User)-[:PRIMARY_EMAIL]->(:EmailAddress {email: "user@example.org"}) MATCH(u:User)-[:PRIMARY_EMAIL]->(:EmailAddress {email: "user@example.org"})
MATCH(u:User)<-[:BELONGS_TO]-(e:UnverifiedEmailAddress {email: "new-email@example.org"}) MATCH(u:User)<-[:BELONGS_TO]-(e:UnverifiedEmailAddress {email: "new-email@example.org"})
RETURN e RETURN e
`) `,
{},
)
const email = neode.hydrateFirst(result, 'e', neode.model('UnverifiedEmailAddress')) const email = neode.hydrateFirst(result, 'e', neode.model('UnverifiedEmailAddress'))
await expect(email.toJson()).resolves.toMatchObject({ await expect(email.toJson()).resolves.toMatchObject({
email: 'new-email@example.org', email: 'new-email@example.org',
@ -257,10 +260,13 @@ describe('VerifyEmailAddress', () => {
it('connects the new `EmailAddress` as PRIMARY', async () => { it('connects the new `EmailAddress` as PRIMARY', async () => {
await mutate({ mutation, variables }) await mutate({ mutation, variables })
const result = await neode.cypher(` const result = await neode.cypher(
`
MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "to-be-verified@example.org"}) MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "to-be-verified@example.org"})
RETURN e RETURN e
`) `,
{},
)
const email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) const email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress'))
await expect(email.toJson()).resolves.toMatchObject({ await expect(email.toJson()).resolves.toMatchObject({
email: 'to-be-verified@example.org', email: 'to-be-verified@example.org',
@ -272,13 +278,13 @@ describe('VerifyEmailAddress', () => {
MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "user@example.org"}) MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "user@example.org"})
RETURN e RETURN e
` `
let result = await neode.cypher(cypherStatement) let result = await neode.cypher(cypherStatement, {})
let email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) let email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress'))
await expect(email.toJson()).resolves.toMatchObject({ await expect(email.toJson()).resolves.toMatchObject({
email: 'user@example.org', email: 'user@example.org',
}) })
await mutate({ mutation, variables }) await mutate({ mutation, variables })
result = await neode.cypher(cypherStatement) result = await neode.cypher(cypherStatement, {})
email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress'))
await expect(email).toBe(false) await expect(email).toBe(false)
}) })
@ -288,13 +294,13 @@ describe('VerifyEmailAddress', () => {
MATCH(u:User {id: "567"})<-[:BELONGS_TO]-(e:EmailAddress {email: "user@example.org"}) MATCH(u:User {id: "567"})<-[:BELONGS_TO]-(e:EmailAddress {email: "user@example.org"})
RETURN e RETURN e
` `
let result = await neode.cypher(cypherStatement) let result = await neode.cypher(cypherStatement, {})
let email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) let email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress'))
await expect(email.toJson()).resolves.toMatchObject({ await expect(email.toJson()).resolves.toMatchObject({
email: 'user@example.org', email: 'user@example.org',
}) })
await mutate({ mutation, variables }) await mutate({ mutation, variables })
result = await neode.cypher(cypherStatement) result = await neode.cypher(cypherStatement, {})
email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress'))
await expect(email).toBe(false) await expect(email).toBe(false)
}) })
@ -319,10 +325,13 @@ describe('VerifyEmailAddress', () => {
it('connects the new `EmailAddress` as PRIMARY', async () => { it('connects the new `EmailAddress` as PRIMARY', async () => {
await mutate({ mutation, variables }) await mutate({ mutation, variables })
const result = await neode.cypher(` const result = await neode.cypher(
`
MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "to-be-verified@example.org"}) MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "to-be-verified@example.org"})
RETURN e RETURN e
`) `,
{},
)
const email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) const email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress'))
await expect(email.toJson()).resolves.toMatchObject({ await expect(email.toJson()).resolves.toMatchObject({
email: 'to-be-verified@example.org', email: 'to-be-verified@example.org',

View File

@ -38,7 +38,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('Filter Posts', () => { describe('Filter Posts', () => {

View File

@ -76,7 +76,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */

View File

@ -241,7 +241,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('in mode', () => { describe('in mode', () => {

View File

@ -25,7 +25,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {
@ -83,7 +83,7 @@ describe('deleteImage', () => {
return result return result
}) })
} finally { } finally {
session.close() await session.close()
} }
await expect(neode.all('Image')).resolves.toHaveLength(0) await expect(neode.all('Image')).resolves.toHaveLength(0)
await expect(someString).toEqual('Hello') await expect(someString).toEqual('Hello')
@ -106,7 +106,7 @@ describe('deleteImage', () => {
await expect(neode.all('Image')).resolves.toHaveLength(1) await expect(neode.all('Image')).resolves.toHaveLength(1)
// all good // all good
} finally { } finally {
session.close() await session.close()
} }
}) })
}) })
@ -198,9 +198,10 @@ describe('mergeImage', () => {
it('connects resource with image via given image type', async () => { it('connects resource with image via given image type', async () => {
await mergeImage(post, 'HERO_IMAGE', imageInput, { uploadCallback, deleteCallback }) await mergeImage(post, 'HERO_IMAGE', imageInput, { uploadCallback, deleteCallback })
const result = await neode.cypher(` const result = await neode.cypher(
MATCH(p:Post {id: "p99"})-[:HERO_IMAGE]->(i:Image) RETURN i,p `MATCH(p:Post {id: "p99"})-[:HERO_IMAGE]->(i:Image) RETURN i,p`,
`) {},
)
post = neode.hydrateFirst(result, 'p', neode.model('Post')) post = neode.hydrateFirst(result, 'p', neode.model('Post'))
const image = neode.hydrateFirst(result, 'i', neode.model('Image')) const image = neode.hydrateFirst(result, 'i', neode.model('Image'))
expect(post).toBeTruthy() expect(post).toBeTruthy()
@ -215,7 +216,7 @@ describe('mergeImage', () => {
it('sets metadata', async () => { it('sets metadata', async () => {
await mergeImage(post, 'HERO_IMAGE', imageInput, { uploadCallback, deleteCallback }) await mergeImage(post, 'HERO_IMAGE', imageInput, { uploadCallback, deleteCallback })
const image = await neode.first('Image', {}) const image = await neode.first<typeof Image>('Image', {}, undefined)
await expect(image.toJson()).resolves.toMatchObject({ await expect(image.toJson()).resolves.toMatchObject({
alt: 'A description of the new image', alt: 'A description of the new image',
createdAt: expect.any(String), createdAt: expect.any(String),
@ -243,9 +244,13 @@ describe('mergeImage', () => {
) )
}) })
} finally { } finally {
session.close() await session.close()
} }
const image = await neode.first('Image', { alt: 'This alt text gets overwritten' }) const image = await neode.first<typeof Image>(
'Image',
{ alt: 'This alt text gets overwritten' },
undefined,
)
await expect(image.toJson()).resolves.toMatchObject({ await expect(image.toJson()).resolves.toMatchObject({
alt: 'This alt text gets overwritten', alt: 'This alt text gets overwritten',
}) })
@ -268,7 +273,7 @@ describe('mergeImage', () => {
await expect(neode.all('Image')).resolves.toHaveLength(0) await expect(neode.all('Image')).resolves.toHaveLength(0)
// all good // all good
} finally { } finally {
session.close() await session.close()
} }
}) })
}) })
@ -296,7 +301,7 @@ describe('mergeImage', () => {
await expect(neode.all('Image')).resolves.toHaveLength(1) await expect(neode.all('Image')).resolves.toHaveLength(1)
await mergeImage(post, 'HERO_IMAGE', imageInput, { uploadCallback, deleteCallback }) await mergeImage(post, 'HERO_IMAGE', imageInput, { uploadCallback, deleteCallback })
await expect(neode.all('Image')).resolves.toHaveLength(1) await expect(neode.all('Image')).resolves.toHaveLength(1)
const image = await neode.first('Image', {}) const image = await neode.first<typeof Image>('Image', {}, undefined)
await expect(image.toJson()).resolves.toMatchObject({ await expect(image.toJson()).resolves.toMatchObject({
alt: 'A description of the new image', alt: 'A description of the new image',
createdAt: expect.any(String), createdAt: expect.any(String),

View File

@ -91,7 +91,7 @@ const wrapTransaction = async (wrappedCallback, args, opts) => {
}) })
return result return result
} finally { } finally {
session.close() await session.close()
} }
} }

View File

@ -57,7 +57,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('inviteCodes', () => { describe('inviteCodes', () => {

View File

@ -30,7 +30,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -45,7 +45,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('Message', () => { describe('Message', () => {

View File

@ -75,7 +75,7 @@ describe('moderate resources', () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {
@ -194,7 +194,7 @@ describe('moderate resources', () => {
]) ])
const cypher = const cypher =
'MATCH (:Report)<-[review:REVIEWED]-(moderator:User {id: "moderator-id"}) RETURN review' 'MATCH (:Report)<-[review:REVIEWED]-(moderator:User {id: "moderator-id"}) RETURN review'
const reviews = await neode.cypher(cypher) const reviews = await neode.cypher(cypher, {})
expect(reviews.records).toHaveLength(1) expect(reviews.records).toHaveLength(1)
}) })

View File

@ -38,7 +38,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -61,7 +61,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('observing posts', () => { describe('observing posts', () => {

View File

@ -22,6 +22,7 @@ let variables
const getAllPasswordResets = async () => { const getAllPasswordResets = async () => {
const passwordResetQuery = await neode.cypher( const passwordResetQuery = await neode.cypher(
'MATCH (passwordReset:PasswordReset) RETURN passwordReset', 'MATCH (passwordReset:PasswordReset) RETURN passwordReset',
{},
) )
const resets = passwordResetQuery.records.map((record) => record.get('passwordReset')) const resets = passwordResetQuery.records.map((record) => record.get('passwordReset'))
return resets return resets
@ -44,7 +45,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(() => { beforeEach(() => {

View File

@ -10,6 +10,7 @@ import CONFIG from '@config/index'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j' import { getNeode, getDriver } from '@db/neo4j'
import { createPostMutation } from '@graphql/queries/createPostMutation' import { createPostMutation } from '@graphql/queries/createPostMutation'
import Image from '@models/Image'
import createServer from '@src/server' import createServer from '@src/server'
CONFIG.CATEGORIES_ACTIVE = true CONFIG.CATEGORIES_ACTIVE = true
@ -46,7 +47,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {
@ -974,9 +975,13 @@ describe('UpdatePost', () => {
variables = { ...variables, image: { sensitive: true } } variables = { ...variables, image: { sensitive: true } }
}) })
it('updates the image', async () => { it('updates the image', async () => {
await expect(neode.first('Image', { sensitive: true })).resolves.toBeFalsy() await expect(
neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeFalsy()
await mutate({ mutation: updatePostMutation, variables }) await mutate({ mutation: updatePostMutation, variables })
await expect(neode.first('Image', { sensitive: true })).resolves.toBeTruthy() await expect(
neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeTruthy()
}) })
}) })
@ -996,9 +1001,13 @@ describe('UpdatePost', () => {
delete variables.image delete variables.image
}) })
it('keeps the image unchanged', async () => { it('keeps the image unchanged', async () => {
await expect(neode.first('Image', { sensitive: true })).resolves.toBeFalsy() await expect(
neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeFalsy()
await mutate({ mutation: updatePostMutation, variables }) await mutate({ mutation: updatePostMutation, variables })
await expect(neode.first('Image', { sensitive: true })).resolves.toBeFalsy() await expect(
neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeFalsy()
}) })
}) })
}) })
@ -1244,11 +1253,11 @@ describe('pin posts', () => {
it('removes previous `pinned` attribute', async () => { it('removes previous `pinned` attribute', async () => {
const cypher = 'MATCH (post:Post) WHERE post.pinned IS NOT NULL RETURN post' const cypher = 'MATCH (post:Post) WHERE post.pinned IS NOT NULL RETURN post'
pinnedPost = await neode.cypher(cypher) pinnedPost = await neode.cypher(cypher, {})
expect(pinnedPost.records).toHaveLength(1) expect(pinnedPost.records).toHaveLength(1)
variables = { ...variables, id: 'only-pinned-post' } variables = { ...variables, id: 'only-pinned-post' }
await mutate({ mutation: pinPostMutation, variables }) await mutate({ mutation: pinPostMutation, variables })
pinnedPost = await neode.cypher(cypher) pinnedPost = await neode.cypher(cypher, {})
expect(pinnedPost.records).toHaveLength(1) expect(pinnedPost.records).toHaveLength(1)
}) })
@ -1257,6 +1266,7 @@ describe('pin posts', () => {
await mutate({ mutation: pinPostMutation, variables }) await mutate({ mutation: pinPostMutation, variables })
pinnedPost = await neode.cypher( pinnedPost = await neode.cypher(
`MATCH (:User)-[pinned:PINNED]->(post:Post) RETURN post, pinned`, `MATCH (:User)-[pinned:PINNED]->(post:Post) RETURN post, pinned`,
{},
) )
expect(pinnedPost.records).toHaveLength(1) expect(pinnedPost.records).toHaveLength(1)
}) })

View File

@ -63,7 +63,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('Posts in Groups', () => { describe('Posts in Groups', () => {

View File

@ -9,6 +9,8 @@ import gql from 'graphql-tag'
import CONFIG from '@config/index' import CONFIG from '@config/index'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getDriver, getNeode } from '@db/neo4j' import { getDriver, getNeode } from '@db/neo4j'
import EmailAddress from '@models/EmailAddress'
import User from '@models/User'
import createServer from '@src/server' import createServer from '@src/server'
const neode = getNeode() const neode = getNeode()
@ -35,7 +37,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {
@ -97,17 +99,27 @@ describe('Signup', () => {
describe('creates a EmailAddress node', () => { describe('creates a EmailAddress node', () => {
it('with `createdAt` attribute', async () => { it('with `createdAt` attribute', async () => {
await mutate({ mutation, variables }) await mutate({ mutation, variables })
let emailAddress = await neode.first('EmailAddress', { email: 'someuser@example.org' }) const emailAddress = await neode.first<typeof EmailAddress>(
emailAddress = await emailAddress.toJson() 'EmailAddress',
expect(emailAddress.createdAt).toBeTruthy() { email: 'someuser@example.org' },
expect(Date.parse(emailAddress.createdAt)).toEqual(expect.any(Number)) undefined,
)
const emailAddressJson = await emailAddress.toJson()
expect(emailAddressJson.createdAt).toBeTruthy()
expect(Date.parse(emailAddressJson.createdAt as unknown as string)).toEqual(
expect.any(Number),
)
}) })
it('with a cryptographic `nonce`', async () => { it('with a cryptographic `nonce`', async () => {
await mutate({ mutation, variables }) await mutate({ mutation, variables })
let emailAddress = await neode.first('EmailAddress', { email: 'someuser@example.org' }) const emailAddress = await neode.first<typeof EmailAddress>(
emailAddress = await emailAddress.toJson() 'EmailAddress',
expect(emailAddress.nonce).toEqual(expect.any(String)) { email: 'someuser@example.org' },
undefined,
)
const emailAddressJson = await emailAddress.toJson()
expect(emailAddressJson.nonce).toEqual(expect.any(String))
}) })
describe('if the email already exists', () => { describe('if the email already exists', () => {
@ -247,7 +259,11 @@ describe('SignupVerification', () => {
it('sets `verifiedAt` attribute of EmailAddress', async () => { it('sets `verifiedAt` attribute of EmailAddress', async () => {
await mutate({ mutation, variables }) await mutate({ mutation, variables })
const email = await neode.first('EmailAddress', { email: 'john@example.org' }) const email = await neode.first(
'EmailAddress',
{ email: 'john@example.org' },
undefined,
)
await expect(email.toJson()).resolves.toEqual( await expect(email.toJson()).resolves.toEqual(
expect.objectContaining({ expect.objectContaining({
verifiedAt: expect.any(String), verifiedAt: expect.any(String),
@ -268,7 +284,7 @@ describe('SignupVerification', () => {
it('sets `about` attribute of User', async () => { it('sets `about` attribute of User', async () => {
variables = { ...variables, about: 'Find this description in the user profile' } variables = { ...variables, about: 'Find this description in the user profile' }
await mutate({ mutation, variables }) await mutate({ mutation, variables })
const user = await neode.first('User', { name: 'John Doe' }) const user = await neode.first<typeof User>('User', { name: 'John Doe' }, undefined)
await expect(user.toJson()).resolves.toMatchObject({ await expect(user.toJson()).resolves.toMatchObject({
about: 'Find this description in the user profile', about: 'Find this description in the user profile',
}) })

View File

@ -122,7 +122,7 @@ describe('file a report on a resource', () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -40,7 +40,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('Room', () => { describe('Room', () => {

View File

@ -30,7 +30,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
neode.close() neode.close()
}) })

View File

@ -56,7 +56,7 @@ describe('shout and unshout posts', () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -18,7 +18,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('SocialMedia', () => { describe('SocialMedia', () => {

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */

View File

@ -44,7 +44,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -64,7 +64,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('resolvers/userData', () => { describe('resolvers/userData', () => {

View File

@ -4,6 +4,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable promise/prefer-await-to-callbacks */ /* eslint-disable promise/prefer-await-to-callbacks */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable jest/unbound-method */
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import jwt from 'jsonwebtoken' import jwt from 'jsonwebtoken'
@ -56,7 +58,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(() => { beforeEach(() => {
@ -160,8 +162,16 @@ describe('currentUser', () => {
await respondsWith({ await respondsWith({
data: { data: {
currentUser: expect.objectContaining({ currentUser: expect.objectContaining({
activeCategories: [ activeCategories: expect.arrayContaining([
'cat1', 'cat1',
'cat2',
'cat3',
'cat4',
'cat5',
'cat6',
'cat7',
'cat8',
'cat9',
'cat10', 'cat10',
'cat11', 'cat11',
'cat12', 'cat12',
@ -172,15 +182,7 @@ describe('currentUser', () => {
'cat17', 'cat17',
'cat18', 'cat18',
'cat19', 'cat19',
'cat2', ]),
'cat3',
'cat4',
'cat5',
'cat6',
'cat7',
'cat8',
'cat9',
],
}), }),
}, },
}) })
@ -272,7 +274,11 @@ describe('login', () => {
describe('normalization', () => { describe('normalization', () => {
describe('email address is a gmail address ', () => { describe('email address is a gmail address ', () => {
beforeEach(async () => { beforeEach(async () => {
const email = await neode.first('EmailAddress', { email: 'test@example.org' }) const email = await neode.first(
'EmailAddress',
{ email: 'test@example.org' },
undefined,
)
await email.update({ email: 'someuser@gmail.com' }) await email.update({ email: 'someuser@gmail.com' })
}) })

View File

@ -61,7 +61,7 @@ export default {
changePassword: async (_, { oldPassword, newPassword }, { user }) => { changePassword: async (_, { oldPassword, newPassword }, { user }) => {
const currentUser = await neode.find('User', user.id) const currentUser = await neode.find('User', user.id)
const encryptedPassword = currentUser.get('encryptedPassword') const encryptedPassword = currentUser.get<string>('encryptedPassword')
if (!(await bcrypt.compare(oldPassword, encryptedPassword))) { if (!(await bcrypt.compare(oldPassword, encryptedPassword))) {
throw new AuthenticationError('Old password is not correct') throw new AuthenticationError('Old password is not correct')
} }

View File

@ -9,6 +9,7 @@ import gql from 'graphql-tag'
import { categories } from '@constants/categories' import { categories } from '@constants/categories'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j' import { getNeode, getDriver } from '@db/neo4j'
import User from '@models/User'
import createServer from '@src/server' import createServer from '@src/server'
const categoryIds = ['cat9'] const categoryIds = ['cat9']
@ -125,7 +126,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543
@ -1083,7 +1084,7 @@ describe('updateOnlineStatus', () => {
const cypher = 'MATCH (u:User {id: $id}) RETURN u' const cypher = 'MATCH (u:User {id: $id}) RETURN u'
const result = await neode.cypher(cypher, { id: authenticatedUser.id }) const result = await neode.cypher(cypher, { id: authenticatedUser.id })
const dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) const dbUser = neode.hydrateFirst<typeof User>(result, 'u', neode.model('User'))
await expect(dbUser.toJson()).resolves.toMatchObject({ await expect(dbUser.toJson()).resolves.toMatchObject({
lastOnlineStatus: 'away', lastOnlineStatus: 'away',
awaySince: expect.any(String), awaySince: expect.any(String),
@ -1587,14 +1588,14 @@ describe('resetTrophyBadgesSelected', () => {
isDefault: true, isDefault: true,
}, },
], ],
badgeTrophiesUnused: [ badgeTrophiesUnused: expect.arrayContaining([
{ {
id: 'trophy_panda', id: 'trophy_panda',
}, },
{ {
id: 'trophy_bear', id: 'trophy_bear',
}, },
], ]),
badgeTrophiesUnusedCount: 2, badgeTrophiesUnusedCount: 2,
}, },
}, },

View File

@ -94,7 +94,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(() => { beforeEach(() => {
@ -213,6 +213,7 @@ describe('userMiddleware', () => {
await mutate({ mutation: updateUserMutation, variables }) await mutate({ mutation: updateUserMutation, variables })
const locations = await neode.cypher( const locations = await neode.cypher(
`MATCH (city:Location)-[:IS_IN]->(district:Location)-[:IS_IN]->(state:Location)-[:IS_IN]->(country:Location) return city {.*}, state {.*}, country {.*}`, `MATCH (city:Location)-[:IS_IN]->(district:Location)-[:IS_IN]->(state:Location)-[:IS_IN]->(country:Location) return city {.*}, state {.*}, country {.*}`,
{},
) )
expect( expect(
locations.records.map((record) => { locations.records.map((record) => {

View File

@ -23,7 +23,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
beforeEach(() => { beforeEach(() => {

View File

@ -32,7 +32,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close() await driver.close()
}) })
describe('count post teaser views', () => { describe('count post teaser views', () => {