spb/cancerindex
Public
TypeScript 97.2%
SQL 1.5%
CSS 0.6%
JavaScript 0.5%
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