renderCallback = $render; } public function render($fileName, $variableArray = [], $ext = 'html.twig'): string { return ($this->renderCallback)($fileName, $variableArray, $ext); } }; $di = new Pimple\Container(); $di['logger'] = new class { public function withChannel(string $channel): self { return $this; } public function error(string|Stringable $message, array $context = []): void { } public function info(string|Stringable $message, array $context = []): void { } }; $extensionService = new class { public function isExtensionActive(): bool { return false; } }; $di['request'] = Request::create('http://localhost/test'); $di['mod_service'] = $di->protect(static fn (): object => $extensionService); $di['auth'] = Mockery::mock(Box_Authorization::class) ->shouldReceive('isClientLoggedIn')->andReturn($clientLoggedIn)->getMock(); $di['url'] = Mockery::mock(FOSSBilling\Url::class) ->shouldReceive('link')->andReturnArg(0)->getMock(); $app->setDi($di); $app->setUrl('/test'); return $app; } test('get_custom_page returns 200 on successful render', function (): void { $app = appClientWithRender(fn (): string => 'ok'); $response = $app->get_custom_page('signup'); expect($response->getStatusCode())->toBe(200); expect($response->getContent())->toBe('ok'); }); test('get_custom_page returns XML content type for sitemap', function (): void { $app = appClientWithRender(fn (): string => ''); $response = $app->get_custom_page('sitemap.xml'); expect($response->getStatusCode())->toBe(200) ->and($response->headers->get('Content-Type'))->toContain('application/xml') ->and($response->getContent())->toBe(''); }); test('get_custom_page redirects authenticated clients away from login', function (): void { $app = appClientWithRender( static fn (): never => throw new RuntimeException('The login page should not be rendered.'), clientLoggedIn: true, ); $response = $app->get_custom_page('login'); expect($response->getStatusCode())->toBe(302) ->and($response->headers->get('Location'))->toBe('/'); }); test('get_custom_page returns 500 (not 404) when the template throws a RuntimeError (regression for #3818)', function (): void { $app = appClientWithRender(function (string $fileName): string { if ($fileName === 'error') { return 'error body'; } throw new Twig\Error\RuntimeError('Variable "company" does not exist.'); }); $response = $app->get_custom_page('signup'); expect($response->getStatusCode())->toBe(500); }); test('get_custom_page returns 500 (not 404) when the template throws a SyntaxError', function (): void { $app = appClientWithRender(function (string $fileName): string { if ($fileName === 'error') { return 'error body'; } throw new Twig\Error\SyntaxError('Unexpected token.'); }); $response = $app->get_custom_page('signup'); expect($response->getStatusCode())->toBe(500); }); test('get_custom_page returns 500 (not 404) when a nested template is missing during render', function (): void { $app = appClientWithRender(function (string $fileName): string { if ($fileName === 'error') { return 'error body'; } throw new Twig\Error\LoaderError('Template "missing-partial.html.twig" is not defined.'); }); $response = $app->get_custom_page('signup'); expect($response->getStatusCode())->toBe(500); }); test('get_custom_page still returns 404 when the top-level template is missing', function (): void { $app = appClientWithRender(function (string $fileName): string { if ($fileName === 'error') { return 'error body'; } throw new FOSSBilling\InformationException('Page not found', null, 404); }); $response = $app->get_custom_page('signup'); expect($response->getStatusCode())->toBe(404); }); test('numeric custom page paths return a themed 404', function (): void { $app = appClientWithRender(static function (string $fileName): string { if ($fileName === 'error') { return 'error body'; } throw new FOSSBilling\InformationException('Page not found', null, 404); }); $app->setUrl('/12345'); $response = $app->run(); expect($response->getStatusCode())->toBe(404) ->and($response->getContent())->toBe('error body'); });