PHP Classes

File: tests/test_routing.php

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   tests/test_routing.php   Download  
File: tests/test_routing.php
Role: Example script
Content type: text/plain
Description: Example script
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 13,235 bytes
 

Contents

Class file image Download
#!/usr/bin/env php <?php /** * Routing validation test * * Tests FtnRouter specificity and BinkpConfig uplink-selection logic to ensure * more specific routes override wildcards regardless of uplink order in config. * * Run: php tests/test_routing.php */ require_once __DIR__ . '/../vendor/autoload.php'; use BinktermPHP\FtnRouter; // ????????????????????????????????????????????????????????????????????????????? // Helpers // ????????????????????????????????????????????????????????????????????????????? $pass = 0; $fail = 0; function assert_route(FtnRouter $rt, string $address, ?string $expected, string $label): void { global $pass, $fail; $got = $rt->routeAddress($address); $ok = ($got === $expected); if ($ok) { $pass++; echo " PASS $label\n"; echo " $address ? " . ($got ?? 'null') . "\n"; } else { $fail++; echo " FAIL $label\n"; echo " $address expected=" . ($expected ?? 'null') . " got=" . ($got ?? 'null') . "\n"; } } /** * Simulate BinkpConfig::getUplinkForDestination() as fixed: * one combined router, most-specific pattern wins. */ function uplinkForDestination(array $uplinks, string $destAddr): ?array { $rt = new FtnRouter(); foreach ($uplinks as $uplink) { foreach ($uplink['networks'] as $network) { $rt->addRoute($network, $uplink['address']); } } $matchedAddress = $rt->routeAddress($destAddr); if ($matchedAddress === null) { return null; } foreach ($uplinks as $uplink) { if ($uplink['address'] === $matchedAddress) { return $uplink; } } return null; } /** * Simulate the OLD (broken) behaviour: first uplink that matches any pattern wins. */ function uplinkForDestinationBroken(array $uplinks, string $destAddr): ?array { foreach ($uplinks as $uplink) { $rt = new FtnRouter(); foreach ($uplink['networks'] as $network) { $rt->addRoute($network, $uplink['address']); } if ($rt->routeAddress($destAddr) !== null) { return $uplink; } } return null; } function assert_uplink(array $uplinks, string $address, ?string $expectedAddr, string $label): void { global $pass, $fail; $uplink = uplinkForDestination($uplinks, $address); $got = $uplink['address'] ?? null; $ok = ($got === $expectedAddr); if ($ok) { $pass++; echo " PASS $label\n"; echo " $address ? " . ($got ?? 'null') . "\n"; } else { $fail++; echo " FAIL $label\n"; echo " $address expected=" . ($expectedAddr ?? 'null') . " got=" . ($got ?? 'null') . "\n"; } } // ????????????????????????????????????????????????????????????????????????????? // Section 1: FtnRouter ? address parsing // ????????????????????????????????????????????????????????????????????????????? echo "\n1. Address parsing\n"; echo "?????????????????????????????????????????????????????????????????????????\n"; $rt = new FtnRouter(); $rt->addRoute('1:*/*', 'fidonet'); assert_route($rt, '1:100/200', 'fidonet', '4D address matches zone wildcard'); assert_route($rt, '1:100/200.0', 'fidonet', '5D point .0 matches zone wildcard'); assert_route($rt, '1:100/200.5', 'fidonet', '5D point .5 matches zone wildcard'); assert_route($rt, '2:100/200', null, 'Different zone returns null'); assert_route($rt, 'bad-address', null, 'Invalid address returns null'); // Domain-qualified address (foo@zone:net/node) $rt2 = new FtnRouter(); $rt2->addRoute('21:*/*', 'fsxnet'); assert_route($rt2, 'sysop@21:1/100', 'fsxnet', 'Domain-prefix stripped before routing'); // ????????????????????????????????????????????????????????????????????????????? // Section 2: FtnRouter ? specificity ordering (within one router) // ????????????????????????????????????????????????????????????????????????????? echo "\n2. Specificity ordering within a single router\n"; echo "?????????????????????????????????????????????????????????????????????????\n"; $rt = new FtnRouter(); $rt->addRoute('*:*/*', 'default'); $rt->addRoute('1:*/*', 'zone1'); $rt->addRoute('1:123/*', 'net123'); $rt->addRoute('1:123/456', 'node456'); $rt->addRoute('1:123/456.78', 'point78'); assert_route($rt, '2:999/1', 'default', 'Unmatched zone falls to default'); assert_route($rt, '1:999/1', 'zone1', 'Zone wildcard beats default'); assert_route($rt, '1:123/1', 'net123', 'Net route beats zone wildcard'); assert_route($rt, '1:123/456', 'node456', 'Node route beats net wildcard'); assert_route($rt, '1:123/456.0', 'node456', 'Point .0 resolves to node route'); assert_route($rt, '1:123/456.78', 'point78', 'Specific point beats node route'); assert_route($rt, '1:123/456.99', 'node456', 'Non-configured point falls to node route'); // ????????????????????????????????????????????????????????????????????????????? // Section 3: FtnRouter ? point wildcard "node.*" // ????????????????????????????????????????????????????????????????????????????? echo "\n3. Point wildcard (node.*)\n"; echo "?????????????????????????????????????????????????????????????????????????\n"; $rt = new FtnRouter(); $rt->addRoute('1:*/*', 'zone1'); $rt->addRoute('1:123/456', 'node456'); $rt->addRoute('1:123/456.*', 'allpoints'); $rt->addRoute('1:123/456.5', 'point5'); assert_route($rt, '1:123/456.5', 'point5', 'Specific point beats node.* wildcard'); assert_route($rt, '1:123/456.99', 'allpoints', 'Unknown point matches node.* wildcard'); assert_route($rt, '1:123/789', 'zone1', 'Different node falls to zone wildcard'); // ????????????????????????????????????????????????????????????????????????????? // Section 4: Multi-uplink ? specific route beats wildcard (the fixed bug) // ????????????????????????????????????????????????????????????????????????????? echo "\n4. Multi-uplink routing ? specific route overrides wildcard\n"; echo "?????????????????????????????????????????????????????????????????????????\n"; // Uplink order: wildcard FIRST (this was the failing case before the fix) $uplinks = [ ['address' => '1:1/1', 'me' => '1:100/1', 'networks' => ['1:*/*']], ['address' => '1:2/100', 'me' => '1:100/1.1', 'networks' => ['1:123/456.12']], ]; assert_uplink($uplinks, '1:123/456.12', '1:2/100', 'Specific point route beats earlier wildcard uplink'); assert_uplink($uplinks, '1:123/456.5', '1:1/1', 'Non-configured point falls to wildcard uplink'); assert_uplink($uplinks, '1:200/300', '1:1/1', 'General zone traffic routes to wildcard uplink'); assert_uplink($uplinks, '2:100/1', null, 'No matching route returns null'); // Uplink order: specific FIRST (was also correct before; must still work) $uplinksReversed = array_reverse($uplinks); assert_uplink($uplinksReversed, '1:123/456.12', '1:2/100', 'Specific point wins even when listed first'); assert_uplink($uplinksReversed, '1:200/300', '1:1/1', 'Wildcard uplink still catches general traffic'); // ????????????????????????????????????????????????????????????????????????????? // Section 5: Multi-uplink ? zone separation // ????????????????????????????????????????????????????????????????????????????? echo "\n5. Multi-zone uplink separation\n"; echo "?????????????????????????????????????????????????????????????????????????\n"; $uplinks = [ ['address' => 'hub1.fidonet', 'me' => '1:100/1', 'networks' => ['1:*/*']], ['address' => 'hub2.fsxnet', 'me' => '21:1/99', 'networks' => ['21:*/*']], ['address' => 'hub3.lovly', 'me' => '99:1/5', 'networks' => ['99:*/*']], ]; assert_uplink($uplinks, '1:261/38', 'hub1.fidonet', 'Zone 1 routes to fidonet uplink'); assert_uplink($uplinks, '21:1/100', 'hub2.fsxnet', 'Zone 21 routes to fsxnet uplink'); assert_uplink($uplinks, '99:123/45', 'hub3.lovly', 'Zone 99 routes to lovlynet uplink'); assert_uplink($uplinks, '5:1/1', null, 'Unknown zone returns null'); // ????????????????????????????????????????????????????????????????????????????? // Section 6: Demonstrate the OLD broken behaviour for comparison // ????????????????????????????????????????????????????????????????????????????? echo "\n6. Old (broken) behaviour comparison\n"; echo "?????????????????????????????????????????????????????????????????????????\n"; $uplinks = [ ['address' => '1:1/1', 'me' => '1:100/1', 'networks' => ['1:*/*']], ['address' => '1:2/100', 'me' => '1:100/1.1', 'networks' => ['1:123/456.12']], ]; $broken = uplinkForDestinationBroken($uplinks, '1:123/456.12'); $fixed = uplinkForDestination($uplinks, '1:123/456.12'); $brokenAddr = $broken['address'] ?? 'null'; $fixedAddr = $fixed['address'] ?? 'null'; echo " Route 1:123/456.12 with wildcard uplink listed first:\n"; echo " Old result : $brokenAddr " . ($brokenAddr === '1:1/1' ? "(wrong ? wildcard won)" : "") . "\n"; echo " New result : $fixedAddr " . ($fixedAddr === '1:2/100' ? "(correct ? specific wins)" : "") . "\n"; if ($brokenAddr === '1:1/1' && $fixedAddr === '1:2/100') { $pass++; echo " PASS Old behaviour was wrong, new behaviour is correct\n"; } else { $fail++; echo " FAIL Unexpected values ? review test\n"; } // ????????????????????????????????????????????????????????????????????????????? // Summary // ????????????????????????????????????????????????????????????????????????????? $total = $pass + $fail; echo "\n???????????????????????????????????????????????????????????????????????????\n"; echo "Results: $pass/$total passed"; if ($fail > 0) { echo ", $fail FAILED"; } echo "\n"; exit($fail > 0 ? 1 : 0);