refactor: reorganize project structure and consolidate network utilities

Major restructuring to improve codebase organization:
- Moved test files to src/__tests__/ directory
- Reorganized UI components from src/components/common to src/components/ui
- Consolidated RDAP-related code into src/rdap/ directory structure
- Split network helpers into modular files (asn.ts, ipv4.ts, ipv6.ts)
- Created centralized exports via src/lib/network/index.ts
- Migrated utility functions from src/helpers.ts to src/lib/utils.ts
- Separated RDAP services into dedicated modules (rdap-api.ts, registry.ts, url-resolver.ts)

This refactoring enhances code maintainability and follows a clearer separation of concerns.
This commit is contained in:
2025-10-22 12:21:00 -05:00
parent 7073936e6c
commit 0e9336df1d
36 changed files with 926 additions and 658 deletions

View File

@@ -0,0 +1,144 @@
import { describe, it, expect } from "vitest";
import { asnInRange } from "@/lib/network";
describe("asnInRange", () => {
describe("basic matching", () => {
it("should match ASN in single number range", () => {
expect(asnInRange(100, "100-200")).toBe(true);
expect(asnInRange(150, "100-200")).toBe(true);
expect(asnInRange(200, "100-200")).toBe(true);
});
it("should not match ASN outside single number range", () => {
expect(asnInRange(99, "100-200")).toBe(false);
expect(asnInRange(201, "100-200")).toBe(false);
});
it("should match ASN at boundaries", () => {
expect(asnInRange(1, "1-10")).toBe(true);
expect(asnInRange(10, "1-10")).toBe(true);
});
it("should match single ASN (same start and end)", () => {
expect(asnInRange(12345, "12345-12345")).toBe(true);
});
it("should not match single ASN outside", () => {
expect(asnInRange(12346, "12345-12345")).toBe(false);
expect(asnInRange(12344, "12345-12345")).toBe(false);
});
});
describe("real-world ASN ranges from IANA", () => {
// ARIN ranges
it("should match ARIN ASN ranges", () => {
// ARIN typically has ranges like 1-1876, 1902-2042, etc.
expect(asnInRange(100, "1-1876")).toBe(true);
expect(asnInRange(1876, "1-1876")).toBe(true);
expect(asnInRange(2000, "1902-2042")).toBe(true);
});
// RIPE ranges
it("should match RIPE ASN ranges", () => {
// RIPE has ranges like 1877-1901, 2043-2109, etc.
expect(asnInRange(1900, "1877-1901")).toBe(true);
expect(asnInRange(2100, "2043-2109")).toBe(true);
});
// APNIC ranges
it("should match APNIC ASN ranges", () => {
// APNIC has ranges like 2110-2136, 4608-4864, etc.
expect(asnInRange(2120, "2110-2136")).toBe(true);
expect(asnInRange(4700, "4608-4864")).toBe(true);
});
// Well-known ASNs
it("should match Google ASN (AS15169)", () => {
// Google's ASN 15169 falls in range that includes it
expect(asnInRange(15169, "15000-16000")).toBe(true);
expect(asnInRange(15169, "15169-15169")).toBe(true);
expect(asnInRange(15169, "15360-16383")).toBe(false); // Not in this range
});
it("should match Cloudflare ASN (AS13335)", () => {
// Cloudflare's ASN 13335 should be in ARIN range 13312-18431
expect(asnInRange(13335, "13312-18431")).toBe(true);
});
it("should match Amazon ASN (AS16509)", () => {
// Amazon's ASN 16509
expect(asnInRange(16509, "15360-16383")).toBe(false);
expect(asnInRange(16509, "16384-18431")).toBe(true);
});
});
describe("private ASN ranges", () => {
it("should match 16-bit private ASN range", () => {
// Private range: 64512-65534
expect(asnInRange(64512, "64512-65534")).toBe(true);
expect(asnInRange(65000, "64512-65534")).toBe(true);
expect(asnInRange(65534, "64512-65534")).toBe(true);
});
it("should not match outside private range", () => {
expect(asnInRange(64511, "64512-65534")).toBe(false);
expect(asnInRange(65535, "64512-65534")).toBe(false);
});
it("should match 32-bit private ASN range", () => {
// Private range: 4200000000-4294967294
expect(asnInRange(4200000000, "4200000000-4294967294")).toBe(true);
expect(asnInRange(4250000000, "4200000000-4294967294")).toBe(true);
expect(asnInRange(4294967294, "4200000000-4294967294")).toBe(true);
});
});
describe("large ASN numbers (32-bit)", () => {
it("should handle large ASN numbers", () => {
expect(asnInRange(4200000000, "4200000000-4294967294")).toBe(true);
expect(asnInRange(4294967295, "4200000000-4294967294")).toBe(false);
});
it("should handle ASNs near 32-bit limit", () => {
const maxAsn = 4294967295;
expect(asnInRange(maxAsn, `${maxAsn}-${maxAsn}`)).toBe(true);
expect(asnInRange(maxAsn - 1, `${maxAsn}-${maxAsn}`)).toBe(false);
});
});
describe("edge cases", () => {
it("should handle invalid range format", () => {
expect(asnInRange(100, "invalid")).toBe(false);
expect(asnInRange(100, "100")).toBe(false);
expect(asnInRange(100, "100-")).toBe(false);
expect(asnInRange(100, "-100")).toBe(false);
});
it("should handle negative numbers gracefully", () => {
expect(asnInRange(-1, "1-100")).toBe(false);
expect(asnInRange(50, "-100-100")).toBe(false);
});
it("should handle reversed ranges (end < start)", () => {
// Invalid range where end is less than start
expect(asnInRange(150, "200-100")).toBe(false);
});
it("should handle zero", () => {
expect(asnInRange(0, "0-100")).toBe(true);
expect(asnInRange(0, "1-100")).toBe(false);
});
});
describe("ASN number parsing", () => {
it("should handle number inputs", () => {
expect(asnInRange(12345, "10000-20000")).toBe(true);
});
it("should handle very large numbers", () => {
const largeAsn = 4000000000;
expect(asnInRange(largeAsn, "3000000000-4294967295")).toBe(true);
expect(asnInRange(largeAsn, "1-1000000000")).toBe(false);
});
});
});

