Skip to content

Commit 08496da

Browse files
authored
Merge pull request #5320 from nextcloud/testsCachedRequestedService
test: add unit tests for CachedRequestService
2 parents 67a017d + c88d4ee commit 08496da

1 file changed

Lines changed: 176 additions & 0 deletions

File tree

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace Tests\Richdocuments;
11+
12+
use OCA\Richdocuments\Service\CachedRequestService;
13+
use OCP\Files\AppData\IAppDataFactory;
14+
use OCP\Files\IAppData;
15+
use OCP\Files\SimpleFS\ISimpleFile;
16+
use OCP\Files\SimpleFS\ISimpleFolder;
17+
use OCP\Http\Client\IClient;
18+
use OCP\Http\Client\IClientService;
19+
use OCP\IAppConfig;
20+
use OCP\ICache;
21+
use OCP\ICacheFactory;
22+
use PHPUnit\Framework\MockObject\MockObject;
23+
use PHPUnit\Framework\TestCase;
24+
use Psr\Log\LoggerInterface;
25+
26+
// Test double for CachedRequestService (abstract)
27+
class TestCachedRequestService extends CachedRequestService {
28+
public string $responseToReturn = 'default-response';
29+
30+
protected function sendRequest(IClient $client): string {
31+
return $this->responseToReturn;
32+
}
33+
}
34+
35+
class CachedRequestServiceTest extends TestCase {
36+
private IClientService&MockObject $clientService;
37+
private ICacheFactory&MockObject $cacheFactory;
38+
private ICache&MockObject $cache;
39+
private IAppDataFactory&MockObject $appDataFactory;
40+
private IAppData&MockObject $appData;
41+
private ISimpleFolder&MockObject $folder;
42+
private ISimpleFile&MockObject $file;
43+
private IAppConfig&MockObject $appConfig;
44+
private LoggerInterface&MockObject $logger;
45+
private TestCachedRequestService $service;
46+
47+
protected function setUp(): void {
48+
parent::setUp();
49+
$this->clientService = $this->createMock(IClientService::class);
50+
$this->cacheFactory = $this->createMock(ICacheFactory::class);
51+
$this->cache = $this->createMock(ICache::class);
52+
$this->appDataFactory = $this->createMock(IAppDataFactory::class);
53+
$this->appData = $this->createMock(IAppData::class);
54+
$this->folder = $this->createMock(ISimpleFolder::class);
55+
$this->file = $this->createMock(ISimpleFile::class);
56+
$this->appConfig = $this->createMock(IAppConfig::class);
57+
$this->logger = $this->createMock(LoggerInterface::class);
58+
59+
$this->cacheFactory->method('createDistributed')->willReturn($this->cache);
60+
$this->appDataFactory->method('get')->willReturn($this->appData);
61+
$this->appData->method('getFolder')->willReturn($this->folder);
62+
63+
$this->service = new TestCachedRequestService(
64+
$this->clientService,
65+
$this->cacheFactory,
66+
$this->appDataFactory,
67+
$this->appConfig,
68+
$this->logger,
69+
'unittest-cachekey'
70+
);
71+
}
72+
73+
public function testFetchStoresToCacheAndReturnsResponse(): void {
74+
$this->cache->expects($this->once())
75+
->method('set')
76+
->with('unittest-cachekey', 'default-response');
77+
$this->folder->expects($this->once())
78+
->method('newFile')
79+
->with('unittest-cachekey', 'default-response');
80+
81+
$result = $this->service->fetch();
82+
$this->assertEquals('default-response', $result);
83+
}
84+
85+
public function testGetReturnsCachedValueIfPresent(): void {
86+
$this->cache->expects($this->once())
87+
->method('get')
88+
->with('unittest-cachekey')
89+
->willReturn('from-cache');
90+
$this->folder->expects($this->never())->method('fileExists');
91+
$this->folder->expects($this->never())->method('getFile');
92+
$this->cache->expects($this->never())->method('set');
93+
$result = $this->service->get();
94+
$this->assertEquals('from-cache', $result);
95+
}
96+
97+
public function testGetReturnsFileValueIfCacheMiss(): void {
98+
$this->cache->expects($this->once())
99+
->method('get')
100+
->with('unittest-cachekey')
101+
->willReturn(null);
102+
$this->folder->expects($this->once())
103+
->method('fileExists')
104+
->with('unittest-cachekey')
105+
->willReturn(true);
106+
$this->folder->expects($this->once())
107+
->method('getFile')
108+
->with('unittest-cachekey')
109+
->willReturn($this->file);
110+
$this->file->expects($this->once())
111+
->method('getContent')
112+
->willReturn('from-file');
113+
$this->cache->expects($this->once())
114+
->method('set')
115+
->with('unittest-cachekey', 'from-file', 3600);
116+
117+
$result = $this->service->get();
118+
$this->assertEquals('from-file', $result);
119+
}
120+
121+
public function testResetCacheRemovesCacheAndFile(): void {
122+
$this->cache->expects($this->once())->method('remove')->with('unittest-cachekey');
123+
$this->folder->method('fileExists')->with('unittest-cachekey')->willReturn(true);
124+
$this->folder->method('getFile')->with('unittest-cachekey')->willReturn($this->file);
125+
$this->file->expects($this->once())->method('delete');
126+
127+
$this->service->resetCache();
128+
$this->assertTrue(true); // No exceptions thrown
129+
}
130+
131+
public function testGetLastUpdateReturnsMTimeIfFileExists(): void {
132+
$this->folder->expects($this->once())
133+
->method('fileExists')
134+
->with('unittest-cachekey')
135+
->willReturn(true);
136+
$this->folder->expects($this->once())
137+
->method('getFile')
138+
->with('unittest-cachekey')
139+
->willReturn($this->file);
140+
$this->file->expects($this->once())
141+
->method('getMTime')
142+
->willReturn(1700000000); // Some fake timestamp
143+
144+
$result = $this->service->getLastUpdate();
145+
$this->assertEquals(1700000000, $result);
146+
}
147+
148+
public function testGetLastUpdateReturnsNullIfFileDoesNotExist(): void {
149+
$this->folder->expects($this->once())
150+
->method('fileExists')
151+
->with('unittest-cachekey')
152+
->willReturn(false);
153+
$this->folder->expects($this->never())->method('getFile');
154+
$this->file->expects($this->never())->method('getMTime');
155+
156+
$result = $this->service->getLastUpdate();
157+
$this->assertNull($result);
158+
}
159+
160+
public function testFetchPropagatesExceptionAndLeavesCacheIntact(): void {
161+
// Set up a test double that throws
162+
$service = new class($this->clientService, $this->cacheFactory, $this->appDataFactory, $this->appConfig, $this->logger, 'unittest-cachekey') extends CachedRequestService {
163+
protected function sendRequest(IClient $client): string {
164+
throw new \RuntimeException('Remote endpoint unreachable!');
165+
}
166+
};
167+
168+
// Existing cache should not be set, folder->newFile should not be called
169+
$this->cache->expects($this->never())->method('set');
170+
$this->folder->expects($this->never())->method('newFile');
171+
172+
$this->expectException(\RuntimeException::class);
173+
$this->expectExceptionMessage('Remote endpoint unreachable!');
174+
$service->fetch();
175+
}
176+
}

0 commit comments

Comments
 (0)