Skip to content

Commit c49fc84

Browse files
committed
TEST: add full test suite for annotations from archive 7.0
1 parent aa33e37 commit c49fc84

File tree

1 file changed

+231
-32
lines changed

1 file changed

+231
-32
lines changed

q2cli/tests/test_tools.py

Lines changed: 231 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,43 +1277,242 @@ def test_replay_supplement_zipfile(self):
12771277

12781278
self.assertEqual(os.listdir(unzipped_path), ['supplement'])
12791279

1280-
# TODO: this is a super minimal test just to confirm these commands all
1281-
# run without failing. After 2025.4 release, I will add a more complete
1282-
# test suite to assert that the outputs of each of these commands are
1283-
# what we expect them to be
1280+
1281+
class TestAnnotations(unittest.TestCase):
1282+
def setUp(self):
1283+
get_dummy_plugin()
1284+
self.runner = CliRunner()
1285+
self.tempdir = tempfile.mkdtemp(prefix='qiime2-q2cli-test-temp-')
1286+
1287+
self.art1 = os.path.join(self.tempdir, 'ints1.qza')
1288+
Artifact.import_data('IntSequence1', [0, 1, 2]).save(self.art1)
1289+
self.output1 = os.path.join(self.tempdir, 'ints1_annotated.qza')
1290+
1291+
self.note_file = os.path.join(
1292+
self.tempdir, 'note_file.txt')
1293+
1294+
with open(self.note_file, 'w') as f:
1295+
f.write('my special text')
1296+
1297+
def tearDown(self):
1298+
shutil.rmtree(self.tempdir)
1299+
12841300
def test_annotation_commands_roundtrip(self):
1285-
with tempfile.TemporaryDirectory() as tempdir:
1286-
in_fp = os.path.join(self.tempdir, 'concated_ints.qza')
1287-
out_fp = os.path.join(tempdir, 'concated_ints_roundtrip.qza')
1301+
create_result = self.runner.invoke(
1302+
tools,
1303+
['annotation-create', '--input-path', self.art1,
1304+
'--annotation-type', 'Note',
1305+
'--name', 'mynote', '--text', 'my special text',
1306+
'--output-path', self.output1]
1307+
)
1308+
self.assertEqual(create_result.exit_code, 0)
12881309

1289-
create_result = self.runner.invoke(
1290-
tools,
1291-
['annotation-create', '--input-path', in_fp,
1292-
'--annotation-type', 'Note',
1293-
'--name', 'mynote', '--text', 'my special text',
1294-
'--output-path', out_fp]
1295-
)
1296-
self.assertEqual(create_result.exit_code, 0)
1310+
fetch_result = self.runner.invoke(
1311+
tools,
1312+
['annotation-fetch', '--input-path', self.output1,
1313+
'--name', 'mynote']
1314+
)
1315+
self.assertEqual(fetch_result.exit_code, 0)
12971316

1298-
fetch_result = self.runner.invoke(
1299-
tools,
1300-
['annotation-fetch', '--input-path', out_fp,
1301-
'--name', 'mynote']
1302-
)
1303-
self.assertEqual(fetch_result.exit_code, 0)
1317+
list_result = self.runner.invoke(
1318+
tools,
1319+
['annotation-list', '--input-path', self.output1]
1320+
)
1321+
self.assertEqual(list_result.exit_code, 0)
13041322

1305-
list_result = self.runner.invoke(
1306-
tools,
1307-
['annotation-list', '--input-path', out_fp]
1308-
)
1309-
self.assertEqual(list_result.exit_code, 0)
1323+
remove_result = self.runner.invoke(
1324+
tools,
1325+
['annotation-remove', '--input-path', self.output1,
1326+
'--name', 'mynote', '--output-path', self.output1]
1327+
)
1328+
self.assertEqual(remove_result.exit_code, 0)
13101329

