arxiv-api-wrapper 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,222 +1,247 @@
1
- /**
2
- * Integration tests for the arXiv OAI-PMH interface (real HTTP calls).
3
- * Conservative request size and rate; same pattern as arxivAPI.integration.test.ts.
4
- */
5
- import { describe, it, expect } from 'vitest';
6
- import {
7
- oaiIdentify,
8
- oaiListRecords,
9
- oaiListRecordsAsyncIterator,
10
- oaiListRecordsAll,
11
- oaiListIdentifiersAsyncIterator,
12
- oaiListIdentifiersAll,
13
- oaiListSetsAsyncIterator,
14
- oaiListSetsAll,
15
- } from '../src/oaiClient.js';
16
-
17
- const OAI_OPTIONS = {
18
- timeoutMs: 15000,
19
- retries: 2,
20
- rateLimit: { tokensPerInterval: 1, intervalMs: 1000 },
21
- userAgent: 'arxiv-api-wrapper-tests/1.0',
22
- };
23
-
24
- describe('OAI-PMH integration', () => {
25
- it('oaiIdentify returns repository info and protocol version 2.0', async () => {
26
- let result;
27
- try {
28
- result = await oaiIdentify(OAI_OPTIONS);
29
- } catch (error) {
30
- console.error('oaiIdentify failed:', error);
31
- throw new Error(
32
- `OAI Identify failed: ${error instanceof Error ? error.message : String(error)}`
33
- );
34
- }
35
- expect(result.repositoryName).toBeTruthy();
36
- expect(result.baseURL).toContain('oaipmh.arxiv.org');
37
- expect(result.protocolVersion).toBe('2.0');
38
- expect(Array.isArray(result.adminEmail)).toBe(true);
39
- expect(result.earliestDatestamp).toBeTruthy();
40
- }, 30000);
41
-
42
- it('oaiListRecords returns one page of records with header and metadata', async () => {
43
- let result;
44
- try {
45
- result = await oaiListRecords('oai_dc', {
46
- ...OAI_OPTIONS,
47
- from: '2024-01-01',
48
- until: '2024-01-02',
49
- });
50
- } catch (error) {
51
- console.error('oaiListRecords failed:', error);
52
- throw new Error(
53
- `OAI ListRecords failed: ${error instanceof Error ? error.message : String(error)}`
54
- );
55
- }
56
- expect(Array.isArray(result.records)).toBe(true);
57
- if (result.records.length > 0) {
58
- const rec = result.records[0];
59
- expect(rec.header).toBeTruthy();
60
- expect(rec.header.identifier).toBeTruthy();
61
- expect(rec.header.datestamp).toBeTruthy();
62
- expect(rec.metadata).toBeDefined();
63
- expect(typeof rec.metadata).toBe('object');
64
- }
65
- // May or may not have resumptionToken depending on result size
66
- if (result.resumptionToken) {
67
- expect(result.resumptionToken.value).toBeTruthy();
68
- }
69
- }, 30000);
70
-
71
- it('oaiListRecordsAll returns records across all pages within a small date range', async () => {
72
- let result;
73
- try {
74
- result = await oaiListRecordsAll('oai_dc', {
75
- ...OAI_OPTIONS,
76
- from: '2024-01-01',
77
- until: '2024-01-02',
78
- maxRecords: 200,
79
- });
80
- } catch (error) {
81
- console.error('oaiListRecordsAll failed:', error);
82
- throw new Error(
83
- `OAI ListRecordsAll failed: ${error instanceof Error ? error.message : String(error)}`
84
- );
85
- }
86
-
87
- expect(Array.isArray(result.records)).toBe(true);
88
- if (result.records.length > 0) {
89
- const rec = result.records[0];
90
- expect(rec.header).toBeTruthy();
91
- expect(rec.header.identifier).toBeTruthy();
92
- expect(rec.header.datestamp).toBeTruthy();
93
- expect(rec.metadata).toBeDefined();
94
- expect(typeof rec.metadata).toBe('object');
95
- }
96
- }, 30000);
97
-
98
- it('oaiListRecordsAsyncIterator yields records and matches oaiListRecordsAll count for the same cap', async () => {
99
- let iteratedRecords: unknown[] = [];
100
- let allResult;
101
- try {
102
- const maxRecords = 25;
103
- for await (const record of oaiListRecordsAsyncIterator('oai_dc', {
104
- ...OAI_OPTIONS,
105
- from: '2024-01-01',
106
- until: '2024-01-02',
107
- maxRecords,
108
- })) {
109
- iteratedRecords.push(record);
110
- }
111
- allResult = await oaiListRecordsAll('oai_dc', {
112
- ...OAI_OPTIONS,
113
- from: '2024-01-01',
114
- until: '2024-01-02',
115
- maxRecords,
116
- });
117
- } catch (error) {
118
- console.error('oaiListRecordsAsyncIterator failed:', error);
119
- throw new Error(
120
- `OAI ListRecordsAsyncIterator failed: ${error instanceof Error ? error.message : String(error)}`
121
- );
122
- }
123
-
124
- expect(Array.isArray(iteratedRecords)).toBe(true);
125
- expect(iteratedRecords.length).toBeLessThanOrEqual(25);
126
- expect(iteratedRecords.length).toBe(allResult.records.length);
127
- }, 30000);
128
-
129
- it('oaiListIdentifiersAll returns headers across pages with maxHeaders cap', async () => {
130
- let result;
131
- try {
132
- result = await oaiListIdentifiersAll('oai_dc', {
133
- ...OAI_OPTIONS,
134
- from: '2024-01-01',
135
- until: '2024-01-02',
136
- maxHeaders: 50,
137
- });
138
- } catch (error) {
139
- console.error('oaiListIdentifiersAll failed:', error);
140
- throw new Error(
141
- `OAI ListIdentifiersAll failed: ${error instanceof Error ? error.message : String(error)}`
142
- );
143
- }
144
- expect(Array.isArray(result.headers)).toBe(true);
145
- expect(result.headers.length).toBeLessThanOrEqual(50);
146
- if (result.headers.length > 0) {
147
- expect(result.headers[0].identifier).toBeTruthy();
148
- expect(result.headers[0].datestamp).toBeTruthy();
149
- }
150
- }, 30000);
151
-
152
- it('oaiListIdentifiersAsyncIterator yields headers and honors maxHeaders', async () => {
153
- const headers = [];
154
- try {
155
- for await (const header of oaiListIdentifiersAsyncIterator('oai_dc', {
156
- ...OAI_OPTIONS,
157
- from: '2024-01-01',
158
- until: '2024-01-02',
159
- maxHeaders: 20,
160
- })) {
161
- headers.push(header);
162
- }
163
- } catch (error) {
164
- console.error('oaiListIdentifiersAsyncIterator failed:', error);
165
- throw new Error(
166
- `OAI ListIdentifiersAsyncIterator failed: ${error instanceof Error ? error.message : String(error)}`
167
- );
168
- }
169
-
170
- expect(Array.isArray(headers)).toBe(true);
171
- expect(headers.length).toBeLessThanOrEqual(20);
172
- if (headers.length > 0) {
173
- expect(headers[0].identifier).toBeTruthy();
174
- expect(headers[0].datestamp).toBeTruthy();
175
- }
176
- }, 30000);
177
-
178
- it('oaiListSetsAll returns sets with maxSets cap', async () => {
179
- let result;
180
- try {
181
- result = await oaiListSetsAll({
182
- ...OAI_OPTIONS,
183
- maxSets: 20,
184
- });
185
- } catch (error) {
186
- console.error('oaiListSetsAll failed:', error);
187
- throw new Error(
188
- `OAI ListSetsAll failed: ${error instanceof Error ? error.message : String(error)}`
189
- );
190
- }
191
- expect(Array.isArray(result.sets)).toBe(true);
192
- expect(result.sets.length).toBeLessThanOrEqual(20);
193
- if (result.sets.length > 0) {
194
- expect(result.sets[0].setSpec).toBeTruthy();
195
- expect(result.sets[0].setName).toBeTruthy();
196
- }
197
- }, 30000);
198
-
199
- it('oaiListSetsAsyncIterator yields sets and honors maxSets', async () => {
200
- const sets = [];
201
- try {
202
- for await (const set of oaiListSetsAsyncIterator({
203
- ...OAI_OPTIONS,
204
- maxSets: 10,
205
- })) {
206
- sets.push(set);
207
- }
208
- } catch (error) {
209
- console.error('oaiListSetsAsyncIterator failed:', error);
210
- throw new Error(
211
- `OAI ListSetsAsyncIterator failed: ${error instanceof Error ? error.message : String(error)}`
212
- );
213
- }
214
-
215
- expect(Array.isArray(sets)).toBe(true);
216
- expect(sets.length).toBeLessThanOrEqual(10);
217
- if (sets.length > 0) {
218
- expect(sets[0].setSpec).toBeTruthy();
219
- expect(sets[0].setName).toBeTruthy();
220
- }
221
- }, 30000);
222
- });
1
+ /**
2
+ * Integration tests for the arXiv OAI-PMH interface (real HTTP calls).
3
+ * Conservative request size and rate; same pattern as arxivAPI.integration.test.ts.
4
+ */
5
+ import { describe, it, expect } from 'vitest';
6
+ import {
7
+ oaiIdentify,
8
+ oaiListRecords,
9
+ oaiListRecordsAsyncIterator,
10
+ oaiListRecordsAll,
11
+ oaiListIdentifiersAsyncIterator,
12
+ oaiListIdentifiersAll,
13
+ oaiListSetsAsyncIterator,
14
+ oaiListSetsAll,
15
+ } from '../src/oaiClient.js';
16
+ import { OaiError } from '../src/oaiTypes.js';
17
+
18
+ const OAI_OPTIONS = {
19
+ timeoutMs: 15000,
20
+ retries: 2,
21
+ rateLimit: { tokensPerInterval: 1, intervalMs: 1000 },
22
+ userAgent: 'arxiv-api-wrapper-tests/1.0',
23
+ };
24
+
25
+ describe('OAI-PMH integration', () => {
26
+ it('oaiIdentify returns repository info and protocol version 2.0', async () => {
27
+ let result;
28
+ try {
29
+ result = await oaiIdentify(OAI_OPTIONS);
30
+ } catch (error) {
31
+ console.error('oaiIdentify failed:', error);
32
+ throw new Error(
33
+ `OAI Identify failed: ${error instanceof Error ? error.message : String(error)}`
34
+ );
35
+ }
36
+ expect(result.repositoryName).toBeTruthy();
37
+ expect(result.baseURL).toContain('oaipmh.arxiv.org');
38
+ expect(result.protocolVersion).toBe('2.0');
39
+ expect(Array.isArray(result.adminEmail)).toBe(true);
40
+ expect(result.earliestDatestamp).toBeTruthy();
41
+ }, 30000);
42
+
43
+ it('oaiListRecords returns one page of records with header and metadata', async () => {
44
+ let result;
45
+ try {
46
+ result = await oaiListRecords('oai_dc', {
47
+ ...OAI_OPTIONS,
48
+ from: '2024-01-01',
49
+ until: '2024-01-02',
50
+ });
51
+ } catch (error) {
52
+ console.error('oaiListRecords failed:', error);
53
+ throw new Error(
54
+ `OAI ListRecords failed: ${error instanceof Error ? error.message : String(error)}`
55
+ );
56
+ }
57
+ expect(Array.isArray(result.records)).toBe(true);
58
+ if (result.records.length > 0) {
59
+ const rec = result.records[0];
60
+ expect(rec.header).toBeTruthy();
61
+ expect(rec.header.identifier).toBeTruthy();
62
+ expect(rec.header.datestamp).toBeTruthy();
63
+ expect(rec.metadata).toBeDefined();
64
+ expect(typeof rec.metadata).toBe('object');
65
+ }
66
+ // May or may not have resumptionToken depending on result size
67
+ if (result.resumptionToken) {
68
+ expect(result.resumptionToken.value).toBeTruthy();
69
+ }
70
+ }, 30000);
71
+
72
+ it('oaiListRecords continuation requests work with resumptionToken-only options', async () => {
73
+ const firstPage = await oaiListRecords('oai_dc', OAI_OPTIONS);
74
+ expect(firstPage.resumptionToken?.value).toBeTruthy();
75
+
76
+ const secondPage = await oaiListRecords('oai_dc', {
77
+ ...OAI_OPTIONS,
78
+ resumptionToken: firstPage.resumptionToken!.value,
79
+ });
80
+
81
+ expect(Array.isArray(secondPage.records)).toBe(true);
82
+ }, 30000);
83
+
84
+ it('throws a local OaiError for resumptionToken combined with selective params', async () => {
85
+ const invalidOptions = {
86
+ ...OAI_OPTIONS,
87
+ from: '2024-01-01',
88
+ resumptionToken: 'fake-token',
89
+ } as unknown as Parameters<typeof oaiListRecords>[1];
90
+
91
+ await expect(
92
+ oaiListRecords('oai_dc', invalidOptions)
93
+ ).rejects.toBeInstanceOf(OaiError);
94
+ });
95
+
96
+ it('oaiListRecordsAll returns records across all pages within a small date range', async () => {
97
+ let result;
98
+ try {
99
+ result = await oaiListRecordsAll('oai_dc', {
100
+ ...OAI_OPTIONS,
101
+ from: '2024-01-01',
102
+ until: '2024-01-02',
103
+ maxRecords: 200,
104
+ });
105
+ } catch (error) {
106
+ console.error('oaiListRecordsAll failed:', error);
107
+ throw new Error(
108
+ `OAI ListRecordsAll failed: ${error instanceof Error ? error.message : String(error)}`
109
+ );
110
+ }
111
+
112
+ expect(Array.isArray(result.records)).toBe(true);
113
+ if (result.records.length > 0) {
114
+ const rec = result.records[0];
115
+ expect(rec.header).toBeTruthy();
116
+ expect(rec.header.identifier).toBeTruthy();
117
+ expect(rec.header.datestamp).toBeTruthy();
118
+ expect(rec.metadata).toBeDefined();
119
+ expect(typeof rec.metadata).toBe('object');
120
+ }
121
+ }, 30000);
122
+
123
+ it('oaiListRecordsAsyncIterator yields records and matches oaiListRecordsAll count for the same cap', async () => {
124
+ let iteratedRecords: unknown[] = [];
125
+ let allResult;
126
+ try {
127
+ const maxRecords = 25;
128
+ for await (const record of oaiListRecordsAsyncIterator('oai_dc', {
129
+ ...OAI_OPTIONS,
130
+ from: '2024-01-01',
131
+ until: '2024-01-02',
132
+ maxRecords,
133
+ })) {
134
+ iteratedRecords.push(record);
135
+ }
136
+ allResult = await oaiListRecordsAll('oai_dc', {
137
+ ...OAI_OPTIONS,
138
+ from: '2024-01-01',
139
+ until: '2024-01-02',
140
+ maxRecords,
141
+ });
142
+ } catch (error) {
143
+ console.error('oaiListRecordsAsyncIterator failed:', error);
144
+ throw new Error(
145
+ `OAI ListRecordsAsyncIterator failed: ${error instanceof Error ? error.message : String(error)}`
146
+ );
147
+ }
148
+
149
+ expect(Array.isArray(iteratedRecords)).toBe(true);
150
+ expect(iteratedRecords.length).toBeLessThanOrEqual(25);
151
+ expect(iteratedRecords.length).toBe(allResult.records.length);
152
+ }, 30000);
153
+
154
+ it('oaiListIdentifiersAll returns headers across pages with maxHeaders cap', async () => {
155
+ let result;
156
+ try {
157
+ result = await oaiListIdentifiersAll('oai_dc', {
158
+ ...OAI_OPTIONS,
159
+ from: '2024-01-01',
160
+ until: '2024-01-02',
161
+ maxHeaders: 50,
162
+ });
163
+ } catch (error) {
164
+ console.error('oaiListIdentifiersAll failed:', error);
165
+ throw new Error(
166
+ `OAI ListIdentifiersAll failed: ${error instanceof Error ? error.message : String(error)}`
167
+ );
168
+ }
169
+ expect(Array.isArray(result.headers)).toBe(true);
170
+ expect(result.headers.length).toBeLessThanOrEqual(50);
171
+ if (result.headers.length > 0) {
172
+ expect(result.headers[0].identifier).toBeTruthy();
173
+ expect(result.headers[0].datestamp).toBeTruthy();
174
+ }
175
+ }, 30000);
176
+
177
+ it('oaiListIdentifiersAsyncIterator yields headers and honors maxHeaders', async () => {
178
+ const headers = [];
179
+ try {
180
+ for await (const header of oaiListIdentifiersAsyncIterator('oai_dc', {
181
+ ...OAI_OPTIONS,
182
+ from: '2024-01-01',
183
+ until: '2024-01-02',
184
+ maxHeaders: 20,
185
+ })) {
186
+ headers.push(header);
187
+ }
188
+ } catch (error) {
189
+ console.error('oaiListIdentifiersAsyncIterator failed:', error);
190
+ throw new Error(
191
+ `OAI ListIdentifiersAsyncIterator failed: ${error instanceof Error ? error.message : String(error)}`
192
+ );
193
+ }
194
+
195
+ expect(Array.isArray(headers)).toBe(true);
196
+ expect(headers.length).toBeLessThanOrEqual(20);
197
+ if (headers.length > 0) {
198
+ expect(headers[0].identifier).toBeTruthy();
199
+ expect(headers[0].datestamp).toBeTruthy();
200
+ }
201
+ }, 30000);
202
+
203
+ it('oaiListSetsAll returns sets with maxSets cap', async () => {
204
+ let result;
205
+ try {
206
+ result = await oaiListSetsAll({
207
+ ...OAI_OPTIONS,
208
+ maxSets: 20,
209
+ });
210
+ } catch (error) {
211
+ console.error('oaiListSetsAll failed:', error);
212
+ throw new Error(
213
+ `OAI ListSetsAll failed: ${error instanceof Error ? error.message : String(error)}`
214
+ );
215
+ }
216
+ expect(Array.isArray(result.sets)).toBe(true);
217
+ expect(result.sets.length).toBeLessThanOrEqual(20);
218
+ if (result.sets.length > 0) {
219
+ expect(result.sets[0].setSpec).toBeTruthy();
220
+ expect(result.sets[0].setName).toBeTruthy();
221
+ }
222
+ }, 30000);
223
+
224
+ it('oaiListSetsAsyncIterator yields sets and honors maxSets', async () => {
225
+ const sets = [];
226
+ try {
227
+ for await (const set of oaiListSetsAsyncIterator({
228
+ ...OAI_OPTIONS,
229
+ maxSets: 10,
230
+ })) {
231
+ sets.push(set);
232
+ }
233
+ } catch (error) {
234
+ console.error('oaiListSetsAsyncIterator failed:', error);
235
+ throw new Error(
236
+ `OAI ListSetsAsyncIterator failed: ${error instanceof Error ? error.message : String(error)}`
237
+ );
238
+ }
239
+
240
+ expect(Array.isArray(sets)).toBe(true);
241
+ expect(sets.length).toBeLessThanOrEqual(10);
242
+ if (sets.length > 0) {
243
+ expect(sets[0].setSpec).toBeTruthy();
244
+ expect(sets[0].setName).toBeTruthy();
245
+ }
246
+ }, 30000);
247
+ });