$url]; } function curl_setopt_array($ch, $options) { $ch->options = $options; return true; } function curl_exec($ch) { if ($ch->url !== 'https://usfjwhztqoopzzfmfbis.supabase.co/rest/v1/rpc/sun_fetch_app_state?select=revision') { throw new \RuntimeException('Proxy did not preserve the RPC projection query'); } $headers = $ch->options[\CURLOPT_HTTPHEADER]; foreach (['accept: application/vnd.pgrst.object+json', 'authorization: Bearer test-token', 'apikey: test-key', 'content-type: application/json'] as $header) { if (!in_array($header, $headers, true)) throw new \RuntimeException('Required header lost: ' . explode(':', $header)[0]); } if ($ch->options[\CURLOPT_CUSTOMREQUEST] !== 'POST' || !$ch->options[\CURLOPT_SSL_VERIFYPEER]) throw new \RuntimeException('Method/TLS changed'); $ch->responseHeaders = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n"; return $ch->responseHeaders . '{"revision":7}'; } function curl_getinfo($ch, $option) { return $option === \CURLINFO_HEADER_SIZE ? strlen($ch->responseHeaders) : 200; } function curl_close($ch) {} $app = ($argv[1] ?? 'app') === 'app'; $_SERVER['HTTP_HOST'] = $app ? 'app.caterium.ru' : 'api.caterium.ru'; $_SERVER['REQUEST_URI'] = $app ? '/api/index.php?__caterium_path=%2Frest%2Fv1%2Frpc%2Fsun_fetch_app_state&select=revision' : '/rest/v1/rpc/sun_fetch_app_state?select=revision'; $_SERVER['REQUEST_METHOD'] = 'POST'; $_SERVER['HTTP_ACCEPT'] = 'application/vnd.pgrst.object+json'; $_SERVER['HTTP_AUTHORIZATION'] = 'Bearer test-token'; $_SERVER['HTTP_APIKEY'] = 'test-key'; $_SERVER['CONTENT_TYPE'] = 'application/json'; $_GET['__caterium_path'] = '/rest/v1/rpc/sun_fetch_app_state'; ob_start(); eval('namespace CateriumProxyTest;' . substr(file_get_contents(__DIR__ . '/../public/api/index.php'), 5)); $body = ob_get_clean(); if (json_decode($body, true) !== ['revision' => 7]) throw new \RuntimeException('RPC response changed'); echo 'PASS proxy HTTP contract: ' . ($app ? 'same-origin' : 'dedicated API') . "\n";