Skip to content

Commit

Permalink
Add database tests (#285)
Browse files Browse the repository at this point in the history
* Add database tests

* Fix typo
  • Loading branch information
Duddino authored Dec 19, 2023
1 parent bf5074d commit e2491f5
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 8 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^4.2.2",
"eslint": "^8.31.0",
"fake-indexeddb": "^5.0.1",
"file-loader": "^6.2.0",
"gh-pages": "^5.0.0",
"happy-dom": "^12.10.3",
Expand Down
22 changes: 14 additions & 8 deletions scripts/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,13 @@ export class Database {
}
/**
* Removes a Promo Code from the Promo management system
* @param {string} promo - the promo code to remove
* @param {string} promoCode - the promo code to remove
*/
async removePromo(promo) {
async removePromo(promoCode) {
const store = this.#db
.transaction('promos', 'readwrite')
.objectStore('promos');
await store.delete(promo);
await store.delete(promoCode);
}

/**
Expand All @@ -127,7 +127,9 @@ export class Database {
'warning',
'<b>Account Creation Error</b><br>Logs were dumped in your Browser Console<br>Please submit these privately to PIVX Labs Developers!'
);
return false;
throw new Error(
'addAccount was called with with an invalid account'
);
}

// Create an empty DB Account
Expand Down Expand Up @@ -157,7 +159,7 @@ export class Database {

// Check this account isn't already added (by pubkey once multi-account)
if (await store.get('account'))
return console.error(
throw new Error(
'DB: Ran addAccount() when account already exists!'
);

Expand Down Expand Up @@ -190,7 +192,9 @@ export class Database {
'warning',
'<b>DB Update Error</b><br>Your wallet is safe, logs were dumped in your Browser Console<br>Please submit these privately to PIVX Labs Developers!'
);
return false;
throw new Error(
'addAccount was called with with an invalid account'
);
}

// Fetch the DB account
Expand All @@ -208,7 +212,9 @@ export class Database {
'warning',
'<b>DB Update Error</b><br>Logs were dumped in your Browser Console<br>Please submit these privately to PIVX Labs Developers!'
);
return false;
throw new Error(
"updateAccount was called, but the account doesn't exist"
);
}

// We'll overlay the `account` keys atop the `DB Account` keys:
Expand Down Expand Up @@ -483,7 +489,7 @@ export class Database {
});
database.#db = db;
if (migrate) {
database.#migrateLocalStorage();
await database.#migrateLocalStorage();
}
return database;
}
Expand Down
226 changes: 226 additions & 0 deletions tests/unit/database.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import 'fake-indexeddb/auto';
import { PromoWallet } from '../../scripts/promos.js';
import { it, describe, vi, expect } from 'vitest';
import { Database } from '../../scripts/database.js';
import { Account } from '../../scripts/accounts';
import * as misc from '../../scripts/misc.js';
import { Settings } from '../../scripts/settings';
import Masternode from '../../scripts/masternode';
describe('database tests', () => {
beforeAll(() => {
// Mock createAlert
vi.spyOn(misc, 'createAlert').mockImplementation(vi.fn());
vi.stubGlobal(global.console, 'error');
return () => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
};
});
beforeEach(async () => {
// Reset indexedDB before each test
vi.stubGlobal('indexedDB', new IDBFactory());
return vi.unstubAllGlobals;
});
it('stores account correctly', async () => {
const db = await Database.create('test');
const account = new Account({
publicKey: 'test1',
coldAddress: 'very cold',
});
await db.addAccount(account);
expect(await db.getAccount()).toStrictEqual(account);
await db.updateAccount(
new Account({
encWif: 'newWIF!',
localProposals: ['prop1', 'prop2'],
})
);
expect((await db.getAccount()).encWif).toBe('newWIF!');
expect((await db.getAccount()).publicKey).toBe('test1');
expect((await db.getAccount()).coldAddress).toBe('very cold');
expect((await db.getAccount()).localProposals).toStrictEqual([
'prop1',
'prop2',
]);

// Setting localProposals as empty doesn't overwrite the array
await db.updateAccount(
new Account({
encWif: 'newWIF2!',
localProposals: [],
})
);
expect((await db.getAccount()).localProposals).toStrictEqual([
'prop1',
'prop2',
]);

// Unless `allowDeletion` is set to true
await db.updateAccount(
new Account({
encWif: 'newWIF2!',
localProposals: [],
}),
true
);
expect((await db.getAccount()).localProposals).toHaveLength(0);

await db.removeAccount({ publicKey: 'test1' });

expect(await db.getAccount()).toBeNull();
});

it.todo('stores transaction correctly', () => {
// To avoid conflicts, I will implement this after #284
});

it('stores masternodes correctly', async () => {
const db = await Database.create('test');
// Masternode should be null by default
expect(await db.getMasternode()).toBe(null);
let masternode = new Masternode({
collateralTxId: 'mntxid',
});
await db.addMasternode(masternode);
expect(await db.getMasternode()).toStrictEqual(masternode);
masternode = new Masternode({
collateralTxId: 'mntxid2',
});
// Subsequent calls to `addMasternode` should overwrite it.
await db.addMasternode(masternode);
expect(await db.getMasternode()).toStrictEqual(masternode);
// Check that it removes mn correectly
await db.removeMasternode();
expect(await db.getMasternode()).toBe(null);
});

it('stores promos correctly', async () => {
const testPromos = new Array(50).fill(0).map(
(_, i) =>
new PromoWallet({
code: `${i}`,
})
);
const db = await Database.create('test');
// It starts with no promos
expect(await db.getAllPromos()).toHaveLength(0);

await db.addPromo(testPromos[0]);
expect(await db.getAllPromos()).toStrictEqual([testPromos[0]]);

// If we add the same promo twice, it should not duplicate it
await db.addPromo(testPromos[0]);
expect(await db.getAllPromos()).toStrictEqual([testPromos[0]]);

// Removes correctly
await db.removePromo(testPromos[0].code);
expect(await db.getAllPromos()).toHaveLength(0);

for (const promo of testPromos) {
await db.addPromo(promo);
}
expect(
(await db.getAllPromos()).sort(
(a, b) => parseInt(a.code) - parseInt(b.code)
)
).toStrictEqual(testPromos);
await db.removePromo('23');
expect(
(await db.getAllPromos()).sort(
(a, b) => parseInt(a.code) - parseInt(b.code)
)
).toStrictEqual(testPromos.filter((p) => p.code != '23'));
});

it('stores settings correctly', async () => {
const db = await Database.create('test');
const settings = new Settings({
explorer: 'duddino.com',
node: 'pivx.com',
});
// Settings should be left as default at the beginning
expect(await db.getSettings()).toStrictEqual(new Settings());
await db.setSettings(settings);
expect(await db.getSettings()).toStrictEqual(settings);
// Test that overwrite works as expected
await db.setSettings({
node: 'pivx.org',
});
expect(await db.getSettings()).toStrictEqual(
new Settings({
explorer: 'duddino.com',
node: 'pivx.org',
})
);
});

it('throws when calling addAccount twice', async () => {
const db = await Database.create('test');
const account = new Account();
db.addAccount(account);
expect(() => db.addAccount(account)).rejects.toThrow(
/account already exists/i
);
});
it('throws when called with an invalid account', async () => {
const db = await Database.create('test');
expect(() => db.addAccount({ publicKey: 'jaeir' })).rejects.toThrow(
/invalid account/
);
expect(() => db.updateAccount({ publicKey: 'jaeir' })).rejects.toThrow(
/invalid account/
);
});
it("throws when updating an account that doesn't exist", async () => {
const db = await Database.create('test');
expect(() => db.updateAccount(new Account())).rejects.toThrow(
/account doesn't exist/
);
});

it('migrates from local storage correctly', async () => {
vi.stubGlobal('localStorage', {
explorer: 'duddino.com',
translation: 'DE',
encwif: 'ENCRYPTED_WIF',
publicKey: 'PUB_KEY',
masternode: JSON.stringify(
new Masternode({ collateralTxId: 'mntxid' })
),
});
const db = await Database.create('test');
expect(await db.getAccount()).toStrictEqual(
new Account({
publicKey: 'PUB_KEY',
encWif: 'ENCRYPTED_WIF',
})
);
expect(await db.getSettings()).toStrictEqual(
new Settings({
explorer: 'duddino.com',
translation: 'DE',
})
);
expect(await db.getMasternode()).toStrictEqual(
new Masternode({ collateralTxId: 'mntxid' })
);

vi.unstubAllGlobals();
});

it('is isolated between different instances', async () => {
const db = await Database.create('test');
const db2 = await Database.create('test2');
// Initially, both accounts are null
expect(await db.getAccount()).toBe(null);
expect(await db2.getAccount()).toBe(null);
const account = new Account({
publicKey: 'test1',
});
// Let's add an account to the first db
await db.addAccount(account);
// First DB has the account, the second one is undefined
expect((await db.getAccount())?.publicKey).toBe('test1');
expect((await db2.getAccount())?.publicKey).toBeUndefined();
});
});

0 comments on commit e2491f5

Please sign in to comment.