Completed
Push — master ( f0bd62...1253ac )
by
unknown
16s queued 13s
created

OaiPmhTest::importSolrDocuments()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 11
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 15
rs 9.9
1
<?php
2
3
namespace Kitodo\Dlf\Tests\Functional\Api;
4
5
use DateTime;
6
use GuzzleHttp\Client as HttpClient;
7
use Kitodo\Dlf\Common\Solr;
8
use Kitodo\Dlf\Domain\Repository\SolrCoreRepository;
9
use Kitodo\Dlf\Tests\Functional\FunctionalTestCase;
10
use Phpoaipmh\Endpoint;
11
use Phpoaipmh\Exception\OaipmhException;
12
use SimpleXMLElement;
13
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
14
15
class OaiPmhTest extends FunctionalTestCase
16
{
17
    protected $disableJsonWrappedResponse = true;
18
19
    protected $coreExtensionsToLoad = [
20
        'fluid',
21
        'fluid_styled_content',
22
    ];
23
24
    /** @var int */
25
    protected $oaiPage = 20001;
26
27
    /** @var string */
28
    protected $oaiUrl;
29
30
    /** @var int */
31
    protected $oaiPageNoStoragePid = 20002;
32
33
    /** @var string */
34
    protected $oaiUrlNoStoragePid;
35
36
    public function setUp(): void
37
    {
38
        parent::setUp();
39
40
        $this->oaiUrl = $this->baseUrl . '/index.php?id=' . $this->oaiPage;
41
        $this->oaiUrlNoStoragePid = $this->baseUrl . '/index.php?id=' . $this->oaiPageNoStoragePid;
42
43
        $this->importDataSet(__DIR__ . '/../../Fixtures/Common/documents_1.xml');
44
        $this->importDataSet(__DIR__ . '/../../Fixtures/Common/metadata.xml');
45
        $this->importDataSet(__DIR__ . '/../../Fixtures/Common/libraries.xml');
46
        $this->importDataSet(__DIR__ . '/../../Fixtures/Common/pages.xml');
47
        $this->importDataSet(__DIR__ . '/../../Fixtures/OaiPmh/pages.xml');
48
        $this->importDataSet(__DIR__ . '/../../Fixtures/OaiPmh/solrcores.xml');
49
50
        $this->persistenceManager = $this->objectManager->get(PersistenceManager::class);
0 ignored issues
show
Bug Best Practice introduced by
The property persistenceManager does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Deprecated Code introduced by
The function TYPO3\CMS\Extbase\Object\ObjectManager::get() has been deprecated: since TYPO3 10.4, will be removed in version 12.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

50
        $this->persistenceManager = /** @scrutinizer ignore-deprecated */ $this->objectManager->get(PersistenceManager::class);

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.

Loading history...
51
        $this->solrCoreRepository = $this->initializeRepository(SolrCoreRepository::class, 20000);
0 ignored issues
show
Bug Best Practice introduced by
The property solrCoreRepository does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
52
53
        $this->setUpOaiSolr();
54
    }
55
56
    protected function setUpOaiSolr()
57
    {
58
        // Setup Solr only once for all tests in this suite
59
        static $solr = null;
60
61
        if ($solr === null) {
62
            $coreName = Solr::createCore();
63
            $solr = Solr::getInstance($coreName);
64
65
            $this->importSolrDocuments($solr, __DIR__ . '/../../Fixtures/Common/documents_1.solr.json');
66
        }
67
68
        $oaiCoreModel = $this->solrCoreRepository->findByUid(11001);
69
        $oaiCoreModel->setIndexName($solr->core);
70
        $this->solrCoreRepository->update($oaiCoreModel);
71
        $this->persistenceManager->persistAll();
72
    }
73
74
    /**
75
     * @test
76
     */
77
    public function correctlyRespondsOnBadVerb()
78
    {
79
        $client = new HttpClient();
80
        $response = $client->get($this->baseUrl, [
81
            'query' => [
82
                'id' => $this->oaiPage,
83
                'verb' => 'nastyVerb',
84
            ],
85
        ]);
86
        $xml = new SimpleXMLElement((string) $response->getBody());
87
88
        $this->assertEquals('badVerb', (string) $xml->error['code']);
89
90
        // The base URL may be different from the one used that we actually used,
91
        // but it shouldn't contain the verb argument
92
        $this->assertStringNotContainsString('nastyVerb', (string) $xml->request);
93
94
        // For bad verbs, the <request> element must not contain any attributes
95
        // - http://www.openarchives.org/OAI/openarchivesprotocol.html#XMLResponse
96
        // - http://www.openarchives.org/OAI/openarchivesprotocol.html#ErrorConditions
97
        $this->assertEmpty($xml->request->attributes());
98
    }
99
100
    /**
101
     * @test
102
     */
103
    public function canIdentify()
104
    {
105
        $oai = Endpoint::build($this->oaiUrl);
106
        $identity = $oai->identify();
107
108
        $this->assertEquals('Identify', (string) $identity->request['verb']);
109
        $this->assertEquals('Default Library - OAI Repository', (string) $identity->Identify->repositoryName);
110
        $this->assertUtcDateString((string) $identity->Identify->earliestDatestamp);
111
        $this->assertEquals('[email protected]', (string) $identity->Identify->adminEmail);
112
    }
113
114
    /**
115
     * @test
116
     */
117
    public function identifyGivesFallbackDatestampWhenNoDocuments()
118
    {
119
        $oai = Endpoint::build($this->oaiUrlNoStoragePid);
120
        $identity = $oai->identify();
121
122
        $this->assertUtcDateString((string) $identity->Identify->earliestDatestamp);
123
    }
124
125
    /**
126
     * @test
127
     */
128
    public function canListMetadataFormats()
129
    {
130
        $oai = Endpoint::build($this->oaiUrl);
131
        $formats = $oai->listMetadataFormats();
132
133
        $formatMap = [];
134
        foreach ($formats as $format) {
135
            $formatMap[(string) $format->metadataPrefix] = $format;
136
        }
137
138
        $this->assertEquals('http://www.loc.gov/METS/', (string) $formatMap['mets']->metadataNamespace);
139
    }
140
141
    /**
142
     * @test
143
     */
144
    public function canListRecords()
145
    {
146
        $oai = Endpoint::build($this->oaiUrl);
147
        $result = $oai->listRecords('mets');
148
149
        $record = $result->current();
150
        $metsRoot = $record->metadata->children('http://www.loc.gov/METS/')[0];
151
        $this->assertNotNull($metsRoot);
152
        $this->assertEquals('mets', $metsRoot->getName());
153
    }
154
155
    /**
156
     * @test
157
     */
158
    public function noRecordsUntil1900()
159
    {
160
        $this->expectException(OaipmhException::class);
161
        $this->expectExceptionMessage('empty list');
162
163
        $oai = Endpoint::build($this->oaiUrl);
164
        $result = $oai->listRecords('mets', null, (new DateTime())->setDate(1900, 1, 1));
165
166
        $result->current();
167
    }
168
169
    /**
170
     * @test
171
     */
172
    public function canUseResumptionToken()
173
    {
174
        // NOTE: cursor and expirationDate are optional by the specification,
175
        //       but we include them in our implementation
176
177
        $client = new HttpClient();
178
179
        // The general handling of resumption tokens should be the same for these verbs
180
        foreach (['ListIdentifiers', 'ListRecords'] as $verb) {
181
            // Check that we get a proper resumption token when starting a list
182
            $response = $client->get($this->baseUrl, [
183
                'query' => [
184
                    'id' => $this->oaiPage,
185
                    'verb' => $verb,
186
                    'metadataPrefix' => 'mets',
187
                ],
188
            ]);
189
            $xml = new SimpleXMLElement((string) $response->getBody());
190
191
            $resumptionToken = $xml->$verb->resumptionToken;
192
            $this->assertEquals('0', (string) $resumptionToken['cursor']);
193
            $this->assertInFuture((string) $resumptionToken['expirationDate']);
194
            $this->assertNotEmpty((string) $resumptionToken);
195
196
            // Store list size to check that it remains constant (and check its sanity)
197
            $completeListSize = (int) $resumptionToken['completeListSize'];
198
            $this->assertGreaterThan(2, $completeListSize); // we have more than two documents in document set
199
200
            // Check that we can resume and get a proper cursor value
201
            $cursor = 1;
202
            do {
203
                $response = $client->get($this->baseUrl, [
204
                    'query' => [
205
                        'id' => $this->oaiPage,
206
                        'verb' => $verb,
207
                        'resumptionToken' => (string) $resumptionToken,
208
                    ],
209
                ]);
210
                $xml = new SimpleXMLElement((string) $response->getBody());
211
212
                $resumptionToken = $xml->$verb->resumptionToken;
213
                $tokenStr = (string) $resumptionToken;
214
215
                $this->assertEquals($cursor, (string) $resumptionToken['cursor']); // settings.limit = 1
216
                $this->assertEquals($completeListSize, (string) $resumptionToken['completeListSize']);
217
218
                // The last resumptionToken is empty and doesn't have expirationDate
219
                $isLastBatch = $cursor + 1 >= $completeListSize;
220
                $this->assertEquals($isLastBatch, empty((string) $resumptionToken['expirationDate']));
221
                $this->assertEquals($isLastBatch, empty($tokenStr));
222
223
                $cursor++;
224
            } while ($tokenStr);
225
        }
226
    }
227
228
    /**
229
     * @test
230
     */
231
    public function noResumptionTokenForCompleteList()
232
    {
233
        $client = new HttpClient();
234
235
        foreach (['ListIdentifiers', 'ListRecords'] as $verb) {
236
            $response = $client->get($this->baseUrl, [
237
                'query' => [
238
                    'id' => $this->oaiPage,
239
                    'verb' => $verb,
240
                    'metadataPrefix' => 'mets',
241
                    'set' => 'collection-with-single-document',
242
                ],
243
            ]);
244
            $xml = new SimpleXMLElement((string) $response->getBody());
245
246
            $this->assertEquals(1, count($xml->$verb->children()));
247
            $this->assertEmpty($xml->$verb->resumptionToken);
248
        }
249
    }
250
251
    /**
252
     * @test
253
     */
254
    public function canListAndResumeIdentifiers()
255
    {
256
        $oai = Endpoint::build($this->oaiUrl);
257
        $result = $oai->listIdentifiers('mets');
258
259
        $record = $result->current();
260
        $this->assertEquals('oai:de:slub-dresden:db:id-476251419', $record->identifier);
261
        $this->assertEquals(['collection-with-single-document', 'music'], (array) $record->setSpec);
262
263
        // This should use a resumption token because settings.limit is 1
264
        $record = $result->next();
265
        $this->assertEquals('oai:de:slub-dresden:db:id-476248086', $record->identifier);
266
    }
267
268
    protected function parseUtc(string $dateTime)
269
    {
270
        return DateTime::createFromFormat('Y-m-d\TH:i:s\Z', $dateTime);
271
    }
272
273
    protected function assertUtcDateString(string $dateTime)
274
    {
275
        $this->assertInstanceOf(DateTime::class, $this->parseUtc($dateTime));
276
    }
277
278
    protected function assertInFuture(string $dateTime)
279
    {
280
        $this->assertGreaterThan(new DateTime(), $this->parseUtc($dateTime));
281
    }
282
}
283