View File

@@ -0,0 +1,170 @@
import { describe, it, expect } from "vitest";
import { ipv4InCIDR, ipv6InCIDR } from "@/lib/network";
describe("ipv4InCIDR", () => {
describe("basic matching", () => {
it("should match IP in /8 network", () => {
expect(ipv4InCIDR("8.8.8.8", "8.0.0.0/8")).toBe(true);
expect(ipv4InCIDR("8.255.255.255", "8.0.0.0/8")).toBe(true);
expect(ipv4InCIDR("8.0.0.0", "8.0.0.0/8")).toBe(true);
});
it("should not match IP outside /8 network", () => {
expect(ipv4InCIDR("9.0.0.0", "8.0.0.0/8")).toBe(false);
expect(ipv4InCIDR("7.255.255.255", "8.0.0.0/8")).toBe(false);
});
it("should match IP in /16 network", () => {
expect(ipv4InCIDR("192.168.1.1", "192.168.0.0/16")).toBe(true);
expect(ipv4InCIDR("192.168.255.255", "192.168.0.0/16")).toBe(true);
expect(ipv4InCIDR("192.168.0.0", "192.168.0.0/16")).toBe(true);
});
it("should not match IP outside /16 network", () => {
expect(ipv4InCIDR("192.169.1.1", "192.168.0.0/16")).toBe(false);
expect(ipv4InCIDR("192.167.1.1", "192.168.0.0/16")).toBe(false);
});
it("should match IP in /24 network", () => {
expect(ipv4InCIDR("192.168.1.1", "192.168.1.0/24")).toBe(true);
expect(ipv4InCIDR("192.168.1.255", "192.168.1.0/24")).toBe(true);
expect(ipv4InCIDR("192.168.1.0", "192.168.1.0/24")).toBe(true);
});
it("should not match IP outside /24 network", () => {
expect(ipv4InCIDR("192.168.2.1", "192.168.1.0/24")).toBe(false);
expect(ipv4InCIDR("192.168.0.255", "192.168.1.0/24")).toBe(false);
});
it("should match IP in /32 network (single host)", () => {
expect(ipv4InCIDR("192.168.1.1", "192.168.1.1/32")).toBe(true);
});
it("should not match different IP in /32 network", () => {
expect(ipv4InCIDR("192.168.1.2", "192.168.1.1/32")).toBe(false);
});
});
describe("real-world RDAP bootstrap ranges", () => {
// ARIN ranges (from IANA bootstrap data)
it("should match Google DNS (8.8.8.8) in ARIN range", () => {
expect(ipv4InCIDR("8.8.8.8", "8.0.0.0/8")).toBe(true);
});
// APNIC ranges
it("should match Cloudflare DNS (1.1.1.1) in APNIC range", () => {
expect(ipv4InCIDR("1.1.1.1", "1.0.0.0/8")).toBe(true);
});
// Private ranges
it("should match private IPs in their ranges", () => {
expect(ipv4InCIDR("10.0.0.1", "10.0.0.0/8")).toBe(true);
expect(ipv4InCIDR("172.16.0.1", "172.16.0.0/12")).toBe(true);
expect(ipv4InCIDR("192.168.0.1", "192.168.0.0/16")).toBe(true);
});
});
describe("edge cases", () => {
it("should handle /0 (all IPs)", () => {
expect(ipv4InCIDR("0.0.0.0", "0.0.0.0/0")).toBe(true);
expect(ipv4InCIDR("255.255.255.255", "0.0.0.0/0")).toBe(true);
expect(ipv4InCIDR("192.168.1.1", "0.0.0.0/0")).toBe(true);
});
it("should handle invalid CIDR notation", () => {
expect(ipv4InCIDR("192.168.1.1", "invalid")).toBe(false);
expect(ipv4InCIDR("192.168.1.1", "192.168.1.0/-1")).toBe(false);
expect(ipv4InCIDR("192.168.1.1", "192.168.1.0/33")).toBe(false);
});
it("should handle malformed IPs", () => {
expect(ipv4InCIDR("invalid", "192.168.1.0/24")).toBe(false);
});
it("should handle partial IPs (wrong number of octets)", () => {
expect(ipv4InCIDR("8.8", "8.0.0.0/8")).toBe(false);
expect(ipv4InCIDR("192.168.1", "192.168.1.0/24")).toBe(false);
expect(ipv4InCIDR("192.168.1.1.1", "192.168.1.0/24")).toBe(false);
});
});
});
describe("ipv6InCIDR", () => {
describe("basic matching", () => {
it("should match IPv6 in /32 network", () => {
expect(ipv6InCIDR("2001:db8::", "2001:db8::/32")).toBe(true);
expect(ipv6InCIDR("2001:db8:1234::", "2001:db8::/32")).toBe(true);
expect(ipv6InCIDR("2001:db8:ffff:ffff:ffff:ffff:ffff:ffff", "2001:db8::/32")).toBe(
true
);
});
it("should not match IPv6 outside /32 network", () => {
expect(ipv6InCIDR("2001:db9::", "2001:db8::/32")).toBe(false);
expect(ipv6InCIDR("2001:db7::", "2001:db8::/32")).toBe(false);
});
it("should match IPv6 in /64 network", () => {
expect(ipv6InCIDR("2001:db8:1234:5678::", "2001:db8:1234:5678::/64")).toBe(true);
expect(ipv6InCIDR("2001:db8:1234:5678:abcd::", "2001:db8:1234:5678::/64")).toBe(true);
expect(
ipv6InCIDR("2001:db8:1234:5678:ffff:ffff:ffff:ffff", "2001:db8:1234:5678::/64")
).toBe(true);
});
it("should not match IPv6 outside /64 network", () => {
expect(ipv6InCIDR("2001:db8:1234:5679::", "2001:db8:1234:5678::/64")).toBe(false);
});
it("should match IPv6 in /128 network (single host)", () => {
expect(ipv6InCIDR("2001:db8::1", "2001:db8::1/128")).toBe(true);
});
it("should not match different IPv6 in /128 network", () => {
expect(ipv6InCIDR("2001:db8::2", "2001:db8::1/128")).toBe(false);
});
});
describe("real-world RDAP bootstrap ranges", () => {
it("should match Google IPv6 DNS in ARIN range", () => {
// Google DNS: 2001:4860:4860::8888
expect(ipv6InCIDR("2001:4860:4860::8888", "2001:4860::/32")).toBe(true);
});
it("should match Cloudflare IPv6 DNS in APNIC range", () => {
// Cloudflare DNS: 2606:4700:4700::1111
expect(ipv6InCIDR("2606:4700:4700::1111", "2606:4700::/32")).toBe(true);
});
});
describe("IPv6 shorthand notation", () => {
it("should handle :: notation correctly", () => {
expect(ipv6InCIDR("2001:db8::1", "2001:db8::/32")).toBe(true);
expect(ipv6InCIDR("::1", "::1/128")).toBe(true);
expect(ipv6InCIDR("::", "::/128")).toBe(true);
});
it("should handle expanded vs compressed notation", () => {
expect(ipv6InCIDR("2001:0db8:0000:0000:0000:0000:0000:0001", "2001:db8::/32")).toBe(
true
);
expect(ipv6InCIDR("2001:db8::1", "2001:0db8:0000:0000:0000:0000:0000:0000/32")).toBe(
true
);
});
});
describe("edge cases", () => {
it("should handle invalid CIDR notation", () => {
expect(ipv6InCIDR("2001:db8::1", "invalid")).toBe(false);
expect(ipv6InCIDR("2001:db8::1", "2001:db8::/-1")).toBe(false);
expect(ipv6InCIDR("2001:db8::1", "2001:db8::/129")).toBe(false);
});
it("should handle malformed IPv6", () => {
expect(ipv6InCIDR("invalid", "2001:db8::/32")).toBe(false);
expect(ipv6InCIDR("zzzz::1", "2001:db8::/32")).toBe(false);
expect(ipv6InCIDR("2001:xyz::1", "2001:db8::/32")).toBe(false);
});
});
});

