from http import HTTPStatus from subprocess import Popen import unittest import time import requests import decode_fbs LOCAL_URL = "http://127.0.0.1:5005/" VERSION = "v0.2" URL_BASE = f"{LOCAL_URL}api/{VERSION}/" BAD_FILTER = {"filter": {"obs": {"annotation_value": [{"name": "xyz"}]}}} class EndPoints(unittest.TestCase): """Test Case for endpoints""" @classmethod def setUpClass(cls): cls.ps = Popen(["cellxgene", "launch", "example-dataset/pbmc3k.h5ad", "--verbose", "--port", "5005"]) session = requests.Session() for i in range(90): try: result = session.get(f"{URL_BASE}schema") cls.schema = result.json() except requests.exceptions.ConnectionError: time.sleep(1) @classmethod def tearDownClass(cls): try: cls.ps.terminate() except ProcessLookupError: pass def setUp(self): self.session = requests.Session() def test_initialize(self): endpoint = "schema" url = f"{URL_BASE}{endpoint}" result = self.session.get(url) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/json") result_data = result.json() self.assertEqual(result_data["schema"]["dataframe"]["nObs"], 2638) self.assertEqual(len(result_data["schema"]["annotations"]["obs"]), 2) self.assertEqual(len(result_data["schema"]["annotations"]["obs"]["columns"]), 5) def test_config(self): endpoint = "config" url = f"{URL_BASE}{endpoint}" result = self.session.get(url) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/json") result_data = result.json() self.assertIn("library_versions", result_data["config"]) self.assertEqual(result_data["config"]["displayNames"]["dataset"], "pbmc3k") self.assertEqual(len(result_data["config"]["features"]), 4) def test_get_layout_fbs(self): endpoint = "layout/obs" url = f"{URL_BASE}{endpoint}" header = {"Accept": "application/octet-stream"} result = self.session.get(url, headers=header) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/octet-stream") df = decode_fbs.decode_matrix_FBS(result.content) self.assertEqual(df['n_rows'], 2638) self.assertEqual(df['n_cols'], 8) self.assertIsNotNone(df['columns']) self.assertListEqual(df['col_idx'], [ 'pca_0', 'pca_1', 'tsne_0', 'tsne_1', 'umap_0', 'umap_1', 'draw_graph_fr_0', 'draw_graph_fr_1' ]) self.assertIsNone(df['row_idx']) self.assertEqual(len(df['columns']), df['n_cols']) def test_bad_filter(self): endpoint = "data/var" url = f"{URL_BASE}{endpoint}" result = self.session.put(url, json=BAD_FILTER) self.assertEqual(result.status_code, HTTPStatus.BAD_REQUEST) def test_get_annotations_obs_fbs(self): endpoint = "annotations/obs" url = f"{URL_BASE}{endpoint}" header = {"Accept": "application/octet-stream"} result = self.session.get(url, headers=header) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/octet-stream") df = decode_fbs.decode_matrix_FBS(result.content) self.assertEqual(df['n_rows'], 2638) self.assertEqual(df['n_cols'], 5) self.assertIsNotNone(df['columns']) self.assertIsNotNone(df['col_idx']) self.assertIsNone(df['row_idx']) self.assertEqual(len(df['columns']), df['n_cols']) obs_index_col_name = self.schema["schema"]["annotations"]["obs"]["index"] self.assertListEqual(df['col_idx'], [obs_index_col_name, 'n_genes', 'percent_mito', 'n_counts', 'louvain']) def test_get_annotations_obs_keys_fbs(self): endpoint = "annotations/obs" query = "annotation-name=n_genes&annotation-name=percent_mito" url = f"{URL_BASE}{endpoint}?{query}" header = {"Accept": "application/octet-stream"} result = self.session.get(url, headers=header) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/octet-stream") df = decode_fbs.decode_matrix_FBS(result.content) self.assertEqual(df['n_rows'], 2638) self.assertEqual(df['n_cols'], 2) self.assertIsNotNone(df['columns']) self.assertIsNotNone(df['col_idx']) self.assertIsNone(df['row_idx']) self.assertEqual(len(df['columns']), df['n_cols']) self.assertListEqual(df['col_idx'], ['n_genes', 'percent_mito']) def test_get_annotations_obs_error(self): endpoint = "annotations/obs" query = "annotation-name=notakey" url = f"{URL_BASE}{endpoint}?{query}" result = self.session.get(url) self.assertEqual(result.status_code, HTTPStatus.BAD_REQUEST) def test_diff_exp(self): endpoint = "diffexp/obs" url = f"{URL_BASE}{endpoint}" params = { "mode": "topN", "set1": {"filter": {"obs": {"annotation_value": [{"name": "louvain", "values": ["NK cells"]}]}}}, "set2": {"filter": {"obs": {"annotation_value": [{"name": "louvain", "values": ["CD8 T cells"]}]}}}, "count": 7, } result = self.session.post(url, json=params) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/json") result_data = result.json() self.assertEqual(len(result_data), 7) def test_diff_exp_indices(self): endpoint = "diffexp/obs" url = f"{URL_BASE}{endpoint}" params = { "mode": "topN", "count": 10, "set1": {"filter": {"obs": {"index": [[0, 500]]}}}, "set2": {"filter": {"obs": {"index": [[500, 1000]]}}}, } result = self.session.post(url, json=params) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/json") result_data = result.json() self.assertEqual(len(result_data), 10) def test_get_annotations_var_fbs(self): endpoint = "annotations/var" url = f"{URL_BASE}{endpoint}" header = {"Accept": "application/octet-stream"} result = self.session.get(url, headers=header) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/octet-stream") df = decode_fbs.decode_matrix_FBS(result.content) self.assertEqual(df['n_rows'], 1838) self.assertEqual(df['n_cols'], 2) self.assertIsNotNone(df['columns']) self.assertIsNotNone(df['col_idx']) self.assertIsNone(df['row_idx']) self.assertEqual(len(df['columns']), df['n_cols']) var_index_col_name = self.schema["schema"]["annotations"]["var"]["index"] self.assertListEqual(df['col_idx'], [var_index_col_name, 'n_cells']) def test_get_annotations_var_keys_fbs(self): endpoint = "annotations/var" query = "annotation-name=n_cells" url = f"{URL_BASE}{endpoint}?{query}" header = {"Accept": "application/octet-stream"} result = self.session.get(url, headers=header) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/octet-stream") df = decode_fbs.decode_matrix_FBS(result.content) self.assertEqual(df['n_rows'], 1838) self.assertEqual(df['n_cols'], 1) self.assertIsNotNone(df['columns']) self.assertIsNotNone(df['col_idx']) self.assertIsNone(df['row_idx']) self.assertEqual(len(df['columns']), df['n_cols']) self.assertListEqual(df['col_idx'], ['n_cells']) def test_get_annotations_var_error(self): endpoint = "annotations/var" query = "annotation-name=notakey" url = f"{URL_BASE}{endpoint}?{query}" result = self.session.get(url) self.assertEqual(result.status_code, HTTPStatus.BAD_REQUEST) def test_data_mimetype_error(self): endpoint = f"data/var" header = {"Accept": "xxx"} url = f"{URL_BASE}{endpoint}" result = self.session.put(url, headers=header) self.assertEqual(result.status_code, HTTPStatus.NOT_ACCEPTABLE) def test_fbs_default(self): endpoint = f"data/var" url = f"{URL_BASE}{endpoint}" result = self.session.put(url) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/octet-stream") def test_data_put_fbs(self): endpoint = f"data/var" url = f"{URL_BASE}{endpoint}" header = {"Accept": "application/octet-stream"} result = self.session.put(url, headers=header) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/octet-stream") df = decode_fbs.decode_matrix_FBS(result.content) self.assertEqual(df['n_rows'], 2638) self.assertEqual(df['n_cols'], 1838) self.assertIsNotNone(df['columns']) self.assertListEqual(df['col_idx'].tolist(), []) self.assertIsNone(df['row_idx']) self.assertEqual(len(df['columns']), df['n_cols']) def test_data_put_filter_fbs(self): endpoint = f"data/var" url = f"{URL_BASE}{endpoint}" header = {"Accept": "application/octet-stream"} filter = { "filter": { "var": { "index": [0, 1, 4] } } } result = self.session.put(url, headers=header, json=filter) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/octet-stream") df = decode_fbs.decode_matrix_FBS(result.content) self.assertEqual(df['n_rows'], 2638) self.assertEqual(df['n_cols'], 3) self.assertIsNotNone(df['columns']) self.assertIsNotNone(df['col_idx']) self.assertIsNone(df['row_idx']) self.assertEqual(len(df['columns']), df['n_cols']) self.assertListEqual(df['col_idx'].tolist(), [0, 1, 4]) def test_data_put_single_var(self): endpoint = f"data/var" url = f"{URL_BASE}{endpoint}" header = {"Accept": "application/octet-stream"} index_col_name = self.schema["schema"]["annotations"]["var"]["index"] var_filter = {"filter": {"var": {"annotation_value": [{"name": index_col_name, "values": ["RER1"]}]}}} result = self.session.put(url, headers=header, json=var_filter) self.assertEqual(result.status_code, HTTPStatus.OK) self.assertEqual(result.headers["Content-Type"], "application/octet-stream") df = decode_fbs.decode_matrix_FBS(result.content) self.assertEqual(df["n_rows"], 2638) self.assertEqual(df["n_cols"], 1) def test_static(self): endpoint = "static" file = "js/service-worker.js" url = f"{LOCAL_URL}{endpoint}/{file}" result = self.session.get(url) self.assertEqual(result.status_code, HTTPStatus.OK)