@natlibfi/melinda-record-matching 5.1.4 → 5.1.5-alpha.1
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.
- package/dist/candidate-search/index.js +2 -0
- package/dist/candidate-search/index.js.map +2 -2
- package/dist/candidate-search/query-list/bib.js +16 -3
- package/dist/candidate-search/query-list/bib.js.map +2 -2
- package/dist/match-detection/features/bib/title.js +51 -5
- package/dist/match-detection/features/bib/title.js.map +3 -3
- package/dist/matching-utils.js +39 -0
- package/dist/matching-utils.js.map +2 -2
- package/package.json +9 -11
- package/src/candidate-search/index.js +3 -0
- package/src/candidate-search/query-list/bib.js +24 -4
- package/src/match-detection/features/bib/title.js +62 -7
- package/src/matching-utils.js +50 -0
- package/{src → test}/candidate-search/index.test.js +3 -1
- package/{src → test}/candidate-search/query-list/auth.test.js +1 -1
- package/{src → test}/candidate-search/query-list/bib.test.js +1 -1
- package/{src → test}/index.test.js +10 -5
- package/{src → test}/match-detection/features/auth/index.test.js +1 -1
- package/{src → test}/match-detection/features/bib/index.test.js +1 -1
- package/{src → test}/match-detection/index.test.js +2 -2
- package/dist/candidate-search/index.test.js +0 -89
- package/dist/candidate-search/index.test.js.map +0 -7
- package/dist/candidate-search/query-list/auth.test.js +0 -31
- package/dist/candidate-search/query-list/auth.test.js.map +0 -7
- package/dist/candidate-search/query-list/bib.test.js +0 -31
- package/dist/candidate-search/query-list/bib.test.js.map +0 -7
- package/dist/index.test.js +0 -69
- package/dist/index.test.js.map +0 -7
- package/dist/match-detection/features/auth/index.test.js +0 -37
- package/dist/match-detection/features/auth/index.test.js.map +0 -7
- package/dist/match-detection/features/bib/index.test.js +0 -37
- package/dist/match-detection/features/bib/index.test.js.map +0 -7
- package/dist/match-detection/index.test.js +0 -40
- package/dist/match-detection/index.test.js.map +0 -7
|
@@ -4,6 +4,7 @@ import {MarcRecord} from '@natlibfi/marc-record';
|
|
|
4
4
|
|
|
5
5
|
import generateQueryList from './query-list/index.js';
|
|
6
6
|
import chooseQueries from './choose-queries.js';
|
|
7
|
+
import {preferenceSortRecords} from '../matching-utils.js';
|
|
7
8
|
|
|
8
9
|
export {searchTypes} from './query-list/index.js';
|
|
9
10
|
|
|
@@ -96,6 +97,8 @@ export default async ({record, searchSpec, url, maxCandidates, maxRecordsPerRequ
|
|
|
96
97
|
}
|
|
97
98
|
debug(`Query ${queryOffset} ${query} done.`);
|
|
98
99
|
debug(`There are (${queryList.length - (queryOffset + 1)} queries left)`);
|
|
100
|
+
|
|
101
|
+
records.sort(preferenceSortRecords);
|
|
99
102
|
return {records, queryOffset: queryOffset + 1, queriesLeft: queryList.length - (queryOffset + 1), totalRecords: newTotalRecords, query, searchCounter: newSearchCounter, queryCandidateCounter: newQueryCandidateCounter, queryCounter: newQueryCounter, maxedQueries: newMaxedQueries};
|
|
100
103
|
}
|
|
101
104
|
|
|
@@ -435,7 +435,8 @@ export function bibStandardIdentifiers(record) {
|
|
|
435
435
|
|
|
436
436
|
// DEVELOP: should we query also f015 and f028?
|
|
437
437
|
|
|
438
|
-
const fields = record.get(/^(?<def>020|022|024)$/u);
|
|
438
|
+
// const fields = record.get(/^(?<def>020|022|024)$/u);
|
|
439
|
+
const fields = record.get(/^(?<def>015|020|022|024|028)$/u);
|
|
439
440
|
const identifiers = [].concat(...fields.map(toIdentifiers));
|
|
440
441
|
const uniqueIdentifiers = [...new Set(identifiers)];
|
|
441
442
|
|
|
@@ -454,10 +455,10 @@ export function bibStandardIdentifiers(record) {
|
|
|
454
455
|
const issnIsbnReqExp = (/^[A-Za-z0-9-]+$/u);
|
|
455
456
|
const otherIdReqExp = (/^[A-Za-z0-9-:]+$/u);
|
|
456
457
|
|
|
457
|
-
if (tag === '
|
|
458
|
+
if (tag === '015') { // TODO: test (does this use the right index etc.)
|
|
458
459
|
return subfields
|
|
459
|
-
.filter(
|
|
460
|
-
.map(
|
|
460
|
+
.filter(sf => sf.code === 'a' && sf.value.match(/^[A-Z0-9\-]+$/ui))
|
|
461
|
+
.map(sf => sf.value)
|
|
461
462
|
}
|
|
462
463
|
|
|
463
464
|
if (tag === '020') {
|
|
@@ -466,6 +467,25 @@ export function bibStandardIdentifiers(record) {
|
|
|
466
467
|
.map(({value}) => String(value));
|
|
467
468
|
}
|
|
468
469
|
|
|
470
|
+
if (tag === '022') {
|
|
471
|
+
return subfields
|
|
472
|
+
.filter(sub => ['a', 'z', 'y'].includes(sub.code) && testStringOrNumber(sub.value) && issnIsbnReqExp.test(String(sub.value)))
|
|
473
|
+
.map(({value}) => String(value));
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (tag === '028') { // TODO: test
|
|
477
|
+
const a = subfields.find(sf => sf.code === 'a');
|
|
478
|
+
if (a) {
|
|
479
|
+
const b = subfields.find(sf => sf.code === 'b');
|
|
480
|
+
// TODO: normalize?
|
|
481
|
+
if (b) {
|
|
482
|
+
return [a.value, `${b.value} ${a.value}`];
|
|
483
|
+
}
|
|
484
|
+
return [a.value];
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Default seems to be 024:
|
|
469
489
|
return subfields
|
|
470
490
|
.filter(sub => ['a', 'z'].includes(sub.code) && testStringOrNumber(sub.value) && otherIdReqExp.test(String(sub.value)))
|
|
471
491
|
.map(({value}) => String(value));
|
|
@@ -40,7 +40,10 @@ export default ({threshold = 0.9} = {}) => ({
|
|
|
40
40
|
// - note: combined with decomposing unicode diacritics this normalizes both 'saa' and 'sää' as 'saa'
|
|
41
41
|
// - we could precompose the Finnish letters back to avoid this
|
|
42
42
|
// - see validator normalize-utf8-diacritics for details
|
|
43
|
-
.replace(/[^\p{Letter}\p{Number}&]/gu, '')
|
|
43
|
+
.replace(/[^\p{Letter}\p{Number}& ]/gu, '') // 2026-07-01: keep ' '. We need it for splitToBaseAndNumber()
|
|
44
|
+
.replace(/\s+/ug, ' ')
|
|
45
|
+
.replace(/ $/u, '')
|
|
46
|
+
.replace(/^ /u, '')
|
|
44
47
|
.toLowerCase();
|
|
45
48
|
}
|
|
46
49
|
|
|
@@ -52,11 +55,12 @@ export default ({threshold = 0.9} = {}) => ({
|
|
|
52
55
|
|
|
53
56
|
const [aa, ab, an, ap] = a;
|
|
54
57
|
const [ba, bb, bn, bp] = b;
|
|
58
|
+
debugData(`COMPARE:\n['${aa}', '${ab}', '${an}', '${ap}'] VS\n['${ba}', '${bb}', '${bn}', '${bp}']`);
|
|
55
59
|
|
|
56
60
|
const aFull = toFullTitle(a);
|
|
57
61
|
const bFull = toFullTitle(b);
|
|
58
62
|
|
|
59
|
-
|
|
63
|
+
// debugData(`JOINED COMPARE:\n '${aFull}' vs \n '${bFull}'`);
|
|
60
64
|
|
|
61
65
|
if (isEmpty(aa) || isEmpty(ba)) {
|
|
62
66
|
return -1.0;
|
|
@@ -66,6 +70,11 @@ export default ({threshold = 0.9} = {}) => ({
|
|
|
66
70
|
return TITLE_MAX;
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
// MELKEHITYS-3496-ish: move part of $a to $n:
|
|
74
|
+
const [altAa, altAn, altBa, altBn] = reconstructTitleAndNumber(aa, an, ba, bn);
|
|
75
|
+
if (altAa) {
|
|
76
|
+
return compare2([altAa, ab, altAn, ap], [altBa, bb, altBn, bp]);
|
|
77
|
+
}
|
|
69
78
|
|
|
70
79
|
if (ab && ab !== '' && ab === bb) { // Remove $b from equation (MELKEHITYS-3494)
|
|
71
80
|
debug(`Ignore \$b ${ab}`);
|
|
@@ -79,7 +88,7 @@ export default ({threshold = 0.9} = {}) => ({
|
|
|
79
88
|
debug(`Ignore \$p ${ap}`);
|
|
80
89
|
return compare2([aa, ab, an, ''], [ba, bb, bn, '']);
|
|
81
90
|
}
|
|
82
|
-
// There seems to be wobble between $b and $p
|
|
91
|
+
// There seems to be wobble between $b and $p:
|
|
83
92
|
if (!isEmpty(ab) && ab === bp) {
|
|
84
93
|
return compare2([aa, '', an, ap], [ba, bb, bn, '']);
|
|
85
94
|
}
|
|
@@ -103,7 +112,7 @@ export default ({threshold = 0.9} = {}) => ({
|
|
|
103
112
|
return -1.0;
|
|
104
113
|
}
|
|
105
114
|
|
|
106
|
-
const [distance, maxLength, correctness] = doLevenshtein(aFull, bFull);
|
|
115
|
+
const [distance, maxLength, correctness] = doLevenshtein(aFull.replace(/ /ug, ''), bFull.replace(/ /ug, ''));
|
|
107
116
|
|
|
108
117
|
debug(`'${aFull}' vs '${bFull}': Max length = ${maxLength}, distance = ${distance}, correctness = ${correctness}`);
|
|
109
118
|
|
|
@@ -122,16 +131,16 @@ export default ({threshold = 0.9} = {}) => ({
|
|
|
122
131
|
return compare2([aa, '', an, ap], [ba, bb.substring(ab.length), bn, bp]);
|
|
123
132
|
}
|
|
124
133
|
}
|
|
125
|
-
|
|
134
|
+
|
|
126
135
|
// Try the same without $p:
|
|
127
136
|
if (localXor(ap, bp)) {
|
|
128
137
|
const result = compare2([aa, ab, an, ''], [ba, bb, bn, '']);
|
|
129
138
|
return result > 0.0 ? result * 0.8 : result;
|
|
130
139
|
}
|
|
131
|
-
|
|
140
|
+
|
|
132
141
|
if (aa === ba && an === bn) {
|
|
133
|
-
// No $n: Don't score $a vs $ab. Return 0, and let other match detectors decide
|
|
134
142
|
const candScore = TITLE_MAX/2;
|
|
143
|
+
// $b is missing from one:
|
|
135
144
|
if (ap === bp && localXor(ab, bb)) {
|
|
136
145
|
debug(`Handle omitted \$b using x ${candScore}`);
|
|
137
146
|
return candScore;
|
|
@@ -142,6 +151,17 @@ export default ({threshold = 0.9} = {}) => ({
|
|
|
142
151
|
}
|
|
143
152
|
}
|
|
144
153
|
|
|
154
|
+
if (an === bn && ap === bp ) {
|
|
155
|
+
// $b-less $a is other record's $b, see MELKEHITYS-3485 for motivation (though we skip 246 and 490 here):
|
|
156
|
+
const candScore = TITLE_MAX/2;
|
|
157
|
+
if (aa === bb && !ab) {
|
|
158
|
+
return candScore;
|
|
159
|
+
}
|
|
160
|
+
if (ba === ab && !bb) {
|
|
161
|
+
return candScore;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
145
165
|
return -0.5; // Not likely
|
|
146
166
|
}
|
|
147
167
|
|
|
@@ -174,6 +194,41 @@ export default ({threshold = 0.9} = {}) => ({
|
|
|
174
194
|
return str1.length > str2.length ? str1.length : str2.length;
|
|
175
195
|
}
|
|
176
196
|
|
|
197
|
+
function reconstructTitleAndNumber(a1, n1, a2, n2) {
|
|
198
|
+
if (!n1 && n2) {
|
|
199
|
+
const [altA1, altN1] = splitToBaseAndNumber(a1);
|
|
200
|
+
if (altN1) {
|
|
201
|
+
debugData(`A: Reconstruct:\n $a '${a1}' =>\n $a '${altA1}' +\n $n '${altN1}`);
|
|
202
|
+
return [altA1, altN1, a2, n2];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (n1 && !n2) {
|
|
206
|
+
const [altA2, altN2] = splitToBaseAndNumber(a2);
|
|
207
|
+
if (altN2) {
|
|
208
|
+
debugData(`B: Reconstruct $a '${a2}' as $a '${altA2}' and $n '${altN2}`);
|
|
209
|
+
return [a1, n1, altA2, altN2];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
return [null, null, null, null]; // Fail
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
function splitToBaseAndNumber(val) {
|
|
218
|
+
const words = val.split(' ');
|
|
219
|
+
const len = words.length;
|
|
220
|
+
if (len < 2 || !words[len-1].match(/^[1-9][0-9]*$/)) {
|
|
221
|
+
return [val, null];
|
|
222
|
+
}
|
|
223
|
+
if (len > 2 && words[len-2].match(/^(del|osa|vol|volume)$/u)) { // NB! "vol." has lost it's '.' during normalization!
|
|
224
|
+
const number = `${words[len-2]} ${words[len-1]}`;
|
|
225
|
+
return [words.slice(0, len-2).join(' '), number];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const number = words.pop();
|
|
229
|
+
return [words.join(' '), number];
|
|
230
|
+
}
|
|
231
|
+
|
|
177
232
|
}
|
|
178
233
|
});
|
|
179
234
|
|
package/src/matching-utils.js
CHANGED
|
@@ -129,3 +129,53 @@ export function stringBefore(string, substring) {
|
|
|
129
129
|
}
|
|
130
130
|
return string.substring(0, pos);
|
|
131
131
|
}
|
|
132
|
+
|
|
133
|
+
// We sort records here (put FIKKA and low-cased records first, ref MELINDA-3492)
|
|
134
|
+
// NB! this only sorts the records the current set. If the best stuff would come in a later set and this set contains good enough record, we'll never reach the best...
|
|
135
|
+
// Still this would alleviate the issue when maxMatches === 1...
|
|
136
|
+
export function preferenceSortRecords(x, y) {
|
|
137
|
+
debug(`sortRecords: RECORD COMPARISON\n${JSON.stringify(x)} vs\n${JSON.stringify(y)}`);
|
|
138
|
+
// Prefer the record with a FIKKA LOW tag:
|
|
139
|
+
const xFikka = hasLow(x.record, 'FIKKA');
|
|
140
|
+
const yFikka = hasLow(y.record, 'FIKKA');
|
|
141
|
+
|
|
142
|
+
if (xFikka && !yFikka) {
|
|
143
|
+
debug('sortRecords: prefer FIKKA');
|
|
144
|
+
return -1;
|
|
145
|
+
}
|
|
146
|
+
if (yFikka && !xFikka) {
|
|
147
|
+
return 1;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Prefer downcased:
|
|
151
|
+
const xAllCaps = hasAllCapsTitle(x.record);
|
|
152
|
+
const yAllCaps = hasAllCapsTitle(y.record);
|
|
153
|
+
|
|
154
|
+
if (!xAllCaps && yAllCaps) {
|
|
155
|
+
debug('sortRecords: prefer non-all caps');
|
|
156
|
+
return -1; // x is better
|
|
157
|
+
}
|
|
158
|
+
if (!yAllCaps && xAllCaps) {
|
|
159
|
+
return 1; // y is better
|
|
160
|
+
}
|
|
161
|
+
// Default:
|
|
162
|
+
return 0;
|
|
163
|
+
|
|
164
|
+
function hasLow(record, lowTag) {
|
|
165
|
+
return record.fields?.some(f => f.tag === 'LOW' && f.subfields?.some(sf => sf.code === 'a' && sf.value === lowTag));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function hasAllCapsTitle(record) {
|
|
169
|
+
const f245 = record.fields?.find(f => f.tag === '245');
|
|
170
|
+
if (!f245) {
|
|
171
|
+
debugData(`WARNING: No f245 found for ALL CAPS check`);
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const title = f245.subfields.filter(sf => ['a', 'b', 'c'].includes(sf.code)).map(sf => sf.value).join(' ');
|
|
176
|
+
if (title.match(/[A-Z]/u) && !title.match(/[a-z]/u)) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -4,11 +4,13 @@ import {READERS} from '@natlibfi/fixura';
|
|
|
4
4
|
import generateTests from '@natlibfi/fixugen-http-client';
|
|
5
5
|
import {MarcRecord} from '@natlibfi/marc-record';
|
|
6
6
|
import {Error as MatchingError} from '@natlibfi/melinda-commons';
|
|
7
|
-
import createSearchInterface, {CandidateSearchError} from '
|
|
7
|
+
import createSearchInterface, {CandidateSearchError} from '../../src/candidate-search/index.js';
|
|
8
8
|
import createDebugLogger from 'debug';
|
|
9
9
|
|
|
10
10
|
const debug = createDebugLogger('@natlibfi/melinda-record-matching:candidate-search:test');
|
|
11
11
|
|
|
12
|
+
// NOTE: it seems that "only" does not work when using generateTests from @natlibfi/fixugen-http-client'
|
|
13
|
+
|
|
12
14
|
describe('candidate-search', () => {
|
|
13
15
|
generateTests({
|
|
14
16
|
callback,
|
|
@@ -4,7 +4,7 @@ import createDebugLogger from 'debug';
|
|
|
4
4
|
import generateTests from '@natlibfi/fixugen';
|
|
5
5
|
import {READERS} from '@natlibfi/fixura';
|
|
6
6
|
import {MarcRecord} from '@natlibfi/marc-record';
|
|
7
|
-
import * as generators from '
|
|
7
|
+
import * as generators from '../../../src/candidate-search/query-list/auth.js';
|
|
8
8
|
|
|
9
9
|
const debug = createDebugLogger('@natlibfi/melinda-record-matching:candidate-search:query-list:auth-test');
|
|
10
10
|
const debugData = debug.extend('data');
|
|
@@ -4,7 +4,7 @@ import createDebugLogger from 'debug';
|
|
|
4
4
|
import generateTests from '@natlibfi/fixugen';
|
|
5
5
|
import {READERS} from '@natlibfi/fixura';
|
|
6
6
|
import {MarcRecord} from '@natlibfi/marc-record';
|
|
7
|
-
import * as generators from '
|
|
7
|
+
import * as generators from '../../../src/candidate-search/query-list/bib.js';
|
|
8
8
|
|
|
9
9
|
const debug = createDebugLogger('@natlibfi/melinda-record-matching:candidate-search:query:bibHostComponents:test');
|
|
10
10
|
const debugData = debug.extend('data');
|
|
@@ -4,12 +4,14 @@ import createDebugLogger from 'debug';
|
|
|
4
4
|
import {READERS} from '@natlibfi/fixura';
|
|
5
5
|
import generateTests from '@natlibfi/fixugen-http-client';
|
|
6
6
|
import {MarcRecord} from '@natlibfi/marc-record';
|
|
7
|
-
import createMatchInterface, {matchDetection} from '
|
|
7
|
+
import createMatchInterface, {matchDetection} from '../src/index.js';
|
|
8
8
|
|
|
9
9
|
const debug = createDebugLogger('@natlibfi/melinda-record-matching:index:test');
|
|
10
10
|
const debugData = debug.extend('data');
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
// NOTE: it seems that "only" does not work when using generateTests from @natlibfi/fixugen-http-client'
|
|
13
|
+
|
|
14
|
+
describe('INDEX-FOO', () => {
|
|
13
15
|
generateTests({
|
|
14
16
|
callback,
|
|
15
17
|
path: [import.meta.dirname, '..', 'test-fixtures', 'index'],
|
|
@@ -21,12 +23,15 @@ describe('INDEX', () => {
|
|
|
21
23
|
|
|
22
24
|
async function callback({getFixture, options, expectedMatchStatus, expectedStopReason, expectedFailures, expectedCandidateCount}) {
|
|
23
25
|
|
|
26
|
+
debug(`Running tests from indezx.test.js`);
|
|
27
|
+
|
|
24
28
|
const record = new MarcRecord(getFixture('inputRecord.json'), {subfieldValues: false});
|
|
25
29
|
const expectedMatches = getFixture('expectedMatches.json');
|
|
26
30
|
const expectedNonMatches = getFixture('expectedNonMatches.json') || [];
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
debugData(`Options: ${JSON.stringify(options)}`);
|
|
33
|
+
debugData(`Formatted options: ${JSON.stringify(formatOptions(options))}`);
|
|
34
|
+
const match = await createMatchInterface(formatOptions(options));
|
|
30
35
|
const {matches, matchStatus, nonMatches, conversionFailures, candidateCount} = await match({record});
|
|
31
36
|
debugData(`Matches: ${matches.length}, Status: ${matchStatus.status}/${matchStatus.stopReason}, NonMatches: ${nonMatches ? nonMatches.length : 'not returned'}, ConversionFailures: ${conversionFailures ? conversionFailures.length : 'not returned'}`);
|
|
32
37
|
|
|
@@ -44,7 +49,7 @@ describe('INDEX', () => {
|
|
|
44
49
|
assert.deepStrictEqual(conversionFailures, expectedFailures);
|
|
45
50
|
}
|
|
46
51
|
|
|
47
|
-
function formatOptions() {
|
|
52
|
+
function formatOptions(options) {
|
|
48
53
|
const contextFeatures = matchDetection.features[options.detection.strategy.type];
|
|
49
54
|
|
|
50
55
|
return {
|
|
@@ -5,7 +5,7 @@ import createDebugLogger from 'debug';
|
|
|
5
5
|
import generateTests from '@natlibfi/fixugen';
|
|
6
6
|
import {READERS} from '@natlibfi/fixura';
|
|
7
7
|
import {MarcRecord} from '@natlibfi/marc-record';
|
|
8
|
-
import * as features from '
|
|
8
|
+
import * as features from '../../../../src/match-detection/features/auth/index.js';
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
const debug = createDebugLogger('@natlibfi/melinda-record-matching:match-detection:features/auth:test');
|
|
@@ -5,7 +5,7 @@ import createDebugLogger from 'debug';
|
|
|
5
5
|
import generateTests from '@natlibfi/fixugen';
|
|
6
6
|
import {READERS} from '@natlibfi/fixura';
|
|
7
7
|
import {MarcRecord} from '@natlibfi/marc-record';
|
|
8
|
-
import * as features from '
|
|
8
|
+
import * as features from '../../../../src/match-detection/features/bib/index.js';
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
const debug = createDebugLogger('@natlibfi/melinda-record-matching:match-detection:features/bib:test');
|
|
@@ -6,8 +6,8 @@ import generateTests from '@natlibfi/fixugen';
|
|
|
6
6
|
import {READERS} from '@natlibfi/fixura';
|
|
7
7
|
import {MarcRecord} from '@natlibfi/marc-record';
|
|
8
8
|
|
|
9
|
-
import createDetectionInterface from '
|
|
10
|
-
import * as features from '
|
|
9
|
+
import createDetectionInterface from '../../src/match-detection/index.js';
|
|
10
|
+
import * as features from '../../src/match-detection/features/index.js';
|
|
11
11
|
|
|
12
12
|
const debug = createDebugLogger('@natlibfi/melinda-record-matching:match-detection:test');
|
|
13
13
|
const debugData = debug.extend('data');
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
import { describe } from "node:test";
|
|
3
|
-
import { READERS } from "@natlibfi/fixura";
|
|
4
|
-
import generateTests from "@natlibfi/fixugen-http-client";
|
|
5
|
-
import { MarcRecord } from "@natlibfi/marc-record";
|
|
6
|
-
import { Error as MatchingError } from "@natlibfi/melinda-commons";
|
|
7
|
-
import createSearchInterface, { CandidateSearchError } from "./index.js";
|
|
8
|
-
import createDebugLogger from "debug";
|
|
9
|
-
const debug = createDebugLogger("@natlibfi/melinda-record-matching:candidate-search:test");
|
|
10
|
-
describe("candidate-search", () => {
|
|
11
|
-
generateTests({
|
|
12
|
-
callback,
|
|
13
|
-
path: [import.meta.dirname, "..", "..", "test-fixtures", "candidate-search", "index"],
|
|
14
|
-
recurse: false,
|
|
15
|
-
fixura: {
|
|
16
|
-
reader: READERS.JSON
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
async function callback({ getFixture, factoryOptions, searchOptions, expectedFactoryError = false, expectedSearchError = false }) {
|
|
20
|
-
const url = "http://foo.bar";
|
|
21
|
-
if (expectedFactoryError) {
|
|
22
|
-
debug(`We're expecting an error`);
|
|
23
|
-
if (expectedFactoryError.isCandidateSearchError) {
|
|
24
|
-
try {
|
|
25
|
-
const result = await createSearchInterface({ ...formatFactoryOptions(), url });
|
|
26
|
-
debug(result);
|
|
27
|
-
} catch (err) {
|
|
28
|
-
assert.equal(err instanceof CandidateSearchError, true);
|
|
29
|
-
assert.match(err.message, new RegExp(expectedFactoryError.message));
|
|
30
|
-
} finally {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
const result = await createSearchInterface({ ...formatFactoryOptions(), url });
|
|
36
|
-
debug(result);
|
|
37
|
-
} catch (err) {
|
|
38
|
-
assert.equal(err instanceof Error, true);
|
|
39
|
-
assert.match(err.message, new RegExp(expectedFactoryError.message));
|
|
40
|
-
} finally {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const { search } = await createSearchInterface({ ...formatFactoryOptions(), url });
|
|
45
|
-
await iterate({ searchOptions, expectedSearchError });
|
|
46
|
-
function formatFactoryOptions() {
|
|
47
|
-
debug(`Using factoryOptions: ${JSON.stringify(factoryOptions)}`);
|
|
48
|
-
return {
|
|
49
|
-
...factoryOptions,
|
|
50
|
-
maxRecordsPerRequest: factoryOptions.maxRecordsPerRequest || 1,
|
|
51
|
-
maxServerResults: factoryOptions.maxServerResults || void 0,
|
|
52
|
-
record: new MarcRecord(factoryOptions.record, { subfieldValues: false })
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
async function iterate({ searchOptions: searchOptions2, expectedSearchError: expectedSearchError2, expectedErrorStatus, count = 1 }) {
|
|
56
|
-
const expectedResults = getFixture(`expectedResults${count}.json`);
|
|
57
|
-
if (expectedSearchError2) {
|
|
58
|
-
try {
|
|
59
|
-
await search(searchOptions2);
|
|
60
|
-
throw new Error("Expected an error");
|
|
61
|
-
} catch (err) {
|
|
62
|
-
debug(`Got an error: ${err}`);
|
|
63
|
-
assert(err instanceof Error);
|
|
64
|
-
const errorMessage = err instanceof MatchingError ? err.payload.message : err.message;
|
|
65
|
-
const errorStatus = err instanceof MatchingError ? err.status : void 0;
|
|
66
|
-
debug(`errorMessage: ${errorMessage}, errorStatus: ${errorStatus}`);
|
|
67
|
-
assert.match(errorMessage, new RegExp(expectedSearchError2, "u"));
|
|
68
|
-
if (expectedErrorStatus) {
|
|
69
|
-
assert.equal(errorStatus, expectedErrorStatus);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (!expectedSearchError2) {
|
|
76
|
-
const results = await search(searchOptions2);
|
|
77
|
-
assert.deepStrictEqual(formatResults(results), expectedResults);
|
|
78
|
-
}
|
|
79
|
-
function formatResults(results) {
|
|
80
|
-
debug(results);
|
|
81
|
-
return {
|
|
82
|
-
...results,
|
|
83
|
-
records: results.records.map(({ record, id }) => ({ id, record: record.toObject() }))
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
//# sourceMappingURL=index.test.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/candidate-search/index.test.js"],
|
|
4
|
-
"sourcesContent": ["import assert from 'node:assert';\nimport {describe} from 'node:test';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen-http-client';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport {Error as MatchingError} from '@natlibfi/melinda-commons';\nimport createSearchInterface, {CandidateSearchError} from './index.js';\nimport createDebugLogger from 'debug';\n\nconst debug = createDebugLogger('@natlibfi/melinda-record-matching:candidate-search:test');\n\ndescribe('candidate-search', () => {\n generateTests({\n callback,\n path: [import.meta.dirname, '..', '..', 'test-fixtures', 'candidate-search', 'index'],\n recurse: false,\n fixura: {\n reader: READERS.JSON\n }\n });\n\n async function callback({getFixture, factoryOptions, searchOptions, expectedFactoryError = false, expectedSearchError = false}) {\n const url = 'http://foo.bar';\n\n if (expectedFactoryError) {\n debug(`We're expecting an error`);\n if (expectedFactoryError.isCandidateSearchError) {\n try {\n const result = await createSearchInterface({...formatFactoryOptions(), url});\n debug(result);\n } catch (err) {\n assert.equal(err instanceof CandidateSearchError, true);\n assert.match(err.message, new RegExp(expectedFactoryError.message));\n } finally {\n return;\n }\n }\n\n try {\n const result = await createSearchInterface({...formatFactoryOptions(), url});\n debug(result);\n } catch (err) {\n assert.equal(err instanceof Error, true);\n assert.match(err.message, new RegExp(expectedFactoryError.message));\n } finally {\n return;\n }\n }\n\n const {search} = await createSearchInterface({...formatFactoryOptions(), url});\n await iterate({searchOptions, expectedSearchError});\n\n function formatFactoryOptions() {\n debug(`Using factoryOptions: ${JSON.stringify(factoryOptions)}`);\n return {\n ...factoryOptions,\n maxRecordsPerRequest: factoryOptions.maxRecordsPerRequest || 1,\n maxServerResults: factoryOptions.maxServerResults || undefined,\n record: new MarcRecord(factoryOptions.record, {subfieldValues: false})\n };\n }\n\n async function iterate({searchOptions, expectedSearchError, expectedErrorStatus, count = 1}) {\n const expectedResults = getFixture(`expectedResults${count}.json`);\n\n if (expectedSearchError) {\n try {\n await search(searchOptions);\n throw new Error('Expected an error');\n } catch (err) {\n debug(`Got an error: ${err}`);\n assert(err instanceof Error);\n const errorMessage = err instanceof MatchingError ? err.payload.message : err.message;\n const errorStatus = err instanceof MatchingError ? err.status : undefined;\n debug(`errorMessage: ${errorMessage}, errorStatus: ${errorStatus}`);\n assert.match(errorMessage, new RegExp(expectedSearchError, 'u'));\n\n if (expectedErrorStatus) {\n assert.equal(errorStatus, expectedErrorStatus);\n return;\n }\n return;\n }\n }\n\n if (!expectedSearchError) {\n const results = await search(searchOptions);\n assert.deepStrictEqual(formatResults(results), expectedResults);\n }\n\n function formatResults(results) {\n debug(results);\n return {\n ...results,\n records: results.records.map(({record, id}) => ({id, record: record.toObject()}))\n };\n }\n }\n }\n});\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["searchOptions", "expectedSearchError"]
|
|
7
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
import { describe } from "node:test";
|
|
3
|
-
import createDebugLogger from "debug";
|
|
4
|
-
import generateTests from "@natlibfi/fixugen";
|
|
5
|
-
import { READERS } from "@natlibfi/fixura";
|
|
6
|
-
import { MarcRecord } from "@natlibfi/marc-record";
|
|
7
|
-
import * as generators from "./auth.js";
|
|
8
|
-
const debug = createDebugLogger("@natlibfi/melinda-record-matching:candidate-search:query-list:auth-test");
|
|
9
|
-
const debugData = debug.extend("data");
|
|
10
|
-
describe("candidate-search/query-list/auth/", () => {
|
|
11
|
-
generateTests({
|
|
12
|
-
path: [import.meta.dirname, "..", "..", "..", "test-fixtures", "candidate-search", "query-list", "auth"],
|
|
13
|
-
useMetadataFile: true,
|
|
14
|
-
fixura: {
|
|
15
|
-
reader: READERS.JSON
|
|
16
|
-
},
|
|
17
|
-
callback: ({ type, inputRecord, expectedQuery, expectedQueryListType }) => {
|
|
18
|
-
const generate = generators[type];
|
|
19
|
-
const record = new MarcRecord(inputRecord, { subfieldValues: false });
|
|
20
|
-
const result = generate(record);
|
|
21
|
-
debugData(`Result: ${JSON.stringify(result)}`);
|
|
22
|
-
if (result.queryListType) {
|
|
23
|
-
assert.deepStrictEqual(result.queryList, expectedQuery);
|
|
24
|
-
assert.equal(result.queryListType, expectedQueryListType);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
assert.deepStrictEqual(result, expectedQuery);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
//# sourceMappingURL=auth.test.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/candidate-search/query-list/auth.test.js"],
|
|
4
|
-
"sourcesContent": ["import assert from 'node:assert';\nimport {describe} from 'node:test';\nimport createDebugLogger from 'debug';\nimport generateTests from '@natlibfi/fixugen';\nimport {READERS} from '@natlibfi/fixura';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport * as generators from './auth.js';\n\nconst debug = createDebugLogger('@natlibfi/melinda-record-matching:candidate-search:query-list:auth-test');\nconst debugData = debug.extend('data');\n\ndescribe('candidate-search/query-list/auth/', () => {\n generateTests({\n path: [import.meta.dirname, '..', '..', '..', 'test-fixtures', 'candidate-search', 'query-list', 'auth'],\n useMetadataFile: true,\n fixura: {\n reader: READERS.JSON\n },\n callback: ({type, inputRecord, expectedQuery, expectedQueryListType}) => {\n const generate = generators[type];\n const record = new MarcRecord(inputRecord, {subfieldValues: false});\n\n const result = generate(record);\n debugData(`Result: ${JSON.stringify(result)}`);\n\n if (result.queryListType) {\n assert.deepStrictEqual(result.queryList, expectedQuery);\n assert.equal(result.queryListType, expectedQueryListType);\n return;\n }\n assert.deepStrictEqual(result, expectedQuery);\n }\n });\n});\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAQ,gBAAe;AACvB,OAAO,uBAAuB;AAC9B,OAAO,mBAAmB;AAC1B,SAAQ,eAAc;AACtB,SAAQ,kBAAiB;AACzB,YAAY,gBAAgB;AAE5B,MAAM,QAAQ,kBAAkB,yEAAyE;AACzG,MAAM,YAAY,MAAM,OAAO,MAAM;AAErC,SAAS,qCAAqC,MAAM;AAClD,gBAAc;AAAA,IACZ,MAAM,CAAC,YAAY,SAAS,MAAM,MAAM,MAAM,iBAAiB,oBAAoB,cAAc,MAAM;AAAA,IACvG,iBAAiB;AAAA,IACjB,QAAQ;AAAA,MACN,QAAQ,QAAQ;AAAA,IAClB;AAAA,IACA,UAAU,CAAC,EAAC,MAAM,aAAa,eAAe,sBAAqB,MAAM;AACvE,YAAM,WAAW,WAAW,IAAI;AAChC,YAAM,SAAS,IAAI,WAAW,aAAa,EAAC,gBAAgB,MAAK,CAAC;AAElE,YAAM,SAAS,SAAS,MAAM;AAC9B,gBAAU,WAAW,KAAK,UAAU,MAAM,CAAC,EAAE;AAE7C,UAAI,OAAO,eAAe;AACxB,eAAO,gBAAgB,OAAO,WAAW,aAAa;AACtD,eAAO,MAAM,OAAO,eAAe,qBAAqB;AACxD;AAAA,MACF;AACA,aAAO,gBAAgB,QAAQ,aAAa;AAAA,IAC9C;AAAA,EACF,CAAC;AACH,CAAC;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
import { describe } from "node:test";
|
|
3
|
-
import createDebugLogger from "debug";
|
|
4
|
-
import generateTests from "@natlibfi/fixugen";
|
|
5
|
-
import { READERS } from "@natlibfi/fixura";
|
|
6
|
-
import { MarcRecord } from "@natlibfi/marc-record";
|
|
7
|
-
import * as generators from "./bib.js";
|
|
8
|
-
const debug = createDebugLogger("@natlibfi/melinda-record-matching:candidate-search:query:bibHostComponents:test");
|
|
9
|
-
const debugData = debug.extend("data");
|
|
10
|
-
describe("candidate-search/query-list/bib/", () => {
|
|
11
|
-
generateTests({
|
|
12
|
-
path: [import.meta.dirname, "..", "..", "..", "test-fixtures", "candidate-search", "query-list", "bib"],
|
|
13
|
-
useMetadataFile: true,
|
|
14
|
-
fixura: {
|
|
15
|
-
reader: READERS.JSON
|
|
16
|
-
},
|
|
17
|
-
callback: ({ type, inputRecord, expectedQuery, expectedQueryListType }) => {
|
|
18
|
-
const generate = generators[type];
|
|
19
|
-
const record = new MarcRecord(inputRecord, { subfieldValues: false });
|
|
20
|
-
const result = generate(record);
|
|
21
|
-
debugData(`Result: ${JSON.stringify(result)}`);
|
|
22
|
-
if (result.queryListType) {
|
|
23
|
-
assert.deepStrictEqual(result.queryList, expectedQuery);
|
|
24
|
-
assert.equal(result.queryListType, expectedQueryListType);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
assert.deepStrictEqual(result, expectedQuery);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
//# sourceMappingURL=bib.test.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/candidate-search/query-list/bib.test.js"],
|
|
4
|
-
"sourcesContent": ["import assert from 'node:assert';\nimport {describe} from 'node:test';\nimport createDebugLogger from 'debug';\nimport generateTests from '@natlibfi/fixugen';\nimport {READERS} from '@natlibfi/fixura';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport * as generators from './bib.js';\n\nconst debug = createDebugLogger('@natlibfi/melinda-record-matching:candidate-search:query:bibHostComponents:test');\nconst debugData = debug.extend('data');\n\ndescribe('candidate-search/query-list/bib/', () => {\n generateTests({\n path: [import.meta.dirname, '..', '..', '..', 'test-fixtures', 'candidate-search', 'query-list', 'bib'],\n useMetadataFile: true,\n fixura: {\n reader: READERS.JSON\n },\n callback: ({type, inputRecord, expectedQuery, expectedQueryListType}) => {\n const generate = generators[type];\n const record = new MarcRecord(inputRecord, {subfieldValues: false});\n\n const result = generate(record);\n debugData(`Result: ${JSON.stringify(result)}`);\n\n if (result.queryListType) {\n assert.deepStrictEqual(result.queryList, expectedQuery);\n assert.equal(result.queryListType, expectedQueryListType);\n return;\n }\n assert.deepStrictEqual(result, expectedQuery);\n }\n });\n});\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAQ,gBAAe;AACvB,OAAO,uBAAuB;AAC9B,OAAO,mBAAmB;AAC1B,SAAQ,eAAc;AACtB,SAAQ,kBAAiB;AACzB,YAAY,gBAAgB;AAE5B,MAAM,QAAQ,kBAAkB,iFAAiF;AACjH,MAAM,YAAY,MAAM,OAAO,MAAM;AAErC,SAAS,oCAAoC,MAAM;AACjD,gBAAc;AAAA,IACZ,MAAM,CAAC,YAAY,SAAS,MAAM,MAAM,MAAM,iBAAiB,oBAAoB,cAAc,KAAK;AAAA,IACtG,iBAAiB;AAAA,IACjB,QAAQ;AAAA,MACN,QAAQ,QAAQ;AAAA,IAClB;AAAA,IACA,UAAU,CAAC,EAAC,MAAM,aAAa,eAAe,sBAAqB,MAAM;AACvE,YAAM,WAAW,WAAW,IAAI;AAChC,YAAM,SAAS,IAAI,WAAW,aAAa,EAAC,gBAAgB,MAAK,CAAC;AAElE,YAAM,SAAS,SAAS,MAAM;AAC9B,gBAAU,WAAW,KAAK,UAAU,MAAM,CAAC,EAAE;AAE7C,UAAI,OAAO,eAAe;AACxB,eAAO,gBAAgB,OAAO,WAAW,aAAa;AACtD,eAAO,MAAM,OAAO,eAAe,qBAAqB;AACxD;AAAA,MACF;AACA,aAAO,gBAAgB,QAAQ,aAAa;AAAA,IAC9C;AAAA,EACF,CAAC;AACH,CAAC;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
package/dist/index.test.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
import { describe } from "node:test";
|
|
3
|
-
import createDebugLogger from "debug";
|
|
4
|
-
import { READERS } from "@natlibfi/fixura";
|
|
5
|
-
import generateTests from "@natlibfi/fixugen-http-client";
|
|
6
|
-
import { MarcRecord } from "@natlibfi/marc-record";
|
|
7
|
-
import createMatchInterface, { matchDetection } from "./index.js";
|
|
8
|
-
const debug = createDebugLogger("@natlibfi/melinda-record-matching:index:test");
|
|
9
|
-
const debugData = debug.extend("data");
|
|
10
|
-
describe("INDEX", () => {
|
|
11
|
-
generateTests({
|
|
12
|
-
callback,
|
|
13
|
-
path: [import.meta.dirname, "..", "test-fixtures", "index"],
|
|
14
|
-
recurse: false,
|
|
15
|
-
fixura: {
|
|
16
|
-
reader: READERS.JSON
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
async function callback({ getFixture, options, expectedMatchStatus, expectedStopReason, expectedFailures, expectedCandidateCount }) {
|
|
20
|
-
const record = new MarcRecord(getFixture("inputRecord.json"), { subfieldValues: false });
|
|
21
|
-
const expectedMatches = getFixture("expectedMatches.json");
|
|
22
|
-
const expectedNonMatches = getFixture("expectedNonMatches.json") || [];
|
|
23
|
-
const match = createMatchInterface(formatOptions());
|
|
24
|
-
const { matches, matchStatus, nonMatches, conversionFailures, candidateCount } = await match({ record });
|
|
25
|
-
debugData(`Matches: ${matches.length}, Status: ${matchStatus.status}/${matchStatus.stopReason}, NonMatches: ${nonMatches ? nonMatches.length : "not returned"}, ConversionFailures: ${conversionFailures ? conversionFailures.length : "not returned"}`);
|
|
26
|
-
assert.equal(matchStatus.status, expectedMatchStatus);
|
|
27
|
-
assert.equal(matchStatus.stopReason, expectedStopReason);
|
|
28
|
-
assert.equal(candidateCount, expectedCandidateCount);
|
|
29
|
-
const formattedMatchResult = formatRecordResults(matches);
|
|
30
|
-
assert.deepStrictEqual(formattedMatchResult, expectedMatches);
|
|
31
|
-
const formattedNonMatchResult = formatRecordResults(nonMatches);
|
|
32
|
-
assert.deepStrictEqual(formattedNonMatchResult, expectedNonMatches);
|
|
33
|
-
if (expectedFailures) {
|
|
34
|
-
assert.deepStrictEqual(conversionFailures, expectedFailures);
|
|
35
|
-
}
|
|
36
|
-
function formatOptions() {
|
|
37
|
-
const contextFeatures = matchDetection.features[options.detection.strategy.type];
|
|
38
|
-
return {
|
|
39
|
-
...options,
|
|
40
|
-
search: {
|
|
41
|
-
...options.search
|
|
42
|
-
},
|
|
43
|
-
detection: {
|
|
44
|
-
...options.detect,
|
|
45
|
-
strategy: options.detection.strategy.features.map((v) => contextFeatures[v]())
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
function formatRecordResults(matches2) {
|
|
50
|
-
if (matches2) {
|
|
51
|
-
debugData(JSON.stringify(matches2));
|
|
52
|
-
return matches2.map((match2) => ({
|
|
53
|
-
...match2,
|
|
54
|
-
candidate: formatCandidate(match2.candidate)
|
|
55
|
-
}));
|
|
56
|
-
}
|
|
57
|
-
return [];
|
|
58
|
-
}
|
|
59
|
-
function formatCandidate({ id, record: record2 }) {
|
|
60
|
-
const newId = id;
|
|
61
|
-
const newRecord = record2;
|
|
62
|
-
return {
|
|
63
|
-
id: newId,
|
|
64
|
-
record: newRecord.toObject()
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
//# sourceMappingURL=index.test.js.map
|