diff --git a/__tests__/integration/auth.spec.js b/__tests__/integration/auth.spec.js index 5849f4ac..dc9426e4 100644 --- a/__tests__/integration/auth.spec.js +++ b/__tests__/integration/auth.spec.js @@ -6,7 +6,7 @@ const sinon = require("sinon"); const chai = require("chai"); const server = require("../../server/app"); chai.use(require('chai-uuid')); -const Zaven = require("../mock-data/Zaven.json"); +const walletA = require("../mock-data/walletA.json"); const testUtils = require("./testUtils"); describe('Authentication', () => { @@ -14,11 +14,11 @@ describe('Authentication', () => { beforeEach(async () => { await testUtils.clear(); - registeredUser = await testUtils.register(Zaven); + registeredUser = await testUtils.register(walletA); }) // Authorization path - it(`[POST /auth] login with ${Zaven.name}`, (done) => { + it(`[POST /auth] login with ${walletA.name}`, (done) => { request(server) .post('/auth') .set('treetracker-api-key', registeredUser.apiKey) @@ -36,7 +36,7 @@ describe('Authentication', () => { }); - it(`[POST /auth] login with using wallet id of ${Zaven.name}`, (done) => { + it(`[POST /auth] login with using wallet id of ${walletA.name}`, (done) => { request(server) .post('/auth') .set('treetracker-api-key', registeredUser.apiKey) diff --git a/__tests__/integration/bundle-transfer-decline.spec.js b/__tests__/integration/bundle-transfer-decline.spec.js index f2b444f8..755c827a 100644 --- a/__tests__/integration/bundle-transfer-decline.spec.js +++ b/__tests__/integration/bundle-transfer-decline.spec.js @@ -5,34 +5,34 @@ const log = require('loglevel'); const chai = require("chai"); const server = require("../../server/app"); chai.use(require('chai-uuid')); -const Zaven = require("../mock-data/Zaven.json"); -const Meisze = require("../mock-data/Meisze.json"); +const walletA = require("../mock-data/walletA.json"); +const walletB = require("../mock-data/walletB.json"); const testUtils = require("./testUtils"); const Transfer = require("../../server/models/Transfer"); const TokenA = require("../mock-data/TokenA"); -describe('Zaven request to send 1 token to Meisze', () => { - let registeredZaven; - let registeredMeisze; +describe('walletA request to send 1 token to walletB', () => { + let registeredWalletA; + let registeredWalletB; let transfer; beforeEach(async () => { await testUtils.clear(); - registeredZaven = await testUtils.registerAndLogin(Zaven); - await testUtils.addToken(registeredZaven, TokenA); - expect(registeredZaven).property("id").a("string"); - registeredMeisze = await testUtils.registerAndLogin(Meisze); - transfer = await testUtils.sendAndPend(registeredZaven,registeredMeisze, 1); + registeredWalletA = await testUtils.registerAndLogin(walletA); + await testUtils.addToken(registeredWalletA, TokenA); + expect(registeredWalletA).property("id").a("string"); + registeredWalletB = await testUtils.registerAndLogin(walletB); + transfer = await testUtils.sendAndPend(registeredWalletA,registeredWalletB, 1); }) - describe("Meisze decline the request", () => { + describe("walletB decline the request", () => { beforeEach(async () => { await request(server) .post(`/transfers/${transfer.id}/decline`) .set('Content-Type', "application/json") - .set('treetracker-api-key', registeredMeisze.apiKey) - .set('Authorization', `Bearer ${registeredMeisze.token}`) + .set('treetracker-api-key', registeredWalletB.apiKey) + .set('Authorization', `Bearer ${registeredWalletB.token}`) .expect(200); }); @@ -40,8 +40,8 @@ describe('Zaven request to send 1 token to Meisze', () => { await request(server) .get(`/transfers?limit=1000`) - .set('treetracker-api-key', registeredMeisze.apiKey) - .set('Authorization', `Bearer ${registeredMeisze.token}`) + .set('treetracker-api-key', registeredWalletB.apiKey) + .set('Authorization', `Bearer ${registeredWalletB.token}`) .expect(200) .then(res => { expect(res.body.transfers).lengthOf(1); @@ -50,12 +50,12 @@ describe('Zaven request to send 1 token to Meisze', () => { }); - it("Zaven should still have 1 token", async () =>{ + it("walletA should still have 1 token", async () =>{ await request(server) .get(`/tokens?limit=10`) - .set('treetracker-api-key', registeredZaven.apiKey) - .set('Authorization', `Bearer ${registeredZaven.token}`) + .set('treetracker-api-key', registeredWalletA.apiKey) + .set('Authorization', `Bearer ${registeredWalletA.token}`) .expect(200) .then(res => { expect(res.body.tokens).lengthOf(1); diff --git a/__tests__/integration/wallet.spec.js b/__tests__/integration/wallet.spec.js new file mode 100644 index 00000000..45a79921 --- /dev/null +++ b/__tests__/integration/wallet.spec.js @@ -0,0 +1,35 @@ +require('dotenv').config() +const request = require('supertest'); +const { expect } = require('chai'); +const log = require('loglevel'); +const chai = require("chai"); +const server = require("../../server/app"); +chai.use(require('chai-uuid')); +const walletA = require("../mock-data/walletA.json"); +const testUtils = require("./testUtils"); +const Transfer = require("../../server/models/Transfer"); +const TokenA = require("../mock-data/TokenA"); + +describe('walletA login', () => { + let registeredWalletA; + let transfer; + + beforeEach(async () => { + await testUtils.clear(); + registeredWalletA = await testUtils.registerAndLogin(walletA); + await testUtils.addToken(registeredWalletA, TokenA); + }) + + it("walletA list his wallet list", async () => { + const res = await request(server) + .get(`/wallets?limit=1`) + .set('Content-Type', "application/json") + .set('treetracker-api-key', registeredWalletA.apiKey) + .set('Authorization', `Bearer ${registeredWalletA.token}`) + .expect(200); + expect(res.body.wallets).lengthOf(1); + expect(res.body.wallets[0].tokens_in_wallet).eq('1'); + expect(res.body.wallets[0].name).eq('walletA'); + }); + +}); diff --git a/__tests__/mock-data/Zaven.json b/__tests__/mock-data/walletA.json similarity index 78% rename from __tests__/mock-data/Zaven.json rename to __tests__/mock-data/walletA.json index e9b07d16..57ec3aae 100644 --- a/__tests__/mock-data/Zaven.json +++ b/__tests__/mock-data/walletA.json @@ -1,5 +1,5 @@ { "id": "082450d6-278c-4887-bf6c-66a63ef177e7", - "name": "zaven", + "name": "walletA", "password": "test1234" } diff --git a/__tests__/mock-data/Meisze.json b/__tests__/mock-data/walletB.json similarity index 78% rename from __tests__/mock-data/Meisze.json rename to __tests__/mock-data/walletB.json index 352f5297..2822956c 100644 --- a/__tests__/mock-data/Meisze.json +++ b/__tests__/mock-data/walletB.json @@ -1,5 +1,5 @@ { "id": "24f44064-df75-4168-8fdd-cc5478146142", - "name": "meisze", + "name": "walletB", "password": "test1234" } diff --git a/server/routes/walletRouter.js b/server/routes/walletRouter.js index 0b70fc50..48636856 100644 --- a/server/routes/walletRouter.js +++ b/server/routes/walletRouter.js @@ -18,31 +18,13 @@ walletRouter.get( req.query, Joi.object({ limit: Joi.number().required(), - offset: Joi.number().min(1).integer(), + offset: Joi.number().min(0).integer(), }), ); const { limit, offset } = req.query; const session = new Session(); const walletService = new WalletService(session); - const loggedInWallet = await walletService.getById(res.locals.wallet_id); - const subWallets = await loggedInWallet.getSubWallets(); - // at logged in wallets to list of wallets - subWallets.push(loggedInWallet); - - let walletsJson = []; - - const tokenService = new TokenService(session); - for (const wallet of subWallets) { - const json = await wallet.toJSON(); - json.tokens_in_wallet = await tokenService.countTokenByWallet(wallet); - walletsJson.push(json); - } - - const numStart = parseInt(offset); - const numLimit = parseInt(limit); - const numBegin = numStart ? numStart - 1 : 0; - const numEnd = numBegin + numLimit; - walletsJson = walletsJson.slice(numBegin, numEnd); + const walletsJson = await walletService.getSubWalletList(res.locals.wallet_id, parseInt(offset || 0), parseInt(limit)) res.status(200).json({ wallets: walletsJson.map((wallet) => diff --git a/server/routes/walletRouter.spec.js b/server/routes/walletRouter.spec.js index ac317d2d..601f69c3 100644 --- a/server/routes/walletRouter.spec.js +++ b/server/routes/walletRouter.spec.js @@ -27,7 +27,7 @@ describe("walletRouter", ()=> { beforeEach(() => { sinon.stub(ApiKeyService.prototype, "check"); sinon.stub(JWTService.prototype, "verify").returns({ - id: authenticatedWallet.id, + id: authenticatedWallet.getId(), }); app = express(); app.use(bodyParser.urlencoded({ extended: false })); // parse application/x-www-form-urlencoded @@ -54,15 +54,15 @@ describe("walletRouter", ()=> { }); it("successfully", async () => { - sinon.stub(WalletService.prototype, "getById").resolves(mockWallet); - sinon.stub(TrustService.prototype, "convertToResponse").resolves(mockTrust); - sinon.stub(TokenService.prototype, "countTokenByWallet").resolves(10); - const fn = sinon.stub(Wallet.prototype, "getSubWallets").resolves([ mockWallet2 ]); + const w1 = await mockWallet.toJSON(); + const w2 = await mockWallet2.toJSON(); + const f1 = sinon.stub(WalletService.prototype, "getSubWalletList").resolves([{...w1, tokens_in_wallet:1}, {...w2, tokens_in_wallet:2}]); const res = await request(app) .get('/?limit=2'); expect(res).property("statusCode").eq(200); expect(res.body.wallets).lengthOf(2); - expect(res.body.wallets[0]).property("tokens_in_wallet").eq(10); + expect(res.body.wallets[0]).property("tokens_in_wallet").eq(1); + expect(f1).calledWith(authenticatedWallet.getId(), 0, 2); }); it("should omit private fields", async () => { @@ -74,10 +74,8 @@ describe("walletRouter", ()=> { "salt": "private field", "tokens_in_wallet": 10 }) - sinon.stub(WalletService.prototype, "getById").resolves(wallet); - sinon.stub(TrustService.prototype, "convertToResponse").resolves(mockTrust); - sinon.stub(TokenService.prototype, "countTokenByWallet").resolves(10); - sinon.stub(Wallet.prototype, "getSubWallets").resolves( []); + const walletJson = await wallet.toJSON(); + const f1 = sinon.stub(WalletService.prototype, "getSubWalletList").resolves([{...walletJson, tokens_in_wallet:1}]); const res = await request(app) .get('/?limit=2'); expect(res).property("statusCode").eq(200); @@ -86,27 +84,6 @@ describe("walletRouter", ()=> { expect(resWallet).not.to.contain.keys(['password', 'salt', 'type']) }); - it("limit and offet working successfully", async () => { - sinon.stub(WalletService.prototype, "getById").resolves(mockWallet); - console.log(mockWallet.getId()) - console.log(mockWallet2.getId()) - console.log(mockWallet3.getId()) - console.log(mockWallet4.getId()) - sinon.stub(TrustService.prototype, "convertToResponse").resolves(mockTrust); - sinon.stub(TokenService.prototype, "countTokenByWallet").resolves(10); - const fn = sinon.stub(Wallet.prototype, "getSubWallets").resolves([ mockWallet2, mockWallet3, mockWallet4]); - const res = await request(app) - .get('/?limit=3&offset=2'); - expect(res).property("statusCode").eq(200); - expect(res.body.wallets).lengthOf(3); - console.log(authenticatedWallet.getId()); - console.log(res.body) - expect(res.body.wallets[0]).property("tokens_in_wallet").eq(10); - expect(res.body.wallets[0]).property("id").eq(mockWallet3.getId()); - expect(res.body.wallets[1]).property("id").eq(mockWallet4.getId()); - expect(res.body.wallets[2]).property("id").eq(mockWallet.getId()); - }) - }) describe("get /wallets/:wallet_id/trust_relationships", () => { diff --git a/server/services/WalletService.js b/server/services/WalletService.js index 85ebeab8..f8512888 100644 --- a/server/services/WalletService.js +++ b/server/services/WalletService.js @@ -33,6 +33,54 @@ class WalletService { const wallet = new Wallet(walletObject.id, this._session); return wallet; } + + /* + * A faster way to get sub wallet list directly, from DB, and count + * the token in these wallet + */ + async getSubWalletList(walletId, offset, limit){ + const result = await this._session.getDB().raw(` + SELECT + w.*, + CASE WHEN tokens_in_wallet IS NULL THEN 0 ELSE tokens_in_wallet END + FROM + wallet w + JOIN + ( + /* including the wallet himself */ + SELECT '${walletId}' AS sub_wallet_id + UNION + /* manage */ + SELECT + wt.target_wallet_id AS sub_wallet_id + FROM + wallet_trust wt + WHERE + wt."state" = 'trusted' + AND actor_wallet_id = '${walletId}' + AND wt.request_type = 'manage' + UNION + /* yield */ + SELECT + wt.actor_wallet_id AS sub_wallet_id + FROM + wallet_trust wt + WHERE + wt."state" = 'trusted' + AND target_wallet_id = '${walletId}' + AND wt.request_type = 'yield' + ) sub_wallet_ids + ON w.id = sub_wallet_ids.sub_wallet_id + LEFT JOIN ( + SELECT wallet_id, count(wallet_id) tokens_in_wallet FROM "token" GROUP BY wallet_id + ) token_stat + ON w.id = token_stat.wallet_id + ORDER BY name + OFFSET ${offset} + LIMIT ${limit} + `); + return result.rows; + } } module.exports = WalletService;