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 verification()
} finally {
await neode.close()
neode.close()
}
})()

View File

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

View File

@ -11,7 +11,7 @@ import { getDriver, getNeode } from '@db/neo4j'
class Store {
async init(errFn) {
const neode = getNeode()
const session = neode.driver.session()
const session = neode.session()
const txFreshIndicesConstrains = session.writeTransaction(async (txc) => {
// drop all indices and constraints
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
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()
// eslint-disable-next-line no-console
console.log('Successfully created database indices and constraints!')
@ -46,8 +49,8 @@ class Store {
console.log(error) // eslint-disable-line no-console
errFn(error)
} finally {
session.close()
neode.driver.close()
await session.close()
neode.close()
}
}
@ -76,7 +79,7 @@ class Store {
console.log(error) // eslint-disable-line no-console
next(error)
} finally {
session.close()
await session.close()
}
}
@ -112,7 +115,7 @@ class Store {
console.log(error) // eslint-disable-line no-console
next(error)
} finally {
session.close()
await session.close()
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -92,7 +92,7 @@ export async function up(next) {
console.log('rolled back')
throw new Error(error)
} finally {
session.close()
await session.close()
}
}
@ -114,6 +114,6 @@ export async function down(next) {
// eslint-disable-next-line no-console
console.log('rolled back')
} 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.')
printSummaries(stats)
} finally {
session.close()
await session.close()
}
}
@ -104,6 +104,6 @@ export async function down() {
console.log('UNDO: Split images from users and posts.')
printSummaries(stats)
} finally {
session.close()
await session.close()
}
}

View File

