project-nomad/admin/tests/unit/drug_labels.spec.ts

353 lines
13 KiB
TypeScript

import { test } from '@japa/runner'
import {
mapDrugLabelRecord,
normalizeDrugName,
parseDrugLabelManifest,
type OpenFdaLabelRecord,
} from '../../util/drug_labels.js'
// ─── mapDrugLabelRecord ───────────────────────────────────────────────────────
test.group('mapDrugLabelRecord', () => {
// Case 1: Full Rx record → all fields mapped; sections flattened with \n\n.
test('maps a full Rx record with all fields present', ({ assert }) => {
const record: OpenFdaLabelRecord = {
set_id: 'abc-123',
id: 'rev-001',
version: '3',
effective_time: '20240115',
openfda: {
brand_name: ['Xarelto'],
generic_name: ['rivaroxaban'],
manufacturer_name: ['Janssen Pharmaceuticals, Inc.'],
product_ndc: ['50458-578-03'],
route: ['ORAL'],
product_type: ['HUMAN PRESCRIPTION DRUG'],
},
indications_and_usage: ['Indicated for treatment of DVT.', 'Also for PE.'],
dosage_and_administration: ['15 mg once daily with food.'],
warnings: ['Premature discontinuation increases risk of thrombotic events.'],
boxed_warning: ['PREMATURE DISCONTINUATION OF ANTICOAGULANTS MAY CAUSE THROMBOSIS.'],
drug_interactions: ['Avoid use with combined P-gp and CYP3A4 inhibitors.'],
contraindications: ['Active pathological bleeding.'],
when_using: [],
stop_use: [],
}
const row = mapDrugLabelRecord(record)
assert.isNotNull(row)
assert.equal(row!.set_id, 'abc-123')
assert.equal(row!.spl_id, 'rev-001')
assert.equal(row!.version, '3')
assert.equal(row!.brand_name, 'Xarelto')
assert.equal(row!.generic_name, 'rivaroxaban')
assert.equal(row!.manufacturer, 'Janssen Pharmaceuticals, Inc.')
assert.equal(row!.product_ndc, '50458-578-03')
assert.equal(row!.route, 'ORAL')
assert.equal(row!.product_type, 'HUMAN PRESCRIPTION DRUG')
assert.equal(row!.source_updated_at, '2024-01-15')
// Sections flattened with \n\n
assert.equal(row!.indications, 'Indicated for treatment of DVT.\n\nAlso for PE.')
assert.equal(row!.dosage, '15 mg once daily with food.')
assert.isNotNull(row!.boxed_warning)
assert.isNotNull(row!.drug_interactions)
assert.isNotNull(row!.contraindications)
// Empty arrays → null
assert.isNull(row!.when_using)
assert.isNull(row!.stop_use)
})
// Case 2: OTC record with when_using/stop_use, absent drug_interactions/contraindications/boxed_warning
test('maps an OTC record with absent Rx sections — no throw', ({ assert }) => {
const record: OpenFdaLabelRecord = {
set_id: 'otc-set-456',
openfda: {
brand_name: ['Tylenol'],
generic_name: ['acetaminophen'],
product_type: ['HUMAN OTC DRUG'],
route: ['ORAL'],
},
indications_and_usage: ['For temporary relief of minor pain.'],
when_using: ['Do not use with other acetaminophen products.'],
stop_use: ['Stop use if symptoms persist more than 3 days.'],
// drug_interactions, contraindications, boxed_warning intentionally absent
}
const row = mapDrugLabelRecord(record)
assert.isNotNull(row)
assert.isNull(row!.drug_interactions)
assert.isNull(row!.contraindications)
assert.isNull(row!.boxed_warning)
assert.isNotNull(row!.when_using)
assert.isNotNull(row!.stop_use)
assert.equal(row!.product_type, 'HUMAN OTC DRUG')
})
// Case 3: Missing set_id → returns null
test('returns null when set_id is missing', ({ assert }) => {
const record: OpenFdaLabelRecord = {
id: 'rev-xyz',
openfda: { brand_name: ['Advil'] },
}
assert.isNull(mapDrugLabelRecord(record))
})
test('returns null when set_id is empty string', ({ assert }) => {
const record: OpenFdaLabelRecord = { set_id: '', openfda: { brand_name: ['Advil'] } }
assert.isNull(mapDrugLabelRecord(record))
})
// Case 4: Multi-element generic_name/product_ndc/route → joined with ", "
test('joins multi-element generic_name, product_ndc, route with comma', ({ assert }) => {
const record: OpenFdaLabelRecord = {
set_id: 'multi-001',
openfda: {
generic_name: ['amoxicillin', 'clavulanate potassium'],
product_ndc: ['0069-0070-41', '0069-0070-83'],
route: ['ORAL', 'SUBLINGUAL'],
},
}
const row = mapDrugLabelRecord(record)
assert.isNotNull(row)
assert.equal(row!.generic_name, 'amoxicillin, clavulanate potassium')
assert.equal(row!.product_ndc, '0069-0070-41, 0069-0070-83')
assert.equal(row!.route, 'ORAL, SUBLINGUAL')
})
// Case 5: Multi-element brand_name → first element only
test('takes only the first element of brand_name', ({ assert }) => {
const record: OpenFdaLabelRecord = {
set_id: 'brand-multi-001',
openfda: { brand_name: ['Augmentin', 'Amoxicillin-Clavulanate'] },
}
const row = mapDrugLabelRecord(record)
assert.isNotNull(row)
assert.equal(row!.brand_name, 'Augmentin')
})
// Case 6: effective_time parsing
test('parses effective_time YYYYMMDD to YYYY-MM-DD', ({ assert }) => {
const record: OpenFdaLabelRecord = { set_id: 's1', effective_time: '20240115' }
assert.equal(mapDrugLabelRecord(record)!.source_updated_at, '2024-01-15')
})
test('returns null source_updated_at for garbage effective_time', ({ assert }) => {
const record: OpenFdaLabelRecord = { set_id: 's2', effective_time: 'garbage' }
assert.isNull(mapDrugLabelRecord(record)!.source_updated_at)
})
test('returns null source_updated_at when effective_time is missing', ({ assert }) => {
const record: OpenFdaLabelRecord = { set_id: 's3' }
assert.isNull(mapDrugLabelRecord(record)!.source_updated_at)
})
// Case 7: Empty-array section → null (not "")
test('empty section array returns null, not empty string', ({ assert }) => {
const record: OpenFdaLabelRecord = {
set_id: 's4',
indications_and_usage: [],
warnings: [],
}
const row = mapDrugLabelRecord(record)!
assert.isNull(row.indications)
assert.isNull(row.warnings)
})
// Case 8: product_type handling
test('takes product_type from openfda array first element', ({ assert }) => {
const r1: OpenFdaLabelRecord = {
set_id: 's5',
openfda: { product_type: ['HUMAN OTC DRUG'] },
}
assert.equal(mapDrugLabelRecord(r1)!.product_type, 'HUMAN OTC DRUG')
const r2: OpenFdaLabelRecord = { set_id: 's6' }
assert.isNull(mapDrugLabelRecord(r2)!.product_type)
})
// Case 9: Section text with embedded whitespace → trimmed, joined properly
test('trims and joins sections with embedded whitespace', ({ assert }) => {
const record: OpenFdaLabelRecord = {
set_id: 's7',
warnings: [' Risk of bleeding. ', '\n\nDo not crush.\n'],
}
const row = mapDrugLabelRecord(record)!
// The two elements are joined with \n\n, then the whole result trimmed.
// Individual elements may have internal whitespace but the join/trim
// removes leading/trailing blank lines from the whole blob.
assert.isNotNull(row.warnings)
assert.isFalse(row.warnings!.startsWith('\n'))
assert.isFalse(row.warnings!.endsWith('\n'))
assert.include(row.warnings!, ' Risk of bleeding. \n\n\n\nDo not crush.')
})
// Case 10: over-long set_id (> 64) → null (the idempotency key can't be truncated)
test('returns null when set_id exceeds the 64-char key width', ({ assert }) => {
const record: OpenFdaLabelRecord = {
set_id: 'x'.repeat(65),
openfda: { brand_name: ['Foo'] },
}
assert.isNull(mapDrugLabelRecord(record))
})
// Case 11: a generic_name join longer than the column width is clamped to fit
test('clamps an over-long generic_name to the column width (512)', ({ assert }) => {
// A combination product can join many active ingredients well past 512 chars.
const manyActives = Array.from({ length: 60 }, (_, i) => `ingredient-number-${i}`)
const record: OpenFdaLabelRecord = {
set_id: 'combo-1',
openfda: { brand_name: ['MegaVitamin'], generic_name: manyActives },
}
const row = mapDrugLabelRecord(record)!
assert.isNotNull(row.generic_name)
assert.isTrue(row.generic_name!.length <= 512)
assert.isTrue(row.searchable_name!.length <= 768)
})
})
// ─── normalizeDrugName ────────────────────────────────────────────────────────
test.group('normalizeDrugName', () => {
// Case 1
test('basic brand + generic combination', ({ assert }) => {
assert.equal(normalizeDrugName('Tylenol', 'acetaminophen'), 'tylenol acetaminophen')
})
// Case 2
test('multi-word brand + generic', ({ assert }) => {
assert.equal(
normalizeDrugName('Tylenol Extra Strength', 'acetaminophen'),
'tylenol extra strength acetaminophen'
)
})
// Case 3: Duplicate tokens deduped
test('deduplicates tokens preserving order', ({ assert }) => {
assert.equal(normalizeDrugName('Silicea', 'SILICEA'), 'silicea')
})
// Case 4: Punctuation stripped to spaces
test('strips punctuation to spaces', ({ assert }) => {
assert.equal(normalizeDrugName('Co-Q10 (50 mg)', null), 'co q10 50 mg')
})
// Case 5: Both null/empty → null
test('returns null when both inputs are null', ({ assert }) => {
assert.isNull(normalizeDrugName(null, null))
})
test('returns null when both inputs are empty strings', ({ assert }) => {
assert.isNull(normalizeDrugName('', ''))
})
// Case 6: Extra whitespace collapsed
test('collapses extra whitespace', ({ assert }) => {
assert.equal(normalizeDrugName(' Advil PM ', 'ibuprofen'), 'advil pm ibuprofen')
})
// Edge: one null
test('handles null brand with non-null generic', ({ assert }) => {
assert.equal(normalizeDrugName(null, 'ibuprofen'), 'ibuprofen')
})
test('handles non-null brand with null generic', ({ assert }) => {
assert.equal(normalizeDrugName('Advil', null), 'advil')
})
})
// ─── parseDrugLabelManifest ───────────────────────────────────────────────────
test.group('parseDrugLabelManifest', () => {
const wellFormed = {
meta: { disclaimer: 'Do not rely...' },
results: {
drug: {
label: {
export_date: '2026-06-06',
total_records: 258914,
partitions: [
{
display_name: '/drug/label (part 1 of 13)',
file: 'https://download.open.fda.gov/drug/label/drug-label-0001-of-0013.json.zip',
size_mb: '128.11',
records: 20000,
},
{
display_name: '/drug/label (part 2 of 13)',
file: 'https://download.open.fda.gov/drug/label/drug-label-0002-of-0013.json.zip',
size_mb: '143.09',
records: 20000,
},
],
},
},
},
}
// Well-formed → typed object
test('parses a well-formed manifest into a typed object', ({ assert }) => {
const result = parseDrugLabelManifest(wellFormed)
assert.equal(result.export_date, '2026-06-06')
assert.equal(result.total_records, 258914)
assert.lengthOf(result.partitions, 2)
assert.equal(
result.partitions[0].file,
'https://download.open.fda.gov/drug/label/drug-label-0001-of-0013.json.zip'
)
assert.equal(result.partitions[0].records, 20000)
assert.equal(result.partitions[1].size_mb, '143.09')
})
// Missing results.drug.label → throws
test('throws when results.drug.label is missing', ({ assert }) => {
const bad = { results: { drug: {} } }
assert.throws(() => parseDrugLabelManifest(bad), /Unexpected FDA manifest format/)
})
test('throws when results is missing entirely', ({ assert }) => {
assert.throws(() => parseDrugLabelManifest({}), /Unexpected FDA manifest format/)
})
// Partition missing file → skipped
test('skips partitions that are missing the file field', ({ assert }) => {
const partial = {
results: {
drug: {
label: {
export_date: '2026-06-06',
total_records: 100,
partitions: [
{ display_name: 'bad', size_mb: '10', records: 5 }, // no file
{
display_name: 'good',
file: 'https://example.com/part.zip',
size_mb: '10',
records: 5,
},
],
},
},
},
}
const result = parseDrugLabelManifest(partial)
assert.lengthOf(result.partitions, 1)
assert.equal(result.partitions[0].file, 'https://example.com/part.zip')
})
// All partitions invalid → throws
test('throws when all partitions lack a file field', ({ assert }) => {
const allBad = {
results: {
drug: {
label: {
export_date: '2026-06-06',
total_records: 100,
partitions: [{ display_name: 'bad', records: 5 }],
},
},
},
}
assert.throws(() => parseDrugLabelManifest(allBad), /all partitions were invalid/)
})
})