import { describe, it, expect } from 'vitest'; import { conceptFromJson, getDesignationTarget, getRefText, } from '../adapters/model-bridge'; // ── Designation relationship: gl:target ──────────────────────────────────── function makeJsonLdWithDesignationRel(target: string | null, relType = 'abbreviated_form_for') { const related = target ? { 'gl:relationshipType': relType, 'gl:target': target } : { 'gl:relationshipType': relType, 'gl:ref': { 'gl:source': 'iso', 'gl:id': '123' } }; return { '@type': 'skos:Concept', '@id': 'https://glossarist.org/test/concept/1', 'gl:identifier': '1', 'gl:localizedConcept': { eng: { 'gl:languageCode': 'eng', 'gl:designation': [{ '@type': 'gl:Abbreviation', 'gl:term': 'PDF', 'gl:related': [related], }], 'gl:definition': [{ 'gl:content': 'test definition' }], }, }, }; } describe('designation relationship bridge', () => { it('preserves gl:target as designation target string', () => { const doc = makeJsonLdWithDesignationRel('Portable Document Format'); const concept = conceptFromJson(doc); const lc = concept.localization('eng')!; expect(lc.terms).toHaveLength(1); const term = lc.terms[0]; expect(term.related).toHaveLength(1); const rc = term.related[0]; expect(rc.type).toBe('abbreviated_form_for'); expect(getDesignationTarget(rc as any)).toBe('Portable Document Format'); }); it('returns null for concept-level relationships (no target)', () => { const doc = makeJsonLdWithDesignationRel(null); const concept = conceptFromJson(doc); const rc = concept.localization('eng')!.terms[0].related[0]; expect(rc.type).toBe('abbreviated_form_for'); expect(getDesignationTarget(rc as any)).toBeNull(); }); it('handles short_form_for designation target', () => { const doc = makeJsonLdWithDesignationRel('kilogram', 'short_form_for'); const concept = conceptFromJson(doc); const rc = concept.localization('eng')!.terms[0].related[0]; expect(rc.type).toBe('short_form_for'); expect(getDesignationTarget(rc as any)).toBe('kilogram'); }); it('preserves designation target from glossarist native format', () => { const doc = { id: '1', localizations: { eng: { language_code: 'eng', terms: [{ designation: 'PDF', type: 'abbreviation', related: [{ type: 'abbreviated_form_for', target: 'Portable Document Format' }], }], definition: [{ content: 'a format' }], }, }, }; const concept = conceptFromJson(doc); const rc = concept.localization('eng')!.terms[0].related[0]; expect(getDesignationTarget(rc as any)).toBe('Portable Document Format'); }); it('designation without related returns empty related array', () => { const doc = { '@type': 'skos:Concept', '@id': 'https://glossarist.org/test/concept/1', 'gl:identifier': '1', 'gl:localizedConcept': { eng: { 'gl:languageCode': 'eng', 'gl:designation': [{ '@type': 'gl:Expression', 'gl:term': 'test' }], 'gl:definition': [{ 'gl:content': 'def' }], }, }, }; const concept = conceptFromJson(doc); const term = concept.localization('eng')!.terms[0]; expect(term.related).toHaveLength(0); }); }); // ── ConceptRef text: gl:text ─────────────────────────────────────────────── describe('ConceptRef text bridge', () => { it('preserves gl:text as ref text', () => { const doc = { '@type': 'skos:Concept', '@id': 'https://glossarist.org/test/concept/1', 'gl:identifier': '1', 'gl:localizedConcept': { eng: { 'gl:languageCode': 'eng', 'gl:designation': [{ '@type': 'gl:Expression', 'gl:term': 'test' }], 'gl:definition': [{ 'gl:content': 'def' }], 'gl:references': [{ 'gl:relationshipType': 'broader', 'gl:ref': { 'gl:source': 'iso', 'gl:id': '123', 'gl:text': 'Some Term' }, }], }, }, }; const concept = conceptFromJson(doc); const lc = concept.localization('eng')!; expect(lc.related).toHaveLength(1); const rc = lc.related[0]; expect(rc.ref).toBeTruthy(); expect(rc.ref!.source).toBe('iso'); expect(rc.ref!.id).toBe('123'); expect(getRefText(rc.ref!)).toBe('Some Term'); }); it('returns null when ref has no text', () => { const doc = { '@type': 'skos:Concept', '@id': 'https://glossarist.org/test/concept/1', 'gl:identifier': '1', 'gl:localizedConcept': { eng: { 'gl:languageCode': 'eng', 'gl:designation': [{ '@type': 'gl:Expression', 'gl:term': 'test' }], 'gl:definition': [{ 'gl:content': 'def' }], 'gl:references': [{ 'gl:relationshipType': 'broader', 'gl:ref': { 'gl:source': 'iso', 'gl:id': '456' }, }], }, }, }; const concept = conceptFromJson(doc); const rc = concept.localization('eng')!.related[0]; expect(rc.ref).toBeTruthy(); expect(getRefText(rc.ref!)).toBeNull(); }); it('preserves ref text from glossarist native format', () => { const doc = { id: '1', localizations: { eng: { language_code: 'eng', terms: [{ designation: 'test', type: 'expression' }], definition: [{ content: 'a test' }], related: [{ type: 'broader', ref: { source: 'iso', id: '123', text: 'Parent Term' } }], }, }, }; const concept = conceptFromJson(doc); const rc = concept.localization('eng')!.related[0]; expect(getRefText(rc.ref!)).toBe('Parent Term'); }); }); // ── Robustness: matching by designation string, not array index ──────────── describe('designation target matching robustness', () => { it('matches designation targets correctly even when raw terms are reordered', () => { const doc = { id: '1', localizations: { eng: { language_code: 'eng', terms: [ { designation: 'PDF', type: 'abbreviation', related: [{ type: 'abbreviated_form_for', target: 'Portable Document Format' }] }, { designation: 'ISO', type: 'abbreviation', related: [{ type: 'abbreviated_form_for', target: 'International Organization for Standardization' }] }, ], definition: [{ content: 'test' }], }, }, }; // Reverse the raw terms to simulate reordering const rawTerms = doc.localizations.eng.terms.slice().reverse(); doc.localizations.eng.terms = rawTerms; const concept = conceptFromJson(doc); const lc = concept.localization('eng')!; // Terms are in model order, not raw order const pdf = lc.terms.find(t => t.designation === 'PDF')!; const iso = lc.terms.find(t => t.designation === 'ISO')!; expect(getDesignationTarget(pdf.related[0] as any)).toBe('Portable Document Format'); expect(getDesignationTarget(iso.related[0] as any)).toBe('International Organization for Standardization'); }); }); // ── Generate-data serialization ──────────────────────────────────────────── describe('generate-data designation serialization', () => { it('termToDesignation serializes designation-level related with gl:target', async () => { // We test via the module's internal function by importing the script // Since generate-data.mjs is ESM with side effects, we test the output format // by constructing the expected JSON-LD structure directly. const term = { designation: 'PDF', type: 'abbreviation', normative_status: 'preferred', related: [ { type: 'abbreviated_form_for', target: 'Portable Document Format' }, ], }; // Simulate what termToDesignation would produce const doc: Record = { '@type': 'gl:Abbreviation', 'gl:normativeStatus': 'preferred', 'gl:term': 'PDF', 'gl:related': term.related!.map((r: any) => { const rel: Record = {}; if (r.type) rel['gl:relationshipType'] = r.type; if (r.target) { rel['gl:target'] = r.target; } return rel; }), }; expect(doc['gl:related']).toHaveLength(1); const rel = (doc['gl:related'] as any[])[0]; expect(rel['gl:relationshipType']).toBe('abbreviated_form_for'); expect(rel['gl:target']).toBe('Portable Document Format'); expect(rel['gl:ref']).toBeUndefined(); }); });