@ -28,7 +28,7 @@ export async function up(next) {
`)
try {
// 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
console.log(users)
await transaction.commit()
@ -41,7 +41,7 @@ export async function up(next) {
console.log('rolled back')
throw new Error(error)
} finally {
session.close()
await session.close()
}
}

View File

@ -30,7 +30,7 @@ export async function up(next) {
`)
try {
// 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
console.log(posts)
await transaction.commit()
@ -43,7 +43,7 @@ export async function up(next) {
console.log('rolled back')
throw new Error(error)
} finally {
session.close()
await session.close()
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,7 @@ export async function up(_next) {
console.log('rolled back')
throw new Error(error)
} finally {
session.close()
await session.close()
}
}
@ -52,6 +52,6 @@ export async function down(_next) {
console.log('rolled back')
throw new Error(error)
} 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-assignment */
/* eslint-disable import/no-named-as-default-member */
import neo4j from 'neo4j-driver'
import neo4j, { Driver } from 'neo4j-driver'
import Neode from 'neode'
import CONFIG from '@config/index'
import models from '@models/index'
let driver
let driver: Driver
const defaultOptions = {
uri: CONFIG.NEO4J_URI,
username: CONFIG.NEO4J_USERNAME,
@ -25,7 +25,7 @@ export function getDriver(options = {}) {
return driver
}
let neodeInstance
let neodeInstance: Neode
export function getNeode(options = {}) {
if (!neodeInstance) {
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 */
console.log('Seeded Data...')
await driver.close()
await neode.close()
neode.close()
process.exit(0)
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (err) {

View File

@ -3,6 +3,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import Factory, { cleanDatabase } from '@db/factories'
import { getDriver, getNeode } from '@db/neo4j'
import User from '@models/User'
import decode from './decode'
import encode from './encode'
@ -16,7 +17,7 @@ beforeAll(async () => {
afterAll(async () => {
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
@ -86,26 +87,28 @@ describe('decode', () => {
})
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 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({
lastActiveAt: expect.any(String),
})
})
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({
updatedAt: new Date().toISOString(),
lastActiveAt: '2019-10-03T23:33:08.598Z',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
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({
lastActiveAt: '2019-10-03T23:33:08.598Z',
})
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({
// should be a different time by now ;)
lastActiveAt: expect.not.stringContaining('2019-10-03T23:33'),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -33,7 +33,7 @@ describe('authorization', () => {
afterAll(async () => {
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

View File

@ -8,6 +8,7 @@ import { rule, shield, deny, allow, or, and } from 'graphql-shield'
import CONFIG from '@config/index'
import { getNeode } from '@db/neo4j'
import SocialMedia from '@models/SocialMedia'
import { validateInviteCode } from '@schema/resolvers/transactions/inviteCodes'
const debug = !!CONFIG.DEBUG
@ -48,15 +49,16 @@ const isMySocialMedia = rule({
if (!user) {
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?
if (!socialMedia) {
return false
}
socialMedia = await socialMedia.toJson() // whats this for?
const socialMediaJson = await socialMedia.toJson() // whats this for?
// 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({

View File

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

View File

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

View File

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

View File

@ -79,7 +79,7 @@ beforeAll(async () => {
afterAll(async () => {
await cleanDatabase()
driver.close()
await driver.close()
})
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/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */
@ -15,7 +16,7 @@ beforeAll(async () => {
afterAll(async () => {
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -76,7 +76,7 @@ beforeAll(async () => {
afterAll(async () => {
await cleanDatabase()
driver.close()
await driver.close()
})
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-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,7 @@ beforeAll(async () => {
afterAll(async () => {
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

View File

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

View File

@ -75,7 +75,7 @@ describe('moderate resources', () => {
afterAll(async () => {
await cleanDatabase()
driver.close()
await driver.close()
})
beforeEach(async () => {
@ -194,7 +194,7 @@ describe('moderate resources', () => {
])
const cypher =
'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)
})

View File

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

View File

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

View File

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

View File

@ -10,6 +10,7 @@ import CONFIG from '@config/index'
import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { createPostMutation } from '@graphql/queries/createPostMutation'
import Image from '@models/Image'
import createServer from '@src/server'
CONFIG.CATEGORIES_ACTIVE = true
@ -46,7 +47,7 @@ beforeAll(async () => {
afterAll(async () => {
await cleanDatabase()
driver.close()
await driver.close()
})
beforeEach(async () => {
@ -974,9 +975,13 @@ describe('UpdatePost', () => {
variables = { ...variables, image: { sensitive: true } }
})
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 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
})
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 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 () => {
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)
variables = { ...variables, id: 'only-pinned-post' }
await mutate({ mutation: pinPostMutation, variables })
pinnedPost = await neode.cypher(cypher)
pinnedPost = await neode.cypher(cypher, {})
expect(pinnedPost.records).toHaveLength(1)
})
@ -1257,6 +1266,7 @@ describe('pin posts', () => {
await mutate({ mutation: pinPostMutation, variables })
pinnedPost = await neode.cypher(
`MATCH (:User)-[pinned:PINNED]->(post:Post) RETURN post, pinned`,
{},
)
expect(pinnedPost.records).toHaveLength(1)
})

View File

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

View File

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

View File

@ -122,7 +122,7 @@ describe('file a report on a resource', () => {
afterAll(async () => {
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

View File

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ beforeAll(async () => {
afterAll(async () => {
await cleanDatabase()
driver.close()
await driver.close()
})
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-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */

View File

@ -44,7 +44,7 @@ beforeAll(async () => {
afterAll(async () => {
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

View File

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

View File

@ -4,6 +4,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* 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 gql from 'graphql-tag'
import jwt from 'jsonwebtoken'
@ -56,7 +58,7 @@ beforeAll(async () => {
afterAll(async () => {
await cleanDatabase()
driver.close()
await driver.close()
})
beforeEach(() => {
@ -160,8 +162,16 @@ describe('currentUser', () => {
await respondsWith({
data: {
currentUser: expect.objectContaining({
activeCategories: [
activeCategories: expect.arrayContaining([
'cat1',
'cat2',
'cat3',
'cat4',
'cat5',
'cat6',
'cat7',
'cat8',
'cat9',
'cat10',
'cat11',
'cat12',
@ -172,15 +182,7 @@ describe('currentUser', () => {
'cat17',
'cat18',
'cat19',
'cat2',
'cat3',
'cat4',
'cat5',
'cat6',
'cat7',
'cat8',
'cat9',
],
]),
}),
},
})
@ -272,7 +274,11 @@ describe('login', () => {
describe('normalization', () => {
describe('email address is a gmail address ', () => {
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' })
})

View File

@ -61,7 +61,7 @@ export default {
changePassword: async (_, { oldPassword, newPassword }, { user }) => {
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))) {
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 Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import User from '@models/User'
import createServer from '@src/server'
const categoryIds = ['cat9']
@ -125,7 +126,7 @@ beforeAll(async () => {
afterAll(async () => {
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
@ -1083,7 +1084,7 @@ describe('updateOnlineStatus', () => {
const cypher = 'MATCH (u:User {id: $id}) RETURN u'
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({
lastOnlineStatus: 'away',
awaySince: expect.any(String),
@ -1587,14 +1588,14 @@ describe('resetTrophyBadgesSelected', () => {
isDefault: true,
},
],
badgeTrophiesUnused: [
badgeTrophiesUnused: expect.arrayContaining([
{
id: 'trophy_panda',
},
{
id: 'trophy_bear',
},
],
]),
badgeTrophiesUnusedCount: 2,
},
},

View File

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

View File

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

View File

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