We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * (c) Kitodo. Key to digital objects e.V. <[email protected]> |
||
5 | * |
||
6 | * This file is part of the Kitodo and TYPO3 projects. |
||
7 | * |
||
8 | * @license GNU General Public License version 3 or later. |
||
9 | * For the full copyright and license information, please read the |
||
10 | * LICENSE.txt file that was distributed with this source code. |
||
11 | */ |
||
12 | |||
13 | namespace Kitodo\Dlf\Tests\Functional\Api; |
||
14 | |||
15 | use DateTime; |
||
16 | use GuzzleHttp\Client as HttpClient; |
||
17 | use Kitodo\Dlf\Common\Solr\Solr; |
||
18 | use Kitodo\Dlf\Domain\Repository\SolrCoreRepository; |
||
19 | use Kitodo\Dlf\Tests\Functional\FunctionalTestCase; |
||
20 | use Phpoaipmh\Endpoint; |
||
21 | use Phpoaipmh\Exception\OaipmhException; |
||
22 | use SimpleXMLElement; |
||
23 | |||
24 | class OaiPmhTest extends FunctionalTestCase |
||
25 | { |
||
26 | protected $disableJsonWrappedResponse = true; |
||
27 | |||
28 | protected $coreExtensionsToLoad = [ |
||
29 | 'fluid', |
||
30 | 'fluid_styled_content', |
||
31 | ]; |
||
32 | |||
33 | /** @var int */ |
||
34 | protected $oaiPage = 20001; |
||
35 | |||
36 | /** @var string */ |
||
37 | protected $oaiUrl; |
||
38 | |||
39 | /** @var int */ |
||
40 | protected $oaiPageNoStoragePid = 20002; |
||
41 | |||
42 | /** @var string */ |
||
43 | protected $oaiUrlNoStoragePid; |
||
44 | |||
45 | /** |
||
46 | * @var SolrCoreRepository |
||
47 | */ |
||
48 | protected $solrCoreRepository; |
||
49 | |||
50 | public function setUp(): void |
||
51 | { |
||
52 | parent::setUp(); |
||
53 | |||
54 | $this->oaiUrl = $this->baseUrl . 'index.php?id=' . $this->oaiPage; |
||
55 | $this->oaiUrlNoStoragePid = $this->baseUrl . 'index.php?id=' . $this->oaiPageNoStoragePid; |
||
56 | |||
57 | $this->importCSVDataSet(__DIR__ . '/../../Fixtures/Common/documents_1.csv'); |
||
58 | $this->importCSVDataSet(__DIR__ . '/../../Fixtures/Common/metadata.csv'); |
||
59 | $this->importCSVDataSet(__DIR__ . '/../../Fixtures/Common/libraries.csv'); |
||
60 | $this->importCSVDataSet(__DIR__ . '/../../Fixtures/Common/pages.csv'); |
||
61 | $this->importDataSet(__DIR__ . '/../../Fixtures/OaiPmh/pages.xml'); |
||
0 ignored issues
–
show
|
|||
62 | $this->importCSVDataSet(__DIR__ . '/../../Fixtures/OaiPmh/solrcores.csv'); |
||
63 | |||
64 | $this->solrCoreRepository = $this->initializeRepository(SolrCoreRepository::class, 20000); |
||
65 | |||
66 | $this->setUpOaiSolr(); |
||
67 | } |
||
68 | |||
69 | protected function setUpOaiSolr() |
||
70 | { |
||
71 | // Setup Solr only once for all tests in this suite |
||
72 | static $solr = null; |
||
73 | |||
74 | if ($solr === null) { |
||
75 | $coreName = Solr::createCore(); |
||
76 | $solr = Solr::getInstance($coreName); |
||
77 | |||
78 | $this->importSolrDocuments($solr, __DIR__ . '/../../Fixtures/Common/documents_1.solr.json'); |
||
79 | } |
||
80 | |||
81 | $oaiCoreModel = $this->solrCoreRepository->findByUid(11001); |
||
82 | $oaiCoreModel->setIndexName($solr->core); |
||
83 | $this->solrCoreRepository->update($oaiCoreModel); |
||
84 | $this->persistenceManager->persistAll(); |
||
85 | } |
||
86 | |||
87 | /** |
||
88 | * @test |
||
89 | */ |
||
90 | public function correctlyRespondsOnBadVerb() |
||
91 | { |
||
92 | $client = new HttpClient(); |
||
93 | $response = $client->get($this->baseUrl, [ |
||
94 | 'query' => [ |
||
95 | 'id' => $this->oaiPage, |
||
96 | 'verb' => 'nastyVerb', |
||
97 | ], |
||
98 | ]); |
||
99 | $xml = new SimpleXMLElement((string) $response->getBody()); |
||
100 | |||
101 | self::assertEquals('badVerb', (string) $xml->error['code']); |
||
102 | |||
103 | // The base URL may be different from the one used that we actually used, |
||
104 | // but it shouldn't contain the verb argument |
||
105 | self::assertStringNotContainsString('nastyVerb', (string) $xml->request); |
||
106 | |||
107 | // For bad verbs, the <request> element must not contain any attributes |
||
108 | // - http://www.openarchives.org/OAI/openarchivesprotocol.html#XMLResponse |
||
109 | // - http://www.openarchives.org/OAI/openarchivesprotocol.html#ErrorConditions |
||
110 | self::assertEmpty($xml->request->attributes()); |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * @test |
||
115 | */ |
||
116 | public function canIdentify() |
||
117 | { |
||
118 | $oai = Endpoint::build($this->oaiUrl); |
||
119 | $identity = $oai->identify(); |
||
120 | |||
121 | self::assertEquals('Identify', (string) $identity->request['verb']); |
||
122 | self::assertEquals('Default Library - OAI Repository', (string) $identity->Identify->repositoryName); |
||
123 | self::assertUtcDateString((string) $identity->Identify->earliestDatestamp); |
||
124 | self::assertEquals('[email protected]', (string) $identity->Identify->adminEmail); |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * @test |
||
129 | */ |
||
130 | public function identifyGivesFallbackDatestampWhenNoDocuments() |
||
131 | { |
||
132 | $oai = Endpoint::build($this->oaiUrlNoStoragePid); |
||
133 | $identity = $oai->identify(); |
||
134 | |||
135 | self::assertUtcDateString((string) $identity->Identify->earliestDatestamp); |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * @test |
||
140 | */ |
||
141 | public function canListMetadataFormats() |
||
142 | { |
||
143 | $oai = Endpoint::build($this->oaiUrl); |
||
144 | $formats = $oai->listMetadataFormats(); |
||
145 | |||
146 | $formatMap = []; |
||
147 | foreach ($formats as $format) { |
||
148 | $formatMap[(string) $format->metadataPrefix] = $format; |
||
149 | } |
||
150 | |||
151 | self::assertEquals('http://www.loc.gov/METS/', (string) $formatMap['mets']->metadataNamespace); |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * @test |
||
156 | */ |
||
157 | public function canListRecords() |
||
158 | { |
||
159 | $oai = Endpoint::build($this->oaiUrl); |
||
160 | $result = $oai->listRecords('mets'); |
||
161 | |||
162 | $record = $result->current(); |
||
163 | $metsRoot = $record->metadata->children('http://www.loc.gov/METS/')[0]; |
||
164 | self::assertNotNull($metsRoot); |
||
165 | self::assertEquals('mets', $metsRoot->getName()); |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * @test |
||
170 | */ |
||
171 | public function noRecordsUntil1900() |
||
172 | { |
||
173 | $this->expectException(OaipmhException::class); |
||
174 | $this->expectExceptionMessage('empty list'); |
||
175 | |||
176 | $oai = Endpoint::build($this->oaiUrl); |
||
177 | $result = $oai->listRecords('mets', null, (new DateTime())->setDate(1900, 1, 1)); |
||
178 | |||
179 | $result->current(); |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * @test |
||
184 | */ |
||
185 | public function canUseResumptionToken() |
||
186 | { |
||
187 | // NOTE: cursor and expirationDate are optional by the specification, |
||
188 | // but we include them in our implementation |
||
189 | |||
190 | $client = new HttpClient(); |
||
191 | |||
192 | // The general handling of resumption tokens should be the same for these verbs |
||
193 | foreach (['ListIdentifiers', 'ListRecords'] as $verb) { |
||
194 | // Check that we get a proper resumption token when starting a list |
||
195 | $response = $client->get($this->baseUrl, [ |
||
196 | 'query' => [ |
||
197 | 'id' => $this->oaiPage, |
||
198 | 'verb' => $verb, |
||
199 | 'metadataPrefix' => 'mets', |
||
200 | ], |
||
201 | ]); |
||
202 | $xml = new SimpleXMLElement((string) $response->getBody()); |
||
203 | |||
204 | $resumptionToken = $xml->$verb->resumptionToken; |
||
205 | self::assertEquals('0', (string) $resumptionToken['cursor']); |
||
206 | self::assertInFuture((string) $resumptionToken['expirationDate']); |
||
207 | self::assertNotEmpty((string) $resumptionToken); |
||
208 | |||
209 | // Store list size to check that it remains constant (and check its sanity) |
||
210 | $completeListSize = (int) $resumptionToken['completeListSize']; |
||
211 | self::assertGreaterThan(2, $completeListSize); // we have more than two documents in document set |
||
212 | |||
213 | // Check that we can resume and get a proper cursor value |
||
214 | $cursor = 1; |
||
215 | do { |
||
216 | $response = $client->get($this->baseUrl, [ |
||
217 | 'query' => [ |
||
218 | 'id' => $this->oaiPage, |
||
219 | 'verb' => $verb, |
||
220 | 'resumptionToken' => (string) $resumptionToken, |
||
221 | ], |
||
222 | ]); |
||
223 | $xml = new SimpleXMLElement((string) $response->getBody()); |
||
224 | |||
225 | $resumptionToken = $xml->$verb->resumptionToken; |
||
226 | $tokenStr = (string) $resumptionToken; |
||
227 | |||
228 | self::assertEquals($cursor, (string) $resumptionToken['cursor']); // settings.limit = 1 |
||
229 | self::assertEquals($completeListSize, (string) $resumptionToken['completeListSize']); |
||
230 | |||
231 | // The last resumptionToken is empty and doesn't have expirationDate |
||
232 | $isLastBatch = $cursor + 1 >= $completeListSize; |
||
233 | self::assertEquals($isLastBatch, empty((string) $resumptionToken['expirationDate'])); |
||
234 | self::assertEquals($isLastBatch, empty($tokenStr)); |
||
235 | |||
236 | $cursor++; |
||
237 | } while ($tokenStr); |
||
238 | } |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * @test |
||
243 | */ |
||
244 | public function noResumptionTokenForCompleteList() |
||
245 | { |
||
246 | $client = new HttpClient(); |
||
247 | |||
248 | foreach (['ListIdentifiers', 'ListRecords'] as $verb) { |
||
249 | $response = $client->get($this->baseUrl, [ |
||
250 | 'query' => [ |
||
251 | 'id' => $this->oaiPage, |
||
252 | 'verb' => $verb, |
||
253 | 'metadataPrefix' => 'mets', |
||
254 | 'set' => 'collection-with-single-document', |
||
255 | ], |
||
256 | ]); |
||
257 | $xml = new SimpleXMLElement((string) $response->getBody()); |
||
258 | |||
259 | self::assertEquals(1, count($xml->$verb->children())); |
||
260 | self::assertEmpty($xml->$verb->resumptionToken); |
||
261 | } |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * @test |
||
266 | */ |
||
267 | public function canListAndResumeIdentifiers() |
||
268 | { |
||
269 | $oai = Endpoint::build($this->oaiUrl); |
||
270 | $result = $oai->listIdentifiers('mets'); |
||
271 | |||
272 | $record = $result->current(); |
||
273 | self::assertEquals('oai:de:slub-dresden:db:id-476251419', $record->identifier); |
||
274 | self::assertEquals(['collection-with-single-document', 'music'], (array) $record->setSpec); |
||
275 | |||
276 | // This should use a resumption token because settings.limit is 1 |
||
277 | $record = $result->next(); |
||
278 | self::assertEquals('oai:de:slub-dresden:db:id-476248086', $record->identifier); |
||
279 | } |
||
280 | |||
281 | protected function parseUtc(string $dateTime) |
||
282 | { |
||
283 | return DateTime::createFromFormat('Y-m-d\TH:i:s\Z', $dateTime); |
||
284 | } |
||
285 | |||
286 | protected function assertUtcDateString(string $dateTime) |
||
287 | { |
||
288 | self::assertInstanceOf(DateTime::class, $this->parseUtc($dateTime)); |
||
289 | } |
||
290 | |||
291 | protected function assertInFuture(string $dateTime) |
||
292 | { |
||
293 | self::assertGreaterThan(new DateTime(), $this->parseUtc($dateTime)); |
||
294 | } |
||
295 | } |
||
296 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.