Revert "feat: add random number and port generators with customizable options and validations"

This reverts commit b8d6924abc.
This commit is contained in:
AshAnand34
2025-07-20 00:09:39 -07:00
parent b8d6924abc
commit 164c2b6ecc
12 changed files with 0 additions and 1653 deletions

View File

@@ -1,5 +1,3 @@
import { tool as numberRandomPortGenerator } from './random-port-generator/meta';
import { tool as numberRandomNumberGenerator } from './random-number-generator/meta';
import { tool as numberSum } from './sum/meta';
import { tool as numberGenerate } from './generate/meta';
import { tool as numberArithmeticSequence } from './arithmetic-sequence/meta';

View File

@@ -1,235 +0,0 @@
import { Box, Alert, Chip } from '@mui/material';
import { useState } from 'react';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import ToolTextResult from '@components/result/ToolTextResult';
import { GetGroupsType } from '@components/options/ToolOptions';
import { generateRandomNumbers, validateInput, formatNumbers } from './service';
import { InitialValuesType, RandomNumberResult } from './types';
import { useTranslation } from 'react-i18next';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
const initialValues: InitialValuesType = {
minValue: 1,
maxValue: 100,
count: 10,
allowDecimals: false,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
export default function RandomNumberGenerator({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [result, setResult] = useState<RandomNumberResult | null>(null);
const [error, setError] = useState<string | null>(null);
const [formattedResult, setFormattedResult] = useState<string>('');
const compute = (values: InitialValuesType) => {
try {
setError(null);
setResult(null);
setFormattedResult('');
// Validate input
const validationError = validateInput(values);
if (validationError) {
setError(validationError);
return;
}
// Generate random numbers
const randomResult = generateRandomNumbers(values);
setResult(randomResult);
// Format for display
const formatted = formatNumbers(
randomResult.numbers,
values.separator,
values.allowDecimals
);
setFormattedResult(formatted);
} catch (err) {
console.error('Random number generation failed:', err);
setError(t('number:randomNumberGenerator.error.generationFailed'));
}
};
const getGroups: GetGroupsType<InitialValuesType> | null = ({
values,
updateField
}) => [
{
title: t('number:randomNumberGenerator.options.range.title'),
component: (
<Box>
<TextFieldWithDesc
value={values.minValue.toString()}
onOwnChange={(value) =>
updateField('minValue', parseInt(value) || 1)
}
description={t(
'number:randomNumberGenerator.options.range.minDescription'
)}
inputProps={{
type: 'number',
'data-testid': 'min-value-input'
}}
/>
<TextFieldWithDesc
value={values.maxValue.toString()}
onOwnChange={(value) =>
updateField('maxValue', parseInt(value) || 100)
}
description={t(
'number:randomNumberGenerator.options.range.maxDescription'
)}
inputProps={{
type: 'number',
'data-testid': 'max-value-input'
}}
/>
</Box>
)
},
{
title: t('number:randomNumberGenerator.options.generation.title'),
component: (
<Box>
<TextFieldWithDesc
value={values.count.toString()}
onOwnChange={(value) => updateField('count', parseInt(value) || 10)}
description={t(
'number:randomNumberGenerator.options.generation.countDescription'
)}
inputProps={{
type: 'number',
min: 1,
max: 10000,
'data-testid': 'count-input'
}}
/>
<CheckboxWithDesc
title={t(
'number:randomNumberGenerator.options.generation.allowDecimals.title'
)}
checked={values.allowDecimals}
onChange={(value) => updateField('allowDecimals', value)}
description={t(
'number:randomNumberGenerator.options.generation.allowDecimals.description'
)}
/>
<CheckboxWithDesc
title={t(
'number:randomNumberGenerator.options.generation.allowDuplicates.title'
)}
checked={values.allowDuplicates}
onChange={(value) => updateField('allowDuplicates', value)}
description={t(
'number:randomNumberGenerator.options.generation.allowDuplicates.description'
)}
/>
<CheckboxWithDesc
title={t(
'number:randomNumberGenerator.options.generation.sortResults.title'
)}
checked={values.sortResults}
onChange={(value) => updateField('sortResults', value)}
description={t(
'number:randomNumberGenerator.options.generation.sortResults.description'
)}
/>
</Box>
)
},
{
title: t('number:randomNumberGenerator.options.output.title'),
component: (
<Box>
<TextFieldWithDesc
value={values.separator}
onOwnChange={(value) => updateField('separator', value)}
description={t(
'number:randomNumberGenerator.options.output.separatorDescription'
)}
inputProps={{
'data-testid': 'separator-input'
}}
/>
</Box>
)
}
];
return (
<ToolContent
title={title}
initialValues={initialValues}
compute={compute}
getGroups={getGroups}
resultComponent={
<Box>
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
{result && (
<Box>
<ToolTextResult
title={t('number:randomNumberGenerator.result.title')}
value={formattedResult}
/>
<Box sx={{ mt: 2, display: 'flex', gap: 2, flexWrap: 'wrap' }}>
<Chip
label={`${t('number:randomNumberGenerator.result.range')}: ${
result.min
} - ${result.max}`}
variant="outlined"
color="primary"
/>
<Chip
label={`${t('number:randomNumberGenerator.result.count')}: ${
result.count
}`}
variant="outlined"
color="secondary"
/>
{result.hasDuplicates && (
<Chip
label={t(
'number:randomNumberGenerator.result.hasDuplicates'
)}
variant="outlined"
color="warning"
/>
)}
{result.isSorted && (
<Chip
label={t('number:randomNumberGenerator.result.isSorted')}
variant="outlined"
color="success"
/>
)}
</Box>
</Box>
)}
</Box>
}
toolInfo={{
title: t('number:randomNumberGenerator.info.title'),
description:
longDescription || t('number:randomNumberGenerator.info.description')
}}
/>
);
}

View File

@@ -1,25 +0,0 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('number', {
i18n: {
name: 'number:randomNumberGenerator.title',
description: 'number:randomNumberGenerator.description',
shortDescription: 'number:randomNumberGenerator.shortDescription',
longDescription: 'number:randomNumberGenerator.longDescription'
},
path: 'random-number-generator',
icon: 'mdi:dice-multiple',
keywords: [
'random',
'number',
'generator',
'range',
'min',
'max',
'integer',
'decimal',
'float'
],
component: lazy(() => import('./index'))
});

View File

@@ -1,248 +0,0 @@
import { expect, describe, it } from 'vitest';
import { generateRandomNumbers, validateInput, formatNumbers } from './service';
import { InitialValuesType } from './types';
describe('Random Number Generator Service', () => {
describe('generateRandomNumbers', () => {
it('should generate random numbers within the specified range', () => {
const options: InitialValuesType = {
minValue: 1,
maxValue: 10,
count: 5,
allowDecimals: false,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = generateRandomNumbers(options);
expect(result.numbers).toHaveLength(5);
expect(result.min).toBe(1);
expect(result.max).toBe(10);
expect(result.count).toBe(5);
// Check that all numbers are within range
result.numbers.forEach((num) => {
expect(num).toBeGreaterThanOrEqual(1);
expect(num).toBeLessThanOrEqual(10);
expect(Number.isInteger(num)).toBe(true);
});
});
it('should generate decimal numbers when allowDecimals is true', () => {
const options: InitialValuesType = {
minValue: 0,
maxValue: 1,
count: 3,
allowDecimals: true,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = generateRandomNumbers(options);
expect(result.numbers).toHaveLength(3);
// Check that numbers are within range and can be decimals
result.numbers.forEach((num) => {
expect(num).toBeGreaterThanOrEqual(0);
expect(num).toBeLessThanOrEqual(1);
});
});
it('should generate unique numbers when allowDuplicates is false', () => {
const options: InitialValuesType = {
minValue: 1,
maxValue: 5,
count: 3,
allowDecimals: false,
allowDuplicates: false,
sortResults: false,
separator: ', '
};
const result = generateRandomNumbers(options);
expect(result.numbers).toHaveLength(3);
// Check for uniqueness
const uniqueNumbers = new Set(result.numbers);
expect(uniqueNumbers.size).toBe(3);
});
it('should sort results when sortResults is true', () => {
const options: InitialValuesType = {
minValue: 1,
maxValue: 10,
count: 5,
allowDecimals: false,
allowDuplicates: true,
sortResults: true,
separator: ', '
};
const result = generateRandomNumbers(options);
expect(result.numbers).toHaveLength(5);
expect(result.isSorted).toBe(true);
// Check that numbers are sorted
for (let i = 1; i < result.numbers.length; i++) {
expect(result.numbers[i]).toBeGreaterThanOrEqual(result.numbers[i - 1]);
}
});
it('should throw error when minValue >= maxValue', () => {
const options: InitialValuesType = {
minValue: 10,
maxValue: 5,
count: 5,
allowDecimals: false,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
expect(() => generateRandomNumbers(options)).toThrow(
'Minimum value must be less than maximum value'
);
});
it('should throw error when count <= 0', () => {
const options: InitialValuesType = {
minValue: 1,
maxValue: 10,
count: 0,
allowDecimals: false,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
expect(() => generateRandomNumbers(options)).toThrow(
'Count must be greater than 0'
);
});
it('should throw error when unique count exceeds available range', () => {
const options: InitialValuesType = {
minValue: 1,
maxValue: 5,
count: 10,
allowDecimals: false,
allowDuplicates: false,
sortResults: false,
separator: ', '
};
expect(() => generateRandomNumbers(options)).toThrow(
'Cannot generate unique numbers: count exceeds available range'
);
});
});
describe('validateInput', () => {
it('should return null for valid input', () => {
const options: InitialValuesType = {
minValue: 1,
maxValue: 10,
count: 5,
allowDecimals: false,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = validateInput(options);
expect(result).toBeNull();
});
it('should return error when minValue >= maxValue', () => {
const options: InitialValuesType = {
minValue: 10,
maxValue: 5,
count: 5,
allowDecimals: false,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = validateInput(options);
expect(result).toBe('Minimum value must be less than maximum value');
});
it('should return error when count <= 0', () => {
const options: InitialValuesType = {
minValue: 1,
maxValue: 10,
count: 0,
allowDecimals: false,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = validateInput(options);
expect(result).toBe('Count must be greater than 0');
});
it('should return error when count > 10000', () => {
const options: InitialValuesType = {
minValue: 1,
maxValue: 10,
count: 10001,
allowDecimals: false,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = validateInput(options);
expect(result).toBe('Count cannot exceed 10,000');
});
it('should return error when range > 1000000', () => {
const options: InitialValuesType = {
minValue: 1,
maxValue: 1000002,
count: 5,
allowDecimals: false,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = validateInput(options);
expect(result).toBe('Range cannot exceed 1,000,000');
});
});
describe('formatNumbers', () => {
it('should format integers correctly', () => {
const numbers = [1, 2, 3, 4, 5];
const result = formatNumbers(numbers, ', ', false);
expect(result).toBe('1, 2, 3, 4, 5');
});
it('should format decimals correctly', () => {
const numbers = [1.5, 2.7, 3.2];
const result = formatNumbers(numbers, ' | ', true);
expect(result).toBe('1.50 | 2.70 | 3.20');
});
it('should handle custom separators', () => {
const numbers = [1, 2, 3];
const result = formatNumbers(numbers, ' -> ', false);
expect(result).toBe('1 -> 2 -> 3');
});
it('should handle empty array', () => {
const numbers: number[] = [];
const result = formatNumbers(numbers, ', ', false);
expect(result).toBe('');
});
});
});

View File

@@ -1,157 +0,0 @@
import { InitialValuesType, RandomNumberResult } from './types';
/**
* Generate random numbers within a specified range
*/
export function generateRandomNumbers(
options: InitialValuesType
): RandomNumberResult {
const {
minValue,
maxValue,
count,
allowDecimals,
allowDuplicates,
sortResults
} = options;
if (minValue >= maxValue) {
throw new Error('Minimum value must be less than maximum value');
}
if (count <= 0) {
throw new Error('Count must be greater than 0');
}
if (!allowDuplicates && count > maxValue - minValue + 1) {
throw new Error(
'Cannot generate unique numbers: count exceeds available range'
);
}
const numbers: number[] = [];
if (allowDuplicates) {
// Generate random numbers with possible duplicates
for (let i = 0; i < count; i++) {
const randomNumber = generateRandomNumber(
minValue,
maxValue,
allowDecimals
);
numbers.push(randomNumber);
}
} else {
// Generate unique random numbers
const availableNumbers = new Set<number>();
// Create a pool of available numbers
for (let i = minValue; i <= maxValue; i++) {
if (allowDecimals) {
// For decimals, we need to generate more granular values
for (let j = 0; j < 100; j++) {
availableNumbers.add(i + j / 100);
}
} else {
availableNumbers.add(i);
}
}
const availableArray = Array.from(availableNumbers);
// Shuffle the available numbers
for (let i = availableArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[availableArray[i], availableArray[j]] = [
availableArray[j],
availableArray[i]
];
}
// Take the first 'count' numbers
for (let i = 0; i < Math.min(count, availableArray.length); i++) {
numbers.push(availableArray[i]);
}
}
// Sort if requested
if (sortResults) {
numbers.sort((a, b) => a - b);
}
return {
numbers,
min: minValue,
max: maxValue,
count,
hasDuplicates: !allowDuplicates && hasDuplicatesInArray(numbers),
isSorted: sortResults
};
}
/**
* Generate a single random number within the specified range
*/
function generateRandomNumber(
min: number,
max: number,
allowDecimals: boolean
): number {
if (allowDecimals) {
return Math.random() * (max - min) + min;
} else {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}
/**
* Check if an array has duplicate values
*/
function hasDuplicatesInArray(arr: number[]): boolean {
const seen = new Set<number>();
for (const num of arr) {
if (seen.has(num)) {
return true;
}
seen.add(num);
}
return false;
}
/**
* Format numbers for display
*/
export function formatNumbers(
numbers: number[],
separator: string,
allowDecimals: boolean
): string {
return numbers
.map((num) => (allowDecimals ? num.toFixed(2) : Math.round(num).toString()))
.join(separator);
}
/**
* Validate input parameters
*/
export function validateInput(options: InitialValuesType): string | null {
const { minValue, maxValue, count } = options;
if (minValue >= maxValue) {
return 'Minimum value must be less than maximum value';
}
if (count <= 0) {
return 'Count must be greater than 0';
}
if (count > 10000) {
return 'Count cannot exceed 10,000';
}
if (maxValue - minValue > 1000000) {
return 'Range cannot exceed 1,000,000';
}
return null;
}

View File

@@ -1,18 +0,0 @@
export type InitialValuesType = {
minValue: number;
maxValue: number;
count: number;
allowDecimals: boolean;
allowDuplicates: boolean;
sortResults: boolean;
separator: string;
};
export type RandomNumberResult = {
numbers: number[];
min: number;
max: number;
count: number;
hasDuplicates: boolean;
isSorted: boolean;
};

View File

@@ -1,295 +0,0 @@
import { Box, Alert, Chip } from '@mui/material';
import { useState } from 'react';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import ToolTextResult from '@components/result/ToolTextResult';
import { GetGroupsType } from '@components/options/ToolOptions';
import {
generateRandomPorts,
validateInput,
formatPorts,
getPortRangeInfo,
isCommonPort,
getPortService
} from './service';
import { InitialValuesType, RandomPortResult } from './types';
import { useTranslation } from 'react-i18next';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import RadioWithTextField from '@components/options/RadioWithTextField';
const initialValues: InitialValuesType = {
portRange: 'registered',
minPort: 1024,
maxPort: 49151,
count: 5,
allowDuplicates: false,
sortResults: false,
separator: ', '
};
export default function RandomPortGenerator({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [result, setResult] = useState<RandomPortResult | null>(null);
const [error, setError] = useState<string | null>(null);
const [formattedResult, setFormattedResult] = useState<string>('');
const compute = (values: InitialValuesType) => {
try {
setError(null);
setResult(null);
setFormattedResult('');
// Validate input
const validationError = validateInput(values);
if (validationError) {
setError(validationError);
return;
}
// Generate random ports
const randomResult = generateRandomPorts(values);
setResult(randomResult);
// Format for display
const formatted = formatPorts(randomResult.ports, values.separator);
setFormattedResult(formatted);
} catch (err) {
console.error('Random port generation failed:', err);
setError(t('number:randomPortGenerator.error.generationFailed'));
}
};
const getGroups: GetGroupsType<InitialValuesType> | null = ({
values,
updateField
}) => [
{
title: t('number:randomPortGenerator.options.range.title'),
component: (
<Box>
<RadioWithTextField
value={values.portRange}
onTextChange={(value: any) => updateField('portRange', value)}
options={[
{
value: 'well-known',
label: t('number:randomPortGenerator.options.range.wellKnown')
},
{
value: 'registered',
label: t('number:randomPortGenerator.options.range.registered')
},
{
value: 'dynamic',
label: t('number:randomPortGenerator.options.range.dynamic')
},
{
value: 'custom',
label: t('number:randomPortGenerator.options.range.custom')
}
]}
/>
{values.portRange === 'custom' && (
<Box sx={{ mt: 2 }}>
<TextFieldWithDesc
value={values.minPort.toString()}
onOwnChange={(value) =>
updateField('minPort', parseInt(value) || 1024)
}
description={t(
'number:randomPortGenerator.options.range.minPortDescription'
)}
inputProps={{
type: 'number',
min: 1,
max: 65535,
'data-testid': 'min-port-input'
}}
/>
<TextFieldWithDesc
value={values.maxPort.toString()}
onOwnChange={(value) =>
updateField('maxPort', parseInt(value) || 49151)
}
description={t(
'number:randomPortGenerator.options.range.maxPortDescription'
)}
inputProps={{
type: 'number',
min: 1,
max: 65535,
'data-testid': 'max-port-input'
}}
/>
</Box>
)}
<Box
sx={{ mt: 2, p: 2, bgcolor: 'background.paper', borderRadius: 1 }}
>
<strong>{getPortRangeInfo(values.portRange).name}</strong>
<br />
{getPortRangeInfo(values.portRange).description}
</Box>
</Box>
)
},
{
title: t('number:randomPortGenerator.options.generation.title'),
component: (
<Box>
<TextFieldWithDesc
value={values.count.toString()}
onOwnChange={(value) => updateField('count', parseInt(value) || 5)}
description={t(
'number:randomPortGenerator.options.generation.countDescription'
)}
inputProps={{
type: 'number',
min: 1,
max: 1000,
'data-testid': 'count-input'
}}
/>
<CheckboxWithDesc
title={t(
'number:randomPortGenerator.options.generation.allowDuplicates.title'
)}
checked={values.allowDuplicates}
onChange={(value) => updateField('allowDuplicates', value)}
description={t(
'number:randomPortGenerator.options.generation.allowDuplicates.description'
)}
/>
<CheckboxWithDesc
title={t(
'number:randomPortGenerator.options.generation.sortResults.title'
)}
checked={values.sortResults}
onChange={(value) => updateField('sortResults', value)}
description={t(
'number:randomPortGenerator.options.generation.sortResults.description'
)}
/>
</Box>
)
},
{
title: t('number:randomPortGenerator.options.output.title'),
component: (
<Box>
<TextFieldWithDesc
value={values.separator}
onOwnChange={(value) => updateField('separator', value)}
description={t(
'number:randomPortGenerator.options.output.separatorDescription'
)}
inputProps={{
'data-testid': 'separator-input'
}}
/>
</Box>
)
}
];
return (
<ToolContent
title={title}
initialValues={initialValues}
compute={compute}
getGroups={getGroups}
resultComponent={
<Box>
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
{result && (
<Box>
<ToolTextResult
title={t('number:randomPortGenerator.result.title')}
value={formattedResult}
/>
<Box sx={{ mt: 2, display: 'flex', gap: 2, flexWrap: 'wrap' }}>
<Chip
label={`${t('number:randomPortGenerator.result.range')}: ${
result.range.min
} - ${result.range.max}`}
variant="outlined"
color="primary"
/>
<Chip
label={`${t('number:randomPortGenerator.result.count')}: ${
result.count
}`}
variant="outlined"
color="secondary"
/>
{result.hasDuplicates && (
<Chip
label={t('number:randomPortGenerator.result.hasDuplicates')}
variant="outlined"
color="warning"
/>
)}
{result.isSorted && (
<Chip
label={t('number:randomPortGenerator.result.isSorted')}
variant="outlined"
color="success"
/>
)}
</Box>
{result.ports.length > 0 && (
<Box sx={{ mt: 2 }}>
<strong>
{t('number:randomPortGenerator.result.portDetails')}:
</strong>
<Box
sx={{ mt: 1, display: 'flex', gap: 1, flexWrap: 'wrap' }}
>
{result.ports.slice(0, 10).map((port, index) => (
<Chip
key={index}
label={`${port}${
isCommonPort(port) ? ` (${getPortService(port)})` : ''
}`}
variant="outlined"
size="small"
color={isCommonPort(port) ? 'warning' : 'default'}
/>
))}
{result.ports.length > 10 && (
<Chip
label={`+${result.ports.length - 10} more`}
variant="outlined"
size="small"
/>
)}
</Box>
</Box>
)}
</Box>
)}
</Box>
}
toolInfo={{
title: t('number:randomPortGenerator.info.title'),
description:
longDescription || t('number:randomPortGenerator.info.description')
}}
/>
);
}

View File

@@ -1,25 +0,0 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('number', {
i18n: {
name: 'number:randomPortGenerator.title',
description: 'number:randomPortGenerator.description',
shortDescription: 'number:randomPortGenerator.shortDescription',
longDescription: 'number:randomPortGenerator.longDescription'
},
path: 'random-port-generator',
icon: 'mdi:network',
keywords: [
'random',
'port',
'generator',
'network',
'tcp',
'udp',
'server',
'client',
'development'
],
component: lazy(() => import('./index'))
});

View File

@@ -1,315 +0,0 @@
import { expect, describe, it } from 'vitest';
import {
generateRandomPorts,
validateInput,
formatPorts,
getPortRangeInfo,
isCommonPort,
getPortService,
PORT_RANGES
} from './service';
import { InitialValuesType } from './types';
describe('Random Port Generator Service', () => {
describe('generateRandomPorts', () => {
it('should generate random ports within the well-known range', () => {
const options: InitialValuesType = {
portRange: 'well-known',
minPort: 1,
maxPort: 1023,
count: 5,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = generateRandomPorts(options);
expect(result.ports).toHaveLength(5);
expect(result.range.min).toBe(1);
expect(result.range.max).toBe(1023);
expect(result.count).toBe(5);
// Check that all ports are within range
result.ports.forEach((port) => {
expect(port).toBeGreaterThanOrEqual(1);
expect(port).toBeLessThanOrEqual(1023);
expect(Number.isInteger(port)).toBe(true);
});
});
it('should generate random ports within the registered range', () => {
const options: InitialValuesType = {
portRange: 'registered',
minPort: 1024,
maxPort: 49151,
count: 3,
allowDuplicates: false,
sortResults: false,
separator: ', '
};
const result = generateRandomPorts(options);
expect(result.ports).toHaveLength(3);
expect(result.range.min).toBe(1024);
expect(result.range.max).toBe(49151);
// Check for uniqueness
const uniquePorts = new Set(result.ports);
expect(uniquePorts.size).toBe(3);
});
it('should generate random ports within custom range', () => {
const options: InitialValuesType = {
portRange: 'custom',
minPort: 8000,
maxPort: 8100,
count: 4,
allowDuplicates: true,
sortResults: true,
separator: ', '
};
const result = generateRandomPorts(options);
expect(result.ports).toHaveLength(4);
expect(result.range.min).toBe(8000);
expect(result.range.max).toBe(8100);
expect(result.isSorted).toBe(true);
// Check that numbers are sorted
for (let i = 1; i < result.ports.length; i++) {
expect(result.ports[i]).toBeGreaterThanOrEqual(result.ports[i - 1]);
}
});
it('should throw error when minPort >= maxPort', () => {
const options: InitialValuesType = {
portRange: 'custom',
minPort: 1000,
maxPort: 500,
count: 5,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
expect(() => generateRandomPorts(options)).toThrow(
'Minimum port must be less than maximum port'
);
});
it('should throw error when count <= 0', () => {
const options: InitialValuesType = {
portRange: 'registered',
minPort: 1024,
maxPort: 49151,
count: 0,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
expect(() => generateRandomPorts(options)).toThrow(
'Count must be greater than 0'
);
});
it('should throw error when ports are outside valid range', () => {
const options: InitialValuesType = {
portRange: 'custom',
minPort: 0,
maxPort: 70000,
count: 5,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
expect(() => generateRandomPorts(options)).toThrow(
'Ports must be between 1 and 65535'
);
});
it('should throw error when unique count exceeds available range', () => {
const options: InitialValuesType = {
portRange: 'custom',
minPort: 1,
maxPort: 5,
count: 10,
allowDuplicates: false,
sortResults: false,
separator: ', '
};
expect(() => generateRandomPorts(options)).toThrow(
'Cannot generate unique ports: count exceeds available range'
);
});
});
describe('validateInput', () => {
it('should return null for valid input', () => {
const options: InitialValuesType = {
portRange: 'registered',
minPort: 1024,
maxPort: 49151,
count: 5,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = validateInput(options);
expect(result).toBeNull();
});
it('should return error when count <= 0', () => {
const options: InitialValuesType = {
portRange: 'registered',
minPort: 1024,
maxPort: 49151,
count: 0,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = validateInput(options);
expect(result).toBe('Count must be greater than 0');
});
it('should return error when count > 1000', () => {
const options: InitialValuesType = {
portRange: 'registered',
minPort: 1024,
maxPort: 49151,
count: 1001,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = validateInput(options);
expect(result).toBe('Count cannot exceed 1,000');
});
it('should return error when custom range has invalid ports', () => {
const options: InitialValuesType = {
portRange: 'custom',
minPort: 0,
maxPort: 70000,
count: 5,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = validateInput(options);
expect(result).toBe('Ports must be between 1 and 65535');
});
it('should return error when custom range has minPort >= maxPort', () => {
const options: InitialValuesType = {
portRange: 'custom',
minPort: 1000,
maxPort: 500,
count: 5,
allowDuplicates: true,
sortResults: false,
separator: ', '
};
const result = validateInput(options);
expect(result).toBe('Minimum port must be less than maximum port');
});
});
describe('formatPorts', () => {
it('should format ports correctly', () => {
const ports = [80, 443, 8080, 3000];
const result = formatPorts(ports, ', ');
expect(result).toBe('80, 443, 8080, 3000');
});
it('should handle custom separators', () => {
const ports = [80, 443, 8080];
const result = formatPorts(ports, ' -> ');
expect(result).toBe('80 -> 443 -> 8080');
});
it('should handle empty array', () => {
const ports: number[] = [];
const result = formatPorts(ports, ', ');
expect(result).toBe('');
});
});
describe('getPortRangeInfo', () => {
it('should return correct port range info for well-known', () => {
const result = getPortRangeInfo('well-known');
expect(result.name).toBe('Well-Known Ports');
expect(result.min).toBe(1);
expect(result.max).toBe(1023);
});
it('should return correct port range info for registered', () => {
const result = getPortRangeInfo('registered');
expect(result.name).toBe('Registered Ports');
expect(result.min).toBe(1024);
expect(result.max).toBe(49151);
});
it('should return correct port range info for dynamic', () => {
const result = getPortRangeInfo('dynamic');
expect(result.name).toBe('Dynamic Ports');
expect(result.min).toBe(49152);
expect(result.max).toBe(65535);
});
it('should return custom range for unknown range', () => {
const result = getPortRangeInfo('unknown');
expect(result.name).toBe('Custom Range');
});
});
describe('isCommonPort', () => {
it('should identify common ports correctly', () => {
expect(isCommonPort(80)).toBe(true);
expect(isCommonPort(443)).toBe(true);
expect(isCommonPort(22)).toBe(true);
expect(isCommonPort(3306)).toBe(true);
});
it('should return false for uncommon ports', () => {
expect(isCommonPort(12345)).toBe(false);
expect(isCommonPort(54321)).toBe(false);
});
});
describe('getPortService', () => {
it('should return correct service names for common ports', () => {
expect(getPortService(80)).toBe('HTTP');
expect(getPortService(443)).toBe('HTTPS');
expect(getPortService(22)).toBe('SSH');
expect(getPortService(3306)).toBe('MySQL');
});
it('should return "Unknown" for uncommon ports', () => {
expect(getPortService(12345)).toBe('Unknown');
expect(getPortService(54321)).toBe('Unknown');
});
});
describe('PORT_RANGES', () => {
it('should have correct port range definitions', () => {
expect(PORT_RANGES['well-known'].min).toBe(1);
expect(PORT_RANGES['well-known'].max).toBe(1023);
expect(PORT_RANGES['registered'].min).toBe(1024);
expect(PORT_RANGES['registered'].max).toBe(49151);
expect(PORT_RANGES['dynamic'].min).toBe(49152);
expect(PORT_RANGES['dynamic'].max).toBe(65535);
});
});
});

View File

@@ -1,214 +0,0 @@
import { InitialValuesType, RandomPortResult, PortRange } from './types';
// Standard port ranges according to IANA
export const PORT_RANGES: Record<string, PortRange> = {
'well-known': {
name: 'Well-Known Ports',
min: 1,
max: 1023,
description:
'System ports (1-1023) - Reserved for common services like HTTP, HTTPS, SSH, etc.'
},
registered: {
name: 'Registered Ports',
min: 1024,
max: 49151,
description:
'User ports (1024-49151) - Available for applications and services'
},
dynamic: {
name: 'Dynamic Ports',
min: 49152,
max: 65535,
description:
'Private ports (49152-65535) - Available for temporary or private use'
},
custom: {
name: 'Custom Range',
min: 1,
max: 65535,
description: 'Custom port range - Specify your own min and max values'
}
};
/**
* Generate random network ports within a specified range
*/
export function generateRandomPorts(
options: InitialValuesType
): RandomPortResult {
const { portRange, minPort, maxPort, count, allowDuplicates, sortResults } =
options;
// Get the appropriate port range
const range = PORT_RANGES[portRange];
const actualMin = portRange === 'custom' ? minPort : range.min;
const actualMax = portRange === 'custom' ? maxPort : range.max;
if (actualMin >= actualMax) {
throw new Error('Minimum port must be less than maximum port');
}
if (count <= 0) {
throw new Error('Count must be greater than 0');
}
if (actualMin < 1 || actualMax > 65535) {
throw new Error('Ports must be between 1 and 65535');
}
if (!allowDuplicates && count > actualMax - actualMin + 1) {
throw new Error(
'Cannot generate unique ports: count exceeds available range'
);
}
const ports: number[] = [];
if (allowDuplicates) {
// Generate random ports with possible duplicates
for (let i = 0; i < count; i++) {
const randomPort = generateRandomPort(actualMin, actualMax);
ports.push(randomPort);
}
} else {
// Generate unique random ports
const availablePorts = new Set<number>();
// Create a pool of available ports
for (let i = actualMin; i <= actualMax; i++) {
availablePorts.add(i);
}
const availableArray = Array.from(availablePorts);
// Shuffle the available ports
for (let i = availableArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[availableArray[i], availableArray[j]] = [
availableArray[j],
availableArray[i]
];
}
// Take the first 'count' ports
for (let i = 0; i < Math.min(count, availableArray.length); i++) {
ports.push(availableArray[i]);
}
}
// Sort if requested
if (sortResults) {
ports.sort((a, b) => a - b);
}
return {
ports,
range: {
...range,
min: actualMin,
max: actualMax
},
count,
hasDuplicates: !allowDuplicates && hasDuplicatesInArray(ports),
isSorted: sortResults
};
}
/**
* Generate a single random port within the specified range
*/
function generateRandomPort(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* Check if an array has duplicate values
*/
function hasDuplicatesInArray(arr: number[]): boolean {
const seen = new Set<number>();
for (const num of arr) {
if (seen.has(num)) {
return true;
}
seen.add(num);
}
return false;
}
/**
* Format ports for display
*/
export function formatPorts(ports: number[], separator: string): string {
return ports.map((port) => port.toString()).join(separator);
}
/**
* Validate input parameters
*/
export function validateInput(options: InitialValuesType): string | null {
const { portRange, minPort, maxPort, count } = options;
if (count <= 0) {
return 'Count must be greater than 0';
}
if (count > 1000) {
return 'Count cannot exceed 1,000';
}
if (portRange === 'custom') {
if (minPort >= maxPort) {
return 'Minimum port must be less than maximum port';
}
if (minPort < 1 || maxPort > 65535) {
return 'Ports must be between 1 and 65535';
}
}
return null;
}
/**
* Get port range information
*/
export function getPortRangeInfo(portRange: string): PortRange {
return PORT_RANGES[portRange] || PORT_RANGES['custom'];
}
/**
* Check if a port is commonly used
*/
export function isCommonPort(port: number): boolean {
const commonPorts = [
20, 21, 22, 23, 25, 53, 80, 110, 143, 443, 993, 995, 3306, 5432, 6379, 8080
];
return commonPorts.includes(port);
}
/**
* Get port service information
*/
export function getPortService(port: number): string {
const portServices: Record<number, string> = {
20: 'FTP Data',
21: 'FTP Control',
22: 'SSH',
23: 'Telnet',
25: 'SMTP',
53: 'DNS',
80: 'HTTP',
110: 'POP3',
143: 'IMAP',
443: 'HTTPS',
993: 'IMAPS',
995: 'POP3S',
3306: 'MySQL',
5432: 'PostgreSQL',
6379: 'Redis',
8080: 'HTTP Alternative'
};
return portServices[port] || 'Unknown';
}

View File

@@ -1,24 +0,0 @@
export type InitialValuesType = {
portRange: 'well-known' | 'registered' | 'dynamic' | 'custom';
minPort: number;
maxPort: number;
count: number;
allowDuplicates: boolean;
sortResults: boolean;
separator: string;
};
export type PortRange = {
name: string;
min: number;
max: number;
description: string;
};
export type RandomPortResult = {
ports: number[];
range: PortRange;
count: number;
hasDuplicates: boolean;
isSorted: boolean;
};