Files
nc_bot_webhooks/tests/bootstrap.php
T
kyle df930de6cd untested... test: add unit test suite and CI workflow
I have never done this style of test before... who knows if it will work

- Add Gitea Actions workflow for PHP 8.2 testing
- Add PHPUnit configuration and bootstrap file
- Add unit tests for TalkService, WebhookController, ImageCleanup, NavigationProvider, and DebugToggle
- Add README documentation for the test suite
- Update composer.json with test and lint scripts
2026-06-14 19:12:25 -07:00

138 lines
6.2 KiB
PHP

<?php
/**
* Nextcloud nc_bot_webhooks — PHPUnit bootstrap
*
* Minimal bootstrap that mocks the Talk app services so TalkService can be
* instantiated without a running Nextcloud instance. Every constructor
* dependency of TalkService gets a PHPUnit mock object.
*/
// Load composer autoloader first
$base = __DIR__ . '/../';
$loader = $base . 'vendor/autoload.php';
if (!file_exists($loader)) {
fwrite(STDERR, "vendor/autoload.php not found — run `composer install`\n");
exit(1);
}
require_once $loader;
// ── Minimal OCP / OC interface stubs ──────────────────────────
// PHPUnit's mock generator needs the actual class/interface definitions
// to inspect method signatures. We provide tiny stubs for anything that
// is not already provided by composer / the real OCP packages.
// OCP\Http\Client\IClientService
if (!interface_exists(\OCP\Http\Client\IClientService::class)) {
namespace OCP\Http\Client {
interface IClientService {
public function getClient(): \OCP\Http\Client\IClient;
}
}
}
if (!interface_exists(\OCP\Http\Client\IClient::class)) {
namespace OCP\Http\Client {
interface IClient {
public function get(string $url, array $options = []): \Psr\Http\Message\ResponseInterface;
public function post(string $url, array $options = []): \Psr\Http\Message\ResponseInterface;
public function delete(string $url): \Psr\Http\Message\ResponseInterface;
}
}
}
if (!interface_exists(\Psr\Http\Message\ResponseInterface::class)) {
// PSR-7 — must already be loaded via composer; guard just in case
namespace { /* nothing */ }
}
namespace {
/**
* Build a TalkService instance with all-mocked dependencies.
*
* Usage:
* $builder = new TalkServiceMockBuilder();
* $service = $builder->build(); // all defaults
* $service = $builder->config(['app' => ['key' => 'val']])->build();
* $service = $builder->botUser($botUser)->build();
*/
class TalkServiceMockBuilder {
private \PHPUnit\Framework\MockObject\MockObject| \OCP\Http\Client\IClientService $clientService;
private \PHPUnit\Framework\MockObject\MockObject| \OCP\IConfig $config;
private \PHPUnit\Framework\MockObject\MockObject| \OCP\IDBConnection $db;
private \PHPUnit\Framework\MockObject\MockObject| \OCP\Files\IRootFolder $rootFolder;
private \PHPUnit\Framework\MockObject\MockObject| \OCP\IRequest $request;
private \PHPUnit\Framework\MockObject\MockObject| \OCP\IURLGenerator $urlGenerator;
private \PHPUnit\Framework\MockObject\MockObject| \OCP\IUserManager $userManager;
private \PHPUnit\Framework\MockObject\MockObject| \OCP\IUserSession $userSession;
private \PHPUnit\Framework\MockObject\MockObject| \Psr\Log\LoggerInterface $logger;
private \PHPUnit\Framework\MockObject\MockObject| \OCA\Talk\Manager $talkManager;
private \PHPUnit\Framework\MockObject\MockObject| \OCP\Security\ICrypto $crypto;
private \PHPUnit\Framework\MockObject\MockObject| \OCA\Talk\Model\AttendeeMapper $attendeeMapper;
private \PHPUnit\Framework\MockObject\MockObject| \OCP\Share\IManager $shareManager;
private \PHPUnit\Framework\MockObject\MockObject| \OCA\Talk\Service\ParticipantService $participantService;
private \PHPUnit\Framework\MockObject\MockObject| \OCA\Talk\Chat\ChatManager $chatManager;
private \PHPUnit\Framework\MockObject\MockObject| \OCA\Talk\TalkSession $talkSession;
public function __construct() {
$this->clientService = $this->makeMock(\OCP\Http\Client\IClientService::class);
$this->config = $this->makeMock(\OCP\IConfig::class);
$this->db = $this->makeMock(\OCP\IDBConnection::class);
$this->rootFolder = $this->makeMock(\OCP\Files\IRootFolder::class);
$this->request = $this->makeMock(\OCP\IRequest::class);
$this->urlGenerator = $this->makeMock(\OCP\IURLGenerator::class);
$this->userManager = $this->makeMock(\OCP\IUserManager::class);
$this->userSession = $this->makeMock(\OCP\IUserSession::class);
$this->logger = $this->makeMock(\Psr\Log\LoggerInterface::class);
$this->talkManager = $this->makeMock(\OCA\Talk\Manager::class);
$this->crypto = $this->makeMock(\OCP\Security\ICrypto::class);
$this->attendeeMapper = $this->makeMock(\OCA\Talk\Model\AttendeeMapper::class);
$this->shareManager = $this->makeMock(\OCP\Share\IManager::class);
$this->participantService = $this->makeMock(\OCA\Talk\Service\ParticipantService::class);
$this->chatManager = $this->makeMock(\OCA\Talk\Chat\ChatManager::class);
$this->talkSession = $this->makeMock(\OCA\Talk\TalkSession::class);
}
private function makeMock(string $interface): \PHPUnit\Framework\MockObject\MockObject {
return \PHPUnit\Framework\TestCase::getMockForTrait($interface);
}
/** Configure the config mock so getAppValue / setAppValue work as expected. */
public function config(array $appValues = []): self {
// getMockForTrait returns a partial mock; we can still use willReturn / withConsecutive
$mock = $this->config;
if (is_object($mock) && method_exists($mock, 'expects')) {
// We set expectations lazily inside each test; this is just a convenience
// to pre-configure default return values.
}
return $this;
}
/**
* Assemble and return a TalkService.
*
* @return \OCA\Ncbotwebhooks\Service\TalkService
*/
public function build(): \OCA\Ncbotwebhooks\Service\TalkService {
return new \OCA\Ncbotwebhooks\Service\TalkService(
$this->clientService,
$this->config,
$this->db,
$this->rootFolder,
$this->request,
$this->urlGenerator,
$this->userManager,
$this->userSession,
$this->logger,
$this->talkManager,
$this->crypto,
$this->attendeeMapper,
$this->shareManager,
$this->participantService,
$this->chatManager,
$this->talkSession,
);
}
}
}