mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2024-11-28 06:46:13 +03:00
perf(faker): Improve torrent mocking (#1187)
This commit is contained in:
parent
65b34ab685
commit
8b1f641fca
9 changed files with 137 additions and 153 deletions
8
__mocks__/torrents.json
Normal file
8
__mocks__/torrents.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
[
|
||||
{
|
||||
"name": "First torrent"
|
||||
},
|
||||
{
|
||||
"name": "Second torrent"
|
||||
}
|
||||
]
|
|
@ -26,9 +26,6 @@ const paginationSizes = ref([
|
|||
50
|
||||
])
|
||||
|
||||
const isProduction = computed(() => process.env.NODE_ENV === 'production')
|
||||
const isDevelopment = computed(() => process.env.NODE_ENV === 'development')
|
||||
|
||||
const theme = computed({
|
||||
get() {
|
||||
if (vueTorrentStore.matchSystemTheme) return 'auto'
|
||||
|
@ -51,9 +48,9 @@ const themeOptions = [
|
|||
]
|
||||
|
||||
const vueTorrentVersion = computed(() => {
|
||||
if (isProduction.value) {
|
||||
if (import.meta.env.PROD) {
|
||||
return 'import.meta.env.VITE_PACKAGE_VERSION'
|
||||
} else if (isDevelopment.value) {
|
||||
} else if (import.meta.env.DEV) {
|
||||
return 'DEV'
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
import { FilePriority, TorrentState } from '@/constants/qbit'
|
||||
import { formatEta, getDomainBody } from '@/helpers'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { useMaindataStore } from '@/stores/maindata.ts'
|
||||
import { Torrent as QbitTorrent } from '@/types/qbit/models'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { faker } from '@faker-js/faker'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
type StaticTorrent = Omit<Torrent, 'avgDownloadSpeed' | 'avgUploadSpeed' | 'globalSpeed' | 'globalVolume'>
|
||||
|
||||
export function useTorrentBuilder() {
|
||||
const { t } = useI18n()
|
||||
const maindataStore = useMaindataStore()
|
||||
|
||||
const categories = computed(() => maindataStore.categories.map(value => value.name) || ['ISO', 'Other', 'Movie', 'Music', 'TV'])
|
||||
|
||||
const computedValues = ['avgDownloadSpeed', 'avgUploadSpeed', 'globalSpeed', 'globalVolume', 'priority']
|
||||
|
||||
function buildFromQbit(data: QbitTorrent): Torrent {
|
||||
const torrent = {
|
||||
return buildTorrent({
|
||||
added_on: data.added_on,
|
||||
amount_left: data.amount_left,
|
||||
auto_tmm: data.auto_tmm,
|
||||
|
@ -47,7 +56,7 @@ export function useTorrentBuilder() {
|
|||
seq_dl: data.seq_dl,
|
||||
size: data.size,
|
||||
state: data.state,
|
||||
stateString: t(`torrent.state.${data.state}`),
|
||||
stateString: t(`torrent.state.${ data.state }`),
|
||||
super_seeding: data.super_seeding,
|
||||
tags: data.tags.length > 0 ? data.tags.split(', ').map(t => t.trim()) : [],
|
||||
time_active: data.time_active,
|
||||
|
@ -59,21 +68,82 @@ export function useTorrentBuilder() {
|
|||
uploaded: data.uploaded,
|
||||
uploaded_session: data.uploaded_session,
|
||||
upspeed: data.upspeed
|
||||
}
|
||||
|
||||
const dlDuration = torrent.time_active - torrent.seeding_time
|
||||
const ulDuration = torrent.time_active
|
||||
|
||||
return Object.freeze({
|
||||
...torrent,
|
||||
// const qlonglong dlDuration = torrent->activeTime() - torrent->finishedTime();
|
||||
// dataDict[KEY_PROP_DL_SPEED_AVG] = torrent->totalDownload() / ((dlDuration == 0) ? -1 : dlDuration);
|
||||
avgDownloadSpeed: torrent.downloaded / ((dlDuration == 0) ? -1 : dlDuration),
|
||||
avgUploadSpeed: torrent.uploaded / ((ulDuration == 0) ? -1 : ulDuration),
|
||||
globalSpeed: torrent.dlspeed + torrent.upspeed,
|
||||
globalVolume: torrent.downloaded + torrent.uploaded
|
||||
})
|
||||
}
|
||||
|
||||
return { computedValues, buildFromQbit }
|
||||
function buildFromFaker(data: Partial<Torrent>, index: number): Torrent {
|
||||
const added_on = data.added_on || faker.date.past().getTime()
|
||||
const available_peers = data.available_peers || faker.number.int({ min: 0, max: 250 })
|
||||
const available_seeds = data.available_seeds || faker.number.int({ min: 0, max: 250 })
|
||||
const state = data.state || faker.helpers.arrayElement(Object.values(TorrentState))
|
||||
const total_size = data.total_size || faker.number.int({ min: 1000, max: 1_000_000_000_000 }) // [1 ko; 1 To]
|
||||
const tracker = data.tracker || faker.internet.url()
|
||||
|
||||
return buildTorrent({
|
||||
added_on,
|
||||
amount_left: data.amount_left || faker.number.int({ min: 0, max: total_size }),
|
||||
auto_tmm: data.auto_tmm || faker.datatype.boolean(),
|
||||
availability: data.availability || faker.number.float({ min: 0, max: 100, precision: 0.01 }),
|
||||
available_peers,
|
||||
available_seeds,
|
||||
category: data.category || faker.helpers.arrayElement(categories.value),
|
||||
completed_on: data.completed_on || faker.date.between({ from: added_on, to: Date.now() }),
|
||||
content_path: data.content_path || faker.system.filePath(),
|
||||
dl_limit: data.dl_limit || faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
dlspeed: data.dlspeed || faker.number.int({ min: 0, max: 5000000 }),
|
||||
download_path: data.download_path || faker.system.filePath(),
|
||||
downloaded: data.downloaded || faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
downloaded_session: data.downloaded_session || faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
eta: data.eta || formatEta(faker.number.int({ min: 0, max: 900000 })),
|
||||
forced: data.forced || faker.datatype.boolean(),
|
||||
force_start: data.force_start || faker.datatype.boolean(),
|
||||
hash: data.hash || faker.string.uuid(),
|
||||
infohash_v1: data.infohash_v1 || faker.string.uuid(),
|
||||
infohash_v2: data.infohash_v2 || faker.string.uuid(),
|
||||
last_activity: data.last_activity || faker.number.int({ min: 0, max: 50 }),
|
||||
magnet: data.magnet_uri || faker.internet.url(),
|
||||
name: data.name || `Torrent ${ index + 1 }`,
|
||||
num_leechs: data.num_leechs || faker.number.int(available_peers),
|
||||
num_seeds: data.num_seeds || faker.number.int(available_seeds),
|
||||
priority: data.priority || FilePriority.NORMAL,
|
||||
progress: data.progress || faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
ratio: data.ratio || faker.number.float({ min: 0, max: 5, precision: 0.01 }),
|
||||
ratio_limit: data.ratio_limit || faker.number.float({ min: 0, max: 4, precision: 0.01 }),
|
||||
ratio_time_limit: data.ratio_time_limit || faker.number.float({ min: 0, max: 4, precision: 0.01 }),
|
||||
savePath: data.savePath || faker.system.filePath(),
|
||||
seeding_time: data.seeding_time || faker.number.int({ min: 0, max: 50 }),
|
||||
seen_complete: data.seen_complete || faker.number.int({ min: 0, max: 50 }),
|
||||
seq_dl: data.seq_dl || faker.datatype.boolean(),
|
||||
size: data.size || faker.number.int({ min: 1000, max: total_size }),
|
||||
state,
|
||||
stateString: t(`torrent.state.${ state }`),
|
||||
super_seeding: data.super_seeding || faker.datatype.boolean(),
|
||||
tags: data.tags || '',
|
||||
time_active: data.time_active || faker.number.int({ min: 1000, max: 900000 }),
|
||||
total_size,
|
||||
tracker,
|
||||
tracker_domain: getDomainBody(tracker),
|
||||
trackers_count: data.trackers_count || faker.number.int({ min: 1, max: 50 }),
|
||||
up_limit: data.up_limit || faker.number.int({ min: 1000, max: 900000 }),
|
||||
uploaded: data.uploaded || faker.number.int({ min: 1000, max: 900000 }),
|
||||
uploaded_session: data.uploaded_session || faker.number.int({ min: 1000, max: 900000 }),
|
||||
upspeed: data.upspeed || faker.number.int({ min: 0, max: 5000000 })
|
||||
})
|
||||
}
|
||||
|
||||
function buildTorrent(data: StaticTorrent): Torrent {
|
||||
const dlDuration = data.time_active - data.seeding_time
|
||||
const ulDuration = data.time_active
|
||||
|
||||
// @ts-expect-error: Type is missing the following properties from type 'Torrent': ...
|
||||
return Object.freeze({
|
||||
...data,
|
||||
avgDownloadSpeed: data.downloaded / ((dlDuration == 0) ? -1 : dlDuration),
|
||||
avgUploadSpeed: data.uploaded / ((ulDuration == 0) ? -1 : ulDuration),
|
||||
globalSpeed: data.dlspeed + data.upspeed,
|
||||
globalVolume: data.downloaded + data.uploaded
|
||||
})
|
||||
}
|
||||
|
||||
return { computedValues, buildFromQbit, buildFromFaker }
|
||||
}
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
import { capitalize, codeToFlag, extractHostname, getDomainBody, splitByUrl, stringContainsUrl, titleCase } from './text'
|
||||
import {
|
||||
capitalize,
|
||||
codeToFlag,
|
||||
extractHostname,
|
||||
getDomainBody,
|
||||
splitByUrl,
|
||||
stringContainsUrl,
|
||||
titleCase,
|
||||
uuidFromRaw
|
||||
} from './text'
|
||||
import { expect, test } from 'vitest'
|
||||
|
||||
test('helpers/text/titleCase', () => {
|
||||
|
@ -68,3 +77,11 @@ test('helpers/text/codeToFlag', () => {
|
|||
expect(codeToFlag('it').char).toBe('🇮🇹')
|
||||
expect(codeToFlag('it').url).toBe('https://cdn.jsdelivr.net/npm/twemoji/2/svg/1f1ee-1f1f9.svg')
|
||||
})
|
||||
|
||||
test('helpers/text/uuidFromRaw', () => {
|
||||
expect(uuidFromRaw(0n)).toBe('00000000-0000-0000-0000-000000000000')
|
||||
expect(uuidFromRaw(1n)).toBe('00000000-0000-0000-0000-000000000001')
|
||||
expect(uuidFromRaw(0xAAAAAn)).toBe('00000000-0000-0000-0000-0000000aaaaa')
|
||||
expect(uuidFromRaw(0x00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFn)).toBe('ffffffff-ffff-ffff-ffff-ffffffffffff')
|
||||
expect(uuidFromRaw(0xABCDEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFn)).toBe('ffffffff-ffff-ffff-ffff-ffffffffffff')
|
||||
})
|
||||
|
|
|
@ -83,3 +83,8 @@ export function codeToFlag(code: string) {
|
|||
url
|
||||
}
|
||||
}
|
||||
|
||||
export function uuidFromRaw(bits: bigint) {
|
||||
let bitString = bits.toString(16).slice(-32).padStart(32, "0");
|
||||
return `${bitString.slice(-32, -24)}-${bitString.slice(-24, -20)}-${bitString.slice(-20, -16)}-${bitString.slice(-16, -12)}-${bitString.slice(-12)}`;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { useTorrentBuilder } from '@/composables'
|
|||
import { FilePriority, TorrentState } from '@/constants/qbit'
|
||||
import { SortOptions } from '@/constants/qbit/SortOptions'
|
||||
import { extractHostname } from '@/helpers'
|
||||
import { uuidFromRaw } from '@/helpers/text'
|
||||
import { qbit } from '@/services'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useDashboardStore } from '@/stores/dashboard'
|
||||
|
@ -10,12 +11,9 @@ import { useVueTorrentStore } from '@/stores/vuetorrent'
|
|||
import { Category, ServerState } from '@/types/qbit/models'
|
||||
import { AddTorrentPayload } from '@/types/qbit/payloads'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { generateMultiple } from '@/utils/faker'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, MaybeRefOrGetter, ref, toValue } from 'vue'
|
||||
|
||||
const isProduction = computed(() => process.env.NODE_ENV === 'production')
|
||||
|
||||
export const useMaindataStore = defineStore('maindata', () => {
|
||||
const categories = ref<Category[]>([])
|
||||
const isUpdatingMaindata = ref(false)
|
||||
|
@ -181,9 +179,13 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
// update torrents
|
||||
torrents.value = data.map(t => torrentBuilder.buildFromQbit(t))
|
||||
|
||||
if (!isProduction.value && import.meta.env.VITE_USE_FAKE_TORRENTS === 'true') {
|
||||
const count = import.meta.env.VITE_FAKE_TORRENT_COUNT
|
||||
torrents.value.push(...generateMultiple(count).map(t => torrentBuilder.buildFromQbit(t)))
|
||||
if (import.meta.env.DEV && import.meta.env.VITE_USE_FAKE_TORRENTS === 'true') {
|
||||
const count = Number(import.meta.env.VITE_FAKE_TORRENT_COUNT)
|
||||
const fakeTorrents: Partial<Torrent> = (await import('../../__mocks__/torrents.json')).default
|
||||
|
||||
for (let i = 1; i <= count; i++) {
|
||||
torrents.value.push(torrentBuilder.buildFromFaker({ ...fakeTorrents.at(i), hash: uuidFromRaw(BigInt(i)) }, i))
|
||||
}
|
||||
}
|
||||
|
||||
// filter out deleted torrents from selection
|
||||
|
|
|
@ -38,6 +38,7 @@ export default interface Torrent {
|
|||
seq_dl: boolean
|
||||
size: number
|
||||
state: TorrentState
|
||||
stateString: string
|
||||
super_seeding: boolean
|
||||
tags: string[]
|
||||
time_active: number
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
import { faker } from '@faker-js/faker'
|
||||
import type Torrent from '@/types/qbit/models/Torrent'
|
||||
import { TorrentState } from '@/constants/qbit/TorrentState'
|
||||
import { FilePriority } from '@/constants/qbit/FilePriority'
|
||||
|
||||
export function generateMultiple(count: number = 1): Torrent[] {
|
||||
const torrents: Torrent[] = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
torrents.push(
|
||||
generateTorrent({
|
||||
name: 'Torrent - ' + i
|
||||
})
|
||||
)
|
||||
}
|
||||
return torrents
|
||||
}
|
||||
|
||||
export function generateTorrent(data: Partial<Torrent>): Torrent {
|
||||
return {
|
||||
/** Time (Unix Epoch) when the torrent was added to the client */
|
||||
added_on: faker.date.recent().getTime(),
|
||||
/** Amount of data left to download (bytes) */
|
||||
amount_left: faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
/** Whether this torrent is managed by Automatic Torrent Management */
|
||||
auto_tmm: faker.datatype.boolean(),
|
||||
/** Percentage of file pieces currently available */
|
||||
availability: faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
/** Category of the torrent */
|
||||
category: faker.helpers.arrayElement(['ISO', 'Other', 'Movie', 'Music', 'TV']),
|
||||
/** Amount of transfer data completed (bytes) */
|
||||
completed: faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
/** Time (Unix Epoch) when the torrent completed */
|
||||
completion_on: faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
/** Absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents) */
|
||||
content_path: faker.system.filePath(),
|
||||
/** Torrent download speed limit (bytes/s). -1 if unlimited. */
|
||||
dl_limit: faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
/** Torrent download speed (bytes/s) */
|
||||
dlspeed: faker.number.int({ min: 0, max: 5000000 }),
|
||||
/** TODO */
|
||||
download_path: faker.system.filePath(),
|
||||
/** Amount of data downloaded */
|
||||
downloaded: faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
/** Amount of data downloaded this session */
|
||||
downloaded_session: faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
/** Torrent ETA (seconds) */
|
||||
eta: faker.number.int({ min: 1000, max: 900000 }),
|
||||
/** True if first last piece are prioritized */
|
||||
f_l_piece_prio: faker.datatype.boolean(),
|
||||
/** True if force start is enabled for this torrent */
|
||||
force_start: faker.datatype.boolean(),
|
||||
/** Torrent hash */
|
||||
hash: faker.string.uuid(),
|
||||
inactive_seeding_time_limit: faker.number.int({ min: 0, max: 50 }),
|
||||
/** TODO */
|
||||
infohash_v1: faker.string.uuid(),
|
||||
/** TODO */
|
||||
infohash_v2: faker.string.uuid(),
|
||||
/** Last time (Unix Epoch) when a chunk was downloaded/uploaded */
|
||||
last_activity: faker.number.int({ min: 0, max: 50 }),
|
||||
/** Magnet URI corresponding to this torrent */
|
||||
magnet_uri: faker.internet.url(),
|
||||
max_inactive_seeding_time: faker.number.int({ min: 0, max: 50 }),
|
||||
/** Maximum share ratio until torrent is stopped from seeding/uploading */
|
||||
max_ratio: faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
/** Maximum seeding time (seconds) until torrent is stopped from seeding */
|
||||
max_seeding_time: faker.number.int({ min: 0, max: 50 }),
|
||||
/** Torrent name */
|
||||
name: data.name || faker.animal.dog(),
|
||||
/** Number of seeds in the swarm */
|
||||
num_complete: faker.number.int({ min: 0, max: 50 }),
|
||||
/** Number of leechers in the swarm */
|
||||
num_incomplete: faker.number.int({ min: 0, max: 50 }),
|
||||
/** Number of leechers connected to */
|
||||
num_leechs: faker.number.int({ min: 0, max: 50 }),
|
||||
/** Number of seeds connected to */
|
||||
num_seeds: faker.number.int({ min: 0, max: 50 }),
|
||||
/** Torrent priority. Returns -1 if queuing is disabled or torrent is in seed mode */
|
||||
priority: FilePriority.NORMAL,
|
||||
/** Torrent progress (percentage/100) */
|
||||
progress: faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
/** Torrent share ratio. Max ratio value: 9999. */
|
||||
ratio: faker.number.float({ min: 0, max: 5, precision: 0.01 }),
|
||||
/** TODO */
|
||||
ratio_limit: faker.number.float({ min: 0, max: 4, precision: 0.01 }),
|
||||
/** Path where this torrent's data is stored */
|
||||
save_path: faker.system.filePath(),
|
||||
/** Torrent elapsed time while complete (seconds) */
|
||||
seeding_time: faker.number.int({ min: 0, max: 50 }),
|
||||
/** TODO */
|
||||
seeding_time_limit: faker.number.int({ min: 0, max: 50 }),
|
||||
/** Time (Unix Epoch) when this torrent was last seen complete */
|
||||
seen_complete: faker.number.int({ min: 0, max: 50 }),
|
||||
/** True if sequential download is enabled */
|
||||
seq_dl: faker.datatype.boolean(),
|
||||
/** Total size (bytes) of files selected for download */
|
||||
size: faker.number.int({ min: 1000000, max: 50000000000 }),
|
||||
/** Torrent state. See table here below for the possible values */
|
||||
state: faker.helpers.arrayElement(Object.values(TorrentState)),
|
||||
/** True if super seeding is enabled */
|
||||
super_seeding: faker.datatype.boolean(),
|
||||
/** Comma-concatenated tag list of the torrent */
|
||||
tags: '',
|
||||
/** Total active time (seconds) */
|
||||
time_active: faker.number.int({ min: 1000, max: 900000 }),
|
||||
/** Total size (bytes) of all file in this torrent (including unselected ones) */
|
||||
total_size: faker.number.int({ min: 1000, max: 900000 }),
|
||||
/** The first tracker with working status. Returns empty string if no tracker is working. */
|
||||
tracker: faker.animal.cat(),
|
||||
/** TODO */
|
||||
trackers_count: faker.number.int({ min: 1000, max: 900000 }),
|
||||
/** Torrent upload speed limit (bytes/s). -1 if unlimited. */
|
||||
up_limit: faker.number.int({ min: 1000, max: 900000 }),
|
||||
/** Amount of data uploaded */
|
||||
uploaded: faker.number.int({ min: 1000, max: 900000 }),
|
||||
/** Amount of data uploaded this session */
|
||||
uploaded_session: faker.number.int({ min: 1000, max: 900000 }),
|
||||
/** Torrent upload speed (bytes/s) */
|
||||
upspeed: faker.number.int({ min: 0, max: 5000000 })
|
||||
}
|
||||
}
|
|
@ -7,12 +7,12 @@ import { VitePWA } from 'vite-plugin-pwa'
|
|||
import { resolve } from 'node:path'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ mode }) => {
|
||||
export default defineConfig(({ command, mode }) => {
|
||||
const env = loadEnv(mode, process.cwd())
|
||||
const qBittorrentPort = env.VITE_QBITTORRENT_PORT ?? '8080'
|
||||
const proxyTarget = env.VITE_QBITTORRENT_TARGET ?? 'http://127.0.0.1'
|
||||
|
||||
const version = process.env.NODE_ENV === 'production' ? process.env.npm_package_version : JSON.stringify(process.env.npm_package_version)
|
||||
const version = command === 'build' ? process.env.npm_package_version : JSON.stringify(process.env.npm_package_version)
|
||||
|
||||
return {
|
||||
base: './',
|
||||
|
@ -22,7 +22,12 @@ export default defineConfig(({ mode }) => {
|
|||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
vue: ['vue', 'vue-router']
|
||||
// vue stuff
|
||||
vue: ['vue', 'vue-router', 'pinia', 'pinia-plugin-persist'],
|
||||
// vuetify stuff
|
||||
vuetify: ['vuetify'],
|
||||
// faker stuff
|
||||
faker: ['@faker-js/faker']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue