add test for transmitToIota context, move some test init code into seeding

This commit is contained in:
einhorn_b 2024-01-27 15:42:14 +01:00
parent d959fdfbd8
commit 8a135bd983
12 changed files with 355 additions and 102 deletions

View File

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

View File

@ -0,0 +1,35 @@
import { Account } from '@entity/Account'
import { LogError } from '@/server/LogError'
import { KeyPair } from './KeyPair'
import { UserLogic } from './User.logic'
export class AccountLogic {
// eslint-disable-next-line no-useless-constructor
public constructor(private self: Account) {}
/**
* calculate account key pair starting from community key pair => derive user key pair => derive account key pair
* @param communityKeyPair
*/
public calculateKeyPair(communityKeyPair: KeyPair): KeyPair {
if (!this.self.user) {
throw new LogError('missing user')
}
const userLogic = new UserLogic(this.self.user)
const accountKeyPair = userLogic
.calculateKeyPair(communityKeyPair)
.derive([this.self.derivationIndex])
if (
this.self.derive2Pubkey &&
this.self.derive2Pubkey.compare(accountKeyPair.publicKey) !== 0
) {
throw new LogError(
'The freshly derived public key does not correspond to the stored public key',
)
}
return accountKeyPair
}
}

View File

@ -77,14 +77,14 @@ export class TransactionLogic {
logger.info('id is the same, it is the same transaction!')
return false
}
if (
this.self.signingAccountId !== otherTransaction.signingAccountId ||
this.self.recipientAccountId !== otherTransaction.recipientAccountId ||
this.self.communityId !== otherTransaction.communityId ||
this.self.otherCommunityId !== otherTransaction.otherCommunityId ||
this.self.amount !== otherTransaction.amount ||
this.self.accountBalanceOnCreation !== otherTransaction.accountBalanceOnCreation ||
this.self.createdAt !== otherTransaction.createdAt
this.self.createdAt.getTime() !== otherTransaction.createdAt.getTime()
) {
logger.debug('transaction a and b are not pairs', {
a: new TransactionLoggingView(this.self),
@ -135,6 +135,25 @@ export class TransactionLogic {
logger.info(`TransactionType ${type} couldn't be a CrossGroup Transaction`)
return false
}
if (
[
TransactionType.GRADIDO_CREATION,
TransactionType.GRADIDO_TRANSFER,
TransactionType.GRADIDO_DEFERRED_TRANSFER,
].includes(type)
) {
if (!this.self.amount || !otherTransaction.amount) {
logger.info('missing amount')
return false
}
if (this.self.amount.cmp(otherTransaction.amount.toString())) {
logger.info('amounts mismatch', {
a: this.self.amount.toString(),
b: otherTransaction.amount.toString(),
})
return false
}
}
if (body.otherGroup === otherBody.otherGroup) {
logger.info('otherGroups are the same', {
a: new TransactionBodyLoggingView(body),

View File

@ -1,10 +1,9 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { isValidDateString } from '@validator/DateString'
import { IsBoolean, IsUUID } from 'class-validator'
import { Field, InputType } from 'type-graphql'
import { isValidDateString } from '@validator/DateString'
@InputType()
export class CommunityDraft {
@Field(() => String)

View File

@ -1,33 +1,25 @@
import 'reflect-metadata'
import { Account } from '@entity/Account'
import { Community } from '@entity/Community'
import { User } from '@entity/User'
import { TestDB } from '@test/TestDB'
import { Decimal } from 'decimal.js-light'
import { v4 } from 'uuid'
import { CONFIG } from '@/config'
import { AccountFactory } from '@/data/Account.factory'
import { KeyPair } from '@/data/KeyPair'
import { Mnemonic } from '@/data/Mnemonic'
import { CrossGroupType } from '@/data/proto/3_3/enum/CrossGroupType'
import { TransactionType } from '@/data/proto/3_3/enum/TransactionType'
import { TransactionBody } from '@/data/proto/3_3/TransactionBody'
import { UserFactory } from '@/data/User.factory'
import { UserLogic } from '@/data/User.logic'
import { AccountType } from '@/graphql/enum/AccountType'
import { InputTransactionType } from '@/graphql/enum/InputTransactionType'
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
import { TransactionBodyLoggingView } from '@/logging/TransactionBodyLogging.view'
import { TransactionLoggingView } from '@/logging/TransactionLogging.view'
import { AddCommunityContext } from '../community/AddCommunity.context'
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
import { CreateTransactionRecipeContext } from './CreateTransationRecipe.context'
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
// eslint-disable-next-line import/order
import { communitySeed } from '@test/seeding/Community.seed'
// eslint-disable-next-line import/order
import { createUserSet, UserSet } from '@test/seeding/UserSet.seed'
jest.mock('@typeorm/DataSource', () => ({
getDataSource: jest.fn(() => TestDB.instance.dbConnect),
@ -37,58 +29,11 @@ CONFIG.IOTA_HOME_COMMUNITY_SEED = '034b0229a2ba4e98e1cc5e8767dca886279b484303ffa
const homeCommunityUuid = v4()
const foreignCommunityUuid = v4()
type UserSet = {
identifier: UserIdentifier
user: User
account: Account
}
function createUserIdentifier(userUuid: string, communityUuid: string): UserIdentifier {
const user = new UserIdentifier()
user.uuid = userUuid
user.communityUuid = communityUuid
return user
}
const keyPair = new KeyPair(new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED))
const foreignKeyPair = new KeyPair(
new Mnemonic('5d4e163c078cc6b51f5c88f8422bc8f21d1d59a284515ab1ea79e1c176ebec50'),
)
function createUserAndAccount(userIdentifier: UserIdentifier): Account {
const accountDraft = new UserAccountDraft()
accountDraft.user = userIdentifier
accountDraft.createdAt = new Date().toISOString()
accountDraft.accountType = AccountType.COMMUNITY_HUMAN
let _keyPair: KeyPair
if (userIdentifier.communityUuid === homeCommunityUuid) {
_keyPair = keyPair
} else {
_keyPair = foreignKeyPair
}
const user = UserFactory.create(accountDraft, _keyPair)
const userLogic = new UserLogic(user)
const account = AccountFactory.createAccountFromUserAccountDraft(
accountDraft,
userLogic.calculateKeyPair(_keyPair),
)
account.user = user
return account
}
function createUserSet(userUuid: string, communityUuid: string): UserSet {
const identifier = createUserIdentifier(userUuid, communityUuid)
const account = createUserAndAccount(identifier)
if (!account.user) {
throw Error('user missing')
}
return {
identifier,
account,
user: account.user,
}
}
let moderator: UserSet
let firstUser: UserSet
let secondUser: UserSet
@ -100,29 +45,13 @@ const foreignTopic = iotaTopicFromCommunityUUID(foreignCommunityUuid)
describe('interactions/backendToDb/transaction/Create Transaction Recipe Context Test', () => {
beforeAll(async () => {
await TestDB.instance.setupTestDB()
const homeCommunityDraft = new CommunityDraft()
homeCommunityDraft.uuid = homeCommunityUuid
homeCommunityDraft.foreign = false
homeCommunityDraft.createdAt = '2024-01-25T13:09:55.339Z'
let addCommunityContext = new AddCommunityContext(homeCommunityDraft)
await addCommunityContext.run()
await communitySeed(homeCommunityUuid, false)
await communitySeed(foreignCommunityUuid, true, foreignKeyPair)
const foreignCommunityDraft = new CommunityDraft()
foreignCommunityDraft.uuid = foreignCommunityUuid
foreignCommunityDraft.foreign = true
foreignCommunityDraft.createdAt = '2024-01-25T13:34:28.020Z'
addCommunityContext = new AddCommunityContext(foreignCommunityDraft)
await addCommunityContext.run()
const foreignCommunity = await Community.findOneOrFail({ where: { foreign: true } })
// that isn't entirely correct, normally only the public key from foreign community is know, and will be come form blockchain
foreignKeyPair.fillInCommunityKeys(foreignCommunity)
foreignCommunity.save()
moderator = createUserSet('ff8bbdcb-fc8b-4b5d-98e3-8bd7e1afcdbb', homeCommunityUuid)
firstUser = createUserSet('8e47e32e-0182-4099-b94d-0cac567d1392', homeCommunityUuid)
secondUser = createUserSet('9c8611dd-ee93-4cdb-a600-396c2ca91cc7', homeCommunityUuid)
foreignUser = createUserSet('b0155716-5219-4c50-b3d3-0757721ae0d2', foreignCommunityUuid)
moderator = createUserSet(homeCommunityUuid, keyPair)
firstUser = createUserSet(homeCommunityUuid, keyPair)
secondUser = createUserSet(homeCommunityUuid, keyPair)
foreignUser = createUserSet(foreignCommunityUuid, foreignKeyPair)
await Account.save([
moderator.account,
@ -197,7 +126,6 @@ describe('interactions/backendToDb/transaction/Create Transaction Recipe Context
sendTransactionDraft.linkedUser = secondUser.identifier
sendTransactionDraft.user = firstUser.identifier
sendTransactionDraft.type = InputTransactionType.SEND
sendTransactionDraft.targetDate = new Date().toISOString()
const context = new CreateTransactionRecipeContext(sendTransactionDraft)
await context.run()
const transaction = context.getTransactionRecipe()
@ -251,7 +179,6 @@ describe('interactions/backendToDb/transaction/Create Transaction Recipe Context
recvTransactionDraft.linkedUser = firstUser.identifier
recvTransactionDraft.user = secondUser.identifier
recvTransactionDraft.type = InputTransactionType.RECEIVE
recvTransactionDraft.targetDate = new Date().toISOString()
const context = new CreateTransactionRecipeContext(recvTransactionDraft)
await context.run()
const transaction = context.getTransactionRecipe()
@ -304,7 +231,6 @@ describe('interactions/backendToDb/transaction/Create Transaction Recipe Context
crossGroupSendTransactionDraft.linkedUser = foreignUser.identifier
crossGroupSendTransactionDraft.user = firstUser.identifier
crossGroupSendTransactionDraft.type = InputTransactionType.SEND
crossGroupSendTransactionDraft.targetDate = new Date().toISOString()
const context = new CreateTransactionRecipeContext(crossGroupSendTransactionDraft)
await context.run()
const transaction = context.getTransactionRecipe()
@ -361,7 +287,6 @@ describe('interactions/backendToDb/transaction/Create Transaction Recipe Context
crossGroupRecvTransactionDraft.linkedUser = firstUser.identifier
crossGroupRecvTransactionDraft.user = foreignUser.identifier
crossGroupRecvTransactionDraft.type = InputTransactionType.RECEIVE
crossGroupRecvTransactionDraft.targetDate = new Date().toISOString()
const context = new CreateTransactionRecipeContext(crossGroupRecvTransactionDraft)
await context.run()
const transaction = context.getTransactionRecipe()

View File

@ -1,5 +1,6 @@
import { Transaction } from '@entity/Transaction'
import { AccountLogic } from '@/data/Account.logic'
import { KeyPair } from '@/data/KeyPair'
import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder'
import { TransactionBuilder } from '@/data/Transaction.builder'
@ -57,9 +58,11 @@ export class TransactionRecipeRole {
await this.transactionBuilder.setOtherCommunityFromRecipientUser(recipientUser)
}
const transaction = this.transactionBuilder.getTransaction()
const communityKeyPair = new KeyPair(this.transactionBuilder.getCommunity())
const accountLogic = new AccountLogic(signingAccount)
// sign
this.transactionBuilder.setSignature(
new KeyPair(this.transactionBuilder.getCommunity()).sign(transaction.bodyBytes),
accountLogic.calculateKeyPair(communityKeyPair).sign(transaction.bodyBytes),
)
return this
}

View File

@ -11,14 +11,14 @@ import { GradidoTransactionLoggingView } from '@/logging/GradidoTransactionLoggi
import { logger } from '@/logging/logger'
export abstract class AbstractTransactionRecipeRole {
protected transactionBody: TransactionBody | undefined
// eslint-disable-next-line no-useless-constructor
public constructor(protected self: Transaction) {}
protected transactionBody: TransactionBody
public constructor(protected self: Transaction) {
this.transactionBody = TransactionBody.fromBodyBytes(this.self.bodyBytes)
}
public abstract transmitToIota(): Promise<Transaction>
protected getGradidoTransaction(): GradidoTransaction {
this.transactionBody = TransactionBody.fromBodyBytes(this.self.bodyBytes)
const transaction = new GradidoTransaction(this.transactionBody)
if (!this.self.signature) {
throw new TransactionError(

View File

@ -0,0 +1,167 @@
import 'reflect-metadata'
import { Account } from '@entity/Account'
import { TestDB } from '@test/TestDB'
import { Decimal } from 'decimal.js-light'
import { v4 } from 'uuid'
import { CONFIG } from '@/config'
import { KeyPair } from '@/data/KeyPair'
import { Mnemonic } from '@/data/Mnemonic'
import { CrossGroupType } from '@/data/proto/3_3/enum/CrossGroupType'
import { TransactionBody } from '@/data/proto/3_3/TransactionBody'
import { InputTransactionType } from '@/graphql/enum/InputTransactionType'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { logger } from '@/logging/logger'
import { CreateTransactionRecipeContext } from '../backendToDb/transaction/CreateTransationRecipe.context'
import { TransmitToIotaContext } from './TransmitToIota.context'
// eslint-disable-next-line import/order
import { communitySeed } from '@test/seeding/Community.seed'
// eslint-disable-next-line import/order
import { createUserSet, UserSet } from '@test/seeding/UserSet.seed'
jest.mock('@typeorm/DataSource', () => ({
getDataSource: jest.fn(() => TestDB.instance.dbConnect),
}))
jest.mock('@/client/IotaClient', () => {
return {
sendMessage: jest.fn().mockReturnValue({
messageId: '5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710',
}),
}
})
CONFIG.IOTA_HOME_COMMUNITY_SEED = '034b0229a2ba4e98e1cc5e8767dca886279b484303ffa73546bd5f5bf0b71285'
const homeCommunityUuid = v4()
const foreignCommunityUuid = v4()
const keyPair = new KeyPair(new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED))
const foreignKeyPair = new KeyPair(
new Mnemonic('5d4e163c078cc6b51f5c88f8422bc8f21d1d59a284515ab1ea79e1c176ebec50'),
)
let moderator: UserSet
let firstUser: UserSet
let secondUser: UserSet
let foreignUser: UserSet
const now = new Date()
describe('interactions/transmitToIota/TransmitToIotaContext', () => {
beforeAll(async () => {
await TestDB.instance.setupTestDB()
await communitySeed(homeCommunityUuid, false)
await communitySeed(foreignCommunityUuid, true, foreignKeyPair)
moderator = createUserSet(homeCommunityUuid, keyPair)
firstUser = createUserSet(homeCommunityUuid, keyPair)
secondUser = createUserSet(homeCommunityUuid, keyPair)
foreignUser = createUserSet(foreignCommunityUuid, foreignKeyPair)
await Account.save([
moderator.account,
firstUser.account,
secondUser.account,
foreignUser.account,
])
})
afterAll(async () => {
await TestDB.instance.teardownTestDB()
})
it('LOCAL transaction', async () => {
const creationTransactionDraft = new TransactionDraft()
creationTransactionDraft.amount = new Decimal('2000')
creationTransactionDraft.backendTransactionId = 1
creationTransactionDraft.createdAt = new Date().toISOString()
creationTransactionDraft.linkedUser = moderator.identifier
creationTransactionDraft.user = firstUser.identifier
creationTransactionDraft.type = InputTransactionType.CREATION
creationTransactionDraft.targetDate = new Date().toISOString()
const transactionRecipeContext = new CreateTransactionRecipeContext(creationTransactionDraft)
await transactionRecipeContext.run()
const transaction = transactionRecipeContext.getTransactionRecipe()
const context = new TransmitToIotaContext(transaction)
const debugSpy = jest.spyOn(logger, 'debug')
await context.run()
expect(
transaction.iotaMessageId?.compare(
Buffer.from('5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710', 'hex'),
),
).toBe(0)
expect(debugSpy).toHaveBeenNthCalledWith(
3,
expect.stringContaining('transmit LOCAL transaction to iota'),
expect.objectContaining({}),
)
})
it('OUTBOUND transaction', async () => {
const crossGroupSendTransactionDraft = new TransactionDraft()
crossGroupSendTransactionDraft.amount = new Decimal('100')
crossGroupSendTransactionDraft.backendTransactionId = 4
crossGroupSendTransactionDraft.createdAt = now.toISOString()
crossGroupSendTransactionDraft.linkedUser = foreignUser.identifier
crossGroupSendTransactionDraft.user = firstUser.identifier
crossGroupSendTransactionDraft.type = InputTransactionType.SEND
const transactionRecipeContext = new CreateTransactionRecipeContext(
crossGroupSendTransactionDraft,
)
await transactionRecipeContext.run()
const transaction = transactionRecipeContext.getTransactionRecipe()
await transaction.save()
const body = TransactionBody.fromBodyBytes(transaction.bodyBytes)
expect(body.type).toBe(CrossGroupType.OUTBOUND)
const context = new TransmitToIotaContext(transaction)
const debugSpy = jest.spyOn(logger, 'debug')
await context.run()
expect(
transaction.iotaMessageId?.compare(
Buffer.from('5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710', 'hex'),
),
).toBe(0)
expect(debugSpy).toHaveBeenNthCalledWith(
5,
expect.stringContaining('transmit OUTBOUND transaction to iota'),
expect.objectContaining({}),
)
})
it('INBOUND transaction', async () => {
const crossGroupRecvTransactionDraft = new TransactionDraft()
crossGroupRecvTransactionDraft.amount = new Decimal('100')
crossGroupRecvTransactionDraft.backendTransactionId = 5
crossGroupRecvTransactionDraft.createdAt = now.toISOString()
crossGroupRecvTransactionDraft.linkedUser = firstUser.identifier
crossGroupRecvTransactionDraft.user = foreignUser.identifier
crossGroupRecvTransactionDraft.type = InputTransactionType.RECEIVE
const transactionRecipeContext = new CreateTransactionRecipeContext(
crossGroupRecvTransactionDraft,
)
await transactionRecipeContext.run()
const transaction = transactionRecipeContext.getTransactionRecipe()
await transaction.save()
// console.log(new TransactionLoggingView(transaction))
const body = TransactionBody.fromBodyBytes(transaction.bodyBytes)
expect(body.type).toBe(CrossGroupType.INBOUND)
const context = new TransmitToIotaContext(transaction)
const debugSpy = jest.spyOn(logger, 'debug')
await context.run()
expect(
transaction.iotaMessageId?.compare(
Buffer.from('5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710', 'hex'),
),
).toBe(0)
expect(debugSpy).toHaveBeenNthCalledWith(
7,
expect.stringContaining('transmit INBOUND transaction to iota'),
expect.objectContaining({}),
)
})
})

View File

@ -2,7 +2,10 @@ import { Transaction } from '@entity/Transaction'
import { CrossGroupType } from '@/data/proto/3_3/enum/CrossGroupType'
import { TransactionBody } from '@/data/proto/3_3/TransactionBody'
import { logger } from '@/logging/logger'
import { TransactionLoggingView } from '@/logging/TransactionLogging.view'
import { LogError } from '@/server/LogError'
import { getDataSource } from '@/typeorm/DataSource'
import { AbstractTransactionRecipeRole } from './AbstractTransactionRecipe.role'
import { InboundTransactionRecipeRole } from './InboundTransactionRecipe.role'
@ -36,7 +39,19 @@ export class TransmitToIotaContext {
public async run(): Promise<void> {
const transaction = await this.transactionRecipeRole.transmitToIota()
logger.debug('transaction sended via iota', new TransactionLoggingView(transaction))
// store changes in db
await transaction.save()
// prevent endless loop
const paringTransaction = transaction.paringTransaction
if (paringTransaction) {
transaction.paringTransaction = undefined
await getDataSource().transaction(async (transactionalEntityManager) => {
await transactionalEntityManager.save(transaction)
await transactionalEntityManager.save(paringTransaction)
})
} else {
await transaction.save()
}
logger.info('sended transaction successfully updated in db')
}
}

View File

@ -18,7 +18,7 @@ export class TransactionLoggingView extends AbstractLoggingView {
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(showBackendTransactions = true): any {
public toJSON(showBackendTransactions = true, deep = 1): any {
return {
id: this.self.id,
nr: this.self.nr,
@ -31,16 +31,23 @@ export class TransactionLoggingView extends AbstractLoggingView {
community: new CommunityLoggingView(this.self.community).toJSON(),
otherCommunity: this.self.otherCommunity
? new CommunityLoggingView(this.self.otherCommunity)
: undefined,
: { id: this.self.otherCommunityId },
iotaMessageId: this.self.iotaMessageId
? this.self.iotaMessageId.toString(this.bufferStringFormat)
: undefined,
signingAccount: this.self.signingAccount
? new AccountLoggingView(this.self.signingAccount)
: undefined,
: { id: this.self.signingAccountId },
recipientAccount: this.self.recipientAccount
? new AccountLoggingView(this.self.recipientAccount)
: undefined,
: { id: this.self.recipientAccountId },
pairingTransaction:
this.self.paringTransaction && deep === 1
? new TransactionLoggingView(this.self.paringTransaction).toJSON(
showBackendTransactions,
deep + 1,
)
: { id: this.self.paringTransactionId },
amount: this.decimalToString(this.self.amount),
accountBalanceOnCreation: this.decimalToString(this.self.accountBalanceOnCreation),
accountBalanceOnConfirmation: this.decimalToString(this.self.accountBalanceOnConfirmation),

View File

@ -0,0 +1,28 @@
import { Community } from '@entity/Community'
import { KeyPair } from '@/data/KeyPair'
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
import { AddCommunityContext } from '@/interactions/backendToDb/community/AddCommunity.context'
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
export const communitySeed = async (
uuid: string,
foreign: boolean,
keyPair: KeyPair | undefined = undefined,
): Promise<Community> => {
const homeCommunityDraft = new CommunityDraft()
homeCommunityDraft.uuid = uuid
homeCommunityDraft.foreign = foreign
homeCommunityDraft.createdAt = new Date().toISOString()
const iotaTopic = iotaTopicFromCommunityUUID(uuid)
const addCommunityContext = new AddCommunityContext(homeCommunityDraft, iotaTopic)
await addCommunityContext.run()
const community = await Community.findOneOrFail({ where: { iotaTopic } })
if (foreign && keyPair) {
// that isn't entirely correct, normally only the public key from foreign community is know, and will be come form blockchain
keyPair.fillInCommunityKeys(community)
await community.save()
}
return community
}

View File

@ -0,0 +1,55 @@
import { Account } from '@entity/Account'
import { User } from '@entity/User'
import { v4 } from 'uuid'
import { AccountFactory } from '@/data/Account.factory'
import { KeyPair } from '@/data/KeyPair'
import { UserFactory } from '@/data/User.factory'
import { UserLogic } from '@/data/User.logic'
import { AccountType } from '@/graphql/enum/AccountType'
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
export type UserSet = {
identifier: UserIdentifier
user: User
account: Account
}
export const createUserIdentifier = (userUuid: string, communityUuid: string): UserIdentifier => {
const user = new UserIdentifier()
user.uuid = userUuid
user.communityUuid = communityUuid
return user
}
export const createUserAndAccount = (
userIdentifier: UserIdentifier,
communityKeyPair: KeyPair,
): Account => {
const accountDraft = new UserAccountDraft()
accountDraft.user = userIdentifier
accountDraft.createdAt = new Date().toISOString()
accountDraft.accountType = AccountType.COMMUNITY_HUMAN
const user = UserFactory.create(accountDraft, communityKeyPair)
const userLogic = new UserLogic(user)
const account = AccountFactory.createAccountFromUserAccountDraft(
accountDraft,
userLogic.calculateKeyPair(communityKeyPair),
)
account.user = user
return account
}
export const createUserSet = (communityUuid: string, communityKeyPair: KeyPair): UserSet => {
const identifier = createUserIdentifier(v4(), communityUuid)
const account = createUserAndAccount(identifier, communityKeyPair)
if (!account.user) {
throw Error('user missing')
}
return {
identifier,
account,
user: account.user,
}
}