def test_table_rest_update_rows( workspace: Workspace, user: User, authenticated_api_client: APIClient, permission: WorkspaceRoleChoice, is_owner: bool, status_code: int, success: bool, ): if permission is not None: workspace.set_user_permission(user, permission) elif is_owner: workspace.set_owner(user) node_table = populated_table(workspace, False) # Assert table contents beforehand original_table_rows = list(node_table.get_rows()) new_table_rows = [{**d, 'extra': 'field'} for d in original_table_rows] inserted_new_table_rows = list( map(dict_to_fuzzy_arango_doc, new_table_rows)) # Assert row update succeeded r = authenticated_api_client.put( f'/api/workspaces/{workspace.name}/tables/{node_table.name}/rows/', new_table_rows, format='json', ) assert r.status_code == status_code if success: assert r.json() == { 'inserted': len(new_table_rows), 'errors': [], } # Assert only existing rows were updated, and no new ones were added r = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/tables/{node_table.name}/rows/', ) r_json = r.json() assert r_json['count'] == len(original_table_rows) for i, row in enumerate(r_json['results']): assert row == inserted_new_table_rows[i] assert row['_id'] == original_table_rows[i]['_id'] else: table: Table = Table.objects.get(name=node_table.name) current_rows = table.get_rows() for row in current_rows: assert row in original_table_rows assert row not in new_table_rows
def test_workspace_rest_put_permissions( workspace: Workspace, user: User, user_factory: UserFactory, authenticated_api_client: APIClient, permission: WorkspaceRoleChoice, is_owner: bool, status_code: int, success: bool, ): old_owner = workspace.owner if permission is not None: workspace.set_user_permission(user, permission) elif is_owner: workspace.set_owner(user) new_owner = user_factory() new_maintainers: List[Dict] = [{'username': user_factory().username} for _ in range(2)] new_writers: List[Dict] = [{'username': user_factory().username} for _ in range(2)] new_readers: List[Dict] = [{'username': user_factory().username} for _ in range(2)] request_data = { 'public': True, 'owner': {'username': new_owner.username}, 'maintainers': new_maintainers, 'writers': new_writers, 'readers': new_readers, } r = authenticated_api_client.put( f'/api/workspaces/{workspace.name}/permissions/', request_data, format='json' ) workspace = Workspace.objects.get(id=workspace.pk) assert r.status_code == status_code if success: assert workspace.public == request_data['public'] if is_owner: assert workspace.owner == new_owner else: assert workspace.owner == old_owner readers_names = [reader['username'] for reader in new_readers] writers_names = [writer['username'] for writer in new_writers] maintainers_names = [maintainer['username'] for maintainer in new_maintainers] assert all([reader.username in readers_names for reader in workspace.readers]) assert all([writer.username in writers_names for writer in workspace.writers]) assert all( [maintainer.username in maintainers_names for maintainer in workspace.maintainers] ) else: assert workspace.public is False assert workspace.owner == old_owner
def test_workspace_rest_delete_unauthorized( workspace: Workspace, user: User, api_client: APIClient ): workspace.set_owner(user) r = api_client.delete(f'/api/workspaces/{workspace.name}/') assert r.status_code == 401 # Assert relevant objects are not deleted assert Workspace.objects.filter(name=workspace.name).exists() assert arango_system_db().has_database(workspace.arango_db_name)
def test_table_rest_delete_unauthorized(table_factory: TableFactory, workspace: Workspace, user: User, api_client: APIClient): workspace.set_user_permission(user, WorkspaceRoleChoice.WRITER) table: Table = table_factory(workspace=workspace) r = api_client.delete( f'/api/workspaces/{workspace.name}/tables/{table.name}/') assert r.status_code == 401 # Assert relevant objects are not deleted assert Table.objects.filter(name=table.name).exists() assert workspace.get_arango_db().has_collection(table.name)
def test_workspace_rest_aql_mutating_query( workspace: Workspace, user: User, authenticated_api_client: APIClient ): workspace.set_user_permission(user, WorkspaceRoleChoice.READER) fake = Faker() node_table = populated_table(workspace, False) # Mutating query query = f"INSERT {{ 'name': {fake.pystr()} }} INTO {node_table.name}" r = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/aql/', data={'query': query} ) assert r.status_code == 400
def test_table_rest_retrieve_rows_filter_invalid( workspace: Workspace, user: User, authenticated_api_client: APIClient, ): """Test that the use of an invalid filter param returns the expected error.""" workspace.set_owner(user) node_table = populated_table(workspace, False) r = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/tables/{node_table.name}/rows/', {'filter': 'foobar'}, # Should be a JSON string, not 'foobar' ) assert r.status_code == 400 assert 'filter' in r.json()
def test_create_upload_model_invalid_field_value( workspace: Workspace, user: User, authenticated_api_client: APIClient): workspace.set_user_permission(user, WorkspaceRoleChoice.WRITER) network_name = f't{uuid.uuid4().hex}' r: Response = authenticated_api_client.post( f'/api/workspaces/{workspace.name}/uploads/d3_json/', { 'field_value': 'field_value', 'network_name': network_name, }, format='json', ) assert r.status_code == 400 assert r.json() == InvalidFieldValueResponse.json()
def test_table_rest_insert_rows( table_factory: TableFactory, workspace: Workspace, user: User, authenticated_api_client: APIClient, permission: WorkspaceRoleChoice, is_owner: bool, status_code: int, success: bool, ): if permission is not None: workspace.set_user_permission(user, permission) elif is_owner: workspace.set_owner(user) table: Table = table_factory(workspace=workspace) table_rows = generate_arango_documents(5) inserted_table_rows = list(map(dict_to_fuzzy_arango_doc, table_rows)) # Test insert response r = authenticated_api_client.put( f'/api/workspaces/{workspace.name}/tables/{table.name}/rows/', table_rows, format='json', ) assert r.status_code == status_code if success: assert r.json() == { 'inserted': len(table_rows), 'errors': [], } # Test that rows are populated after r = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/tables/{table.name}/rows/', ) assert r.status_code == 200 assert r.json() == { 'count': len(table_rows), 'next': None, 'previous': None, 'results': inserted_table_rows, } else: table: Table = Table.objects.get(name=table.name) assert table.get_rows().count() == 0
def test_table_rest_retrieve_rows_filter_one( workspace: Workspace, user: User, authenticated_api_client: APIClient, ): workspace.set_owner(user) node_table = populated_table(workspace, False) table_rows: List[Dict] = list(node_table.get_rows()) filter_doc = dict(table_rows[0]) filter_doc.pop('_key') filter_doc.pop('_id') filter_doc.pop('_rev') assert_limit_offset_results( authenticated_api_client, f'/api/workspaces/{workspace.name}/tables/{node_table.name}/rows/', result=[table_rows[0]], params={'filter': json.dumps(filter_doc)}, )
def test_table_rest_upsert_rows( workspace: Workspace, user: User, authenticated_api_client: APIClient, ): workspace.set_user_permission(user, WorkspaceRoleChoice.WRITER) node_table = populated_table(workspace, False) # Create row lists original_table_rows = list(node_table.get_rows()) new_table_rows = generate_arango_documents(3) partially_updated_table_rows = [{ **d, 'extra': 'field' } for d in original_table_rows[:2]] upsert_payload = [*partially_updated_table_rows, *new_table_rows] # Create fuzzy payload for later assertions fuzzy_upsert_payload = [ *map(dict_to_fuzzy_arango_doc, new_table_rows), *map(arango_doc_to_fuzzy_rev, partially_updated_table_rows), ] # Test combined row insert/update r = authenticated_api_client.put( f'/api/workspaces/{workspace.name}/tables/{node_table.name}/rows/', upsert_payload, format='json', ) assert r.status_code == 200 assert r.json() == { 'inserted': len(upsert_payload), 'errors': [], } # Test row population after r = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/tables/{node_table.name}/rows/', ) r_json = r.json() assert r_json['count'] == len(original_table_rows) + len(new_table_rows) assert all(row in original_table_rows or row in fuzzy_upsert_payload for row in r_json['results'])
def test_table_rest_delete_rows( workspace: Workspace, user: User, authenticated_api_client: APIClient, permission: WorkspaceRoleChoice, is_owner: bool, status_code: int, success: bool, ): if permission is not None: workspace.set_user_permission(user, permission) elif is_owner: workspace.set_owner(user) node_table = populated_table(workspace, False) table_rows = list(node_table.get_rows()) r = authenticated_api_client.delete( f'/api/workspaces/{workspace.name}/tables/{node_table.name}/rows/', table_rows, format='json', ) assert r.status_code == status_code if success: assert r.json() == { 'deleted': len(table_rows), 'errors': [], } r = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/tables/{node_table.name}/rows/') assert r.status_code == 200 assert r.json() == { 'count': 0, 'next': None, 'previous': None, 'results': [], } else: table: Table = Table.objects.get(name=node_table.name) assert table.get_rows().count() == len(table_rows)
def test_workspace_rest_list_no_duplicates( workspace: Workspace, user_factory: UserFactory, user: User, authenticated_api_client: APIClient, ): """Test that multiple roles on a workspace results in no duplicates.""" # Set authenticated user as owner workspace.set_owner(user) # Give multiple users permissions on the workspace workspace.set_user_permissions_bulk(readers=[user_factory() for _ in range(5)]) # Test that there's only one copy of this workspace returned r = authenticated_api_client.get('/api/workspaces/') assert r.json() == { 'count': 1, 'next': None, 'previous': None, 'results': [workspace_re(workspace)], }
def test_create_upload_model_invalid_permissions( workspace: Workspace, user: User, authenticated_api_client: APIClient, miserables_json_field_value, permission: WorkspaceRoleChoice, status_code: int, ): if permission is not None: workspace.set_user_permission(user, permission) network_name = f't{uuid.uuid4().hex}' r: Response = authenticated_api_client.post( f'/api/workspaces/{workspace.name}/uploads/d3_json/', { 'field_value': miserables_json_field_value, 'network_name': network_name, }, format='json', ) assert r.status_code == status_code
def test_table_rest_retrieve_rows_filter_many( workspace: Workspace, user: User, authenticated_api_client: APIClient, ): workspace.set_owner(user) node_table = populated_table(workspace, False) # Create extra documents and insert common field docs = generate_arango_documents(5) for doc in docs: doc['extra_field'] = 'value' node_table.put_rows(docs) docs = [doc for doc in node_table.get_rows() if 'extra_field' in doc] filter_dict = {'extra_field': 'value'} assert_limit_offset_results( authenticated_api_client, f'/api/workspaces/{workspace.name}/tables/{node_table.name}/rows/', result=docs, params={'filter': json.dumps(filter_dict)}, )
def test_workspace_rest_get_permissions( workspace: Workspace, user: User, user_factory: UserFactory, authenticated_api_client: APIClient, permission: WorkspaceRoleChoice, is_owner: bool, status_code: int, success: bool, ): if permission is not None: workspace.set_user_permission(user, permission) workspace.set_owner(user_factory()) elif is_owner: workspace.set_owner(user) create_users_with_permissions(user_factory, workspace) maintainer_names = [maintainer.username for maintainer in workspace.maintainers] writer_names = [writer.username for writer in workspace.writers] reader_names = [reader.username for reader in workspace.readers] r = authenticated_api_client.get(f'/api/workspaces/{workspace.name}/permissions/') assert r.status_code == status_code if success: r_json = r.json() assert r_json['public'] == workspace.public assert r_json['owner']['username'] == workspace.owner.username assert all( maintainer['username'] in maintainer_names for maintainer in r_json['maintainers'] ) assert all(writer['username'] in writer_names for writer in r_json['writers']) assert all(reader['username'] in reader_names for reader in r_json['readers'])
def test_network_rest_delete( workspace: Workspace, user: User, authenticated_api_client: APIClient, permission: WorkspaceRoleChoice, is_owner: bool, status_code: int, success: bool, ): """Tests deleting a network on a workspace for which the user is at least a writer.""" if permission is not None: workspace.set_user_permission(user, permission) elif is_owner: workspace.set_owner(user) network = populated_network(workspace) r = authenticated_api_client.delete( f'/api/workspaces/{workspace.name}/networks/{network.name}/') assert r.status_code == status_code if success: # Assert relevant objects are deleted assert not Network.objects.filter(name=network.name).exists() assert not workspace.get_arango_db().has_graph(network.name) else: # Assert objects are not deleted assert Network.objects.filter(name=network.name).exists() assert workspace.get_arango_db().has_graph(network.name)
def test_table_rest_delete( table_factory: TableFactory, workspace: Workspace, user: User, authenticated_api_client: APIClient, permission: WorkspaceRoleChoice, is_owner: bool, status_code: int, success: bool, ): if permission is not None: workspace.set_user_permission(user, permission) elif is_owner: workspace.set_owner(user) table: Table = table_factory(workspace=workspace) r = authenticated_api_client.delete( f'/api/workspaces/{workspace.name}/tables/{table.name}/') assert r.status_code == status_code if success: # Assert relevant objects are deleted assert not Table.objects.filter(name=table.name).exists() assert not workspace.get_arango_db().has_collection(table.name) else: assert Table.objects.filter(name=table.name).exists() assert workspace.get_arango_db().has_collection(table.name)
def miserables_json( workspace: Workspace, user: User, authenticated_api_client: APIClient, miserables_json_field_value, ) -> Dict: # Model creation request workspace.set_user_permission(user, WorkspaceRoleChoice.WRITER) network_name = f't{uuid.uuid4().hex}' r: Response = authenticated_api_client.post( f'/api/workspaces/{workspace.name}/uploads/d3_json/', { 'field_value': miserables_json_field_value, 'network_name': network_name, }, format='json', ) WorkspaceRole.objects.filter(workspace=workspace, user=user).delete() return { 'response': r, 'network_name': network_name, }
def test_table_rest_retrieve( workspace: Workspace, table_factory: TableFactory, user: User, authenticated_api_client: APIClient, permission: WorkspaceRoleChoice, is_owner: bool, status_code: int, success: bool, ): if permission is not None: workspace.set_user_permission(user, permission) elif is_owner: workspace.set_owner(user) table: Table = table_factory(workspace=workspace) r = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/tables/{table.name}/') assert r.status_code == status_code if success: assert r.data == { 'id': table.pk, 'name': table.name, 'edge': table.edge, 'created': TIMESTAMP_RE, 'modified': TIMESTAMP_RE, 'workspace': { 'id': workspace.pk, 'name': workspace.name, 'created': TIMESTAMP_RE, 'modified': TIMESTAMP_RE, 'arango_db_name': workspace.arango_db_name, 'public': False, }, } else: assert r.data == {'detail': 'Not found.'}
def test_create_upload_model_duplicate_names( workspace: Workspace, user: User, authenticated_api_client: APIClient, miserables_json_field_value, ): """Test that attempting to create a network with names that are already taken, fails.""" workspace.set_user_permission(user, WorkspaceRoleChoice.WRITER) network_name = f't{uuid.uuid4().hex}' def assert_response(): r: Response = authenticated_api_client.post( f'/api/workspaces/{workspace.name}/uploads/d3_json/', { 'field_value': miserables_json_field_value, 'network_name': network_name, }, format='json', ) assert r.status_code == 400 assert 'network_name' in r.json() # Try with just node table node_table: Table = Table.objects.create(name=f'{network_name}_nodes', workspace=workspace, edge=False) assert_response() # Add edge table edge_table: Table = Table.objects.create(name=f'{network_name}_edges', workspace=workspace, edge=True) assert_response() # Add network Network.create_with_edge_definition(network_name, workspace, edge_table.name, [node_table.name]) assert_response()
def test_network_rest_delete_unauthorized(workspace: Workspace, api_client: APIClient): """Tests deleting a network from a workspace with an unauthorized request.""" network = populated_network(workspace) r = api_client.delete( f'/api/workspaces/{workspace.name}/networks/{network.name}/') assert r.status_code == 401 # Assert relevant objects are not deleted assert Network.objects.filter(name=network.name).exists() assert workspace.get_arango_db().has_graph(network.name)
def test_network_rest_retrieve( workspace: Workspace, user: User, authenticated_api_client: APIClient, permission: WorkspaceRoleChoice, is_owner: bool, status_code: int, success: bool, ): if permission is not None: workspace.set_user_permission(user, permission) elif is_owner: workspace.set_owner(user) network = populated_network(workspace) r = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/networks/{network.name}/') assert r.status_code == status_code if success: assert r.data == { 'id': network.pk, 'name': network.name, 'node_count': network.node_count, 'edge_count': network.edge_count, 'created': TIMESTAMP_RE, 'modified': TIMESTAMP_RE, 'workspace': { 'id': workspace.pk, 'name': workspace.name, 'created': TIMESTAMP_RE, 'modified': TIMESTAMP_RE, 'arango_db_name': workspace.arango_db_name, 'public': False, }, } else: assert r.data == {'detail': 'Not found.'}
def test_network_rest_create( workspace: Workspace, user: User, authenticated_api_client: APIClient, permission: WorkspaceRoleChoice, is_owner: bool, status_code: int, success: bool, ): if permission is not None: workspace.set_user_permission(user, permission) elif is_owner: workspace.set_owner(user) edge_table = populated_table(workspace, True) node_table_name = list(edge_table.find_referenced_node_tables().keys())[0] node_table = Table.objects.get(name=node_table_name) network_name = 'network' r = authenticated_api_client.post( f'/api/workspaces/{workspace.name}/networks/', { 'name': network_name, 'edge_table': edge_table.name }, format='json', ) assert r.status_code == status_code if success: assert r.json() == { 'name': network_name, 'node_count': len(node_table.get_rows()), 'edge_count': len(edge_table.get_rows()), 'id': INTEGER_ID_RE, 'created': TIMESTAMP_RE, 'modified': TIMESTAMP_RE, 'workspace': { 'id': workspace.pk, 'name': workspace.name, 'created': TIMESTAMP_RE, 'modified': TIMESTAMP_RE, 'arango_db_name': workspace.arango_db_name, 'public': False, }, } # Django will raise an exception if this fails, implicitly validating that the object exists network: Network = Network.objects.get(name=network_name) # Assert that object was created in arango assert workspace.get_arango_db().has_graph(network.name) else: assert not Network.objects.filter(name=network_name).exists() assert not workspace.get_arango_db().has_graph(network_name)
def test_table_rest_list_public(table_factory: TableFactory, public_workspace: Workspace, api_client: APIClient): """Test whether a user can see all tables on a public workspace.""" fake = Faker() table_names: List[str] = [ table_factory(name=fake.pystr(), workspace=public_workspace).name for _ in range(3) ] r = api_client.get(f'/api/workspaces/{public_workspace.name}/tables/') r_json = r.json() # Test that we get the expected results from both django and arango arango_db = public_workspace.get_arango_db() assert r_json['count'] == len(table_names) for table in r_json['results']: assert table['name'] in table_names assert arango_db.has_collection(table['name'])
def test_table_rest_create( workspace: Workspace, user: User, authenticated_api_client: APIClient, edge: bool, permission: WorkspaceRoleChoice, is_owner: bool, status_code: int, success: bool, ): if permission is not None: workspace.set_user_permission(user, permission) elif is_owner: workspace.set_owner(user) table_name = Faker().pystr() r = authenticated_api_client.post( f'/api/workspaces/{workspace.name}/tables/', { 'name': table_name, 'edge': edge }, format='json', ) assert r.status_code == status_code if success: r_json = r.json() assert r_json == { 'name': table_name, 'edge': edge, 'id': INTEGER_ID_RE, 'created': TIMESTAMP_RE, 'modified': TIMESTAMP_RE, 'workspace': { 'id': workspace.pk, 'name': workspace.name, 'created': TIMESTAMP_RE, 'modified': TIMESTAMP_RE, 'arango_db_name': workspace.arango_db_name, 'public': False, }, } # Django will raise an exception if this fails, implicitly validating that the object exists table: Table = Table.objects.get(name=table_name) # Assert that object was created in arango assert workspace.get_arango_db().has_collection(table.name) else: assert not Table.objects.filter(name=table_name).exists() assert not workspace.get_arango_db().has_collection(table_name)
def test_d3_json_task_filter_missing( workspace: Workspace, user: User, authenticated_api_client: APIClient, s3ff_field_value_factory, ): """Test that missing node.id or link.[source/target] fields are removed.""" workspace.set_user_permission(user, WorkspaceRoleChoice.WRITER) # Read original file json_dict = json.load(open(miserables_json_file, 'r')) original_node_length = len(json_dict['nodes']) original_link_length = len(json_dict['links']) # Add empty entries json_dict['nodes'].extend([{} for _ in range(10)]) new_node_length = len(json_dict['nodes']) assert new_node_length != original_node_length json_dict['links'].extend([{} for _ in range(15)]) new_links_length = len(json_dict['links']) assert new_links_length != original_link_length # Upload new broken JSON file = io.BytesIO(json.dumps(json_dict).encode('utf-8')) upload = json_upload(file, 'miserables', workspace, user) field_value = s3ff_field_value_factory(upload.blob) network_name = f't{uuid.uuid4().hex}' node_table_name = f'{network_name}_nodes' edge_table_name = f'{network_name}_edges' upload_resp: Response = authenticated_api_client.post( f'/api/workspaces/{workspace.name}/uploads/d3_json/', { 'field_value': field_value, 'network_name': network_name, }, format='json', ) # Assert upload succeeds r: Response = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/uploads/{upload_resp.json()["id"]}/' ) assert r.status_code == 200 assert r.json()['status'] == Upload.Status.FINISHED assert r.json()['error_messages'] is None # Assert node table doesn't contain empty rows r: Response = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/tables/{node_table_name}/rows/', {'limit': 1}) assert r.status_code == 200 assert r.json()['count'] == original_node_length # Assert edge table doesn't contain empty rows r: Response = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/tables/{edge_table_name}/rows/', {'limit': 1}) assert r.status_code == 200 assert r.json()['count'] == original_link_length
def test_valid_d3_json_task_response(workspace: Workspace, user: User, authenticated_api_client: APIClient, miserables_json): """Test just the response of the model creation, not the task itself.""" # Get upload info workspace.set_user_permission(user, WorkspaceRoleChoice.WRITER) r = miserables_json['response'] network_name = miserables_json['network_name'] node_table_name = f'{network_name}_nodes' edge_table_name = f'{network_name}_edges' # Since we're running with celery_task_always_eager=True, this job is finished r: Response = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/uploads/{r.json()["id"]}/') r_json = r.json() assert r.status_code == 200 assert r_json['status'] == Upload.Status.FINISHED assert r_json['error_messages'] is None # Check that tables are created for table_name in (node_table_name, edge_table_name): r: Response = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/tables/{table_name}/') assert r.status_code == 200 # Check that network was created r: Response = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/networks/{network_name}/') assert r.status_code == 200 # Get source data with open(miserables_json_file) as file_stream: loaded_miserables_json_file = json.load(file_stream) nodes = sorted( (d3_node_to_arango_doc(node) for node in loaded_miserables_json_file['nodes']), key=operator.itemgetter('_key'), ) links = sorted( (d3_link_to_arango_doc(link, node_table_name) for link in loaded_miserables_json_file['links']), key=operator.itemgetter('_from'), ) # Check that nodes were ingested correctly r: Response = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/networks/{network_name}/nodes/') r_json = r.json() assert r.status_code == 200 assert r_json['count'] == len(nodes) results = sorted(r_json['results'], key=operator.itemgetter('_key')) for i, node in enumerate(nodes): assert results[i] == dict_to_fuzzy_arango_doc(node, exclude=['_key']) # Check that links were ingested correctly r: Response = authenticated_api_client.get( f'/api/workspaces/{workspace.name}/networks/{network_name}/edges/') r_json = r.json() assert r.status_code == 200 assert r_json['count'] == len(links) results = sorted(r_json['results'], key=operator.itemgetter('_from')) for i, link in enumerate(links): assert results[i] == dict_to_fuzzy_arango_doc(link)