Browse Source

Fix ip range pick initial range; add test

Owen 5 tháng trước cách đây
mục cha
commit
a418195b28
2 tập tin đã thay đổi với 196 bổ sung6 xóa
  1. 183 0
      server/lib/ip.test.ts
  2. 13 6
      server/lib/ip.ts

+ 183 - 0
server/lib/ip.test.ts

@@ -0,0 +1,183 @@
+import { cidrToRange, findNextAvailableCidr } from "./ip";
+
+/**
+ * Compares two objects for deep equality
+ * @param actual The actual value to test
+ * @param expected The expected value to compare against
+ * @param message The message to display if assertion fails
+ * @throws Error if objects are not equal
+ */
+export function assertEqualsObj<T>(actual: T, expected: T, message: string): void {
+    const actualStr = JSON.stringify(actual);
+    const expectedStr = JSON.stringify(expected);
+    if (actualStr !== expectedStr) {
+        throw new Error(`${message}\nExpected: ${expectedStr}\nActual: ${actualStr}`);
+    }
+}
+
+/**
+ * Compares two primitive values for equality
+ * @param actual The actual value to test
+ * @param expected The expected value to compare against
+ * @param message The message to display if assertion fails
+ * @throws Error if values are not equal
+ */
+export function assertEquals<T>(actual: T, expected: T, message: string): void {
+    if (actual !== expected) {
+        throw new Error(`${message}\nExpected: ${expected}\nActual: ${actual}`);
+    }
+}
+
+/**
+ * Tests if a function throws an expected error
+ * @param fn The function to test
+ * @param expectedError The expected error message or part of it
+ * @param message The message to display if assertion fails
+ * @throws Error if function doesn't throw or throws unexpected error
+ */
+export function assertThrows(
+    fn: () => void,
+    expectedError: string,
+    message: string
+): void {
+    try {
+        fn();
+        throw new Error(`${message}: Expected to throw "${expectedError}"`);
+    } catch (error) {
+        if (!(error instanceof Error)) {
+            throw new Error(`${message}\nUnexpected error type: ${typeof error}`);
+        }
+        
+        if (!error.message.includes(expectedError)) {
+            throw new Error(
+                `${message}\nExpected error: ${expectedError}\nActual error: ${error.message}`
+            );
+        }
+    }
+}
+
+
+// Test cases
+function testFindNextAvailableCidr() {
+    console.log("Running findNextAvailableCidr tests...");
+    
+    // Test 1: Basic IPv4 allocation
+    {
+        const existing = ["10.0.0.0/16", "10.1.0.0/16"];
+        const result = findNextAvailableCidr(existing, 16, "10.0.0.0/8");
+        assertEquals(result, "10.2.0.0/16", "Basic IPv4 allocation failed");
+    }
+
+    // Test 2: Finding gap between allocations
+    {
+        const existing = ["10.0.0.0/16", "10.2.0.0/16"];
+        const result = findNextAvailableCidr(existing, 16, "10.0.0.0/8");
+        assertEquals(result, "10.1.0.0/16", "Finding gap between allocations failed");
+    }
+
+    // Test 3: No available space
+    {
+        const existing = ["10.0.0.0/8"];
+        const result = findNextAvailableCidr(existing, 8, "10.0.0.0/8");
+        assertEquals(result, null, "No available space test failed");
+    }
+
+    // // Test 4: IPv6 allocation
+    // {
+    //     const existing = ["2001:db8::/32", "2001:db8:1::/32"];
+    //     const result = findNextAvailableCidr(existing, 32, "2001:db8::/16");
+    //     assertEquals(result, "2001:db8:2::/32", "Basic IPv6 allocation failed");
+    // }
+
+    // // Test 5: Mixed IP versions
+    // {
+    //     const existing = ["10.0.0.0/16", "2001:db8::/32"];
+    //     assertThrows(
+    //         () => findNextAvailableCidr(existing, 16),
+    //         "All CIDRs must be of the same IP version",
+    //         "Mixed IP versions test failed"
+    //     );
+    // }
+
+    // Test 6: Empty input
+    {
+        const existing: string[] = [];
+        const result = findNextAvailableCidr(existing, 16);
+        assertEquals(result, null, "Empty input test failed");
+    }
+
+    // Test 7: Block size alignment
+    {
+        const existing = ["10.0.0.0/24"];
+        const result = findNextAvailableCidr(existing, 24, "10.0.0.0/16");
+        assertEquals(result, "10.0.1.0/24", "Block size alignment test failed");
+    }
+
+    // Test 8: Block size alignment
+    {
+        const existing: string[] = [];
+        const result = findNextAvailableCidr(existing, 24, "10.0.0.0/16");
+        assertEquals(result, "10.0.0.0/24", "Block size alignment test failed");
+    }
+
+    // Test 9: Large block size request
+    {
+        const existing = ["10.0.0.0/24", "10.0.1.0/24"];
+        const result = findNextAvailableCidr(existing, 16, "10.0.0.0/16");
+        assertEquals(result, null, "Large block size request test failed");
+    }
+
+    console.log("All findNextAvailableCidr tests passed!");
+}
+
+// function testCidrToRange() {
+//     console.log("Running cidrToRange tests...");
+
+//     // Test 1: Basic IPv4 conversion
+//     {
+//         const result = cidrToRange("192.168.0.0/24");
+//         assertEqualsObj(result, {
+//             start: BigInt("3232235520"),
+//             end: BigInt("3232235775")
+//         }, "Basic IPv4 conversion failed");
+//     }
+
+//     // Test 2: IPv6 conversion
+//     {
+//         const result = cidrToRange("2001:db8::/32");
+//         assertEqualsObj(result, {
+//             start: BigInt("42540766411282592856903984951653826560"),
+//             end: BigInt("42540766411282592875350729025363378175")
+//         }, "IPv6 conversion failed");
+//     }
+
+//     // Test 3: Invalid prefix length
+//     {
+//         assertThrows(
+//             () => cidrToRange("192.168.0.0/33"),
+//             "Invalid prefix length for IPv4",
+//             "Invalid IPv4 prefix test failed"
+//         );
+//     }
+
+//     // Test 4: Invalid IPv6 prefix
+//     {
+//         assertThrows(
+//             () => cidrToRange("2001:db8::/129"),
+//             "Invalid prefix length for IPv6",
+//             "Invalid IPv6 prefix test failed"
+//         );
+//     }
+
+//     console.log("All cidrToRange tests passed!");
+// }
+
+// Run all tests
+try {
+    // testCidrToRange();
+    testFindNextAvailableCidr();
+    console.log("All tests passed successfully!");
+} catch (error) {
+    console.error("Test failed:", error);
+    process.exit(1);
+}

