diff --git a/docker-compose.test.yaml b/docker-compose.test.yaml index f167171..bd585a0 100644 --- a/docker-compose.test.yaml +++ b/docker-compose.test.yaml @@ -20,7 +20,8 @@ services: command: /bin/bash -c ' cd /tds && /poetry_user_install_dev.bash && - pytest -svv --cov=transcriptomics_data_service --cov-branch + pytest -svv --cov=transcriptomics_data_service --cov-branch && + coverage html ' tds-db: diff --git a/tests/test_db.py b/tests/test_db.py index f8f5dff..2dafec3 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -1,16 +1,84 @@ +from asyncpg import Connection, UniqueViolationError import pytest from transcriptomics_data_service.db import Database -from transcriptomics_data_service.models import ExperimentResult +from transcriptomics_data_service.models import ExperimentResult, GeneExpression +TEST_EXPERIMENT_RESULT_ID = "test-experiment-id" +TEST_EXPERIMENT_RESULT = ExperimentResult( + experiment_result_id=TEST_EXPERIMENT_RESULT_ID, + assembly_id="assembly_test_id", + assembly_name="assembly_test_name", +) +TEST_GENE_EXPRESSION = GeneExpression( + experiment_result_id=TEST_EXPERIMENT_RESULT_ID, + gene_code="test-gene-code", + sample_id="test-sample-id", + raw_count=90, +) +########################## +# CRUD: experiment_results +########################## + + +@pytest.mark.asyncio +async def test_create_read_experiment_result(db: Database, db_cleanup): + # exp_result = await _create_experiment_result(db) + await db.create_experiment_result(TEST_EXPERIMENT_RESULT) + db_exp_result = await db.read_experiment_result(TEST_EXPERIMENT_RESULT_ID) + assert TEST_EXPERIMENT_RESULT == db_exp_result + + +@pytest.mark.asyncio +async def test_update_experiment_result(db: Database, db_cleanup): + await db.create_experiment_result(TEST_EXPERIMENT_RESULT) + new_exp = TEST_EXPERIMENT_RESULT.model_copy() + new_exp.assembly_name = "updated_assembly_name" + await db.update_experiment_result(new_exp) + db_exp_result = await db.read_experiment_result(new_exp.experiment_result_id) + assert db_exp_result.assembly_name == new_exp.assembly_name + + +@pytest.mark.asyncio +async def test_delete_experiment_result(db: Database, db_cleanup): + await db.create_experiment_result(TEST_EXPERIMENT_RESULT) + await db.delete_experiment_result(TEST_EXPERIMENT_RESULT_ID) + db_exp_result = await db.read_experiment_result(TEST_EXPERIMENT_RESULT_ID) + assert db_exp_result == None + + +######################## +# CRUD: gene_expressions +######################## + + +@pytest.mark.asyncio +async def test_gene_expression(db: Database, db_cleanup): + async with db.transaction_connection() as conn: + await db.create_experiment_result(TEST_EXPERIMENT_RESULT, conn) + await db.create_gene_expressions([TEST_GENE_EXPRESSION], conn) + + # read all + db_expressions = await db.fetch_expressions() + assert len(db_expressions) == 1 + assert db_expressions[0] == TEST_GENE_EXPRESSION + + # read by ExperimentResult ID + db_expressions = await db.fetch_expressions(experiment_result_id=TEST_EXPERIMENT_RESULT_ID) + assert db_expressions[0] == TEST_GENE_EXPRESSION + + +# TEST TRANSACTIONS @pytest.mark.asyncio -async def test_create_experiment_result(db: Database, db_cleanup): - exp_id = "exp_test" - exp_result = ExperimentResult( - experiment_result_id=exp_id, - assembly_id="assembly_test_id", - assembly_name="assembly_test_name", - ) - await db.create_experiment_result(exp_result) - db_exp_result = await db.read_experiment_result(exp_id) - assert exp_result == db_exp_result +async def test_transaction(db: Database, db_cleanup): + async with db.transaction_connection() as conn: + try: + await db.create_experiment_result(TEST_EXPERIMENT_RESULT, conn) + await db.create_gene_expressions([TEST_GENE_EXPRESSION, TEST_GENE_EXPRESSION], conn) + assert False + except: + assert True + db_exp = await db.read_experiment_result(TEST_EXPERIMENT_RESULT_ID) + db_gene_expr = await db.fetch_expressions() + assert db_exp == None + assert len(db_gene_expr) == 0 diff --git a/transcriptomics_data_service/db.py b/transcriptomics_data_service/db.py index bdf3307..a9b76f2 100644 --- a/transcriptomics_data_service/db.py +++ b/transcriptomics_data_service/db.py @@ -91,10 +91,8 @@ async def create_gene_expressions(self, expressions: list[GeneExpression], trans Rows on gene_expressions can only be created as part of an RCM ingestion. Ingestion is all-or-nothing, hence the transaction. """ - async with transaction_conn.transaction(): - # sub-transaction - for gene_expression in expressions: - await self._create_gene_expression(gene_expression, transaction_conn) + for gene_expression in expressions: + await self._create_gene_expression(gene_expression, transaction_conn) async def _create_gene_expression(self, expression: GeneExpression, transaction_conn: asyncpg.Connection): # Creates a row on gene_expressions within a transaction. @@ -112,15 +110,15 @@ async def _create_gene_expression(self, expression: GeneExpression, transaction_ expression.tmm_count, ) - async def fetch_expressions(self) -> tuple[GeneExpression, ...]: - return tuple([r async for r in self._select_expressions(None)]) + async def fetch_expressions(self, experiment_result_id: str | None = None) -> tuple[GeneExpression, ...]: + return tuple([r async for r in self._select_expressions(exp_id=experiment_result_id)]) async def _select_expressions(self, exp_id: str | None) -> AsyncIterator[GeneExpression]: conn: asyncpg.Connection where_clause = "WHERE experiment_result_id = $1" if exp_id is not None else "" query = f"SELECT * FROM gene_expressions {where_clause}" async with self.connect() as conn: - res = await conn.fetch(query, *((exp_id) if exp_id is not None else ())) + res = await conn.fetch(query, *((exp_id,) if exp_id is not None else ())) for r in map(lambda g: self._deserialize_gene_expression(g), res): yield r