1311-
remove_result = self.runner.invoke(
1312-
tools,
1313-
['annotation-remove', '--input-path', out_fp,
1314-
'--name', 'mynote', '--output-path', out_fp]
1315-
)
1316-
self.assertEqual(remove_result.exit_code, 0)
1330+
def test_annotation_create_with_text(self):
1331+
create_result = self.runner.invoke(
1332+
tools,
1333+
['annotation-create', '--input-path', self.art1,
1334+
'--annotation-type', 'Note',
1335+
'--name', 'mynote', '--text', 'my special text',
1336+
'--output-path', self.output1]
1337+
)
1338+
# confirm the command doesn't produce an error
1339+
self.assertEqual(create_result.exit_code, 0)
1340+
1341+
output_uuid = Result.load(self.output1).uuid
1342+
# this will just produce one annotation
1343+
for annotation in Result.load(self.output1).iter_annotations():
1344+
annotation_uuid = annotation.id
1345+
1346+
exp_namelist = {
1347+
f'{output_uuid}/checksums.sha512',
1348+
f'{output_uuid}/metadata.yaml',
1349+
f'{output_uuid}/VERSION',
1350+
f'{output_uuid}/annotations/{annotation_uuid}/checksums.sha512',
1351+
f'{output_uuid}/annotations/{annotation_uuid}/metadata.yaml',
1352+
f'{output_uuid}/annotations/{annotation_uuid}/note.txt',
1353+
f'{output_uuid}/provenance/metadata.yaml',
1354+
f'{output_uuid}/provenance/citations.bib',
1355+
f'{output_uuid}/provenance/VERSION',
1356+
f'{output_uuid}/provenance/conda-env.yaml',
1357+
f'{output_uuid}/provenance/action/action.yaml',
1358+
f'{output_uuid}/data/ints.txt'
1359+
}
1360+
1361+
exp_contents = 'my special text'
1362+
1363+
with zipfile.ZipFile(self.output1, 'r') as zfh:
1364+
# confirm file structure is what we expect
1365+
self.assertEqual(exp_namelist, set(zfh.namelist()))
1366+
1367+
annotation = zfh.read(
1368+
f'{output_uuid}/annotations/{annotation_uuid}/note.txt'
1369+
).decode('utf-8')
1370+
1371+
# confirm the annotation file contains the text we expect
1372+
self.assertEqual(exp_contents, annotation)
1373+
1374+
# make sure we see same results as w/inline text for input
1375+
def test_annotation_create_with_file(self):
1376+
create_result = self.runner.invoke(
1377+
tools,
1378+
['annotation-create', '--input-path', self.art1,
1379+
'--annotation-type', 'Note',
1380+
'--name', 'mynote', '--file', self.note_file,
1381+
'--output-path', self.output1]
1382+
)
1383+
# confirm the command doesn't produce an error
1384+
self.assertEqual(create_result.exit_code, 0)
1385+
1386+
output_uuid = Result.load(self.output1).uuid
1387+
# this will just produce one annotation
1388+
for annotation in Result.load(self.output1).iter_annotations():
1389+
annotation_uuid = annotation.id
1390+
1391+
exp_namelist = {
1392+
f'{output_uuid}/checksums.sha512',
1393+
f'{output_uuid}/metadata.yaml',
1394+
f'{output_uuid}/VERSION',
1395+
f'{output_uuid}/annotations/{annotation_uuid}/checksums.sha512',
1396+
f'{output_uuid}/annotations/{annotation_uuid}/metadata.yaml',
1397+
f'{output_uuid}/annotations/{annotation_uuid}/note.txt',
1398+
f'{output_uuid}/provenance/metadata.yaml',
1399+
f'{output_uuid}/provenance/citations.bib',
1400+
f'{output_uuid}/provenance/VERSION',
1401+
f'{output_uuid}/provenance/conda-env.yaml',
1402+
f'{output_uuid}/provenance/action/action.yaml',
1403+
f'{output_uuid}/data/ints.txt'
1404+
}
1405+
1406+
exp_contents = 'my special text'
1407+
1408+
with zipfile.ZipFile(self.output1, 'r') as zfh:
1409+
# confirm file structure is what we expect
1410+
self.assertEqual(exp_namelist, set(zfh.namelist()))
1411+
1412+
annotation = zfh.read(
1413+
f'{output_uuid}/annotations/{annotation_uuid}/note.txt'
1414+
).decode('utf-8')
1415+
1416+
# confirm the annotation file contains the text we expect
1417+
self.assertEqual(exp_contents, annotation)
1418+
1419+
# make sure we don't run into any errors & see same result
1420+
# when overwriting the original artifact fp
1421+
def test_annotation_create_overwriting_input_file(self):
1422+
create_result = self.runner.invoke(
1423+
tools,
1424+
['annotation-create', '--input-path', self.art1,
1425+
'--annotation-type', 'Note',
1426+
'--name', 'mynote', '--text', 'my special text',
1427+
'--output-path', self.art1]
1428+
)
1429+
# confirm the command doesn't produce an error
1430+
self.assertEqual(create_result.exit_code, 0)
1431+
1432+
output_uuid = Result.load(self.art1).uuid
1433+
# this will just produce one annotation
1434+
for annotation in Result.load(self.art1).iter_annotations():
1435+
annotation_uuid = annotation.id
1436+
1437+
exp_namelist = {
1438+
f'{output_uuid}/checksums.sha512',
1439+
f'{output_uuid}/metadata.yaml',
1440+
f'{output_uuid}/VERSION',
1441+
f'{output_uuid}/annotations/{annotation_uuid}/checksums.sha512',
1442+
f'{output_uuid}/annotations/{annotation_uuid}/metadata.yaml',
1443+
f'{output_uuid}/annotations/{annotation_uuid}/note.txt',
1444+
f'{output_uuid}/provenance/metadata.yaml',
1445+
f'{output_uuid}/provenance/citations.bib',
1446+
f'{output_uuid}/provenance/VERSION',
1447+
f'{output_uuid}/provenance/conda-env.yaml',
1448+
f'{output_uuid}/provenance/action/action.yaml',
1449+
f'{output_uuid}/data/ints.txt'
1450+
}
1451+
1452+
exp_contents = 'my special text'
1453+
1454+
with zipfile.ZipFile(self.art1, 'r') as zfh:
1455+
# confirm file structure is what we expect
1456+
self.assertEqual(exp_namelist, set(zfh.namelist()))
1457+
1458+
annotation = zfh.read(
1459+
f'{output_uuid}/annotations/{annotation_uuid}/note.txt'
1460+
).decode('utf-8')
1461+
1462+
# confirm the annotation file contains the text we expect
1463+
self.assertEqual(exp_contents, annotation)
1464+
1465+
def test_annotation_create_invalid_annotation_type(self):
1466+
create_result = self.runner.invoke(
1467+
tools,
1468+
['annotation-create', '--input-path', self.art1,
1469+
'--annotation-type', 'Foo',
1470+
'--name', 'mynote', '--text', 'my special text',
1471+
'--output-path', self.output1]
1472+
)
1473+
# confirm the command does produce an error
1474+
self.assertEqual(create_result.exit_code, 1)
1475+
self.assertIn("Invalid value for '--annotation-type': 'Foo'",
1476+
create_result.output)
1477+
1478+
def test_annotation_create_text_and_filepath_provided(self):
1479+
create_result = self.runner.invoke(
1480+
tools,
1481+
['annotation-create', '--input-path', self.art1,
1482+
'--annotation-type', 'Note',
1483+
'--name', 'mynote', '--text', 'my special text',
1484+
'--file', self.note_file, '--output-path', self.output1]
1485+
)
1486+
# confirm the command does produce an error
1487+
self.assertEqual(create_result.exit_code, 2)
1488+
self.assertIn("Exactly one of `--text` or `--file` must be provided",
1489+
create_result.output)
1490+
1491+
def test_annotation_create_no_text_or_filepath_provided(self):
1492+
create_result = self.runner.invoke(
1493+
tools,
1494+
['annotation-create', '--input-path', self.art1,
1495+
'--annotation-type', 'Note',
1496+
'--name', 'mynote', '--output-path', self.output1]
1497+
)
1498+
# confirm the command does produce an error
1499+
self.assertEqual(create_result.exit_code, 2)
1500+
self.assertIn("Exactly one of `--text` or `--file` must be provided",
1501+
create_result.output)
1502+
1503+
def test_annotation_create_invalid_name(self):
1504+
create_result = self.runner.invoke(
1505+
tools,
1506+
['annotation-create', '--input-path', self.art1,
1507+
'--annotation-type', 'Note',
1508+
'--name', '@#$%^&*', '--text', 'my special text',
1509+
'--output-path', self.output1]
1510+
)
1511+
# confirm the command does produce an error
1512+
self.assertEqual(create_result.exit_code, 1)
1513+
# TODO: this exception should show up in the output same as others
1514+
self.assertIn('Name "@#$%^&*" is not a valid Python identifier',
1515+
str(create_result.exception))
13171516

13181517

13191518
if __name__ == "__main__":

0 commit comments

Comments
 (0)