diff --git a/web/storagenode/src/app/store/modules/node.ts b/web/storagenode/src/app/store/modules/node.ts index c455a0beb..3b2b0661b 100644 --- a/web/storagenode/src/app/store/modules/node.ts +++ b/web/storagenode/src/app/store/modules/node.ts @@ -2,6 +2,7 @@ // See LICENSE for copying information. import { Duration, millisecondsInSecond, secondsInMinute } from '@/app/utils/duration'; +import { getMonthsBeforeNow } from '@/app/utils/payout'; import { SNOApi } from '@/storagenode/api/storagenode'; import { Dashboard, SatelliteInfo } from '@/storagenode/dashboard'; import { BandwidthUsed, EgressUsed, IngressUsed, Satellite, Satellites, Stamp } from '@/storagenode/satellite'; @@ -155,11 +156,7 @@ export function makeNodeModule(api: SNOApi) { }, getters: { monthsOnNetwork: (state): number => { - const now = new Date(); - const secondsInMonthApproximately = 2628000; - const differenceInSeconds = (now.getTime() - state.selectedSatellite.joinDate.getTime()) / 1000; - - return Math.ceil(differenceInSeconds / secondsInMonthApproximately); + return getMonthsBeforeNow(state.selectedSatellite.joinDate); }, }, }; diff --git a/web/storagenode/src/app/utils/payout.ts b/web/storagenode/src/app/utils/payout.ts new file mode 100644 index 000000000..4cd9f2434 --- /dev/null +++ b/web/storagenode/src/app/utils/payout.ts @@ -0,0 +1,33 @@ +// Copyright (C) 2020 Storj Labs, Inc. +// See LICENSE for copying information. + +// TODO: functions should be moved to related business logic layer +/** + * Returns held percentage depends on number of months that node is online. + * @param startedAt date since node is online. + */ +export function getHeldPercentage(startedAt: Date): number { + const monthsOnline = getMonthsBeforeNow(startedAt); + + switch (true) { + case monthsOnline < 4: + return 75; + case monthsOnline < 7: + return 50; + case monthsOnline < 10: + return 25; + default: + return 0; + } +} + +/** + * Returns number of months passes till now. + * @param startedAt date since node is online. + */ +export function getMonthsBeforeNow(startedAt: Date): number { + const now = new Date(); + const yearsDiff = now.getUTCFullYear() - startedAt.getUTCFullYear(); + + return (yearsDiff * 12) + (now.getUTCMonth() - startedAt.getUTCMonth()) + 1; +} diff --git a/web/storagenode/tests/typings.d.ts b/web/storagenode/tests/typings.d.ts new file mode 100644 index 000000000..33df46ce4 --- /dev/null +++ b/web/storagenode/tests/typings.d.ts @@ -0,0 +1,4 @@ +// Copyright (C) 2020 Storj Labs, Inc. +// See LICENSE for copying information. + +declare var global: any; diff --git a/web/storagenode/tests/unit/store/node.spec.ts b/web/storagenode/tests/unit/store/node.spec.ts index e31d67295..0510a0679 100644 --- a/web/storagenode/tests/unit/store/node.spec.ts +++ b/web/storagenode/tests/unit/store/node.spec.ts @@ -283,23 +283,8 @@ describe('actions', () => { describe('getters', () => { it('getter monthsOnNetwork returns correct value', () => { - const dashboardInfo = new Dashboard( - '1', - '2', - [ - new SatelliteInfo('3', 'url1', null, null), - new SatelliteInfo('4', 'url2', new Date(2020, 1, 1), new Date(2020, 0, 1)), - ], - new DiskSpaceInfo(99, 100, 4), - new BandwidthInfo(50), - new Date(), - new Date(2019, 3, 1), - '0.1.1', - '0.2.2', - false, - ); - const now = new Date(); - const testJoinAt = new Date(now.getTime() - 1000000); + const _Date = Date; + const testJoinAt = new Date(Date.UTC(2020, 0, 30)); const satelliteInfo = new Satellite( '3', @@ -316,15 +301,39 @@ describe('getters', () => { testJoinAt, ); + const firstTestDate = new Date(2020, 1, 1); + const secondTestDate = new Date(Date.UTC(2019, 10, 29)); + + const mockedDate = new Date(1580522290000); + global.Date = jest.fn(() => mockedDate); // Sat Feb 01 2020 + + const dashboardInfo = new Dashboard( + '1', + '2', + [ + new SatelliteInfo('3', 'url1', null, null), + new SatelliteInfo('4', 'url2', firstTestDate, new Date(2020, 0, 1)), + ], + new DiskSpaceInfo(99, 100, 4), + new BandwidthInfo(50), + new Date(), + firstTestDate, + '0.1.1', + '0.2.2', + false, + ); + store.commit(NODE_MUTATIONS.POPULATE_STORE, dashboardInfo); store.commit(NODE_MUTATIONS.SELECT_SATELLITE, satelliteInfo); - expect(store.getters.monthsOnNetwork).toBe(1); + expect(store.getters.monthsOnNetwork).toBe(2); - satelliteInfo.joinDate = new Date(testJoinAt.getTime() - 9e9); + satelliteInfo.joinDate = secondTestDate; store.commit(NODE_MUTATIONS.SELECT_SATELLITE, satelliteInfo); expect(store.getters.monthsOnNetwork).toBe(4); + + global.Date = _Date; }); }); diff --git a/web/storagenode/tests/unit/store/payout.spec.ts b/web/storagenode/tests/unit/store/payout.spec.ts index 75a3a2799..dc9349a0d 100644 --- a/web/storagenode/tests/unit/store/payout.spec.ts +++ b/web/storagenode/tests/unit/store/payout.spec.ts @@ -4,8 +4,9 @@ import Vuex from 'vuex'; import { makeNodeModule } from '@/app/store/modules/node'; -import { getHeldPercentage, makePayoutModule, PAYOUT_ACTIONS, PAYOUT_MUTATIONS } from '@/app/store/modules/payout'; +import { makePayoutModule, PAYOUT_ACTIONS, PAYOUT_MUTATIONS } from '@/app/store/modules/payout'; import { HeldInfo, PayoutInfoRange, PayoutPeriod, TotalPayoutInfo } from '@/app/types/payout'; +import { getHeldPercentage, getMonthsBeforeNow } from '@/app/utils/payout'; import { PayoutHttpApi } from '@/storagenode/api/payout'; import { SNOApi } from '@/storagenode/api/storagenode'; import { createLocalVue } from '@vue/test-utils'; @@ -23,12 +24,12 @@ const store = new Vuex.Store({ modules: { payoutModule, node: nodeModule } }); const state = store.state as any; -describe('mutations', () => { +describe('mutations', (): void => { beforeEach(() => { createLocalVue().use(Vuex); }); - it('sets held information', () => { + it('sets held information', (): void => { const heldInfo = new HeldInfo(13, 12, 11); store.commit(PAYOUT_MUTATIONS.SET_HELD_INFO, heldInfo); @@ -38,7 +39,7 @@ describe('mutations', () => { expect(state.payoutModule.heldInfo.usagePut).toBe(11); }); - it('sets total payout information', () => { + it('sets total payout information', (): void => { const totalInfo = new TotalPayoutInfo(50, 100, 22); store.commit(PAYOUT_MUTATIONS.SET_TOTAL, totalInfo); @@ -48,7 +49,7 @@ describe('mutations', () => { expect(state.payoutModule.currentMonthEarnings).toBe(22); }); - it('sets period range', () => { + it('sets period range', (): void => { const range = new PayoutInfoRange(new PayoutPeriod(2019, 2), new PayoutPeriod(2020, 3)); store.commit(PAYOUT_MUTATIONS.SET_RANGE, range); @@ -61,7 +62,7 @@ describe('mutations', () => { expect(state.payoutModule.periodRange.end.period).toBe('2020-04'); }); - it('sets held percentage', () => { + it('sets held percentage', (): void => { const expectedHeldPercentage = 75; store.commit(PAYOUT_MUTATIONS.SET_HELD_PERCENT, expectedHeldPercentage); @@ -75,7 +76,7 @@ describe('actions', () => { jest.resetAllMocks(); }); - it('success get held info by month', async () => { + it('success get held info by month', async (): Promise => { jest.spyOn(payoutApi, 'getHeldInfoByMonth').mockReturnValue( Promise.resolve(new HeldInfo(1, 2 , 3, 4, 5)), ); @@ -91,7 +92,7 @@ describe('actions', () => { expect(state.payoutModule.heldPercentage).toBe(getHeldPercentage(new Date())); }); - it('get held info by month throws an error when api call fails', async () => { + it('get held info by month throws an error when api call fails', async (): Promise => { jest.spyOn(payoutApi, 'getHeldInfoByMonth').mockImplementation(() => { throw new Error(); }); try { @@ -103,7 +104,7 @@ describe('actions', () => { } }); - it('success get held info by period', async () => { + it('success get held info by period', async (): Promise => { jest.spyOn(payoutApi, 'getHeldInfoByPeriod').mockReturnValue( Promise.resolve(new HeldInfo(1, 2 , 3, 4, 5)), ); @@ -118,7 +119,7 @@ describe('actions', () => { expect(state.payoutModule.heldInfo.held).toBe(0); }); - it('get held info by period throws an error when api call fails', async () => { + it('get held info by period throws an error when api call fails', async (): Promise => { jest.spyOn(payoutApi, 'getHeldInfoByPeriod').mockImplementation(() => { throw new Error(); }); try { @@ -130,7 +131,7 @@ describe('actions', () => { } }); - it('success get total', async () => { + it('success get total', async (): Promise => { jest.spyOn(payoutApi, 'getTotal').mockReturnValue( Promise.resolve(new TotalPayoutInfo(10, 20, 5)), ); @@ -142,7 +143,7 @@ describe('actions', () => { expect(state.payoutModule.currentMonthEarnings).toBe(0); }); - it('get total throws an error when api call fails', async () => { + it('get total throws an error when api call fails', async (): Promise => { jest.spyOn(payoutApi, 'getTotal').mockImplementation(() => { throw new Error(); }); try { @@ -155,7 +156,7 @@ describe('actions', () => { } }); - it('success sets period range', async () => { + it('success sets period range', async (): Promise => { await store.dispatch( PAYOUT_ACTIONS.SET_PERIODS_RANGE, new PayoutInfoRange( @@ -169,17 +170,53 @@ describe('actions', () => { }); }); -describe('utils functions', () => { - it('get correct help percentage', () => { - const nowTime = new Date().getTime(); - const testDifferencesInMilliseconds: number[] = [5e9, 1.4e10, 2.3e10, 4e10]; +describe('utils functions', (): void => { + const _Date = Date; + + // TODO: investigate reset mocks in config + beforeEach(() => { + jest.resetAllMocks(); + }); + + afterEach(() => { + global.Date = _Date; + }); + + it('get correct held percentage', (): void => { + const testDates: Date[] = [ + new Date(Date.UTC(2020, 0, 30)), + new Date(Date.UTC(2019, 10, 29)), + new Date(Date.UTC(2019, 7, 24)), + new Date(Date.UTC(2018, 1, 24)), + ]; const expectedHeldPercentages: number[] = [75, 50, 25, 0]; - for (let i = 0; i < testDifferencesInMilliseconds.length; i++) { - const date = new Date(nowTime - testDifferencesInMilliseconds[i]); - const heldPercentage = getHeldPercentage(date); + const mockedDate = new Date(1580522290000); // Sat Feb 01 2020 + global.Date = jest.fn(() => mockedDate); + + for (let i = 0; i < testDates.length; i++) { + const heldPercentage = getHeldPercentage(testDates[i]); expect(heldPercentage).toBe(expectedHeldPercentages[i]); } }); + + it('get correct months difference', (): void => { + const testDates: Date[] = [ + new Date(Date.UTC(2020, 0, 30)), + new Date(Date.UTC(2019, 10, 29)), + new Date(Date.UTC(2019, 7, 24)), + new Date(Date.UTC(2018, 1, 24)), + ]; + const expectedMonthsCount: number[] = [2, 4, 7, 25]; + + const mockedDate = new Date(1580522290000); // Sat Feb 01 2020 + global.Date = jest.fn(() => mockedDate); + + for (let i = 0; i < testDates.length; i++) { + const heldPercentage = getMonthsBeforeNow(testDates[i]); + + expect(heldPercentage).toBe(expectedMonthsCount[i]); + } + }); });