SPB Git forge

spb/cancerindex

Public
37commits 1branches 0releases
2.9 MBsize
maindefault branch
10 days agolast push
TypeScript 97.2% SQL 1.5% CSS 0.6% JavaScript 0.5%
4.3 KB · 63 lines typescript
Raw Blame History
1import { describe, expect, it } from 'vitest';2import { DRUG_SALT_TOKENS, MIN_SHARED_ALIASES, detectDuplicateCandidates, moleculeKey, pickKeep, type DrugLite } from './drug-duplicates.js';34const d = (id: string, name: string, aliases: Array<[string, string]> = []): DrugLite => ({ id, name, aliases: aliases.map(([normalized, aliasType]) => ({ normalized, aliasType })) });56describe('drug duplicates — molecule key', () => {7  it('strips trailing salt tokens and parentheticals, keeps the first token', () => {8    expect(moleculeKey('Imatinib Mesylate')).toBe('imatinib');9    expect(moleculeKey('Erlotinib Hydrochloride')).toBe('erlotinib');10    expect(moleculeKey('Sorafenib Tosylate')).toBe('sorafenib');11    expect(moleculeKey('Abiraterone Acetate')).toBe('abiraterone');12    expect(moleculeKey('Trastuzumab Deruxtecan')).toBe('trastuzumab deruxtecan');13    expect(moleculeKey('Imatinib (Imatinib Mesylate)')).toBe('imatinib');14    expect(moleculeKey('Sodium')).toBe('sodium'); // never empties a name15    expect(moleculeKey('JQ1')).toBe('jq1');16  });17  it('the salt list is the connectors list (kept in sync by hand)', () => {18    expect(DRUG_SALT_TOKENS).toContain('mesylate');19    expect(DRUG_SALT_TOKENS).toContain('tosylate');20    expect(new Set(DRUG_SALT_TOKENS).size).toBe(DRUG_SALT_TOKENS.length);21  });22});2324describe('drug duplicates — detector', () => {25  it('salt-form pairs: keep the base molecule, merge the salt', () => {26    const c = detectDuplicateCandidates([d('CI-DRUG-00000002', 'Imatinib Mesylate'), d('CI-DRUG-00000001', 'Imatinib'), d('CI-DRUG-00000003', 'Osimertinib')]);27    expect(c).toHaveLength(1);28    expect(c[0]).toMatchObject({ keepId: 'CI-DRUG-00000001', mergeId: 'CI-DRUG-00000002', reason: 'salt_form' });29    expect(c[0]!.evidence).toMatchObject({ moleculeKey: 'imatinib', keepName: 'Imatinib', mergeName: 'Imatinib Mesylate' });30  });31  it('identical names → same_name, smaller id kept', () => {32    const c = detectDuplicateCandidates([d('CI-DRUG-00000009', 'Regorafenib'), d('CI-DRUG-00000004', 'Regorafenib')]);33    expect(c).toEqual([expect.objectContaining({ keepId: 'CI-DRUG-00000004', mergeId: 'CI-DRUG-00000009', reason: 'same_name' })]);34  });35  it('shared generic/brand/development_code aliases (≥ 2) → candidate; synonyms and single shared alias do not count', () => {36    const a = d('CI-DRUG-00000010', 'Drug A', [['tagrisso', 'brand'], ['azd9291', 'development_code'], ['foo', 'synonym']]);37    const b = d('CI-DRUG-00000011', 'Drug Beta', [['tagrisso', 'brand'], ['azd9291', 'development_code'], ['foo', 'synonym']]);38    const c1 = d('CI-DRUG-00000012', 'Drug C', [['tagrisso', 'brand'], ['bar', 'synonym'], ['foo', 'synonym']]);39    const out = detectDuplicateCandidates([a, b, c1]);40    expect(out).toHaveLength(1);41    expect(out[0]).toMatchObject({ keepId: 'CI-DRUG-00000010', mergeId: 'CI-DRUG-00000011', reason: 'shared_aliases' });42    expect((out[0]!.evidence.sharedAliases as string[]).length).toBeGreaterThanOrEqual(MIN_SHARED_ALIASES);43  });44  it('one candidate per pair, strongest reason wins', () => {45    const a = d('CI-DRUG-00000020', 'Sorafenib', [['nexavar', 'brand'], ['bay 43 9006', 'development_code']]);46    const b = d('CI-DRUG-00000021', 'Sorafenib Tosylate', [['nexavar', 'brand'], ['bay 43 9006', 'development_code']]);47    const out = detectDuplicateCandidates([a, b]);48    expect(out).toHaveLength(1);49    expect(out[0]!.reason).toBe('salt_form');50  });51  it('pickKeep prefers an INN over a development code, then fewer tokens, then the shorter name, then the smaller id', () => {52    expect(pickKeep(d('CI-DRUG-00000566', 'TAK-788'), d('CI-DRUG-00000382', 'Mobocertinib')).keep.id).toBe('CI-DRUG-00000382');53    expect(pickKeep(d('CI-DRUG-00000499', 'RDEA 119'), d('CI-DRUG-00000511', 'Refametinib')).keep.id).toBe('CI-DRUG-00000511');54    expect(pickKeep(d('CI-DRUG-00000002', 'Erlotinib Hydrochloride'), d('CI-DRUG-00000001', 'Erlotinib')).keep.id).toBe('CI-DRUG-00000001');55    expect(pickKeep(d('CI-DRUG-00000002', 'Abc'), d('CI-DRUG-00000001', 'Abcd')).keep.id).toBe('CI-DRUG-00000002');56    expect(pickKeep(d('CI-DRUG-00000002', 'Same'), d('CI-DRUG-00000001', 'Same')).keep.id).toBe('CI-DRUG-00000001');57  });58  it('no duplicates → empty', () => {59    expect(detectDuplicateCandidates([d('CI-DRUG-00000001', 'Osimertinib'), d('CI-DRUG-00000002', 'Pembrolizumab')])).toEqual([]);60    expect(detectDuplicateCandidates([])).toEqual([]);61  });62});63