View File

@@ -0,0 +1,53 @@
// @vitest-environment node
import { describe, it, expect } from "vitest";
import { getType } from "@/rdap/utils";
import type { Register, RootRegistryType } from "@/rdap/schemas";
import { registryURLs } from "@/rdap/constants";
// Integration tests that fetch real IANA bootstrap data
// These are slower but test against actual registries
// Note: Uses Node.js environment instead of happy-dom to allow real network requests
const registryCache = new Map<RootRegistryType, Register>();
async function getRealRegistry(type: RootRegistryType): Promise<Register> {
if (registryCache.has(type)) {
return registryCache.get(type)!;
}
const response = await fetch(registryURLs[type]);
if (!response.ok) {
throw new Error(`Failed to fetch ${type} registry: ${response.statusText}`);
}
const data = (await response.json()) as Register;
registryCache.set(type, data);
return data;
}
describe("getType - Integration tests with real registries", () => {
it("should detect entity with real entity registry", async () => {
// Test with a known entity tag (RIPE)
const result = await getType("TEST-RIPE", getRealRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("entity");
}
}, 10000); // Longer timeout for network call
it("should detect entity with ARIN tag", async () => {
const result = await getType("NET-ARIN", getRealRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("entity");
}
}, 10000);
it("should not detect invalid entity tag", async () => {
const result = await getType("INVALID-NOTREAL", getRealRegistry);
// Should either error or detect as something else, but not entity
if (result.isOk) {
expect(result.value).not.toBe("entity");
}
}, 10000);
});

