|
| 1 | +#!/usr/bin/env python |
| 2 | +import csv |
| 3 | +import sys |
| 4 | +import unittest |
| 5 | +from io import StringIO |
| 6 | + |
| 7 | +from sqlalchemy import create_engine |
| 8 | + |
| 9 | +from taxtastic.subcommands import append |
| 10 | +from taxtastic.taxonomy import Taxonomy |
| 11 | + |
| 12 | +from . import config |
| 13 | +from .config import TestBase |
| 14 | + |
| 15 | +dbname = config.ncbi_master_db |
| 16 | + |
| 17 | + |
| 18 | +class Args: |
| 19 | + schema = None |
| 20 | + verbosity = 0 |
| 21 | + tax_id_column = 'tax_id' |
| 22 | + |
| 23 | + |
| 24 | +class TestAppendAction(TestBase): |
| 25 | + |
| 26 | + def setUp(self): |
| 27 | + self.engine = create_engine('sqlite:///%s' % dbname) |
| 28 | + self.tax = Taxonomy(self.engine) |
| 29 | + |
| 30 | + def tearDown(self): |
| 31 | + self.engine.dispose() |
| 32 | + |
| 33 | + def _make_args(self, rows, columns): |
| 34 | + buf = StringIO() |
| 35 | + writer = csv.DictWriter(buf, fieldnames=list(rows[0].keys())) |
| 36 | + writer.writeheader() |
| 37 | + writer.writerows(rows) |
| 38 | + buf.seek(0) |
| 39 | + |
| 40 | + args = Args() |
| 41 | + args.url = 'sqlite:///%s' % dbname |
| 42 | + args.infile = buf |
| 43 | + args.columns = columns |
| 44 | + args.outfile = StringIO() |
| 45 | + return args |
| 46 | + |
| 47 | + def test_appends_genus_and_species_name(self): |
| 48 | + """Normal case: valid tax_ids get genus and species_name appended.""" |
| 49 | + rows = [ |
| 50 | + {'seqname': 'seq1', 'tax_id': '1280'}, # Staphylococcus aureus |
| 51 | + {'seqname': 'seq2', 'tax_id': '1378'}, # Gemella |
| 52 | + ] |
| 53 | + args = self._make_args(rows, ['genus', 'species_name']) |
| 54 | + append.action(args) |
| 55 | + |
| 56 | + args.outfile.seek(0) |
| 57 | + result = list(csv.DictReader(args.outfile)) |
| 58 | + |
| 59 | + self.assertEqual(len(result), 2) |
| 60 | + self.assertIn('genus', result[0]) |
| 61 | + self.assertIn('species_name', result[0]) |
| 62 | + |
| 63 | + # Staphylococcus aureus (1280) should have a genus tax_id |
| 64 | + self.assertTrue(result[0]['genus']) |
| 65 | + self.assertEqual( |
| 66 | + result[0]['species_name'], 'Staphylococcus aureus') |
| 67 | + |
| 68 | + def test_existing_columns_not_duplicated(self): |
| 69 | + """Columns already in the CSV are not added twice to fieldnames.""" |
| 70 | + rows = [{'tax_id': '1280', 'genus': 'existing_value'}] |
| 71 | + args = self._make_args(rows, ['genus']) |
| 72 | + append.action(args) |
| 73 | + |
| 74 | + args.outfile.seek(0) |
| 75 | + reader = csv.DictReader(args.outfile) |
| 76 | + self.assertEqual(reader.fieldnames.count('genus'), 1) |
| 77 | + |
| 78 | + def test_missing_tax_id_produces_empty_columns(self): |
| 79 | + """Tax_ids not found in the database produce empty column values.""" |
| 80 | + rows = [{'tax_id': 'invalid_tax_id_999'}] |
| 81 | + args = self._make_args(rows, ['genus', 'species_name']) |
| 82 | + append.action(args) |
| 83 | + |
| 84 | + args.outfile.seek(0) |
| 85 | + result = list(csv.DictReader(args.outfile)) |
| 86 | + |
| 87 | + self.assertEqual(len(result), 1) |
| 88 | + self.assertEqual(result[0]['genus'], '') |
| 89 | + self.assertEqual(result[0]['species_name'], '') |
| 90 | + |
| 91 | + def test_output_preserves_input_columns(self): |
| 92 | + """All original columns are preserved in the output.""" |
| 93 | + rows = [ |
| 94 | + {'seqname': 'seq1', 'description': 'a desc', 'tax_id': '1280'}, |
| 95 | + ] |
| 96 | + args = self._make_args(rows, ['species_name']) |
| 97 | + append.action(args) |
| 98 | + |
| 99 | + args.outfile.seek(0) |
| 100 | + result = list(csv.DictReader(args.outfile)) |
| 101 | + |
| 102 | + self.assertEqual(result[0]['seqname'], 'seq1') |
| 103 | + self.assertEqual(result[0]['description'], 'a desc') |
| 104 | + self.assertEqual(result[0]['tax_id'], '1280') |
0 commit comments