def test_db_api(app_req_ctx): """Test DB API.""" with app_req_ctx("/data/api/v0.1/organisations/", headers=dict(authorization="Bearer TEST")) as ctx: rv = ctx.app.full_dispatch_request() assert rv.status_code == 200 data = json.loads(rv.data) assert "objects" in data assert len(data["objects"]) == 4 with app_req_ctx("/data/api/v0.1/tasks/", headers=dict(authorization="Bearer TEST")) as ctx: rv = ctx.app.full_dispatch_request() assert rv.status_code == 200 data = json.loads(rv.data) assert "objects" in data assert len(data["objects"]) == 0 org = Organisation.get(id=1) with app_req_ctx(f"/data/api/v0.1/organisations/{org.id}", headers=dict(authorization="Bearer TEST")) as ctx: rv = ctx.app.full_dispatch_request() assert rv.status_code == 200 data = json.loads(rv.data) assert data["name"] == org.name assert data["tuakiri_name"] == org.tuakiri_name org = Organisation.get(id=2) with app_req_ctx(f"/data/api/v0.1/organisations/{org.id}", headers=dict(authorization="Bearer TEST")) as ctx: rv = ctx.app.full_dispatch_request() assert rv.status_code == 200 data = json.loads(rv.data) assert data["name"] == org.name assert data["tuakiri_name"] == org.tuakiri_name
def test_test_database(models): """Test of the consitency of the test database.""" assert Organisation.select().count() == 10 assert User.select().count() == 63 assert OrcidToken.select().count() == 60 assert AffiliationRecord.select().count() == 10 assert FundingRecord.select().count() == 10 assert FundingContributor.select().count() == 10 assert FundingInvitee.select().count() == 10 assert ExternalId.select().count() == 10 assert WorkRecord.select().count() == 10 assert WorkContributor.select().count() == 10 assert WorkExternalId.select().count() == 10 assert WorkInvitee.select().count() == 10 assert PeerReviewRecord.select().count() == 10 assert PeerReviewExternalId.select().count() == 10 assert PeerReviewInvitee.select().count() == 10 assert ResearcherUrlRecord.select().count() == 10 assert OtherNameRecord.select().count() == 10 assert KeywordRecord.select().count() == 10 assert Task.select().count() == 30 assert UserOrgAffiliation.select().count() == 30 assert User.get(id=43).admin_for.count() == 10 assert User.get(id=1).admin_for.count() == 0 assert User.get(id=42).admin_for.count() > 0 assert User.get(id=2).organisations.count() > 0 assert Organisation.get(id=1).admins.count() == 1 assert Organisation.get(id=5).users.count() > 0 assert Organisation.get(id=5).admins.count() > 0 assert User.select().where(User.orcid == User.get( email="*****@*****.**").orcid).count() == 3 assert len(User.get(email="*****@*****.**").org_links) == 3 user = User.get(email="*****@*****.**") available_organisations = user.available_organisations assert available_organisations.count() == 10 admin = User.create(email="*****@*****.**", organisation=user.organisation, confirmed=True, first_name="TEST", last_name="ADMIN", roles=Role.ADMIN) ui = UserInvitation.create(email=user.email, invitee=user, inviter=admin, token="TOKEN-123") admin.delete_instance() ui = UserInvitation.get(ui.id) assert ui.inviter_id is None user.delete_instance() assert not UserInvitation.select().where(UserInvitation.id == ui.id).exists() org = Organisation.select().limit(1).first() user = User.select().limit(1).first() ot = OrcidToken.create(user=user, org=org, scope="S1,S2,S3") assert len(ot.scopes) == 3 ot.scopes = ["A", "B", "C", "D"] assert ot.scope == "A,B,C,D"
def test_user_org_link(test_models): assert User.get(id=43).admin_for.count() == 10 assert User.get(id=1).admin_for.count() == 0 assert User.get(id=42).admin_for.count() > 0 assert User.get(id=2).organisations.count() > 0 assert Organisation.get(id=1).admins.count() == 1 assert Organisation.get(id=5).users.count() > 0 assert Organisation.get(id=5).admins.count() > 0 assert len( User.get(email="*****@*****.**").linked_accounts) == 3 user = User.get(email="*****@*****.**") available_organisations = user.available_organisations assert available_organisations.count() == 10
def test_orcid_login(client): """Test login from orcid.""" org = Organisation.get(name="THE ORGANISATION") u = User.create(email="*****@*****.**", name="TEST USER", roles=Role.TECHNICAL, orcid="123", confirmed=True, organisation=org) user_org = UserOrg.create(user=u, org=org, is_admin=True) resp = client.get("/orcid/login/NOT-EXISTTING", follow_redirects=True) assert resp.status_code == 200 assert b"Failed to login via ORCID using token NOT-EXISTTING" in resp.data token = "TOKEN-1234567" ui = UserInvitation.create(org=org, invitee=u, email=u.email, token=token) resp = client.get(f"/orcid/login/{token}") assert resp.status_code == 200 orcid_authorize = OrcidAuthorizeCall.get(method="GET") assert "&email=test123_test_orcid_login%40test.test.net" in orcid_authorize.url ui.created_at -= timedelta(days=100) ui.save() resp = client.get(f"/orcid/login/{token}") assert resp.status_code == 302 url = urlparse(resp.location) assert url.path == '/' # Testing the expired token flow for researcher user_org.is_admin = False user_org.save() resp = client.get(f"/orcid/login/{token}") assert resp.status_code == 302 url = urlparse(resp.location) assert url.path == '/'
def test_upload_affiliation_with_wrong_country(request_ctx, mocker): """Test task loading and processing with failures.""" org = Organisation.get(name="TEST0") super_user = User.get(email="*****@*****.**") with request_ctx("/") as ctx: exception = mocker.patch.object(ctx.app.logger, "exception") login_user(super_user) # flake8: noqa with pytest.raises(ModelException): task = Task.load_from_csv( """First name\tLast name\temail address\tOrganisation\tCampus/Department\tCity\tCourse or Job title\tStart date\tEnd date\tStudent/Staff\tCountry FNA\tLBA\[email protected]\tTEST1\tResearch Funding\tWellington\tProgramme Manager - ORCID\t2016-09 19:00:00 PM\t\tStaff\tNO COUNTRY """, filename="TEST.tsv", org=org) # this should work: task = Task.load_from_csv( """First name\tLast name\temail address\tOrganisation\tCampus/Department\tCity\tCourse or Job title\tStart date\tEnd date\tStudent/Staff\tCountry FNA\tLBA\[email protected]\tTEST1\tResearch Funding\tWellington\tProgramme Manager - ORCID\t2016-09 19:00:00 PM\t\tStaff\t """, filename="TEST-2.tsv", org=org) rec = task.records.first() assert rec.country is None exception.assert_called_once()
def test_link_already_affiliated(request_ctx): """Test a user affiliation initialization if the uerer is already affilated.""" with request_ctx("/link") as ctx: org = Organisation.get(name="THE ORGANISATION") test_user = User( email="*****@*****.**", name="TEST USER", organisation=org, orcid="ABC123", confirmed=True) test_user.save() orcidtoken = OrcidToken( user=test_user, org=org, scopes="/read-limited", access_token="ABC1234") orcidtoken_write = OrcidToken( user=test_user, org=org, scopes="/read-limited,/activities/update", access_token="ABC234") orcidtoken.save() orcidtoken_write.save() login_user(test_user, remember=True) uo = UserOrg(user=test_user, org=org) uo.save() rv = ctx.app.full_dispatch_request() assert rv.status_code == 302, "If the user is already affiliated, the user should be redirected ..." assert "profile" in rv.location, "redirection to 'profile' showing the ORCID"
def test_link_orcid_auth_callback(name, mocker, client): """Test ORCID callback - the user authorized the organisation access to the ORCID profile.""" mocker.patch("requests_oauthlib.OAuth2Session.fetch_token", lambda self, *args, **kwargs: dict( name="NEW TEST", access_token="ABC123", orcid="ABC-123-456-789", scope=["/read-limited"], expires_in="1212", refresh_token="ABC1235")) org = Organisation.get(name="THE ORGANISATION") test_user = User.create( name=name, email="*****@*****.**", organisation=org, orcid="ABC123", confirmed=True) UserOrg.create(user=test_user, org=org, affiliations=Affiliation.NONE) client.login(test_user) User.update(name=name).execute() resp = client.get("/link") state = session['oauth_state'] resp = client.get(f"/auth?state={state}") assert resp.status_code == 302, "If the user is already affiliated, the user should be redirected ..." assert "profile" in resp.location, "redirection to 'profile' showing the ORCID" u = User.get(id=test_user.id) orcidtoken = OrcidToken.get(user=u) assert u.orcid == "ABC-123-456-789" assert orcidtoken.access_token == "ABC123" if name: assert u.name == name, "The user name should be changed" else: assert u.name == "NEW TEST", "the user name should be set from record coming from ORCID"
def test_profile(client): """Test an affilated user profile and ORCID data retrieval and a user profile that doesn't hava an ORCID.""" org = Organisation.get(name="THE ORGANISATION") test_user = User.create(email="*****@*****.**", organisation=org, orcid="ABC123", confirmed=True) OrcidToken.create(user=test_user, org=org, scope="/read-limited,/activities/update", access_token="ABC1234") resp = client.login(test_user, follow_redirects=True) resp = client.get("/profile", follow_redirects=True) assert resp.status_code == 200 assert b"ABC123" in resp.data client.logout() # Test a user profile that doesn't hava an ORCID. user = User.select().where(User.organisation == org, User.orcid.is_null()).first() resp = client.login(user, follow_redirects=True) resp = client.get("/profile") assert resp.status_code == 302 assert "/link" in resp.location
def test_load_resources_from_csv(models): org = Organisation.get() raw_data = readup_test_data("resources.tsv", "r") task = ResourceRecord.load_from_csv(raw_data, filename="resources.tsv", org=org) assert task assert task.records.count() == 2
def test_link(request_ctx): """Test a user affiliation initialization.""" with request_ctx("/link") as ctx: org = Organisation.get(name="THE ORGANISATION") test_user = User.create( name="TEST USER 123", email="*****@*****.**", organisation=org, confirmed=True) login_user(test_user, remember=True) rv = ctx.app.full_dispatch_request() assert b"<!DOCTYPE html>" in rv.data, "Expected HTML content" assert b"TEST USER 123" in rv.data, "Expected to have the user name on the page" assert b"*****@*****.**" in rv.data, "Expected to have the user email on the page" assert b"URL_123" in rv.data, "Expected to have ORCiD authorization link on the page"
def test_link_with_unconfirmed_org(request_ctx): """Test a user affiliation initialization if the user Organisation isn't registered yet.""" with request_ctx("/link") as ctx: org = Organisation.get(name="THE ORGANISATION") org.confirmed = False org.orcid_client_id = "Test Client id" org.save() test_user = User( name="TEST USER", email="*****@*****.**", confirmed=True, organisation=org) test_user.save() login_user(test_user, remember=True) rv = ctx.app.full_dispatch_request() assert rv.status_code == 302
def test_upload_affiliation_with_wrong_country(request_ctx): """Test task loading and processing with failures.""" org = Organisation.get(name="TEST0") super_user = User.get(email="*****@*****.**") with patch("emails.html") as mock_msg, request_ctx("/") as ctx: login_user(super_user) # flake8: noqa with pytest.raises(ModelException): task = Task.load_from_csv( """First name\tLast name\temail address\tOrganisation\tCampus/Department\tCity\tCourse or Job title\tStart date\tEnd date\tStudent/Staff\tCountry FNA\tLBA\[email protected]\tTEST1\tResearch Funding\tWellington\tProgramme Manager - ORCID\t2016-09 19:00:00 PM\t\tStaff\tNO COUNTRY """, filename="TEST.tsv", org=org)
def test_researcher_urls(models): org = Organisation.get() raw_data0 = readup_test_data("researchurls.json", "r") data0 = load_yaml_json("researchurls.json", raw_data0) assert isinstance(data0, list) and isinstance(data0[0], NestedDict) task0 = PropertyRecord.load_from_json(filename="researchurls000.json", source=raw_data0, org=org, file_property_type="URL") data = task0.to_dict(recurse=True) raw_data = json.dumps(data, cls=JSONEncoder) task = PropertyRecord.load_from_json(filename="researchurls001.json", source=raw_data, org=org, file_property_type="URL") assert len(data0) == len(task.to_dict(recurse=True)["records"])
def test_process_task_from_csv_with_failures(request_ctx): """Test task loading and processing with failures.""" org = Organisation.get(name="TEST0") super_user = User.get(email="*****@*****.**") with patch("emails.html") as mock_msg, request_ctx("/") as ctx: login_user(super_user) # flake8: noqa task = Task.load_from_csv( """First name Last name email address Organisation Campus/Department City Course or Job title Start date End date Student/Staff FNA LBA [email protected] TEST1 Research Funding Wellington Programme Manager - ORCID 2016-09 Staff """, filename="TEST.tsv", org=org) AffiliationRecord.update(is_active=True).where( AffiliationRecord.task_id == task.id).execute() mock_msg().send = Mock(side_effect=Exception("FAILED TO SEND EMAIL")) utils.process_affiliation_records(10000) rec = AffiliationRecord.select().where(AffiliationRecord.task_id == task.id).first() assert "FAILED TO SEND EMAIL" in rec.status assert rec.processed_at is not None
def test_link_orcid_auth_callback_with_affiliation(name, request_ctx): """Test ORCID callback - the user authorized the organisation access to the ORCID profile.""" with patch("orcid_hub.orcid_client.MemberAPI") as m, patch( "orcid_hub.orcid_client.SourceClientId"), request_ctx( "/auth?state=xyz") as ctx: org = Organisation.get(name="THE ORGANISATION") test_user = User.create(name=name, email="*****@*****.**", organisation=org, orcid="ABC123", confirmed=True) UserOrg.create(user=test_user, org=org, affiliations=Affiliation.EMP | Affiliation.EDU) login_user(test_user, remember=True) session['oauth_state'] = "xyz" api_mock = m.return_value ctx.app.full_dispatch_request() assert test_user.orcid == "ABC-123-456-789" orcid_token = OrcidToken.get(user=test_user, org=org) assert orcid_token.access_token == "ABC123" api_mock.create_or_update_affiliation.assert_has_calls([ call(affiliation=Affiliation.EDU, initial=True), call(affiliation=Affiliation.EMP, initial=True), ]) # User with no Affiliation, should get flash warning. user_org = UserOrg.get(user=test_user, org=org) user_org.affiliations = Affiliation.NONE user_org.save() orcid_token.delete_instance() resp = ctx.app.full_dispatch_request() assert resp.status_code == 302 assert b"<!DOCTYPE HTML" in resp.data, "Expected HTML content" assert "profile" in resp.location, "redirection to 'profile' showing the ORCID"
def test_link_orcid_auth_callback(name, request_ctx): """Test ORCID callback - the user authorized the organisation access to the ORCID profile.""" with request_ctx("/auth?state=xyz") as ctx: org = Organisation.get(name="THE ORGANISATION") test_user = User.create(name=name, email="*****@*****.**", organisation=org, orcid="ABC123", confirmed=True) UserOrg.create(user=test_user, org=org, affiliations=Affiliation.NONE) login_user(test_user, remember=True) session['oauth_state'] = "xyz" rv = ctx.app.full_dispatch_request() assert rv.status_code == 302, "If the user is already affiliated, the user should be redirected ..." assert "profile" in rv.location, "redirection to 'profile' showing the ORCID" u = User.get(id=test_user.id) orcidtoken = OrcidToken.get(user=u) assert u.orcid == "ABC-123-456-789" assert orcidtoken.access_token == "ABC123" if name: assert u.name == name, "The user name should be changed" else: assert u.name == "NEW TEST", "the user name should be set from record coming from ORCID"
def test_link_orcid_auth_callback_with_affiliation(name, mocker, client): """Test ORCID callback - the user authorized the organisation access to the ORCID profile.""" mocker.patch("requests_oauthlib.OAuth2Session.fetch_token", lambda self, *args, **kwargs: dict( name="NEW TEST", access_token="ABC123", orcid="ABC-123-456-789", scope=['/read-limited,/activities/update'], expires_in="1212", refresh_token="ABC1235")) m = mocker.patch("orcid_hub.orcid_client.MemberAPI") mocker.patch("orcid_hub.orcid_client.SourceClientId") org = Organisation.get(name="THE ORGANISATION") test_user = User.create( name=name, email="*****@*****.**", organisation=org, orcid="ABC123", confirmed=True) UserOrg.create(user=test_user, org=org, affiliations=Affiliation.EMP | Affiliation.EDU) client.login(test_user) resp = client.get("/link") state = session['oauth_state'] resp = client.get(f"/auth?state={state}") api_mock = m.return_value test_user = User.get(test_user.id) assert test_user.orcid == "ABC-123-456-789" orcid_token = OrcidToken.get(user=test_user, org=org) assert orcid_token.access_token == "ABC123" api_mock.create_or_update_affiliation.assert_has_calls([ call(affiliation=Affiliation.EDU, initial=True), call(affiliation=Affiliation.EMP, initial=True), ]) # User with no Affiliation, should get flash warning. user_org = UserOrg.get(user=test_user, org=org) user_org.affiliations = Affiliation.NONE user_org.save() orcid_token.delete_instance() assert OrcidToken.select().where(OrcidToken.user == test_user, OrcidToken.org == org).count() == 0 resp = client.get(f"/auth?state={state}") assert resp.status_code == 302 assert b"<!DOCTYPE HTML" in resp.data, "Expected HTML content" assert "profile" in resp.location, "redirection to 'profile' showing the ORCID" assert OrcidToken.select().where(OrcidToken.user == test_user, OrcidToken.org == org).count() == 1 get_person = mocker.patch("requests_oauthlib.OAuth2Session.get", return_value=Mock(status_code=200)) resp = client.get(f"/profile", follow_redirects=True) assert b"can create and update research activities" in resp.data get_person.assert_called_once() get_person = mocker.patch("requests_oauthlib.OAuth2Session.get", return_value=Mock(status_code=401)) resp = client.get(f"/profile", follow_redirects=True) assert b"you'll be taken to ORCID to create or sign into your ORCID record" in resp.data get_person.assert_called_once()
def test_member_api_v3(app, mocker): """Test MemberAPI extension and wrapper of ORCID API.""" mocker.patch.multiple("orcid_hub.app.logger", error=DEFAULT, exception=DEFAULT, info=DEFAULT) org = Organisation.get(name="THE ORGANISATION") user = User.create( orcid="1001-0001-0001-0001", name="TEST USER 123", email="*****@*****.**", organisation=org, confirmed=True) UserOrg.create(user=user, org=org, affiliation=Affiliation.EDU) api = MemberAPIV3(user=user) assert api.api_client.configuration.access_token is None or api.api_client.configuration.access_token == '' api = MemberAPIV3(user=user, org=org) assert api.api_client.configuration.access_token is None or api.api_client.configuration.access_token == '' api = MemberAPIV3(user=user, org=org, access_token="ACCESS000") assert api.api_client.configuration.access_token == 'ACCESS000' OrcidToken.create(access_token="ACCESS123", user=user, org=org, scopes="/read-limited,/activities/update", expires_in='121') api = MemberAPIV3(user=user, org=org) assert api.api_client.configuration.access_token == "ACCESS123" # Test API call auditing: request_mock = mocker.patch.object( api.api_client.rest_client.pool_manager, "request", MagicMock(return_value=Mock(data=b"""{"mock": "data"}""", status=200))) api.get_record() request_mock.assert_called_once_with( "GET", "https://api.sandbox.orcid.org/v3.0/1001-0001-0001-0001", fields=None, preload_content=False, timeout=None, headers={ "Accept": "application/json", "Content-Type": "application/json", "User-Agent": "Swagger-Codegen/1.0.0/python", "Authorization": "Bearer ACCESS123" }) api_call = OrcidApiCall.select().first() assert api_call.response == '{"mock": "data"}' assert api_call.url == "https://api.sandbox.orcid.org/v3.0/1001-0001-0001-0001" create = mocker.patch.object(OrcidApiCall, "create", side_effect=Exception("FAILURE")) api.get_record() create.assert_called_once() app.logger.exception.assert_called_with("Failed to create API call log entry.") # Handling of get_record call_api = mocker.patch.object( api.api_client, "call_api", side_effect=v3.rest.ApiException(reason="FAILURE", status=401)) delete = mocker.patch.object(OrcidToken, "delete") api.get_record() call_api.assert_called_once() delete.assert_called_once() call_api = mocker.patch.object( api.api_client, "call_api", side_effect=v3.rest.ApiException(reason="FAILURE 999", status=999)) api.get_record() app.logger.error.assert_called_with("ApiException Occurred: (999)\nReason: FAILURE 999\n") call_api = mocker.patch.object( api.api_client, "call_api", side_effect=v3.rest.ApiException(reason="FAILURE", status=401)) api.get_record() app.logger.error.assert_called_with("ApiException Occurred: (401)\nReason: FAILURE\n") call_api.assert_called_once() call_api = mocker.patch.object( api.api_client, "call_api", return_value=(Mock(data=b"""{"mock": "data"}"""), 200, [],)) api.get_record() call_api.assert_called_with( f"/v3.0/{user.orcid}", "GET", _preload_content=False, auth_settings=["orcid_auth"], header_params={"Accept": "application/json"}, response_type=None) # Failed logging: request_mock = mocker.patch( "orcid_api_v3.rest.RESTClientObject.request", return_value=Mock(data=None, status_code=200))
def test_create_or_update_resource(app, mocker): """Test research resources.""" mocker.patch.multiple("orcid_hub.app.logger", error=DEFAULT, exception=DEFAULT, info=DEFAULT) org = Organisation.get(name="THE ORGANISATION") user = User.create(orcid="1001-0001-0001-0001", name="TEST USER 123", email="*****@*****.**", organisation=org, confirmed=True) UserOrg.create(user=user, org=org, affiliation=Affiliation.EDU) api = MemberAPIV3(user=user, org=org, access_token="ACCESS000") assert api.api_client.configuration.access_token == 'ACCESS000' OrcidToken.create(access_token="ACCESS123", user=user, org=org, scopes="/read-limited,/activities/update", expires_in='121') api = MemberAPIV3(user=user, org=org) assert api.api_client.configuration.access_token == "ACCESS123" # Test API call auditing: request_mock = mocker.patch.object( api.api_client.rest_client.pool_manager, "request", MagicMock(return_value=Mock(headers=HTTPHeaderDict({ 'Server': 'nginx/1.10.0', 'Date': 'Wed, 26 Jun 2019 05:26:32 GMT', 'Content-Type': 'application/json;charset=UTF-8', 'Content-Length': '0', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*', 'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'X-XSS-Protection': '1; mode=block', 'X-Frame-Options': 'DENY', 'X-Content-Type-Options': 'nosniff', 'Location': f'http://api.sandbox.orcid.org/v3.0/{user.orcid}/research-resource/2052' }), data=b'', status=201))) hosts = [ dict( name="TEST HOST", city="Auckland", country="NZ", disambiguated_id="3232", disambiguation_source="RINGGOLD", ) ] external_ids = [dict(value="ABC123", type="agr", relationship="self")] put_code, orcid, created, _ = api.create_or_update_resource( title="TEST", hosts=hosts, external_ids=external_ids, items=[{ "name": "TEST", "type": "equipment", "hosts": hosts, "external_ids": external_ids, }], display_index=999, visibility="public", ) request_mock.assert_called_once() assert put_code == 2052 assert orcid == user.orcid
def test_org_webhook(app_req_ctx, monkeypatch): """Test Organisation webhooks.""" monkeypatch.setattr( utils.requests, "post", lambda *args, **kwargs: SimpleObject( status_code=201, json=lambda: dict(access_token="ABC123", refresh_token="REFRESS_ME", expires_in=123456789))) monkeypatch.setattr(utils.requests, "put", lambda *args, **kwargs: SimpleObject(status_code=201)) monkeypatch.setattr(utils.requests, "delete", lambda *args, **kwargs: SimpleObject(status_code=204)) org = app_req_ctx.data["org"] admin = org.tech_contact user = app_req_ctx.data["user"] monkeypatch.setattr(utils.register_orcid_webhook, "queue", utils.register_orcid_webhook) utils.enable_org_webhook(org) assert org.webhook_enabled assert org.users.where(User.orcid.is_null(False), User.webhook_enabled).count() > 0 utils.disable_org_webhook(org) assert not org.webhook_enabled assert org.users.where(User.orcid.is_null(False), User.webhook_enabled).count() == 0 with app_req_ctx(f"/services/{user.id}/updated", method="POST") as ctx, patch.object( utils, "send_email") as send_email: send_email.assert_not_called() with app_req_ctx("/settings/webhook", method="POST", data=dict(webhook_url="https://ORG.org/HANDLE", webhook_enabled='y')) as ctx: login_user(admin) assert not Organisation.get(id=org.id).webhook_enabled resp = ctx.app.full_dispatch_request() assert Organisation.get(id=org.id).webhook_enabled assert resp.status_code == 200 with app_req_ctx(f"/services/{user.id}/updated", method="POST") as ctx, patch.object( utils, "send_email") as send_email: resp = ctx.app.full_dispatch_request() send_email.assert_not_called() assert resp.status_code == 204 with app_req_ctx("/settings/webhook", method="POST", data=dict(webhook_url="https://ORG.org/HANDLE", webhook_enabled='y', email_notifications_enabled='y')) as ctx: login_user(admin) assert not Organisation.get(id=org.id).email_notifications_enabled resp = ctx.app.full_dispatch_request() assert Organisation.get(id=org.id).email_notifications_enabled assert resp.status_code == 200 with app_req_ctx(f"/services/{user.id}/updated", method="POST") as ctx, patch.object( utils, "send_email") as send_email: resp = ctx.app.full_dispatch_request() send_email.assert_called() assert resp.status_code == 204
def test_onboard_org(client): """Test to organisation onboarding.""" org = Organisation.create(name="THE ORGANISATION:test_onboard_org", tuakiri_name="THE ORGANISATION:test_onboard_org", confirmed=False, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="RINGGOLD", is_email_sent=True) u = User.create(email="*****@*****.**", name="TEST USER", roles=Role.TECHNICAL, orcid="123", confirmed=True, organisation=org) second_user = User.create(email="*****@*****.**", name="TEST USER", roles=Role.ADMIN, orcid="1243", confirmed=True, organisation=org) UserOrg.create(user=second_user, org=org, is_admin=True) org_info = OrgInfo.create(name="A NEW ORGANISATION", tuakiri_name="A NEW ORGANISATION") org.tech_contact = u org_info.save() org.save() client.login_root() with patch("orcid_hub.utils.send_email"): resp = client.post("/invite/organisation", data=dict(org_name="A NEW ORGANISATION", org_email="*****@*****.**"), follow_redirects=True) assert User.select().where( User.email == "*****@*****.**").exists() resp = client.post("/invite/organisation", data=dict(org_name="A NEW ORGANISATION", org_email="*****@*****.**", tech_contact='y'), follow_redirects=True) assert User.select().where( User.email == "*****@*****.**").exists() org = Organisation.get(name="A NEW ORGANISATION") user = User.get(email="*****@*****.**") assert user.name is None assert org.tech_contact == user client.logout() resp = client.login(user, **{ "Sn": "TECHNICAL", "Givenname": "CONTACT", "Displayname": "Test User", "shib_O": "NEW ORGANISATION" }, follow_redirects=True) user = User.get(email="*****@*****.**") org = user.organisation assert user.is_tech_contact_of(org) resp = client.get("/confirm/organisation") assert resp.status_code == 200 org = Organisation.get(org.id) assert b"<!DOCTYPE html>" in resp.data, "Expected HTML content" assert b"Take me to ORCID to obtain my Client ID and Client Secret" in resp.data with patch("orcid_hub.authcontroller.requests") as requests: requests.post.return_value = Mock(data=b'XXXX', status_code=200) resp = client.post("/confirm/organisation", data={ "orcid_client_id": "APP-1234567890ABCDEF", "orcid_secret": "12345678-1234-1234-1234-1234567890ab", "country": "NZ", "city": "Auckland", "disambiguated_id": "XYZ123", "disambiguation_source": "RINGGOLD", "name": org.name, "email": user.email, }) assert resp.status_code == 302 url = urlparse(resp.location).path assert url == "/link" resp = client.get(url) client.logout() org = Organisation.get(org.id) assert org.disambiguated_id == "XYZ123" assert org.disambiguation_source == "RINGGOLD" assert org.orcid_client_id == "APP-1234567890ABCDEF" assert org.orcid_secret == "12345678-1234-1234-1234-1234567890ab" user = User.get(email="*****@*****.**") resp = client.login(user, **{ "Sn": "NEW ORGANISATION", "Givenname": "ADMINISTRATOR", "Displayname": "Admin User", "shib_O": "NEW ORGANISATION" }, follow_redirects=True) assert b"Take me to ORCID to allow A NEW ORGANISATION permission to access my ORCID record" in resp.data resp = client.get("/confirm/organisation") assert resp.status_code == 302 assert urlparse(resp.location).path == "/admin/viewmembers/" resp = client.get("/admin/viewmembers/") assert b"*****@*****.**" in resp.data resp = client.get("/admin/viewmembers/export/csv/") assert resp.headers["Content-Type"] == "text/csv; charset=utf-8" assert b"*****@*****.**" in resp.data assert b"*****@*****.**" in resp.data
def test_user_org_link_user_constraint(models): org = Organisation.get(id=1) uo = UserOrg(user_id=999999, org=org) with pytest.raises(User.DoesNotExist): uo.save()
def test_is_emp_or_edu_record_present(app, mocker): """Test 'is_emp_or_edu_record_present' method.""" mocker.patch.multiple("orcid_hub.app.logger", error=DEFAULT, exception=DEFAULT, info=DEFAULT) org = Organisation.get(name="THE ORGANISATION") user = User.create( orcid="1001-0001-0001-0001", name="TEST USER 123", email="*****@*****.**", organisation=org, confirmed=True) UserOrg.create(user=user, org=org, affiliation=Affiliation.EDU) api = MemberAPI(user=user, org=org) test_responses = [ None, """{"mock": "data"}""", """{ "employment-summary": [{"source": {"source-client-id": {"path": "CLIENT000"}}, "put-code": 123}], "education-summary": [{"source": {"source-client-id": {"path": "CLIENT000"}}, "put-code": 456}] }""", """{"employment-summary": [], "education-summary": []}""" ] for data in test_responses: with patch.object( api_client.ApiClient, "call_api", return_value=Mock(data=data)) as call_api: api.is_emp_or_edu_record_present(Affiliation.EDU) call_api.assert_called_with( "/v2.0/{orcid}/educations", "GET", {"orcid": "1001-0001-0001-0001"}, {}, {"Accept": "application/json"}, _preload_content=False, _request_timeout=None, _return_http_data_only=True, auth_settings=["orcid_auth"], body=None, callback=None, collection_formats={}, files={}, post_params=[], response_type="Educations") api.is_emp_or_edu_record_present(Affiliation.EMP) call_api.assert_called_with( "/v2.0/{orcid}/employments", "GET", {"orcid": "1001-0001-0001-0001"}, {}, {"Accept": "application/json"}, _preload_content=False, _request_timeout=None, _return_http_data_only=True, auth_settings=["orcid_auth"], body=None, callback=None, collection_formats={}, files={}, post_params=[], response_type="Employments") with patch.object( api_client.ApiClient, "call_api", side_effect=ApiException( reason="FAILURE", status=401)) as call_api: api.is_emp_or_edu_record_present(Affiliation.EDU) app.logger.error.assert_called_with( "For TEST USER 123 ([email protected]) while checking for employment " "and education records, Encountered Exception: (401)\nReason: FAILURE\n") with patch.object( api_client.ApiClient, "call_api", side_effect=Exception("EXCEPTION")) as call_api: api.is_emp_or_edu_record_present(Affiliation.EDU) app.logger.exception.assert_called_with( "Failed to verify presence of employment or education record.")
def test_process_tasks(request_ctx): """Test expiration data setting and deletion of the exprired tasks.""" org = Organisation.get(name="TEST0") super_user = User.get(email="*****@*****.**") with patch("orcid_hub.utils.send_email") as send_email, request_ctx("/") as ctx: login_user(super_user) # flake8: noqa task = Task.load_from_csv( """First name Last name email address Organisation Campus/Department City Course or Job title\tStart date End date Student/Staff\tCountry FNA LBA [email protected] TEST1 Research Funding Wellington Programme Manager - ORCID 2016-09 Staff\tNew Zealand """, filename="TEST_TASK.tsv", org=org) Task.update(created_at=datetime(1999, 1, 1), updated_at=datetime(1999, 1, 1)).execute() utils.process_tasks() assert Task.select().count() == 1 assert not Task.select().where(Task.expires_at.is_null()).exists() send_email.assert_called_once() task = Task.select().first() args, kwargs = send_email.call_args assert "email/task_expiration.html" in args assert kwargs["error_count"] == 0 hostname = ctx.request.host assert kwargs["export_url"] == ( f"https://{hostname}/admin/affiliationrecord/export/csv/?task_id={task.id}") assert kwargs["recipient"] == ( super_user.name, super_user.email, ) assert kwargs["subject"] == "Batch process task is about to expire" assert kwargs["task"] == task # After the second go everything should be deleted utils.process_tasks() assert Task.select().count() == 0 # Funding processing task: task = Task.create( created_at=datetime(1999, 1, 1), org=org, filename="FUNDING.json", created_by=super_user, updated_by=super_user, task_type=TaskType.FUNDING.value) Task.update(updated_at=datetime(1999, 1, 1)).execute() assert Task.select().where(Task.expires_at.is_null()).count() == 1 utils.process_tasks() assert Task.select().count() == 1 assert Task.select().where(Task.expires_at.is_null()).count() == 0 utils.process_tasks() assert Task.select().count() == 0 args, kwargs = send_email.call_args assert "email/task_expiration.html" in args assert kwargs["error_count"] == 0 hostname = ctx.request.host assert kwargs["export_url"] == ( f"https://{hostname}/admin/fundingrecord/export/csv/?task_id={task.id}") assert kwargs["recipient"] == ( super_user.name, super_user.email, ) assert kwargs["subject"] == "Batch process task is about to expire" assert kwargs["task"] == task # Incorrect task type: task = Task.create( created_at=datetime(1999, 1, 1), org=org, filename="ERROR.err", created_by=super_user, updated_by=super_user, task_type=-12345) Task.update(updated_at=datetime(1999, 1, 1)).execute() with pytest.raises(Exception, message="Unexpeced task type: -12345 (ERROR.err)."): utils.process_tasks() task.delete().execute() # Cover case with an exterenal SP: with patch("orcid_hub.utils.EXTERNAL_SP", "SOME.EXTERNAL.SP"): Task.create( created_at=datetime(1999, 1, 1), org=org, filename="FILE.file", created_by=super_user, updated_by=super_user) Task.update(updated_at=datetime(1999, 1, 1)).execute() assert Task.select().count() == 1 utils.process_tasks() utils.process_tasks() assert Task.select().count() == 0
def test_member_api(app, mocker): """Test MemberAPI extension and wrapper of ORCID API.""" mocker.patch.multiple("orcid_hub.app.logger", error=DEFAULT, exception=DEFAULT, info=DEFAULT) org = Organisation.get(name="THE ORGANISATION") user = User.create(orcid="1001-0001-0001-0001", name="TEST USER 123", email="*****@*****.**", organisation=org, confirmed=True) UserOrg.create(user=user, org=org, affiliation=Affiliation.EDU) MemberAPI(user=user) assert configuration.access_token is None or configuration.access_token == '' MemberAPI(user=user, org=org) assert configuration.access_token is None or configuration.access_token == '' MemberAPI(user=user, org=org, access_token="ACCESS000") assert configuration.access_token == 'ACCESS000' OrcidToken.create(access_token="ACCESS123", user=user, org=org, scope="/read-limited,/activities/update", expires_in='121') api = MemberAPI(user=user, org=org) assert configuration.access_token == "ACCESS123" with patch.object(api_client.ApiClient, "call_api", side_effect=ApiException(reason="FAILURE", status=401)) as call_api: with patch.object(OrcidToken, "delete") as delete: api.get_record() app.logger.error.assert_called_with( "ApiException Occured: (401)\nReason: FAILURE\n") call_api.assert_called_once() delete.assert_called_once() with patch.object(api_client.ApiClient, "call_api", side_effect=ApiException(reason="FAILURE 999", status=999)) as call_api: api.get_record() app.logger.error.assert_called_with( "ApiException Occured: (999)\nReason: FAILURE 999\n") with patch.object(api_client.ApiClient, "call_api", side_effect=ApiException(reason="FAILURE", status=401)) as call_api: with patch.object(OrcidToken, "get", side_effect=Exception("FAILURE")) as get: api.get_record() app.logger.exception.assert_called_with( "Exception occured while retriving ORCID Token") call_api.assert_called_once() get.assert_called_once() with patch.object(api_client.ApiClient, "call_api", return_value=( Mock(data=b"""{"mock": "data"}"""), 200, [], )) as call_api: api.get_record() call_api.assert_called_with( f"/v2.0/{user.orcid}", "GET", _preload_content=False, auth_settings=["orcid_auth"], header_params={"Accept": "application/json"}, response_type=None) # Test API call auditing: with patch.object(api_client.RESTClientObject.__base__, "request", return_value=Mock(data=b"""{"mock": "data"}""", status_code=200)) as request_mock: api.get_record() request_mock.assert_called_once_with( _preload_content=False, _request_timeout=None, body=None, headers={ "Accept": "application/json", "User-Agent": "Swagger-Codegen/1.0.0/python", "Authorization": "Bearer ACCESS123" }, method="GET", post_params=None, query_params=None, url="https://api.sandbox.orcid.org/v2.0/1001-0001-0001-0001") api_call = OrcidApiCall.select().first() assert api_call.response == '{"mock": "data"}' assert api_call.url == "https://api.sandbox.orcid.org/v2.0/1001-0001-0001-0001" with patch.object(OrcidApiCall, "create", side_effect=Exception("FAILURE")) as create: api.get_record() create.assert_called_once() with patch.object(api_client.RESTClientObject.__base__, "request", return_value=Mock(data=None, status_code=200)) as request_mock: # api.get_record() OrcidApiCall.delete().execute() api.view_person("1234-XXXX-XXXX-XXXX") api_call = OrcidApiCall.select().first() assert api_call.response is None assert api_call.url == "https://api.sandbox.orcid.org/v2.0/1234-XXXX-XXXX-XXXX/person"
def models(testdb): Organisation.insert_many((dict(name="Organisation #%d" % i, tuakiri_name="Organisation #%d" % i, orcid_client_id="client-%d" % i, orcid_secret="secret-%d" % i, confirmed=(i % 2 == 0)) for i in range(10))).execute() User.insert_many( (dict(name="Test User #%d" % i, first_name="Test_%d" % i, last_name="User_%d" % i, email="user%d@org%d.org.nz" % (i, i * 4 % 10), confirmed=(i % 3 != 0), roles=Role.SUPERUSER if i % 42 == 0 else Role.ADMIN if i % 13 == 0 else Role.RESEARCHER) for i in range(60))).execute() User.insert_many((dict(name="Test User with ORCID ID 'ABC-123' #%d" % i, orcid="ABC-123", first_name="Test_%d" % i, last_name="User_%d" % i, email="user_the_same_id_%d@org%d.org.nz" % (i, i), confirmed=True, organisation=(i + 1), roles=Role.RESEARCHER) for i in range(3))).execute() UserOrg.insert_many( dict(user=u.id, org=u.organisation_id) for u in User.select().where(User.orcid == "ABC-123")).execute() UserOrg.insert_many( (dict(is_admin=((u + o) % 23 == 0), user=u, org=o) for (u, o) in product(range(2, 60, 4), range(2, 10)))).execute() UserOrg.insert_many( (dict(is_admin=True, user=43, org=o) for o in range(1, 11))).execute() OrcidToken.insert_many((dict(user=User.get(id=1), org=Organisation.get(id=1), scopes="/read-limited", access_token="Test_%d" % i) for i in range(60))).execute() UserOrgAffiliation.insert_many((dict(user=User.get(id=1), organisation=Organisation.get(id=1), department_name="Test_%d" % i, department_city="Test_%d" % i, role_title="Test_%d" % i, path="Test_%d" % i, put_code="%d" % i) for i in range(30))).execute() Task.insert_many((dict(org=Organisation.get(id=1), created_by=User.get(id=1), updated_by=User.get(id=1), filename="Test_%d" % i, task_type=0) for i in range(30))).execute() AffiliationRecord.insert_many((dict(is_active=False, task=Task.get(id=1), put_code=90, external_id="Test_%d" % i, status="Test_%d" % i, first_name="Test_%d" % i, last_name="Test_%d" % i, email="Test_%d" % i, orcid="123112311231%d" % i, organisation="Test_%d" % i, affiliation_type="Test_%d" % i, role="Test_%d" % i, department="Test_%d" % i, city="Test_%d" % i, state="Test_%d" % i, country="Test_%d" % i, disambiguated_id="Test_%d" % i, disambiguation_source="Test_%d" % i) for i in range(10))).execute() PropertyRecord.insert_many((dict(type="URL", is_active=False, task=Task.get(id=1), put_code=90, status="Test_%d" % i, first_name="Test_%d" % i, last_name="Test_%d" % i, email="Test_%d" % i, orcid="123112311231%d" % i, name="Test_%d" % i, value="Test_%d" % i, visibility="Test_%d" % i, display_index=i) for i in range(10))).execute() PropertyRecord.insert_many((dict(type="NAME", is_active=False, task=Task.get(id=1), put_code=90, status="Test_%d" % i, first_name="Test_%d" % i, last_name="Test_%d" % i, email="Test_%d" % i, orcid="123112311231%d" % i, value="Test_%d" % i, visibility="Test_%d" % i, display_index=i) for i in range(10))).execute() PropertyRecord.insert_many((dict(type="KEYWORD", is_active=False, task=Task.get(id=1), put_code=90, status="Test_%d" % i, first_name="Test_%d" % i, last_name="Test_%d" % i, email="Test_%d" % i, orcid="123112311231%d" % i, value="Test_%d" % i, visibility="Test_%d" % i, display_index=i) for i in range(10))).execute() FundingRecord.insert_many( (dict(task=Task.get(id=1), title="Test_%d" % i, translated_title="Test_%d" % i, translated_title_language_code="Test_%d" % i, type="Test_%d" % i, organization_defined_type="Test_%d" % i, short_description="Test_%d" % i, amount="Test_%d" % i, currency="Test_%d" % i, org_name="Test_%d" % i, city="Test_%d" % i, region="Test_%d" % i, country="Test_%d" % i, disambiguated_id="Test_%d" % i, disambiguation_source="Test_%d" % i, is_active=False, status="Test_%d" % i) for i in range(10))).execute() record = FundingRecord.get() FundingContributor.insert_many((dict(record=record, orcid="123112311231%d" % i, name="Test_%d" % i, role="Test_%d" % i) for i in range(10))).execute() FundingInvitee.insert_many((dict(record=record, orcid="123112311231%d" % i, first_name="Test_%d" % i, last_name="Test_%d" % i, put_code=i, status="Test_%d" % i, identifier="%d" % i, visibility="Test_%d" % i, email="Test_%d" % i) for i in range(10))).execute() ExternalId.insert_many((dict(record=record, type="Test_%d" % i, value="Test_%d" % i, url="Test_%d" % i, relationship="Test_%d" % i) for i in range(10))).execute() task = Task.get() PeerReviewRecord.insert_many( (dict(task=task, review_group_id="issn:1212_%d" % i, reviewer_role="reviewer_%d" % i, review_url="xyz_%d" % i, review_type="REVIEW_%d" % i, subject_external_id_type="doi_%d" % i, subject_external_id_value="1212_%d" % i, subject_external_id_url="url/SELF_%d" % i, subject_external_id_relationship="SELF_%d" % i, subject_container_name="Journal title_%d" % i, subject_type="JOURNAL_ARTICLE_%d" % i, subject_name_title="name_%d" % i, subject_name_subtitle="subtitle_%d" % i, subject_name_translated_title_lang_code="en", subject_name_translated_title="sdsd_%d" % i, subject_url="url_%d" % i, convening_org_name="THE ORGANISATION_%d" % i, convening_org_city="auckland_%d" % i, convening_org_region="auckland_%d" % i, convening_org_country="nz_%d" % i, convening_org_disambiguated_identifier="123_%d" % i, convening_org_disambiguation_source="1212_%d" % i, is_active=False) for i in range(10))).execute() record = PeerReviewRecord.get() PeerReviewExternalId.insert_many((dict(record=record, type="Test1_%d" % i, value="Test1_%d" % i, url="Test1_%d" % i, relationship="Test1_%d" % i) for i in range(10))).execute() PeerReviewInvitee.insert_many((dict(record=record, orcid="1231123112311%d" % i, first_name="Test1_%d" % i, last_name="Test1_%d" % i, put_code=i, status="Test1_%d" % i, identifier="1%d" % i, visibility="PUBLIC", email="Test1_%d" % i) for i in range(10))).execute() WorkRecord.insert_many((dict(task=task, title="Test_%d" % i, subtitle="Test_%d" % i, translated_title="Test_%d" % i, translated_title_language_code="Test_%d" % i, journal_title="Test_%d" % i, short_description="Test_%d" % i, citation_type="Test_%d" % i, citation_value="Test_%d" % i, type="Test_%d" % i, url="Test_%d" % i, language_code="Test_%d" % i, country="Test_%d" % i, is_active=False, status="Test_%d" % i) for i in range(10))).execute() record = WorkRecord.get() WorkContributor.insert_many((dict(record=record, orcid="123112311231%d" % i, name="Test_%d" % i, contributor_sequence="%d" % i, role="Test_%d" % i) for i in range(10))).execute() WorkExternalId.insert_many((dict(record=record, type="Test_%d" % i, value="Test_%d" % i, url="Test_%d" % i, relationship="Test_%d" % i) for i in range(10))).execute() WorkInvitee.insert_many((dict(record=record, orcid="123112311231%d" % i, first_name="Test_%d" % i, last_name="Test_%d" % i, put_code=i, status="Test_%d" % i, identifier="%d" % i, visibility="Test_%d" % i, email="Test_%d" % i) for i in range(10))).execute() yield testdb
def test_org_webhook(client, mocker): """Test Organisation webhooks.""" mocker.patch.object( utils.requests, "post", lambda *args, **kwargs: SimpleObject( status_code=201, json=lambda: dict( access_token="ABC123", refresh_token="REFRESS_ME", expires_in=123456789 ), ), ) mocker.patch.object( utils.requests, "put", lambda *args, **kwargs: SimpleObject(status_code=201, text="") ) mocker.patch.object( utils.requests, "delete", lambda *args, **kwargs: SimpleObject(status_code=204, text="") ) org = client.data["org"] admin = org.tech_contact user = client.data["user"] mocker.patch.object(utils.register_orcid_webhook, "queue", utils.register_orcid_webhook) mocker.patch.object(utils.invoke_webhook_handler, "queue", utils.invoke_webhook_handler) utils.enable_org_webhook(org) assert org.webhook_enabled assert org.users.where(User.orcid.is_null(False), User.webhook_enabled).count() > 0 utils.disable_org_webhook(org) assert not org.webhook_enabled assert org.users.where(User.orcid.is_null(False), User.webhook_enabled).count() == 0 resp = client.login(admin) resp = client.post(f"/services/{user.id}/updated") send_email = mocker.patch.object(utils, "send_email") send_email.assert_not_called() assert not Organisation.get(org.id).webhook_enabled resp = client.post( "/settings/webhook", data=dict(webhook_url="https://ORG.org/HANDLE", webhook_enabled="y") ) assert Organisation.get(org.id).webhook_enabled assert resp.status_code == 200 resp = client.post(f"/services/{user.id}/updated") send_email.assert_not_called() assert resp.status_code == 204 assert not Organisation.get(org.id).email_notifications_enabled resp = client.post( "/settings/webhook", data=dict( webhook_url="https://ORG.org/HANDLE", webhook_enabled="y", email_notifications_enabled="y", ), ) assert Organisation.get(org.id).email_notifications_enabled assert resp.status_code == 200 resp = client.post(f"/services/{user.id}/updated") send_email.assert_called() assert resp.status_code == 204 post = mocker.patch.object(utils.requests, "post", return_value=Mock(status_code=204)) for u in User.select().where( User.organisation == user.organisation, User.orcid.is_null(False) ): post.reset_mock() send_email.reset_mock() resp = client.post(f"/services/{u.id}/updated") send_email.assert_called() post.assert_called() assert resp.status_code == 204 # Enable only email notification: resp = client.post( "/settings/webhook", data=dict(webhook_enabled="", email_notifications_enabled="") ) assert resp.status_code == 200 assert not Organisation.get(org.id).email_notifications_enabled resp = client.post( "/settings/webhook", data=dict(webhook_url="", webhook_enabled="", email_notifications_enabled="y"), ) assert Organisation.get(org.id).email_notifications_enabled assert resp.status_code == 200 post.reset_mock() send_email.reset_mock() resp = client.post(f"/services/{user.id}/updated") send_email.assert_called() post.assert_not_called() assert resp.status_code == 204 # Test update summary: mocker.patch.object(utils.send_orcid_update_summary, "queue", utils.send_orcid_update_summary) send_email.reset_mock() utils.send_orcid_update_summary() send_email.assert_not_called() utils.send_orcid_update_summary(org.id) send_email.assert_not_called() utils.send_orcid_update_summary(9999999) send_email.assert_not_called() user.orcid_updated_at = utils.date.today().replace(day=1) - utils.timedelta(days=1) user.save() utils.send_orcid_update_summary() send_email.assert_called() send_email.reset_mock() org.notification_email = "*****@*****.**" org.save() utils.send_orcid_update_summary() send_email.assert_called_once() resp = client.post("/settings/webhook", data=dict(save_webhook="Save")) assert resp.status_code == 200 assert not Organisation.get(org.id).webhook_enabled assert not Organisation.get(org.id).email_notifications_enabled