View File

@@ -0,0 +1,406 @@
import { describe, it, expect, vi } from "vitest";
import { getType } from "@/rdap/utils";
import type { Register } from "@/rdap/schemas";
// Mock registry getter (matches real IANA structure: [email, tags, urls])
const mockRegistry: Register = {
description: "Test registry",
publication: "2024-01-01",
version: "1.0",
services: [
[
["test@example.com"], // email
["RIPE", "APNIC"], // tags
["https://rdap.example.com/"], // urls
],
],
};
const mockGetRegistry = vi.fn(() => Promise.resolve(mockRegistry));
describe("getType - IP address detection", () => {
describe("IPv4 detection", () => {
it("should detect standard IPv4 addresses", async () => {
const result = await getType("192.168.1.1", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("ip4");
}
});
it("should detect IPv4 with CIDR notation", async () => {
const result = await getType("192.168.1.0/24", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("ip4");
}
});
it("should detect various IPv4 addresses", async () => {
const ips = [
"8.8.8.8",
"1.1.1.1",
"10.0.0.1",
"172.16.0.1",
"255.255.255.255",
"0.0.0.0",
];
for (const ip of ips) {
const result = await getType(ip, mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("ip4");
}
}
});
it("should detect IPv4 with various CIDR prefixes", async () => {
const cidrs = [
"192.168.1.0/8",
"10.0.0.0/16",
"172.16.0.0/12",
"8.8.8.0/24",
"1.1.1.1/32",
];
for (const cidr of cidrs) {
const result = await getType(cidr, mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("ip4");
}
}
});
});
describe("IPv6 detection", () => {
it("should detect standard IPv6 addresses", async () => {
const result = await getType("2001:db8::1", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("ip6");
}
});
it("should detect IPv6 with CIDR notation", async () => {
const result = await getType("2001:db8::/32", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("ip6");
}
});
it("should detect various IPv6 addresses", async () => {
const ips = [
"2001:4860:4860::8888", // Google DNS
"2606:4700:4700::1111", // Cloudflare DNS
"::1", // Localhost
"::", // All zeros
"fe80::1", // Link-local
"2001:db8:85a3::8a2e:370:7334", // Full notation
];
for (const ip of ips) {
const result = await getType(ip, mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("ip6");
}
}
});
it("should detect IPv6 with various CIDR prefixes", async () => {
const cidrs = ["2001:db8::/32", "2001:4860::/32", "fe80::/10", "::1/128"];
for (const cidr of cidrs) {
const result = await getType(cidr, mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("ip6");
}
}
});
});
});
describe("getType - Domain detection", () => {
it("should detect standard domains", async () => {
const result = await getType("example.com", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("domain");
}
});
it("should detect various domain formats", async () => {
const domains = [
"google.com",
"www.example.com",
"sub.domain.example.com",
"test-domain.com",
"example123.org",
"a.b.c.d.example.net",
];
for (const domain of domains) {
const result = await getType(domain, mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("domain");
}
}
});
});
describe("getType - ASN detection", () => {
it("should detect standard ASN format", async () => {
const result = await getType("AS12345", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("autnum");
}
});
it("should detect various ASN formats", async () => {
const asns = [
"AS1",
"AS13335", // Cloudflare
"AS15169", // Google
"AS8075", // Microsoft
"AS16509", // Amazon
"AS999999",
];
for (const asn of asns) {
const result = await getType(asn, mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("autnum");
}
}
});
});
describe("getType - TLD detection", () => {
it("should detect TLD format", async () => {
const result = await getType(".com", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("tld");
}
});
it("should detect various TLDs", async () => {
const tlds = [".com", ".org", ".net", ".dev", ".io", ".ai", ".co"];
for (const tld of tlds) {
const result = await getType(tld, mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("tld");
}
}
});
});
describe("getType - URL detection", () => {
it("should detect HTTP URLs", async () => {
const result = await getType("http://example.com", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("url");
}
});
it("should detect HTTPS URLs", async () => {
const result = await getType("https://example.com", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("url");
}
});
it("should detect RDAP URLs", async () => {
const urls = [
"https://rdap.arin.net/registry/ip/8.8.8.8",
"http://rdap.apnic.net/ip/1.1.1.1",
"https://rdap.org/domain/example.com",
];
for (const url of urls) {
const result = await getType(url, mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("url");
}
}
});
});
describe("getType - JSON detection", () => {
it("should detect JSON objects", async () => {
const result = await getType('{"objectClassName":"domain"}', mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("json");
}
});
it("should detect various JSON formats", async () => {
const jsons = [
"{}",
'{"key": "value"}',
'{"objectClassName":"ip network"}',
'{"handle":"TEST"}',
];
for (const json of jsons) {
const result = await getType(json, mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("json");
}
}
});
});
describe("getType - Invalid inputs", () => {
it("should return error for empty string", async () => {
const result = await getType("", mockGetRegistry);
expect(result.isErr).toBe(true);
});
it("should return error for unrecognized format", async () => {
const result = await getType("not-a-valid-input!!@@##", mockGetRegistry);
expect(result.isErr).toBe(true);
});
describe("Invalid IPv4 addresses", () => {
it("should return error for IPv4 with octet > 255", async () => {
const result = await getType("256.1.1.1", mockGetRegistry);
expect(result.isErr).toBe(true);
if (result.isErr) {
expect(result.error.message).toContain("Invalid IPv4 address");
expect(result.error.message).toContain("octet");
}
});
it("should return error for IPv4 with octet 999", async () => {
const result = await getType("192.999.1.1", mockGetRegistry);
expect(result.isErr).toBe(true);
if (result.isErr) {
expect(result.error.message).toContain("Invalid IPv4 address");
}
});
it("should return error for IPv4 with invalid CIDR prefix", async () => {
const result = await getType("192.168.1.1/33", mockGetRegistry);
expect(result.isErr).toBe(true);
if (result.isErr) {
expect(result.error.message).toContain("CIDR prefix");
}
});
it("should return error for IPv4 with negative CIDR", async () => {
const result = await getType("192.168.1.1/-1", mockGetRegistry);
expect(result.isErr).toBe(true);
});
});
describe("Invalid IPv6 addresses", () => {
it("should return error for IPv6 with multiple ::", async () => {
const result = await getType("2001::db8::1", mockGetRegistry);
expect(result.isErr).toBe(true);
if (result.isErr) {
expect(result.error.message).toContain("::");
}
});
it("should return error for IPv6 with invalid CIDR prefix", async () => {
const result = await getType("2001:db8::1/129", mockGetRegistry);
expect(result.isErr).toBe(true);
if (result.isErr) {
expect(result.error.message).toContain("CIDR prefix");
}
});
it("should not match completely invalid hex strings as IPv6", async () => {
// "gggg" doesn't match the basic IPv6 pattern, so it won't be detected as IPv6
const result = await getType("gggg::1", mockGetRegistry);
expect(result.isErr).toBe(true);
// Won't have IPv6-specific error since it didn't match the pattern
if (result.isErr) {
expect(result.error.message).toContain("No patterns matched");
}
});
});
});
describe("getType - Type detection priority", () => {
it("should detect URL before domain", async () => {
const result = await getType("https://example.com", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("url");
}
});
it("should detect JSON before domain", async () => {
const result = await getType('{"key":"value"}', mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("json");
}
});
it("should detect TLD before domain", async () => {
const result = await getType(".com", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("tld");
}
});
it("should detect IP before domain", async () => {
const result = await getType("8.8.8.8", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("ip4");
}
});
});
describe("getType - Case sensitivity", () => {
it("should detect uppercase domains", async () => {
const result = await getType("GOOGLE.COM", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("domain");
}
});
it("should detect mixed case domains", async () => {
const result = await getType("GoOgLe.CoM", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("domain");
}
});
it("should detect lowercase ASN", async () => {
const result = await getType("as12345", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("autnum");
}
});
it("should detect uppercase ASN", async () => {
const result = await getType("AS12345", mockGetRegistry);
expect(result.isOk).toBe(true);
if (result.isOk) {
expect(result.value).toBe("autnum");
}
});
});