permission = $p; $n->subusers_node_key = $k; return $n; } } } namespace objects { class logs_o { public function add($a, $b, $c, $d, $e, $f) {} } class users_o { public $id = 1; public $group_id; public function __construct() { $this->group_id = new class { public function value() { return 2; } }; } public function hasPermission($p) { echo "[DB] Checking permission: $p\n"; return $p === 'allowed_perm'; } } class subusers_o { public $id = 10; public function hasPermission($node) { echo "[DB] Checking subuser node: " . $node->name . "\n"; return $node->name === 'ALLOWED_NODE'; } } } namespace { // Include Redis classes first so we can mock REDIS_CONFIG global $REDIS_CONFIG; $REDIS_CONFIG = [ 'host' => getenv('REDIS_CONFIG_HOST') ?: 'redis', 'user' => getenv('REDIS_CONFIG_USER') ?: 'default', 'database' => (int)(getenv('REDIS_CONFIG_DATABASE') ?: 0), 'password' => getenv('REDIS_CONFIG_PASSWORD') ?: '', 'port' => (int)(getenv('REDIS_CONFIG_PORT') ?: 6379), ]; require_once WD . '/vendor/autoload.php'; require_once WD . '/interfaces/redis_i.php'; require_once WD . '/traits/redis_t.php'; require_once WD . '/classes/redis.php'; require_once WD . '/traits/route_t.php'; // Set the redis constant global $response; $response = new classes\response(); try { $redis_instance = (new classes\redis())->connect(); if (!defined('redis')) { define("redis", $redis_instance); } } catch (Exception $e) { die("Could not connect to Redis: " . $e->getMessage()); } use traits\route_t; use classes\authentication; use classes\permission_node; use objects\users_o; use objects\subusers_o; class TestRoute { use route_t; public function testEval($p, $c = null, $t = false) { return $this->evaluatePermission($p, $c, $t); } // Mocking resolveCustomerNumberForSubuser as it's private and uses headers private function resolveCustomerNumberForSubuser($auth, ?int $customer_number = null): ?int { return $customer_number ?? 999; } } $route = new TestRoute(); function assertResult($actual, $expected, $msg) { if ($actual === $expected) { echo "\033[32m✔ $msg\033[0m\n"; } else { echo "\033[31m✖ $msg (Expected " . var_export($expected, true) . ", got " . var_export($actual, true) . ")\033[0m\n"; exit(1); } } // 1. Test classic user permission caching echo "--- Testing Classic User Permission Caching ---\n"; $user = new users_o(); authentication::$user = $user; // Clear previous cache if any redis->clear_permission("user:1:allowed_perm"); redis->clear_permission("user:1:denied_perm"); echo "First call (should hit DB):\n"; assertResult($route->testEval('allowed_perm'), true, "User should have allowed_perm"); echo "Second call (should hit memory cache, no DB output expected):\n"; assertResult($route->testEval('allowed_perm'), true, "User should still have allowed_perm (from memory)"); // Clear memory cache by creating new route object $route = new TestRoute(); echo "Third call after new object (should hit Redis, no DB output expected):\n"; assertResult($route->testEval('allowed_perm'), true, "User should still have allowed_perm (from Redis)"); // Test denied perm echo "\nTesting denied perm:\n"; assertResult($route->testEval('denied_perm'), false, "User should NOT have denied_perm"); $route = new TestRoute(); echo "Call after new object (should hit Redis, no DB output expected):\n"; assertResult($route->testEval('denied_perm'), false, "User should still NOT have denied_perm (from Redis)"); // 2. Test subuser node caching echo "\n--- Testing Subuser Node Caching ---\n"; $subuser = new subusers_o(); authentication::$subuser = $subuser; $allowedNode = new stdClass(); $allowedNode->name = 'ALLOWED_NODE'; $deniedNode = new stdClass(); $deniedNode->name = 'DENIED_NODE'; $allowedPermNode = permission_node::create('allowed_node_perm', $allowedNode); $deniedPermNode = permission_node::create('denied_node_perm', $deniedNode); redis->clear_permission("subuser:10:999:ALLOWED_NODE"); redis->clear_permission("subuser:10:999:DENIED_NODE"); echo "First call (should hit DB):\n"; assertResult($route->testEval($allowedPermNode), true, "Subuser should have ALLOWED_NODE"); $route = new TestRoute(); echo "Second call after new object (should hit Redis, no DB output expected):\n"; assertResult($route->testEval($allowedPermNode), true, "Subuser should still have ALLOWED_NODE (from Redis)"); echo "\nTesting denied node:\n"; assertResult($route->testEval($deniedPermNode), false, "Subuser should NOT have DENIED_NODE"); $route = new TestRoute(); echo "Call after new object (should hit Redis, no DB output expected):\n"; assertResult($route->testEval($deniedPermNode), false, "Subuser should still NOT have DENIED_NODE (from Redis)"); echo "\nPermissionRedisCacheTest completed successfully.\n"; }