web/storagenode: dependencies for testing and payout store tests added
Change-Id: Iae18d073ba35ba8ac48e2d4c88476b38b96bbd9b
This commit is contained in:
parent
9b4a3f8fcc
commit
51bf2b6155
@ -2,7 +2,5 @@
|
||||
// See LICENSE for copying information.
|
||||
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/app'
|
||||
]
|
||||
}
|
||||
presets: [ [ "@vue/app", { useBuiltIns: "entry" } ] ]
|
||||
};
|
||||
|
9
web/storagenode/jestSetup.ts
Normal file
9
web/storagenode/jestSetup.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { GlobalWithFetchMock } from 'jest-fetch-mock';
|
||||
|
||||
const customGlobal: GlobalWithFetchMock = global as GlobalWithFetchMock;
|
||||
|
||||
customGlobal.fetch = require('jest-fetch-mock');
|
||||
customGlobal.fetchMock = customGlobal.fetch;
|
5409
web/storagenode/package-lock.json
generated
5409
web/storagenode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,17 +6,15 @@
|
||||
"serve": "vue-cli-service serve",
|
||||
"lint": "vue-cli-service lint && stylelint '**/*.{vue,scss}' --fix",
|
||||
"build": "vue-cli-service build",
|
||||
"debug": "vue-cli-service build --mode development"
|
||||
"debug": "vue-cli-service build --mode development",
|
||||
"test": "vue-cli-service test:unit"
|
||||
},
|
||||
"dependencies": {
|
||||
"chart.js": "2.9.3",
|
||||
"stylelint": "13.0.0",
|
||||
"stylelint-config-standard": "19.0.0",
|
||||
"stylelint-scss": "3.14.2",
|
||||
"stylelint-webpack-plugin": "1.2.1",
|
||||
"vue": "2.6.11",
|
||||
"vue-chartjs": "3.5.0",
|
||||
"vue-class-component": "7.2.2",
|
||||
"vue-jest": "3.0.5",
|
||||
"vue-property-decorator": "8.3.0",
|
||||
"vue-router": "3.1.5",
|
||||
"vuex": "3.1.2"
|
||||
@ -24,18 +22,27 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.8.4",
|
||||
"@babel/plugin-proposal-object-rest-spread": "7.8.3",
|
||||
"@types/sinon": "7.5.1",
|
||||
"@vue/cli-plugin-babel": "4.1.2",
|
||||
"@vue/cli-plugin-typescript": "4.1.2",
|
||||
"@vue/cli-plugin-unit-jest": "4.1.2",
|
||||
"@vue/cli-service": "4.2.3",
|
||||
"@vue/cli-plugin-babel": "4.1.1",
|
||||
"@vue/cli-plugin-typescript": "4.1.1",
|
||||
"@vue/cli-plugin-unit-jest": "4.1.1",
|
||||
"@vue/cli-service": "4.1.1",
|
||||
"@vue/test-utils": "1.0.0-beta.30",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"compression-webpack-plugin": "3.0.1",
|
||||
"core-js": "3.6.5",
|
||||
"jest-fetch-mock": "3.0.0",
|
||||
"node-sass": "4.13.1",
|
||||
"sass-loader": "8.0.2",
|
||||
"tslint": "6.0.0",
|
||||
"sass-loader": "8.0.0",
|
||||
"sinon": "7.5.0",
|
||||
"stylelint": "12.0.1",
|
||||
"stylelint-config-standard": "19.0.0",
|
||||
"stylelint-scss": "3.13.0",
|
||||
"stylelint-webpack-plugin": "1.2.1",
|
||||
"ts-jest": "24.2.0",
|
||||
"tslint": "5.20.1",
|
||||
"tslint-consistent-codestyle": "1.16.0",
|
||||
"tslint-loader": "3.5.4",
|
||||
"typescript": "3.7.5",
|
||||
"typescript": "3.7.4",
|
||||
"vue-svg-loader": "0.15.0",
|
||||
"vue-template-compiler": "2.6.11",
|
||||
"vue-tslint": "0.3.2",
|
||||
@ -87,5 +94,45 @@
|
||||
"media-feature-colon-space-before": "never",
|
||||
"media-feature-colon-space-after": "always"
|
||||
}
|
||||
},
|
||||
"jest": {
|
||||
"automock": false,
|
||||
"setupFiles": [
|
||||
"./jestSetup.ts"
|
||||
],
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"diagnostics": false
|
||||
}
|
||||
},
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"jsx",
|
||||
"json",
|
||||
"vue",
|
||||
"ts",
|
||||
"tsx"
|
||||
],
|
||||
"collectCoverage": true,
|
||||
"transform": {
|
||||
"^.+\\.js$": "babel-jest",
|
||||
"^.+\\.vue$": "vue-jest",
|
||||
".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
|
||||
"^.+\\.tsx?$": "ts-jest",
|
||||
"^.+\\.svg$": "<rootDir>/tests/unit/mock/svgTransform.js"
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"/node_modules/(?!(apollo-client|apollo-link))"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"^@/(.*)$": "<rootDir>/src/$1"
|
||||
},
|
||||
"snapshotSerializers": [
|
||||
"jest-serializer-vue"
|
||||
],
|
||||
"testMatch": [
|
||||
"**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"
|
||||
],
|
||||
"testURL": "http://localhost/"
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import { SNO_THEME } from '@/app/types/theme';
|
||||
components: {
|
||||
SunIcon,
|
||||
MoonIcon,
|
||||
}
|
||||
},
|
||||
})
|
||||
export default class OptionsDropdown extends Vue {
|
||||
/**
|
||||
|
@ -29,7 +29,7 @@ import WalletIcon from '@/../static/images/wallet.svg';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
WalletIcon
|
||||
WalletIcon,
|
||||
},
|
||||
})
|
||||
export default class PayoutArea extends Vue {
|
||||
|
@ -475,7 +475,7 @@ export default class SNOContentFilling extends Vue {
|
||||
line-height: 21px;
|
||||
|
||||
&__link {
|
||||
color: var(--navigation-link-color)
|
||||
color: var(--navigation-link-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -501,7 +501,7 @@ export default class SNOContentFilling extends Vue {
|
||||
line-height: 21px;
|
||||
|
||||
&__link {
|
||||
color: var(--navigation-link-color)
|
||||
color: var(--navigation-link-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ export default class SNOContentTitle extends Vue {
|
||||
|
||||
public get currentMonth(): string {
|
||||
const monthNames = ['January', 'February', 'March', 'April', 'May', 'June',
|
||||
'July', 'August', 'September', 'October', 'November', 'December'
|
||||
'July', 'August', 'September', 'October', 'November', 'December',
|
||||
];
|
||||
const date = new Date();
|
||||
|
||||
|
@ -30,12 +30,12 @@ class DayShowingConditions {
|
||||
}
|
||||
|
||||
@Component({
|
||||
extends: VueChart.Line
|
||||
extends: VueChart.Line,
|
||||
})
|
||||
export default class VChart extends Vue {
|
||||
@Prop({default: '$'})
|
||||
private readonly currency: string;
|
||||
@Prop({default: () => { console.error('Tooltip constructor is undefined'); }, })
|
||||
@Prop({default: () => { console.error('Tooltip constructor is undefined'); } })
|
||||
private tooltipConstructor: (tooltipModel) => void;
|
||||
@Prop({default: {}})
|
||||
private readonly chartData: ChartData;
|
||||
@ -69,7 +69,7 @@ export default class VChart extends Vue {
|
||||
radius: 0,
|
||||
hoverRadius: 0,
|
||||
hitRadius: 500,
|
||||
}
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
yAxes: [{
|
||||
@ -80,8 +80,8 @@ export default class VChart extends Vue {
|
||||
},
|
||||
gridLines: {
|
||||
borderDash: [2, 5],
|
||||
drawBorder: false
|
||||
}
|
||||
drawBorder: false,
|
||||
},
|
||||
}],
|
||||
xAxes: [{
|
||||
display: true,
|
||||
@ -100,7 +100,7 @@ export default class VChart extends Vue {
|
||||
layout: {
|
||||
padding: {
|
||||
left: 25,
|
||||
}
|
||||
},
|
||||
},
|
||||
tooltips: {
|
||||
enabled: false,
|
||||
@ -111,8 +111,8 @@ export default class VChart extends Vue {
|
||||
|
||||
labels: {
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ import { RouteConfig } from '@/app/router';
|
||||
@Component({
|
||||
components: {
|
||||
SNONotification,
|
||||
}
|
||||
},
|
||||
})
|
||||
export default class NotificationsPopup extends Vue {
|
||||
/**
|
||||
|
@ -39,7 +39,7 @@ const monthNames = [
|
||||
PayoutPeriodCalendar,
|
||||
BlackArrowExpand,
|
||||
BlackArrowHide,
|
||||
}
|
||||
},
|
||||
})
|
||||
export default class EstimationPeriodDropdown extends Vue {
|
||||
/**
|
||||
|
@ -77,7 +77,7 @@ class MonthButton {
|
||||
@Component({
|
||||
components: {
|
||||
GrayArrowLeftIcon,
|
||||
}
|
||||
},
|
||||
})
|
||||
export default class PayoutPeriodCalendar extends Vue {
|
||||
private now: Date = new Date();
|
||||
|
@ -28,22 +28,22 @@ export const router = new Router({
|
||||
{
|
||||
path: RouteConfig.Root.path,
|
||||
name: RouteConfig.Root.name,
|
||||
component: DashboardArea
|
||||
component: DashboardArea,
|
||||
},
|
||||
{
|
||||
path: RouteConfig.Notifications.path,
|
||||
name: RouteConfig.Notifications.name,
|
||||
component: NotificationsArea
|
||||
component: NotificationsArea,
|
||||
},
|
||||
{
|
||||
path: RouteConfig.Payout.path,
|
||||
name: RouteConfig.Payout.name,
|
||||
component: PayoutArea
|
||||
component: PayoutArea,
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
name: '404',
|
||||
component: Page404,
|
||||
},
|
||||
]
|
||||
],
|
||||
});
|
||||
|
@ -8,12 +8,14 @@ import { makeNotificationsModule } from '@/app/store/modules/notifications';
|
||||
import { makePayoutModule } from '@/app/store/modules/payout';
|
||||
import { NotificationsHttpApi } from '@/storagenode/api/notifications';
|
||||
import { PayoutHttpApi } from '@/storagenode/api/payout';
|
||||
import { SNOApi } from '@/storagenode/api/storagenode';
|
||||
|
||||
import { appStateModule } from './modules/appState';
|
||||
import { node } from './modules/node';
|
||||
import { makeNodeModule } from './modules/node';
|
||||
|
||||
const notificationsApi = new NotificationsHttpApi();
|
||||
const payoutApi = new PayoutHttpApi();
|
||||
const nodeApi = new SNOApi();
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
@ -22,7 +24,7 @@ Vue.use(Vuex);
|
||||
*/
|
||||
export const store = new Vuex.Store({
|
||||
modules: {
|
||||
node,
|
||||
node: makeNodeModule(nodeApi),
|
||||
appStateModule,
|
||||
notificationsModule: makeNotificationsModule(notificationsApi),
|
||||
payoutModule: makePayoutModule(payoutApi),
|
||||
|
@ -29,141 +29,142 @@ const {
|
||||
} = NODE_MUTATIONS;
|
||||
|
||||
const statusThreshHoldMinutes = 120;
|
||||
const snoAPI = new SNOApi();
|
||||
|
||||
export const node = {
|
||||
state: {
|
||||
info: {
|
||||
id: '',
|
||||
status: StatusOffline,
|
||||
lastPinged: new Date(),
|
||||
startedAt: new Date(),
|
||||
version: '',
|
||||
allowedVersion: '',
|
||||
wallet: '',
|
||||
isLastVersion: false
|
||||
},
|
||||
utilization: {
|
||||
bandwidth: {
|
||||
used: 0,
|
||||
export function makeNodeModule(api: SNOApi) {
|
||||
return {
|
||||
state: {
|
||||
info: {
|
||||
id: '',
|
||||
status: StatusOffline,
|
||||
lastPinged: new Date(),
|
||||
startedAt: new Date(),
|
||||
version: '',
|
||||
allowedVersion: '',
|
||||
wallet: '',
|
||||
isLastVersion: false,
|
||||
},
|
||||
diskSpace: {
|
||||
used: 0,
|
||||
remaining: 1,
|
||||
available: 1,
|
||||
utilization: {
|
||||
bandwidth: {
|
||||
used: 0,
|
||||
},
|
||||
diskSpace: {
|
||||
used: 0,
|
||||
remaining: 1,
|
||||
available: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
satellites: new Array<SatelliteInfo>(),
|
||||
disqualifiedSatellites: new Array<SatelliteInfo>(),
|
||||
suspendedSatellites: new Array<SatelliteInfo>(),
|
||||
selectedSatellite: {
|
||||
id: null,
|
||||
disqualified: null,
|
||||
joinDate: new Date(),
|
||||
},
|
||||
bandwidthChartData: new Array<BandwidthUsed>(),
|
||||
egressChartData: new Array<EgressUsed>(),
|
||||
ingressChartData: new Array<IngressUsed>(),
|
||||
storageChartData: new Array<Stamp>(),
|
||||
storageSummary: 0,
|
||||
bandwidthSummary: 0,
|
||||
egressSummary: 0,
|
||||
ingressSummary: 0,
|
||||
checks: {
|
||||
uptime: 0,
|
||||
audit: 0,
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
[POPULATE_STORE](state: any, nodeInfo: Dashboard): void {
|
||||
state.info.id = nodeInfo.nodeID;
|
||||
state.info.isLastVersion = nodeInfo.isUpToDate;
|
||||
state.info.version = nodeInfo.version;
|
||||
state.info.allowedVersion = nodeInfo.allowedVersion;
|
||||
state.info.wallet = nodeInfo.wallet;
|
||||
state.utilization.diskSpace.used = nodeInfo.diskSpace.used;
|
||||
state.utilization.diskSpace.remaining = nodeInfo.diskSpace.available - nodeInfo.diskSpace.used;
|
||||
state.utilization.diskSpace.available = nodeInfo.diskSpace.available;
|
||||
state.utilization.bandwidth.used = nodeInfo.bandwidth.used;
|
||||
|
||||
state.disqualifiedSatellites = nodeInfo.satellites.filter((satellite: SatelliteInfo) => satellite.disqualified);
|
||||
state.suspendedSatellites = nodeInfo.satellites.filter((satellite: SatelliteInfo) => satellite.suspended);
|
||||
|
||||
state.satellites = nodeInfo.satellites || [];
|
||||
|
||||
state.info.status = StatusOffline;
|
||||
|
||||
state.info.startedAt = nodeInfo.startedAt;
|
||||
state.info.lastPinged = nodeInfo.lastPinged;
|
||||
|
||||
const minutesPassed = Duration.difference(new Date(), new Date(nodeInfo.lastPinged)) / millisecondsInSecond / secondsInMinute;
|
||||
|
||||
if (minutesPassed < statusThreshHoldMinutes) {
|
||||
state.info.status = StatusOnline;
|
||||
}
|
||||
},
|
||||
[SELECT_SATELLITE](state: any, satelliteInfo: Satellite): void {
|
||||
const selectedSatellite = state.satellites.find(satellite => satelliteInfo.id === satellite.id);
|
||||
|
||||
if (!selectedSatellite) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.selectedSatellite = {
|
||||
id: satelliteInfo.id,
|
||||
disqualified: selectedSatellite.disqualified,
|
||||
joinDate: satelliteInfo.joinDate,
|
||||
url: selectedSatellite.url,
|
||||
suspended: selectedSatellite.suspended,
|
||||
};
|
||||
|
||||
state.checks.audit = parseFloat(parseFloat(`${satelliteInfo.audit.score * 100}`).toFixed(1));
|
||||
state.checks.uptime = satelliteInfo.uptime.totalCount ? 100 : satelliteInfo.uptime.successCount / satelliteInfo.uptime.totalCount * 100;
|
||||
},
|
||||
[SELECT_ALL_SATELLITES](state: any, satelliteInfo: Satellites): void {
|
||||
state.selectedSatellite = {
|
||||
satellites: new Array<SatelliteInfo>(),
|
||||
disqualifiedSatellites: new Array<SatelliteInfo>(),
|
||||
suspendedSatellites: new Array<SatelliteInfo>(),
|
||||
selectedSatellite: {
|
||||
id: null,
|
||||
disqualified: null,
|
||||
joinDate: satelliteInfo.joinDate,
|
||||
};
|
||||
joinDate: new Date(),
|
||||
},
|
||||
bandwidthChartData: new Array<BandwidthUsed>(),
|
||||
egressChartData: new Array<EgressUsed>(),
|
||||
ingressChartData: new Array<IngressUsed>(),
|
||||
storageChartData: new Array<Stamp>(),
|
||||
storageSummary: 0,
|
||||
bandwidthSummary: 0,
|
||||
egressSummary: 0,
|
||||
ingressSummary: 0,
|
||||
checks: {
|
||||
uptime: 0,
|
||||
audit: 0,
|
||||
},
|
||||
},
|
||||
[SET_DAILY_DATA](state: any, satelliteInfo: Satellite): void {
|
||||
state.bandwidthChartData = satelliteInfo.bandwidthDaily;
|
||||
state.egressChartData = satelliteInfo.egressDaily;
|
||||
state.ingressChartData = satelliteInfo.ingressDaily;
|
||||
state.storageChartData = satelliteInfo.storageDaily;
|
||||
state.bandwidthSummary = satelliteInfo.bandwidthSummary;
|
||||
state.egressSummary = satelliteInfo.egressSummary;
|
||||
state.ingressSummary = satelliteInfo.ingressSummary;
|
||||
state.storageSummary = satelliteInfo.storageSummary;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
[NODE_ACTIONS.GET_NODE_INFO]: async function ({ commit }: any): Promise<void> {
|
||||
const response = await snoAPI.dashboard();
|
||||
mutations: {
|
||||
[POPULATE_STORE](state: any, nodeInfo: Dashboard): void {
|
||||
state.info.id = nodeInfo.nodeID;
|
||||
state.info.isLastVersion = nodeInfo.isUpToDate;
|
||||
state.info.version = nodeInfo.version;
|
||||
state.info.allowedVersion = nodeInfo.allowedVersion;
|
||||
state.info.wallet = nodeInfo.wallet;
|
||||
state.utilization.diskSpace.used = nodeInfo.diskSpace.used;
|
||||
state.utilization.diskSpace.remaining = nodeInfo.diskSpace.available - nodeInfo.diskSpace.used;
|
||||
state.utilization.diskSpace.available = nodeInfo.diskSpace.available;
|
||||
state.utilization.bandwidth.used = nodeInfo.bandwidth.used;
|
||||
|
||||
commit(NODE_MUTATIONS.POPULATE_STORE, response);
|
||||
},
|
||||
[NODE_ACTIONS.SELECT_SATELLITE]: async function ({ commit }, id?: string): Promise<void> {
|
||||
let response: Satellite | Satellites;
|
||||
if (id) {
|
||||
response = await snoAPI.satellite(id);
|
||||
commit(NODE_MUTATIONS.SELECT_SATELLITE, response);
|
||||
} else {
|
||||
response = await snoAPI.satellites();
|
||||
commit(NODE_MUTATIONS.SELECT_ALL_SATELLITES, response);
|
||||
}
|
||||
state.disqualifiedSatellites = nodeInfo.satellites.filter((satellite: SatelliteInfo) => satellite.disqualified);
|
||||
state.suspendedSatellites = nodeInfo.satellites.filter((satellite: SatelliteInfo) => satellite.suspended);
|
||||
|
||||
commit(NODE_MUTATIONS.SET_DAILY_DATA, response);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
monthsOnNetwork: (state): number => {
|
||||
const now = new Date();
|
||||
const secondsInMonthApproximately = 2628000;
|
||||
const differenceInSeconds = (now.getTime() - state.selectedSatellite.joinDate.getTime()) / 1000;
|
||||
state.satellites = nodeInfo.satellites || [];
|
||||
|
||||
return Math.ceil(differenceInSeconds / secondsInMonthApproximately);
|
||||
state.info.status = StatusOffline;
|
||||
|
||||
state.info.startedAt = nodeInfo.startedAt;
|
||||
state.info.lastPinged = nodeInfo.lastPinged;
|
||||
|
||||
const minutesPassed = Duration.difference(new Date(), new Date(nodeInfo.lastPinged)) / millisecondsInSecond / secondsInMinute;
|
||||
|
||||
if (minutesPassed < statusThreshHoldMinutes) {
|
||||
state.info.status = StatusOnline;
|
||||
}
|
||||
},
|
||||
[SELECT_SATELLITE](state: any, satelliteInfo: Satellite): void {
|
||||
const selectedSatellite = state.satellites.find(satellite => satelliteInfo.id === satellite.id);
|
||||
|
||||
if (!selectedSatellite) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.selectedSatellite = {
|
||||
id: satelliteInfo.id,
|
||||
disqualified: selectedSatellite.disqualified,
|
||||
joinDate: satelliteInfo.joinDate,
|
||||
url: selectedSatellite.url,
|
||||
suspended: selectedSatellite.suspended,
|
||||
};
|
||||
|
||||
state.checks.audit = parseFloat(parseFloat(`${satelliteInfo.audit.score * 100}`).toFixed(1));
|
||||
state.checks.uptime = satelliteInfo.uptime.totalCount === 0 ? 100 : satelliteInfo.uptime.successCount / satelliteInfo.uptime.totalCount * 100;
|
||||
},
|
||||
[SELECT_ALL_SATELLITES](state: any, satelliteInfo: Satellites): void {
|
||||
state.selectedSatellite = {
|
||||
id: null,
|
||||
disqualified: null,
|
||||
joinDate: satelliteInfo.joinDate,
|
||||
};
|
||||
},
|
||||
[SET_DAILY_DATA](state: any, satelliteInfo: Satellite): void {
|
||||
state.bandwidthChartData = satelliteInfo.bandwidthDaily;
|
||||
state.egressChartData = satelliteInfo.egressDaily;
|
||||
state.ingressChartData = satelliteInfo.ingressDaily;
|
||||
state.storageChartData = satelliteInfo.storageDaily;
|
||||
state.bandwidthSummary = satelliteInfo.bandwidthSummary;
|
||||
state.egressSummary = satelliteInfo.egressSummary;
|
||||
state.ingressSummary = satelliteInfo.ingressSummary;
|
||||
state.storageSummary = satelliteInfo.storageSummary;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
actions: {
|
||||
[NODE_ACTIONS.GET_NODE_INFO]: async function ({commit}: any): Promise<void> {
|
||||
const response = await api.dashboard();
|
||||
|
||||
commit(NODE_MUTATIONS.POPULATE_STORE, response);
|
||||
},
|
||||
[NODE_ACTIONS.SELECT_SATELLITE]: async function ({commit}, id?: string): Promise<void> {
|
||||
let response: Satellite | Satellites;
|
||||
if (id) {
|
||||
response = await api.satellite(id);
|
||||
commit(NODE_MUTATIONS.SELECT_SATELLITE, response);
|
||||
} else {
|
||||
response = await api.satellites();
|
||||
commit(NODE_MUTATIONS.SELECT_ALL_SATELLITES, response);
|
||||
}
|
||||
|
||||
commit(NODE_MUTATIONS.SET_DAILY_DATA, response);
|
||||
},
|
||||
},
|
||||
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);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -5,8 +5,10 @@ import {
|
||||
HeldInfo,
|
||||
PaymentInfoParameters,
|
||||
PayoutApi,
|
||||
PayoutInfoRange, PayoutPeriod,
|
||||
PayoutState, TotalPayoutInfo,
|
||||
PayoutInfoRange,
|
||||
PayoutPeriod,
|
||||
PayoutState,
|
||||
TotalPayoutInfo,
|
||||
} from '@/app/types/payout';
|
||||
import { TB } from '@/app/utils/converter';
|
||||
|
||||
@ -104,7 +106,7 @@ export function makePayoutModule(api: PayoutApi) {
|
||||
* Returns held percentage depends on number of months that node is online.
|
||||
* @param startedAt date since node is online.
|
||||
*/
|
||||
function getHeldPercentage(startedAt: Date): number {
|
||||
export function getHeldPercentage(startedAt: Date): number {
|
||||
const now = new Date();
|
||||
const secondsInMonthApproximately = 2628000;
|
||||
const differenceInSeconds = (now.getTime() - startedAt.getTime()) / 1000;
|
||||
|
@ -41,7 +41,7 @@ export class NotificationsHttpApi implements NotificationsApi {
|
||||
item.message,
|
||||
!!item.readAt,
|
||||
new Date(item.createdAt),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
pageCount = notificationResponse.page.pageCount;
|
||||
|
@ -104,7 +104,7 @@ export class PayoutHttpApi implements PayoutApi {
|
||||
* @returns total payout information
|
||||
* @throws Error
|
||||
*/
|
||||
private async getHeld(path): Promise<HeldInfo> {
|
||||
public async getHeld(path): Promise<HeldInfo> {
|
||||
const response = await this.client.get(path);
|
||||
|
||||
if (!response.ok) {
|
||||
|
26
web/storagenode/tests/unit/mock/api/payout.ts
Normal file
26
web/storagenode/tests/unit/mock/api/payout.ts
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import {
|
||||
HeldInfo,
|
||||
PaymentInfoParameters,
|
||||
PayoutApi,
|
||||
TotalPayoutInfo,
|
||||
} from '@/app/types/payout';
|
||||
|
||||
/**
|
||||
* Mock for PayoutApi.
|
||||
*/
|
||||
export class PayoutApiMock implements PayoutApi {
|
||||
public getHeldInfoByMonth(paymentInfoParameters: PaymentInfoParameters): Promise<HeldInfo> {
|
||||
return Promise.resolve(new HeldInfo());
|
||||
}
|
||||
|
||||
public getHeldInfoByPeriod(paymentInfoParameters: PaymentInfoParameters): Promise<HeldInfo> {
|
||||
return Promise.resolve(new HeldInfo());
|
||||
}
|
||||
|
||||
public getTotal(paymentInfoParameters: PaymentInfoParameters): Promise<TotalPayoutInfo> {
|
||||
return Promise.resolve(new TotalPayoutInfo());
|
||||
}
|
||||
}
|
17
web/storagenode/tests/unit/mock/svgTransform.js
Normal file
17
web/storagenode/tests/unit/mock/svgTransform.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
const vueJest = require('vue-jest/lib/template-compiler');
|
||||
|
||||
module.exports = {
|
||||
process(content) {
|
||||
const { render } = vueJest({
|
||||
content,
|
||||
attrs: {
|
||||
functional: false,
|
||||
},
|
||||
});
|
||||
|
||||
return `module.exports = { render: ${render} }`;
|
||||
},
|
||||
};
|
182
web/storagenode/tests/unit/store/payout.spec.ts
Normal file
182
web/storagenode/tests/unit/store/payout.spec.ts
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import Vuex from 'vuex';
|
||||
|
||||
import { makeNodeModule } from '@/app/store/modules/node';
|
||||
import { getHeldPercentage, makePayoutModule, PAYOUT_ACTIONS, PAYOUT_MUTATIONS } from '@/app/store/modules/payout';
|
||||
import { HeldInfo, PayoutInfoRange, PayoutPeriod, TotalPayoutInfo } from '@/app/types/payout';
|
||||
import { PayoutHttpApi } from '@/storagenode/api/payout';
|
||||
import { SNOApi } from '@/storagenode/api/storagenode';
|
||||
import { createLocalVue } from '@vue/test-utils';
|
||||
|
||||
const Vue = createLocalVue();
|
||||
const payoutApi = new PayoutHttpApi();
|
||||
const payoutModule = makePayoutModule(payoutApi);
|
||||
|
||||
const nodeApi = new SNOApi();
|
||||
const nodeModule = makeNodeModule(nodeApi);
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const store = new Vuex.Store({ modules: { payoutModule, node: nodeModule } });
|
||||
|
||||
const state = store.state as any;
|
||||
|
||||
describe('mutations', () => {
|
||||
beforeEach(() => {
|
||||
createLocalVue().use(Vuex);
|
||||
});
|
||||
|
||||
it('sets held information', () => {
|
||||
const heldInfo = new HeldInfo(13, 12, 11);
|
||||
|
||||
store.commit(PAYOUT_MUTATIONS.SET_HELD_INFO, heldInfo);
|
||||
|
||||
expect(state.payoutModule.heldInfo.usageAtRest).toBe(13);
|
||||
expect(state.payoutModule.heldInfo.usageGet).toBe(12);
|
||||
expect(state.payoutModule.heldInfo.usagePut).toBe(11);
|
||||
});
|
||||
|
||||
it('sets total payout information', () => {
|
||||
const totalInfo = new TotalPayoutInfo(50, 100);
|
||||
|
||||
store.commit(PAYOUT_MUTATIONS.SET_TOTAL, totalInfo);
|
||||
|
||||
expect(state.payoutModule.totalHeldAmount).toBe(50);
|
||||
expect(state.payoutModule.totalEarnings).toBe(100);
|
||||
});
|
||||
|
||||
it('sets period range', () => {
|
||||
const range = new PayoutInfoRange(new PayoutPeriod(2019, 2), new PayoutPeriod(2020, 3));
|
||||
|
||||
store.commit(PAYOUT_MUTATIONS.SET_RANGE, range);
|
||||
|
||||
if (!state.payoutModule.periodRange.start) {
|
||||
fail('periodRange.start is null');
|
||||
}
|
||||
|
||||
expect(state.payoutModule.periodRange.start.period).toBe('2019-03');
|
||||
expect(state.payoutModule.periodRange.end.period).toBe('2020-04');
|
||||
});
|
||||
|
||||
it('sets held percentage', () => {
|
||||
const expectedHeldPercentage = 75;
|
||||
|
||||
store.commit(PAYOUT_MUTATIONS.SET_HELD_PERCENT, expectedHeldPercentage);
|
||||
|
||||
expect(state.payoutModule.heldPercentage).toBe(expectedHeldPercentage);
|
||||
});
|
||||
});
|
||||
|
||||
describe('actions', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('success get held info by month', async () => {
|
||||
jest.spyOn(payoutApi, 'getHeldInfoByMonth').mockReturnValue(
|
||||
Promise.resolve(new HeldInfo(1, 2 , 3, 4, 5)),
|
||||
);
|
||||
|
||||
const range = new PayoutInfoRange(null, new PayoutPeriod(2020, 3));
|
||||
|
||||
store.commit(PAYOUT_MUTATIONS.SET_RANGE, range);
|
||||
|
||||
await store.dispatch(PAYOUT_ACTIONS.GET_HELD_INFO);
|
||||
|
||||
expect(state.payoutModule.heldInfo.usagePut).toBe(3);
|
||||
expect(state.payoutModule.heldInfo.held).toBe(0);
|
||||
expect(state.payoutModule.heldPercentage).toBe(getHeldPercentage(new Date()));
|
||||
});
|
||||
|
||||
it('get held info by month throws an error when api call fails', async () => {
|
||||
jest.spyOn(payoutApi, 'getHeldInfoByMonth').mockImplementation(() => { throw new Error(); });
|
||||
|
||||
try {
|
||||
await store.dispatch(PAYOUT_ACTIONS.GET_HELD_INFO);
|
||||
expect(true).toBe(false);
|
||||
} catch (error) {
|
||||
expect(state.payoutModule.heldInfo.usagePut).toBe(3);
|
||||
expect(state.payoutModule.heldInfo.held).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('success get held info by period', async () => {
|
||||
jest.spyOn(payoutApi, 'getHeldInfoByPeriod').mockReturnValue(
|
||||
Promise.resolve(new HeldInfo(1, 2 , 3, 4, 5)),
|
||||
);
|
||||
|
||||
const range = new PayoutInfoRange(new PayoutPeriod(2019, 2), new PayoutPeriod(2020, 3));
|
||||
|
||||
store.commit(PAYOUT_MUTATIONS.SET_RANGE, range);
|
||||
|
||||
await store.dispatch(PAYOUT_ACTIONS.GET_HELD_INFO);
|
||||
|
||||
expect(state.payoutModule.heldInfo.usagePut).toBe(3);
|
||||
expect(state.payoutModule.heldInfo.held).toBe(0);
|
||||
});
|
||||
|
||||
it('get held info by period throws an error when api call fails', async () => {
|
||||
jest.spyOn(payoutApi, 'getHeldInfoByPeriod').mockImplementation(() => { throw new Error(); });
|
||||
|
||||
try {
|
||||
await store.dispatch(PAYOUT_ACTIONS.GET_HELD_INFO);
|
||||
expect(true).toBe(false);
|
||||
} catch (error) {
|
||||
expect(state.payoutModule.heldInfo.usagePut).toBe(3);
|
||||
expect(state.payoutModule.heldInfo.held).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('success get total', async () => {
|
||||
jest.spyOn(payoutApi, 'getTotal').mockReturnValue(
|
||||
Promise.resolve(new TotalPayoutInfo(10, 20)),
|
||||
);
|
||||
|
||||
await store.dispatch(PAYOUT_ACTIONS.GET_TOTAL);
|
||||
|
||||
expect(state.payoutModule.totalHeldAmount).toBe(10);
|
||||
expect(state.payoutModule.totalEarnings).toBe(0);
|
||||
});
|
||||
|
||||
it('get total throws an error when api call fails', async () => {
|
||||
jest.spyOn(payoutApi, 'getTotal').mockImplementation(() => { throw new Error(); });
|
||||
|
||||
try {
|
||||
await store.dispatch(PAYOUT_ACTIONS.GET_TOTAL);
|
||||
expect(true).toBe(false);
|
||||
} catch (error) {
|
||||
expect(state.payoutModule.totalHeldAmount).toBe(10);
|
||||
expect(state.payoutModule.totalEarnings).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('success sets period range', async () => {
|
||||
await store.dispatch(
|
||||
PAYOUT_ACTIONS.SET_PERIODS_RANGE,
|
||||
new PayoutInfoRange(
|
||||
new PayoutPeriod(2020, 1),
|
||||
new PayoutPeriod(2020, 2),
|
||||
),
|
||||
);
|
||||
|
||||
expect(state.payoutModule.periodRange.start.period).toBe('2020-02');
|
||||
expect(state.payoutModule.periodRange.end.period).toBe('2020-03');
|
||||
});
|
||||
});
|
||||
|
||||
describe('utils functions', () => {
|
||||
it('get correct help percentage', () => {
|
||||
const nowTime = new Date().getTime();
|
||||
const testDifferencesInMilliseconds: number[] = [5e9, 1.4e10, 2.3e10, 4e10];
|
||||
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);
|
||||
|
||||
expect(heldPercentage).toBe(expectedHeldPercentages[i]);
|
||||
}
|
||||
});
|
||||
});
|
@ -14,7 +14,8 @@
|
||||
"baseUrl": ".",
|
||||
"strictPropertyInitialization": false,
|
||||
"types": [
|
||||
"webpack-env"
|
||||
"webpack-env",
|
||||
"jest"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
@ -32,7 +33,9 @@
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"src/app/types/svg.d.ts"
|
||||
"src/app/types/svg.d.ts",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
|
@ -35,7 +35,9 @@
|
||||
"no-eval": true,
|
||||
"no-invalid-template-strings": true,
|
||||
"no-invalid-this": true,
|
||||
"no-misused-new": true,
|
||||
"no-static-this": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-var-keyword": true,
|
||||
"newline-before-return": true,
|
||||
"object-literal-sort-keys": false,
|
||||
@ -64,11 +66,14 @@
|
||||
}]
|
||||
}],
|
||||
"prefer-const": true,
|
||||
"prefer-method-signature": true,
|
||||
"prefer-switch": [true, {"min-cases": 2}],
|
||||
"prefer-while": true,
|
||||
"quotemark": [true, "single", "avoid-escape"],
|
||||
"semicolon": [true, "always"],
|
||||
"space-within-parens": 0,
|
||||
"static-this": true,
|
||||
"trailing-comma": [true, {"multiline": "always", "singleline": "never"}],
|
||||
"triple-equals": true,
|
||||
"typedef": [
|
||||
true,
|
||||
@ -80,11 +85,13 @@
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-module",
|
||||
"check-operator",
|
||||
"check-preblock",
|
||||
"check-rest-spread",
|
||||
"check-separator",
|
||||
"check-type-operator",
|
||||
"check-preblock"
|
||||
"check-type",
|
||||
"check-type-operator"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user