mirror of
https://github.com/Xevion/rdap.git
synced 2025-12-16 20:12:45 -06:00
test: add comprehensive testing infrastructure with critical bug fixes
- Add Vitest testing framework with 88 passing tests across 4 test files - Fix critical entity validator bug (service array index) - Implement validator architecture with 'matched but invalid' state support - Add strict IPv4/IPv6 validation with detailed error messages - Add case-insensitive domain and ASN matching - Add explicit validator priority ordering (url→json→tld→ip→domain) - Add integration tests with real IANA registry validation - Add AutnumCard component for AS number display - Update dependencies: prettier 2.8.1→2.8.8 Test coverage: - helpers.test.ts: IPv4/IPv6 CIDR matching (27 tests) - helpers.asn.test.ts: ASN range validation (22 tests) - rdap.test.ts: Type detection with edge cases (36 tests) - rdap.integration.test.ts: Real IANA registry tests (3 tests) Bug fixes: - Entity validator now correctly uses service[1] for tags (0=email, 1=tags, 2=urls) - IPv4 validation rejects octets >255 with specific error messages - IPv6 validation checks for invalid hex chars and multiple :: - Domain regex supports multi-label domains (a.b.c.d.example.net) - Type detection priority prevents URL/JSON false matches as domains
This commit is contained in:
@@ -17,7 +17,7 @@ import {
|
||||
RegisterSchema,
|
||||
RootRegistryEnum,
|
||||
} from "@/schema";
|
||||
import { truncated, ipv4InCIDR, ipv6InCIDR } from "@/helpers";
|
||||
import { truncated, ipv4InCIDR, ipv6InCIDR, asnInRange } from "@/helpers";
|
||||
import type { ZodSchema } from "zod";
|
||||
import type { ParsedGeneric } from "@/components/lookup/Generic";
|
||||
import { Maybe, Result } from "true-myth";
|
||||
@@ -53,9 +53,12 @@ const useLookup = (warningHandler?: WarningHandler) => {
|
||||
|
||||
useCallback(async () => {
|
||||
if (currentType != null) return Maybe.just(currentType);
|
||||
const uri: Maybe<TargetType> = (await getTypeEasy(target)).mapOr(Maybe.nothing(), (type) => Maybe.just(type));
|
||||
const uri: Maybe<TargetType> = (await getTypeEasy(target)).mapOr(
|
||||
Maybe.nothing(),
|
||||
(type) => Maybe.just(type)
|
||||
);
|
||||
setUriType(uri);
|
||||
}, [target, currentType, getTypeEasy])
|
||||
}, [target, currentType, getTypeEasy]);
|
||||
|
||||
// Fetch & load a specific registry's data into memory.
|
||||
async function loadBootstrap(type: RootRegistryType, force = false) {
|
||||
@@ -85,17 +88,16 @@ const useLookup = (warningHandler?: WarningHandler) => {
|
||||
if (registryDataRef.current[type] == null) await loadBootstrap(type);
|
||||
const registry = registryDataRef.current[type];
|
||||
if (registry == null)
|
||||
throw new Error(
|
||||
`Could not load bootstrap data for ${type} registry.`
|
||||
);
|
||||
throw new Error(`Could not load bootstrap data for ${type} registry.`);
|
||||
return registry;
|
||||
}
|
||||
|
||||
async function getTypeEasy(target: string): Promise<Result<TargetType, Error>> {
|
||||
async function getTypeEasy(
|
||||
target: string
|
||||
): Promise<Result<TargetType, Error>> {
|
||||
return getType(target, getRegistry);
|
||||
}
|
||||
|
||||
|
||||
function getRegistryURL(
|
||||
type: RootRegistryType,
|
||||
lookupTarget: string
|
||||
@@ -120,7 +122,7 @@ const useLookup = (warningHandler?: WarningHandler) => {
|
||||
throw new Error(`No matching domain found.`);
|
||||
case "ip4": {
|
||||
// Extract the IP address without CIDR suffix for matching
|
||||
const ipAddress = lookupTarget.split('/')[0] ?? lookupTarget;
|
||||
const ipAddress = lookupTarget.split("/")[0] ?? lookupTarget;
|
||||
for (const bootstrapItem of bootstrap.services) {
|
||||
// bootstrapItem[0] contains CIDR ranges like ["1.0.0.0/8", "2.0.0.0/8"]
|
||||
if (bootstrapItem[0].some((cidr) => ipv4InCIDR(ipAddress, cidr))) {
|
||||
@@ -132,7 +134,7 @@ const useLookup = (warningHandler?: WarningHandler) => {
|
||||
}
|
||||
case "ip6": {
|
||||
// Extract the IP address without CIDR suffix for matching
|
||||
const ipAddress = lookupTarget.split('/')[0] ?? lookupTarget;
|
||||
const ipAddress = lookupTarget.split("/")[0] ?? lookupTarget;
|
||||
for (const bootstrapItem of bootstrap.services) {
|
||||
// bootstrapItem[0] contains CIDR ranges like ["2001:0200::/23", "2001:0400::/23"]
|
||||
if (bootstrapItem[0].some((cidr) => ipv6InCIDR(ipAddress, cidr))) {
|
||||
@@ -142,10 +144,29 @@ const useLookup = (warningHandler?: WarningHandler) => {
|
||||
}
|
||||
throw new Error(`No matching IPv6 registry found for ${lookupTarget}.`);
|
||||
}
|
||||
case "autnum": {
|
||||
// Extract ASN number from "AS12345" format
|
||||
const asnMatch = lookupTarget.match(/^AS(\d+)$/i);
|
||||
if (!asnMatch || !asnMatch[1]) {
|
||||
throw new Error(`Invalid ASN format: ${lookupTarget}`);
|
||||
}
|
||||
|
||||
const asnNumber = parseInt(asnMatch[1], 10);
|
||||
if (isNaN(asnNumber)) {
|
||||
throw new Error(`Invalid ASN number: ${lookupTarget}`);
|
||||
}
|
||||
|
||||
for (const bootstrapItem of bootstrap.services) {
|
||||
// bootstrapItem[0] contains ASN ranges like ["64512-65534", "13312-18431"]
|
||||
if (bootstrapItem[0].some((range) => asnInRange(asnNumber, range))) {
|
||||
url = getBestURL(bootstrapItem[1] as [string, ...string[]]);
|
||||
break typeSwitch;
|
||||
}
|
||||
}
|
||||
throw new Error(`No matching registry found for ${lookupTarget}.`);
|
||||
}
|
||||
case "entity":
|
||||
throw new Error(`No matching entity found.`);
|
||||
case "autnum":
|
||||
throw new Error(`No matching autnum found.`);
|
||||
default:
|
||||
throw new Error("Invalid lookup target provided.");
|
||||
}
|
||||
@@ -404,7 +425,14 @@ const useLookup = (warningHandler?: WarningHandler) => {
|
||||
}
|
||||
}
|
||||
|
||||
return { error, setTarget, setTargetType, submit, currentType: uriType, getType: getTypeEasy };
|
||||
return {
|
||||
error,
|
||||
setTarget,
|
||||
setTargetType,
|
||||
submit,
|
||||
currentType: uriType,
|
||||
getType: getTypeEasy,
|
||||
};
|
||||
};
|
||||
|
||||
export default useLookup;
|
||||
|
||||
Reference in New Issue
Block a user