Merge branch '2473-feature-emails-further-design-of-html-emails' of https://github.com/gradido/gradido into 2473-feature-emails-further-design-of-html-emails

This commit is contained in:
elweyn 2023-05-19 09:20:18 +02:00
commit e77385e27d
6 changed files with 165 additions and 12 deletions

View File

@ -7,7 +7,7 @@ module.exports = {
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
coverageThreshold: {
global: {
lines: 86,
lines: 89,
},
},
setupFiles: ['<rootDir>/test/testSetup.ts'],

View File

@ -19,6 +19,7 @@ import {
createContribution,
login,
sendCoins,
updateUserInfos,
} from '@/seeds/graphql/mutations'
import { transactionsQuery } from '@/seeds/graphql/queries'
import { bobBaumeister } from '@/seeds/users/bob-baumeister'
@ -52,10 +53,13 @@ let bobData: any
let peterData: any
let user: User[]
let bob: User
let peter: User
describe('send coins', () => {
beforeAll(async () => {
await userFactory(testEnv, peterLustig)
await userFactory(testEnv, bobBaumeister)
peter = await userFactory(testEnv, peterLustig)
bob = await userFactory(testEnv, bobBaumeister)
await userFactory(testEnv, stephenHawking)
await userFactory(testEnv, garrickOllivander)
@ -376,6 +380,114 @@ describe('send coins', () => {
})
})
describe('send coins via gradido ID', () => {
it('sends the coins', async () => {
await expect(
mutate({
mutation: sendCoins,
variables: {
identifier: peter?.gradidoID,
amount: 10,
memo: 'send via gradido ID',
},
}),
).resolves.toMatchObject({
data: {
sendCoins: true,
},
errors: undefined,
})
})
})
describe('send coins via alias', () => {
beforeAll(async () => {
await mutate({
mutation: updateUserInfos,
variables: {
alias: 'bob',
},
})
await mutate({
mutation: login,
variables: peterData,
})
})
afterAll(async () => {
await mutate({
mutation: login,
variables: bobData,
})
})
it('sends the coins', async () => {
await expect(
mutate({
mutation: sendCoins,
variables: {
identifier: 'bob',
amount: 6.66,
memo: 'send via alias',
},
}),
).resolves.toMatchObject({
data: {
sendCoins: true,
},
errors: undefined,
})
})
describe("peter's transactions", () => {
it('has all expected transactions', async () => {
await expect(query({ query: transactionsQuery })).resolves.toMatchObject({
data: {
transactionList: {
balance: expect.any(Object),
transactions: [
expect.objectContaining({
typeId: 'DECAY',
}),
expect.objectContaining({
amount: expect.decimalEqual(-6.66),
linkedUser: {
firstName: 'Bob',
gradidoID: bob?.gradidoID,
lastName: 'der Baumeister',
},
memo: 'send via alias',
typeId: 'SEND',
}),
expect.objectContaining({
amount: expect.decimalEqual(10),
linkedUser: {
firstName: 'Bob',
gradidoID: bob?.gradidoID,
lastName: 'der Baumeister',
},
memo: 'send via gradido ID',
typeId: 'RECEIVE',
}),
expect.objectContaining({
amount: expect.decimalEqual(50),
linkedUser: {
firstName: 'Bob',
gradidoID: bob?.gradidoID,
lastName: 'der Baumeister',
},
memo: 'unrepeatable memo',
typeId: 'RECEIVE',
}),
],
},
},
errors: undefined,
})
})
})
})
describe('more transactions to test semaphore', () => {
it('sends the coins four times in a row', async () => {
await expect(

View File

@ -323,6 +323,7 @@ export class TransactionResolver {
}
// TODO this is subject to replay attacks
// --- WHY?
const senderUser = getUser(context)
// validate recipient user

View File

@ -2358,15 +2358,21 @@ describe('UserResolver', () => {
mutation: login,
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
})
await mutate({
mutation: updateUserInfos,
variables: {
alias: 'bibi',
},
})
})
describe('identifier is no gradido ID and no email', () => {
describe('identifier is no gradido ID, no email and no alias', () => {
it('throws and logs "Unknown identifier type" error', async () => {
await expect(
query({
query: userQuery,
variables: {
identifier: 'identifier',
identifier: 'identifier_is_no_valid_alias!',
},
}),
).resolves.toEqual(
@ -2374,7 +2380,10 @@ describe('UserResolver', () => {
errors: [new GraphQLError('Unknown identifier type')],
}),
)
expect(logger.error).toBeCalledWith('Unknown identifier type', 'identifier')
expect(logger.error).toBeCalledWith(
'Unknown identifier type',
'identifier_is_no_valid_alias!',
)
})
})
@ -2441,6 +2450,29 @@ describe('UserResolver', () => {
)
})
})
describe('identifier is found via alias', () => {
it('returns user', async () => {
await expect(
query({
query: userQuery,
variables: {
identifier: 'bibi',
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
user: {
firstName: 'Bibi',
lastName: 'Bloxberg',
},
},
errors: undefined,
}),
)
})
})
})
})

View File

@ -4,6 +4,8 @@ import { validate, version } from 'uuid'
import { LogError } from '@/server/LogError'
import { VALID_ALIAS_REGEX } from './validateAlias'
export const findUserByIdentifier = async (identifier: string): Promise<DbUser> => {
let user: DbUser | undefined
if (validate(identifier) && version(identifier) === 4) {
@ -27,8 +29,12 @@ export const findUserByIdentifier = async (identifier: string): Promise<DbUser>
}
user = userContact.user
user.emailContact = userContact
} else if (VALID_ALIAS_REGEX.exec(identifier)) {
user = await DbUser.findOne({ where: { alias: identifier }, relations: ['emailContact'] })
if (!user) {
throw new LogError('No user found to given identifier', identifier)
}
} else {
// last is alias when implemented
throw new LogError('Unknown identifier type', identifier)
}

View File

@ -3,7 +3,10 @@ import { User as DbUser } from '@entity/User'
import { LogError } from '@/server/LogError'
const reservedAlias = [
// eslint-disable-next-line security/detect-unsafe-regex
export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/
const RESERVED_ALIAS = [
'admin',
'email',
'gast',
@ -24,10 +27,9 @@ const reservedAlias = [
export const validateAlias = async (alias: string): Promise<boolean> => {
if (alias.length < 3) throw new LogError('Given alias is too short', alias)
if (alias.length > 20) throw new LogError('Given alias is too long', alias)
/* eslint-disable-next-line security/detect-unsafe-regex */
if (!alias.match(/^[0-9A-Za-z]([_-]?[A-Za-z0-9])+$/))
throw new LogError('Invalid characters in alias', alias)
if (reservedAlias.includes(alias.toLowerCase())) throw new LogError('Alias is not allowed', alias)
if (!alias.match(VALID_ALIAS_REGEX)) throw new LogError('Invalid characters in alias', alias)
if (RESERVED_ALIAS.includes(alias.toLowerCase()))
throw new LogError('Alias is not allowed', alias)
const aliasInUse = await DbUser.find({
where: { alias: Raw((a) => `LOWER(${a}) = "${alias.toLowerCase()}"`) },
})