Skip to content

Commit

Permalink
Added tests and fixed conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
holgerkoser committed Sep 27, 2024
1 parent 18adcad commit 1ce23b0
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 21 deletions.
36 changes: 36 additions & 0 deletions frontend/__tests__/utils/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
parseNumberWithMagnitudeSuffix,
normalizeVersion,
isEmail,
convertToGi,
convertToGibibyte,
} from '@/utils'

import {
Expand Down Expand Up @@ -524,4 +526,38 @@ describe('utils', () => {
expect(normalizeVersion('23.2x')).toBeUndefined()
})
})

describe('convertToGi', () => {
const precision = 9
it('should convert binary units to Gibibyte', () => {
expect(convertToGi('1Ki')).toBe(Math.pow(1024, -2))
expect(convertToGi('1Mi')).toBe(Math.pow(1024, -1))
expect(convertToGi('1Gi')).toBe(Math.pow(1024, 0))
expect(convertToGi('1Ti')).toBe(Math.pow(1024, 1))
expect(convertToGi('1Pi')).toBe(Math.pow(1024, 2))
})

it('should convert decimal units to Gibibyte', () => {
expect(convertToGi('1')).toBeCloseTo(Math.pow(1000, 3) * Math.pow(1024, -6), precision)
expect(convertToGi('1K')).toBeCloseTo(Math.pow(1000, 3) * Math.pow(1024, -5), precision)
expect(convertToGi('1M')).toBeCloseTo(Math.pow(1000, 3) * Math.pow(1024, -4), precision)
expect(convertToGi('1G')).toBeCloseTo(Math.pow(1000, 3) * Math.pow(1024, -3), precision)
expect(convertToGi('1T')).toBeCloseTo(Math.pow(1000, 3) * Math.pow(1024, -2), precision)
expect(convertToGi('1P')).toBeCloseTo(Math.pow(1000, 3) * Math.pow(1024, -1), precision)
})

it('should convert floats to Gibibyte', () => {
expect(convertToGi('1.23Gi')).toBe(1.23)
expect(convertToGi('4.56Ti')).toBe(4.56 * Math.pow(1024, 1))
expect(convertToGi('7.89G')).toBeCloseTo(7.89 * Math.pow(1000, 3) * Math.pow(1024, -3), precision)
})

it('should fail to convert to Gibibyte', () => {
expect(() => convertToGibibyte('1.2.3')).toThrow(TypeError)
expect(() => convertToGibibyte('1Xi')).toThrow(TypeError)
expect(() => convertToGibibyte('1E3')).toThrow(TypeError)
expect(() => convertToGibibyte('1,23')).toThrow(TypeError)
expect(() => convertToGibibyte('Gi')).toThrow(TypeError)
})
})
})
82 changes: 61 additions & 21 deletions frontend/src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,6 @@ import {
} from '@/lodash'

const serviceAccountRegex = /^system:serviceaccount:([^:]+):([^:]+)$/
const sizeConversionFactors = {
Gi: 1,
G: 1 / 1.073741824, // 1 Gi = 1.073741824 G
Mi: 1 / 1024, // 1 Gi = 1024 Mi
M: 1 / (1024 * 1.073741824), // 1 Gi = 1073.741824 M
Ki: 1 / (1024 * 1024), // 1 Gi = 1024^2 Ki
K: 1 / (1024 * 1024 * 1.073741824), // 1 Gi = 1073741.824 K
Ti: 1024, // 1 Ti = 1024 Gi
T: 1024 / 1.073741824, // 1 Ti = 1073.741824 Gi
}
const sizeRegex = /^(\d+)Gi$/
const colorCodeRegex = /^#([a-f0-9]{6}|[a-f0-9]{3})$/i

const logger = useLogger()
Expand Down Expand Up @@ -163,20 +152,71 @@ export function displayName (username) {
return username
}

export function convertToGi (value) {
export function convertToGibibyte (value) {
if (!value) {
return 0
throw new TypeError('Value is empty')
}
if (typeof value === 'number') {
value = value.toString()
}
const result = sizeRegex.exec(value)
if (result) {
const [, sizeValue, unit] = result
const conversionFactor = get(sizeConversionFactors, [unit])
if (conversionFactor !== undefined) {
return parseInt(sizeValue, 10) * conversionFactor

value = value.trim().toLowerCase()
let size = ''
let unit = ''
let isFloat = false

for (let i = 0; i < value.length; i++) {
const c = value[i] // eslint-disable-line security/detect-object-injection -- loop variable is controlled
if (c >= '0' && c <= '9') {
size += c
} else if (c === '.' && !isFloat) {
isFloat = true
size += c
} else {
unit = value.slice(i)
break
}
}
logger.error(`Could not parse size ${value} as it does not match expected formats`)
return 0

if (size === '') {
throw new TypeError('Invalid value')
}

const num = parseFloat(size)

if (unit === '') {
return num * 1e9 / Math.pow(2, 6 * 10)
}

const multipliers = {
k: 1e9 * Math.pow(1024, -5),
m: 1e9 * Math.pow(1024, -4),
g: 1e9 * Math.pow(1024, -3),
t: 1e9 * Math.pow(1024, -2),
p: 1e9 * Math.pow(1024, -1),
e: 1e9 * Math.pow(1024, 0),
ki: Math.pow(1024, -2),
mi: Math.pow(1024, -1),
gi: Math.pow(1024, 0),
ti: Math.pow(1024, 1),
pi: Math.pow(1024, 2),
ei: Math.pow(1024, 3),
}

if (!(unit in multipliers)) {
throw new TypeError('Invalid value')
}

return num * multipliers[unit] // eslint-disable-line security/detect-object-injection -- value of unit is validated
}

export function convertToGi (value) {
try {
return convertToGibibyte(value)
} catch (err) {
logger.error('Failed to convert value %s to GiB: %s', value, err.message)
return 0
}
}

export function isEmail (value) {
Expand Down

0 comments on commit 1ce23b0

Please sign in to comment.