+ 13 - 6
server/lib/ip.ts

@@ -100,7 +100,7 @@ function bigIntToIp(num: bigint, version: IPVersion): string {
 /**
  * Converts CIDR to IP range
  */
-function cidrToRange(cidr: string): IPRange {
+export function cidrToRange(cidr: string): IPRange {
     const [ip, prefix] = cidr.split('/');
     const version = detectIpVersion(ip);
     const prefixBits = parseInt(prefix);
@@ -132,15 +132,22 @@ export function findNextAvailableCidr(
     blockSize: number,
     startCidr?: string
 ): string | null {
-    if (existingCidrs.length === 0) return null;
+
+    if (!startCidr && existingCidrs.length === 0) {
+        return null;
+    }
+        
+    // If no existing CIDRs, use the IP version from startCidr
+    const version = startCidr 
+        ? detectIpVersion(startCidr.split('/')[0])
+        : 4; // Default to IPv4 if no startCidr provided
     
-    // Determine IP version from first CIDR
-    const version = detectIpVersion(existingCidrs[0].split('/')[0]);
     // Use appropriate default startCidr if none provided
     startCidr = startCidr || (version === 4 ? "0.0.0.0/0" : "::/0");
     
-    // Ensure all CIDRs are same version
-    if (existingCidrs.some(cidr => detectIpVersion(cidr.split('/')[0]) !== version)) {
+    // If there are existing CIDRs, ensure all are same version
+    if (existingCidrs.length > 0 && 
+        existingCidrs.some(cidr => detectIpVersion(cidr.split('/')[0]) !== version)) {
         throw new Error('All CIDRs must be of the same IP version');
     }