Skip to content

Commit

Permalink
test: add test for persona db
Browse files Browse the repository at this point in the history
  • Loading branch information
guanbinrui committed Mar 3, 2020
1 parent a8f1daf commit 6b17838
Show file tree
Hide file tree
Showing 5 changed files with 521 additions and 5 deletions.
7 changes: 4 additions & 3 deletions src/database/Persona/Persona.db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const db = createDBAccess(() => {
},
})
})
export const createPersonaDBAccess = db
export type FullPersonaDBTransaction<Mode extends 'readonly' | 'readwrite'> = IDBPSafeTransaction<
PersonaDB,
['personas', 'profiles'],
Expand Down Expand Up @@ -186,7 +187,7 @@ export async function updatePersonaDB(
...old,
...nextRecord,
linkedProfiles: nextLinkedProfiles,
updatedAt: new Date(),
updatedAt: nextRecord.updatedAt ?? new Date(),
})
await t.objectStore('personas').put(next)
MessageCenter.emit('personaUpdated', undefined)
Expand All @@ -202,8 +203,8 @@ export async function createOrUpdatePersonaDB(
return createPersonaDB(
{
...record,
createdAt: new Date(),
updatedAt: new Date(),
createdAt: record.createdAt ?? new Date(),
updatedAt: record.updatedAt ?? new Date(),
linkedProfiles: new IdentifierMap(new Map()),
},
t,
Expand Down
2 changes: 1 addition & 1 deletion src/database/Persona/consistency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ async function* checkProfileLink(
const designatedPersona = restorePrototype(invalidLinkedPersona, ECKeyIdentifier.prototype)
const persona = await t.objectStore('personas').get(designatedPersona.toText())
if (!persona) {
yield { type: Type.One_Way_Link_In_Profile, profile: profile, designatedPersona }
yield { type: Type.One_Way_Link_In_Profile, profile, designatedPersona }
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/database/Persona/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export async function queryProfilesWithQuery(query?: Parameters<typeof queryProf
}

/**
* Select a set of Profiles
* Select a set of Personas
*/
export async function queryPersonasWithQuery(query?: Parameters<typeof queryPersonasDB>[0]): Promise<Persona[]> {
const _ = await queryPersonasDB(query || (_ => true))
Expand Down Expand Up @@ -193,6 +193,7 @@ export async function createProfileWithPersona(
profileID: ProfileIdentifier,
data: LinkedProfileDetails,
keys: {
nickname?: string
publicKey: JsonWebKey
privateKey?: JsonWebKey
localKey?: CryptoKey
Expand All @@ -205,6 +206,7 @@ export async function createProfileWithPersona(
updatedAt: new Date(),
identifier: ec_id,
linkedProfiles: new IdentifierMap(new Map(), ProfileIdentifier),
nickname: keys.nickname,
publicKey: keys.publicKey,
privateKey: keys.privateKey,
localKey: keys.localKey,
Expand Down
280 changes: 280 additions & 0 deletions src/database/__tests__/Persona/Persona.db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
import uuid from 'uuid/v4'
import { IdentifierMap } from '../../IdentifierMap'
import { ProfileIdentifier, ECKeyIdentifier } from '../../type'
import {
PersonaRecord,
ProfileRecord,
createPersonaDB,
queryPersonaDB,
FullPersonaDBTransaction,
createPersonaDBAccess,
queryPersonasDB,
deletePersonaDB,
updatePersonaDB,
LinkedProfileDetails,
createProfileDB,
queryProfileDB,
queryProfilesDB,
updateProfileDB,
createOrUpdateProfileDB,
deleteProfileDB,
attachProfileDB,
queryPersonaByProfileDB,
queryPersonasWithPrivateKey,
createOrUpdatePersonaDB,
safeDeletePersonaDB,
} from '../../Persona/Persona.db'
import { generate_ECDH_256k1_KeyPair_ByMnemonicWord } from '../../../utils/mnemonic-code'
import { CryptoKeyToJsonWebKey } from '../../../utils/type-transform/CryptoKey-JsonWebKey'
import { deriveLocalKeyFromECDHKey } from '../../../utils/mnemonic-code/localKeyGenerate'
import { createTransaction } from '../../helpers/openDB'

export async function createPersonaRecord(name: string = uuid(), password: string = uuid()) {
const key = await generate_ECDH_256k1_KeyPair_ByMnemonicWord(password)
const jwkPub = await CryptoKeyToJsonWebKey(key.key.publicKey)
const jwkPriv = await CryptoKeyToJsonWebKey(key.key.privateKey)
const localKey = await deriveLocalKeyFromECDHKey(key.key.publicKey, key.mnemonicRecord.words)
const jwkLocalKey = await CryptoKeyToJsonWebKey(localKey)
const identifier = ECKeyIdentifier.fromJsonWebKey(jwkPub)

return {
nickname: name,
createdAt: new Date(),
updatedAt: new Date(),
identifier: identifier,
linkedProfiles: new IdentifierMap<ProfileIdentifier, LinkedProfileDetails>(new Map(), ProfileIdentifier),
publicKey: jwkPub,
privateKey: jwkPriv,
mnemonic: key.mnemonicRecord,
localKey: jwkLocalKey,
} as PersonaRecord
}

export async function createProfileRecord(
identifier: ProfileIdentifier = new ProfileIdentifier(uuid(), uuid()),
name: string = uuid(),
password: string = uuid(),
) {
const { localKey, identifier: linkedPersona, createdAt, updatedAt } = await createPersonaRecord(name, password)

return {
identifier,
nickname: name,
localKey,
linkedPersona,
createdAt,
updatedAt,
} as ProfileRecord
}

export async function personaDBWriteAccess(action: (t: FullPersonaDBTransaction<'readwrite'>) => Promise<void>) {
await action(createTransaction(await createPersonaDBAccess(), 'readwrite')('profiles', 'personas'))
}

beforeAll(() => {
// MessageCenter will dispatch events on each tab
// but the mocking query method returns undefined
browser.tabs.query = async () => {
return []
}
})

afterEach(async () => {
await personaDBWriteAccess(async t => {
await t.objectStore('personas').clear()
await t.objectStore('profiles').clear()
})
})

test('createPersonaDB & queryPersonaDB', async () => {
const personaRecord = await createPersonaRecord()
await personaDBWriteAccess(t => createPersonaDB(personaRecord, t))
expect(await queryPersonaDB(personaRecord.identifier)).toEqual(personaRecord)
})

test('queryPersonasDB', async () => {
const personaRecordA = await createPersonaRecord()
const personaRecordB = await createPersonaRecord()
const names = [personaRecordA.nickname, personaRecordB.nickname]
await personaDBWriteAccess(async t => {
await createPersonaDB(personaRecordA, t)
await createPersonaDB(personaRecordB, t)
})

const personaRecords = await queryPersonasDB(p => !!(p.nickname && names.includes(p.nickname)))
expect(personaRecords.length).toBe(2)
expect(personaRecords.every(r => names.includes(r.nickname ?? ''))).toBeTruthy()
})

test('updatePersonaDB - replace linked profiles', async () => {
const id = uuid()
const personaRecord = await createPersonaRecord(id, id)
const personaRecordNew = await createPersonaRecord(id, id)
personaRecordNew.identifier = personaRecord.identifier

await personaDBWriteAccess(async t => {
await createPersonaDB(personaRecord, t)
await updatePersonaDB(
personaRecordNew,
{
linkedProfiles: 'replace',
explicitUndefinedField: 'ignore',
},
t,
)
})
expect(await queryPersonaDB(personaRecord.identifier)).toEqual(personaRecordNew)
})

test('deletePersonaDB', async () => {
const personaRecord = await createPersonaRecord()
await personaDBWriteAccess(t => createPersonaDB(personaRecord, t))
expect(await queryPersonaDB(personaRecord.identifier)).toEqual(personaRecord)

await personaDBWriteAccess(t => deletePersonaDB(personaRecord.identifier, 'delete even with private', t))
expect(await queryPersonaDB(personaRecord.identifier)).toEqual(null)
})

test('queryPersonaByProfileDB', async () => {
const profileRecord = await createProfileRecord()
const personaRecord = await createPersonaRecord()
profileRecord.linkedPersona = personaRecord.identifier
await personaDBWriteAccess(async t => {
await createProfileDB(profileRecord, t)
await createPersonaDB(personaRecord, t)
})

expect(await queryPersonaByProfileDB(profileRecord.identifier)).toEqual(personaRecord)
})

test('queryPersonasWithPrivateKey', async () => {
const personaRecordA = await createPersonaRecord()
const personaRecordB = await createPersonaRecord()
await personaDBWriteAccess(async t => {
await createPersonaDB(personaRecordA, t)
await createPersonaDB(personaRecordB, t)
})
const names = (await queryPersonasWithPrivateKey()).map(p => p.nickname)

expect(names.includes(personaRecordA.nickname)).toBe(true)
expect(names.includes(personaRecordB.nickname)).toBe(true)
})

test('createOrUpdatePersonaDB', async () => {
const personaRecord = await createPersonaRecord()
const personaRecordNew = await createPersonaRecord()
const howToMerge = {
linkedProfiles: 'replace',
explicitUndefinedField: 'ignore',
} as Parameters<typeof updatePersonaDB>[1]
personaRecordNew.identifier = personaRecord.identifier

expect(await queryPersonaDB(personaRecord.identifier)).toEqual(null)

await personaDBWriteAccess(t => createOrUpdatePersonaDB(personaRecord, howToMerge, t))
expect(await queryPersonaDB(personaRecord.identifier)).toEqual(personaRecord)

await personaDBWriteAccess(t => createOrUpdatePersonaDB(personaRecordNew, howToMerge, t))
expect(await queryPersonaDB(personaRecord.identifier)).toEqual(personaRecordNew)
})

test('safeDeletePersonaDB', async () => {
const personaRecord = await createPersonaRecord()
const howToMerge = {
linkedProfiles: 'replace',
explicitUndefinedField: 'delete field',
} as Parameters<typeof updatePersonaDB>[1]

await personaDBWriteAccess(t => createPersonaDB(personaRecord, t))
expect(await queryPersonaDB(personaRecord.identifier)).toEqual(personaRecord)

personaRecord.linkedProfiles.clear()
await personaDBWriteAccess(async t => {
await updatePersonaDB(personaRecord, howToMerge, t)
await safeDeletePersonaDB(personaRecord.identifier, t)
})
expect(await queryPersonaDB(personaRecord.identifier)).toEqual(personaRecord)

personaRecord.privateKey = undefined
await personaDBWriteAccess(async t => {
await updatePersonaDB(personaRecord, howToMerge, t)
await safeDeletePersonaDB(personaRecord.identifier, t)
})
expect(await queryPersonaDB(personaRecord.identifier)).toEqual(null)
})

test('createProfileDB && queryProfileDB', async () => {
const profileRecord = await createProfileRecord()
await personaDBWriteAccess(t => createProfileDB(profileRecord, t))
expect(await queryProfileDB(profileRecord.identifier)).toEqual(profileRecord)
})

test('queryProfilesDB', async () => {
const profileRecordA = await createProfileRecord()
const profileRecordB = await createProfileRecord()
const nicknames = [profileRecordA.nickname, profileRecordB.nickname]
const networkIds = [profileRecordA.identifier.network, profileRecordB.identifier.network]
await personaDBWriteAccess(async t => {
await createProfileDB(profileRecordA, t)
await createProfileDB(profileRecordB, t)
})

const profileRecords = await queryProfilesDB(p => networkIds.includes(p.identifier.network))
expect(profileRecords.length).toBe(2)
expect(profileRecords.every(r => nicknames.includes(r.nickname ?? ''))).toBeTruthy()
})

test('updateProfileDB', async () => {
const profileRecord = await createProfileRecord()
const profileRecordNew = await createProfileRecord()
profileRecordNew.identifier = profileRecord.identifier

await personaDBWriteAccess(async t => {
await createProfileDB(profileRecord, t)
await updateProfileDB(profileRecordNew, t)
})

expect(await queryProfileDB(profileRecord.identifier)).toEqual(profileRecordNew)
})

test('createOrUpdateProfileDB', async () => {
const profileRecord = await createProfileRecord()
const profileRecordNew = await createProfileRecord()
profileRecordNew.identifier = profileRecord.identifier

expect(await queryProfileDB(profileRecord.identifier)).toEqual(null)

await personaDBWriteAccess(t => createOrUpdateProfileDB(profileRecord, t))
expect(await queryProfileDB(profileRecord.identifier)).toEqual(profileRecord)

await personaDBWriteAccess(t => createOrUpdateProfileDB(profileRecordNew, t))
expect(await queryProfileDB(profileRecord.identifier)).toEqual(profileRecordNew)
})

test('attachProfileDB && detachProfileDB', async () => {
const profileRecord = await createProfileRecord()
const personaRecord = await createPersonaRecord()

await personaDBWriteAccess(async t => {
await createPersonaDB(personaRecord, t)
await attachProfileDB(
profileRecord.identifier,
personaRecord.identifier,
{
connectionConfirmState: 'confirmed',
},
t,
)
})
expect((await queryProfileDB(profileRecord.identifier))?.linkedPersona).toEqual(personaRecord.identifier)
})

test('deleteProfileDB', async () => {
const profileRecord = await createProfileRecord()

await personaDBWriteAccess(t => createProfileDB(profileRecord, t))
expect(await queryProfileDB(profileRecord.identifier)).toEqual(profileRecord)

await personaDBWriteAccess(t => deleteProfileDB(profileRecord.identifier, t))
expect(await queryProfileDB(profileRecord.identifier)).toEqual(null)
})
Loading

0 comments on commit 6b17838

Please sign in to comment.