def test_create_bucket_permissions(client, json_headers, location, testdata, users): """Test create bucket permissions.""" url = url_for("invenio_app_ils_files.eitmid_bucket", pid_value="eitemid-1") _test_response(client, "post", url, json_headers, None, 401) test_data = [ ("admin", "eitemid-1", 201), ("librarian", "eitemid-2", 201), ("patron1", "eitemid-2", 403), ] for user, pid, status_code in test_data: user_login(client, user, users) url = url_for("invenio_app_ils_files.eitmid_bucket", pid_value=pid) _test_response(client, "post", url, json_headers, None, status_code)
def test_delete_internal_location(client, users, json_headers, testdata): """Test DELETE existing internal_location.""" user_login(client, "admin", users) internal_location_pid = 'ilocid-1' url = url_for('invenio_records_rest.ilocid_item', pid_value=internal_location_pid) res = client.delete(url, headers=json_headers) assert res.status_code == 400 internal_location_pid = 'ilocid-3' url = url_for('invenio_records_rest.ilocid_item', pid_value=internal_location_pid) res = client.delete(url, headers=json_headers) assert res.status_code == 204
def test_patrons_can_search_their_own_loans(client, json_headers, users, testdata): """Test that patrons can search their own loans.""" def _validate_only_patron_loans(res, user, state): """Assert that result loans belong to the given user only.""" patron_loans = [ l for l in testdata["loans"] if l["patron_pid"] == str(user.id) and l["state"] == state ] assert res.status_code == 200 hits = json.loads(res.data.decode("utf-8")) assert len(hits["hits"]["hits"]) == len(patron_loans) for hit in hits["hits"]["hits"]: assert hit["metadata"]["patron_pid"] == str(user.id) state = "PENDING" for username in ["patron1", "patron2"]: user = user_login(client, username, users) # search with no params res = _search_loans(client, json_headers, state=state) _validate_only_patron_loans(res, user, state) # search with params res = _search_loans( client, json_headers, state=state, # test extra query q="patron_pid:{}".format(str(user.id)), ) _validate_only_patron_loans(res, user, state)
def test_create_bucket_endpoint(client, json_headers, location, testdata, users): """Test GET permissions.""" user_login(client, "admin", users) url_with_bucket_id = url_for("invenio_app_ils_files.eitmid_bucket", pid_value="eitemid-3") url_without_bucket_id = url_for("invenio_app_ils_files.eitmid_bucket", pid_value="eitemid-4") res1 = _test_response(client, "post", url_with_bucket_id, json_headers, None, 200) _test_data_exists("bucket_id", res1) res2 = _test_response(client, "post", url_without_bucket_id, json_headers, None, 201) _test_data_exists("bucket_id", res2)
def test_patron_cannot_update_loans(client, json_headers, users, testdata): """Test that patrons cannot update loans.""" loanid = "loanid-1" user = user_login(client, "patron1", users) _test_post_new_loan(client, json_headers, user.id, 403) _test_replace_existing_loan(client, json_headers, user.id, loanid, 403) _test_delete_existing_loan(client, json_headers, loanid, 403)
def test_series_edit_permissions(client, testdata, json_headers, users): """Test series endpoints permissions.""" dummy_series = dict( title="The Gulf: The Making of An American Sea", authors=["Einstein, Albert", "Stachel, John J et al."], mode_of_issuance="MULTIPART_MONOGRAPH", ) tests = [ ("admin", _HTTP_OK, dummy_series), ("librarian", _HTTP_OK, dummy_series), ("patron1", [403], dummy_series), ("anonymous", [401], dummy_series), ] def _test_create(expected_status, data): """Test record creation.""" url = url_for(LIST_ENDPOINT) res = client.post(url, headers=json_headers, data=json.dumps(data)) assert res.status_code in expected_status if res.status_code < 400: record = res.get_json()["metadata"] assert record return record["pid"] def _test_update(expected_status, data, pid): """Test record update.""" pid_value = pid or SERIES_PID url = url_for(ITEM_ENDPOINT, pid_value=pid_value) res = client.put(url, headers=json_headers, data=json.dumps(data)) assert res.status_code in expected_status if res.status_code < 400: record = res.get_json()["metadata"] assert record def _test_delete(expected_status, pid): """Test record delete.""" pid_value = pid or SERIES_PID url = url_for(ITEM_ENDPOINT, pid_value=pid_value) res = client.delete(url, headers=json_headers) assert res.status_code in expected_status for username, expected_status, data in tests: user_login(client, username, users) pid = _test_create(expected_status, data) _test_update(expected_status, data, pid) _test_delete(expected_status, pid)
def test_create_document_request(client, testdata, json_headers, users): """Test creating document requests.""" tests = [ ("admin", 201, dict(title="Test title", request_type="LOAN", medium="Paper", patron_pid="1")), ("librarian", 400, dict(patron_pid="1")), ( "admin", 201, dict(patron_pid="1", title="Test", invalid_param="Test", request_type="LOAN", medium="Paper"), ), ("librarian", 201, dict(title="Test title", patron_pid="1", request_type="BUY", medium="Paper")), ("patron1", 201, dict(title="Test title", patron_pid="1", request_type="BUY", medium="Paper")), ("patron2", 400, dict(title="Test title", patron_pid="1", request_type="LOAN", medium="Paper")), ("patron2", 201, dict(title="Test title", patron_pid="2", request_type="LOAN", medium="Paper")), ] for username, expected_status, data in tests: user_login(client, username, users) url = url_for("invenio_records_rest.dreqid_list") res = client.post(url, headers=json_headers, data=json.dumps(data)) assert res.status_code == expected_status if res.status_code == 201: assert "invalid_param" not in json.loads(res.data)["metadata"]
def test_patron_can_request_loan_with_or_without_end_date( app, client, json_headers, users, testdata ): """Test that a patron can request a loan [with/without] end date.""" url = url_for("invenio_app_ils_circulation.loan_request") user = user_login(client, "patron1", users) now = arrow.utcnow() start_date = (now + timedelta(days=3)).date().isoformat() end_date = (now + timedelta(days=15)).date().isoformat() # it should succeed when start/end dates provided params = deepcopy(NEW_LOAN) params["document_pid"] = "docid-2" params["request_start_date"] = start_date params["request_expire_date"] = end_date params["transaction_user_pid"] = str(user.id) res = client.post(url, headers=json_headers, data=json.dumps(params)) assert res.status_code == 202 loan = res.get_json()["metadata"] assert loan["state"] == "PENDING" assert loan["document_pid"] == params["document_pid"] assert loan["request_start_date"] == start_date assert loan["request_expire_date"] == end_date assert loan["transaction_date"] # it should fail when wrong date provided params = deepcopy(NEW_LOAN) past_end_date = now - timedelta(days=30) params["request_expire_date"] = past_end_date.date().isoformat() params["transaction_user_pid"] = str(user.id) res = client.post(url, headers=json_headers, data=json.dumps(params)) assert res.status_code == 400 # it should fail when request duration over max params = deepcopy(NEW_LOAN) days = app.config["ILS_CIRCULATION_LOAN_REQUEST_DURATION_DAYS"] past_end_date = now + timedelta(days=days + 1) params["request_expire_date"] = past_end_date.date().isoformat() params["transaction_user_pid"] = str(user.id) res = client.post(url, headers=json_headers, data=json.dumps(params)) assert res.status_code == 400 # it should fail when request start/end date not provided params = deepcopy(NEW_LOAN) params["document_pid"] = "docid-4" params["transaction_user_pid"] = str(user.id) del params["request_start_date"] del params["request_expire_date"] res = client.post(url, headers=json_headers, data=json.dumps(params)) assert res.status_code == 202 loan = res.get_json()["metadata"] now = arrow.utcnow() start_date = now.date().isoformat() days = app.config["ILS_CIRCULATION_LOAN_REQUEST_DURATION_DAYS"] end_date = (now + timedelta(days=days)).date().isoformat() assert loan["request_start_date"] == start_date assert loan["request_expire_date"] == end_date
def test_patrons_permissions(client, testdata, json_headers, users): """Test patron endpoints permissions.""" tests = [ ("admin", [200]), ("librarian", [200]), ("patron1", [403]), ("anonymous", [401]), ] def _test_list(expected_status): """Test get list.""" url = url_for(_LIST_ENDPOINT) res = client.get(url, headers=json_headers) assert res.status_code in expected_status for username, expected_status in tests: user_login(client, username, users) _test_list(expected_status)
def test_document_request_reject(client, json_headers, testdata, users): """Test Document Request permissions to reject request.""" tests = [ ("patron1", "dreq-1", 202), ("librarian", "dreq-5", 202), ("admin", "dreq-6", 202), ("anonymous", "dreq-1", 401), ("admin", "dreq-2", 400), ] for user, res_id, expected_resp_code in tests: user_login(client, user, users) url = url_for("invenio_app_ils_document_requests.dreqid_reject", pid_value=res_id) data = {"reject_reason": "USER_CANCEL"} validate_response(client, "post", url, json_headers, data, expected_resp_code)
def test_brwreq_create_loan_fails_on_wrong_status( db, client, testdata, json_headers, users ): """Test borrowing requests create loan action fails on wrong status.""" user_login(client, "librarian", users) def _create_new_brwreq(data=None): brwreq = data or dict( document_pid="docid-3", provider_pid="ill-provid-2", patron_pid="1", status="PENDING", type="PHYSICAL_COPY", ) url = url_for(LIST_ENDPOINT) res = client.post(url, headers=json_headers, data=json.dumps(brwreq)) assert res.status_code in _HTTP_OK brw_req = res.get_json()["metadata"] return brw_req["pid"] def _update_brwreq_with_new_status(pid, status): rec = BorrowingRequest.get_record_by_pid(pid) rec["status"] = status if status == "CANCELLED": rec["cancel_reason"] = "OTHER" rec.commit() db.session.commit() def _assert_fail_when_status(pid, status): _update_brwreq_with_new_status(pid, status) now = arrow.utcnow() future = now + timedelta(days=15) data = dict( loan_start_date=now.isoformat(), loan_end_date=future.isoformat() ) _assert_create_loan_action_fails(pid, data, client, json_headers) pid = _create_new_brwreq() _assert_fail_when_status(pid, "PENDING") _assert_fail_when_status(pid, "ON_LOAN") _assert_fail_when_status(pid, "RETURNED") _assert_fail_when_status(pid, "CANCELLED")
def test_document_request_add_document(client, json_headers, testdata, users): """Test add document to Document Request permissions.""" tests = [ ("patron1", "dreq-1", 403), ("librarian", "dreq-1", 202), ("admin", "dreq-1", 202), ("anonymous", "dreq-1", 401), ] for user, res_id, expected_resp_code in tests: user_login(client, user, users) url = url_for( "invenio_app_ils_document_requests.dreqid_document", pid_value=res_id, ) data = {"document_pid": "docid-1"} validate_response(client, "post", url, json_headers, data, expected_resp_code)
def test_document_request_remove_provider(client, json_headers, testdata, users): """Test remove provider from Document Request permissions.""" tests = [ ("patron1", "dreq-1", 403), ("librarian", "dreq-1", 202), ("admin", "dreq-1", 202), ("anonymous", "dreq-1", 401), ] for user, res_id, expected_resp_code in tests: user_login(client, user, users) url = url_for( "invenio_app_ils_document_requests.dreqid_provider", pid_value=res_id, ) validate_response(client, "delete", url, json_headers, None, expected_resp_code)
def test_access_permissions( client, json_headers, testdata, users, with_access ): """Test GET documents with `_access` ignoring `restricted`.""" # set the documents to have read access only by patron2. `_access` should # be taken into account and take precedence over `restricted`. indexer = RecordIndexer() doc1 = Document.get_record_by_pid("docid-open-access") doc2 = Document.get_record_by_pid("docid-closed-access") for doc in [doc1, doc2]: doc.update(dict(_access=dict(read=[users["patron2"].id]))) doc.commit() db.session.commit() indexer.index(doc) current_search.flush_and_refresh(index="documents") test_data = [ ("anonymous", "docid-open-access", 401, 0), ("patron1", "docid-open-access", 403, 0), ("patron2", "docid-open-access", 200, 1), # should have access ("librarian", "docid-open-access", 200, 1), ("admin", "docid-open-access", 200, 1), ("anonymous", "docid-closed-access", 401, 0), ("patron1", "docid-closed-access", 403, 0), ("patron2", "docid-closed-access", 200, 1), # should have access ("librarian", "docid-closed-access", 200, 1), ("admin", "docid-closed-access", 200, 1), ] for user, pid, status_code, n_hits in test_data: # item endpoint user_login(client, user, users) url = url_for("invenio_records_rest.docid_item", pid_value=pid) res = client.get(url, headers=json_headers) assert res.status_code == status_code # list endpoint user_login(client, user, users) url = url_for( "invenio_records_rest.docid_list", q="pid:{}".format(pid) ) res = client.get(url, headers=json_headers) hits = json.loads(res.data.decode("utf-8")) assert hits["hits"]["total"] == n_hits
def test_email_db_table_and_endpoint(users, client, json_headers): """Test creation of email in db table and read emails from endpoint.""" request = {"id": "test-id", "task": "test-task"} data = { "id": "test-id", "recipients": ["*****@*****.**"], "is_manually_triggered": True, "message_id": "1", } exc = "An error occured." log_successful_mail(None, data) log_error_mail(request=request, exc=exc, traceback=None, data=data) assert EmailLog.query.filter_by(id=1).one().send_log == "Success" assert ( EmailLog.query.filter_by(id=2).one().send_log == "Error: 'An error occured.'" ) ITEM_ENDPOINT = "invenio_app_ils_emails_item.get_email" LIST_ENDPOINT = "invenio_app_ils_emails_list.get_emails" user_login(client, "librarian", users) url = url_for(LIST_ENDPOINT) res = client.get(url, headers=json_headers) assert ( res.get_json()["hits"][0]["send_log"] == "Error: 'An error occured.'" ) assert res.get_json()["hits"][1]["send_log"] == "Success" url = url_for(ITEM_ENDPOINT, id=1) res = client.get(url, headers=json_headers) assert res.get_json()["send_log"] == "Success" assert res.get_json()["id"] == 1 url = url_for(ITEM_ENDPOINT, id=2) res = client.get(url, headers=json_headers) assert res.get_json()["send_log"] == "Error: 'An error occured.'" assert res.get_json()["id"] == 2
def test_force_checkout_specific_permissions(app, client, json_headers, users, testdata): """Test that only allowed users can perform a force checkout.""" default_factory = app.config["ILS_VIEWS_PERMISSIONS_FACTORY"] librarian2 = users["librarian2"] # override default permission factory to require specific permission for # force-checkout action def custom_views_permissions_factory(action): if action == "circulation-loan-force-checkout": # fake permission for a specific user return Permission(UserNeed(librarian2.id)) else: return default_factory(action) app.config[ "ILS_VIEWS_PERMISSIONS_FACTORY"] = custom_views_permissions_factory # prepare request url = url_for("invenio_app_ils_circulation.loan_checkout") params = deepcopy(NEW_LOAN) params["force"] = True params["item_pid"] = dict(type="pitmid", value="itemid-MISSING") params["transaction_user_pid"] = str(librarian2.id) # force-checkout as librarian should fail user_login(client, "librarian", users) res = client.post(url, headers=json_headers, data=json.dumps(params)) assert res.status_code == 403 # force-checkout as librarian2 should succeed user_login(client, "librarian2", users) res = client.post(url, headers=json_headers, data=json.dumps(params)) assert res.status_code == 202 loan = res.get_json()["metadata"] assert loan["state"] == "ITEM_ON_LOAN" assert loan["item_pid"] == params["item_pid"] assert loan["document_pid"] == params["document_pid"] assert loan["patron_pid"] == params["patron_pid"] # restore default config app.config["ILS_VIEWS_PERMISSIONS_FACTORY"] = default_factory
def test_loan_extend_permissions(client, json_headers, users, testdata, loan_params): """Test loan can be extended.""" def _checkout(loan_pid, params): """Perform checkout action before extension.""" user_login(client, "librarian", users) checkout_url = url_for( "invenio_circulation_loan_actions.loanid_actions", pid_value=loan_pid, action="checkout", ) resp = client.post(checkout_url, headers=json_headers, data=json.dumps(params)) assert resp.status_code == 202 return resp.get_json() loan_pid = "loanid-1" params = deepcopy(loan_params) del params["transaction_date"] params["document_pid"] = "docid-1" params["item_pid"]["value"] = "itemid-2" loan = _checkout(loan_pid, params) tests = [ ("admin", 202), ("librarian", 202), ("patron1", 202), ("patron3", 403), ] for username, expected_resp_code in tests: extend_url = loan["links"]["actions"]["extend"] # test extension user_login(client, username, users) extend_res = client.post(extend_url, headers=json_headers, data=json.dumps(params)) assert extend_res.status_code == expected_resp_code
def test_patron_can_get_only_his_loans(client, json_headers, users, testdata): """Test that patrons can get only their loans.""" loan_patron1 = testdata["loans"][0] assert loan_patron1["patron_pid"] == "1" loan_patron2 = testdata["loans"][3] assert loan_patron2["patron_pid"] == "2" # Patron 1 GET his loans user_login(client, "patron1", users) _assert_get_loan_success(client, json_headers, loan_patron1) # Patron 1 GET loans of Patron 2 res = _fetch_loan(client, json_headers, loan_patron2) assert res.status_code == 403 # Patron 2 GET his loans user_login(client, "patron2", users) _assert_get_loan_success(client, json_headers, loan_patron2) # Patron 2 GET loans of Patron 1 res = _fetch_loan(client, json_headers, loan_patron1) assert res.status_code == 403
def test_get_document_request_endpoint(client, json_headers, testdata, users): """Test GET permissions.""" tests = [ ("patron1", "dreq-1", 200), ("librarian", "dreq-1", 200), ("admin", "dreq-1", 200), ("patron2", "dreq-1", 403), ("anonymous", "dreq-1", 401), ("patron2", "dreq-2", 200), ("librarian", "dreq-2", 200), ("admin", "dreq-2", 200), ("patron1", "dreq-2", 403), ("anonymous", "dreq-2", 401), ] for user, res_id, expected_resp_code in tests: user_login(client, user, users) url = url_for("invenio_records_rest.dreqid_item", pid_value=res_id) validate_response(client, "get", url, json_headers, None, expected_resp_code)
def test_brwreq_create_loan_succeeds( db, client, testdata, json_headers, users ): """Test borrowing requests create loan action succeeds.""" user = user_login(client, "librarian", users) # demo data "illbid-2" has the valid state `REQUESTED` pid = "illbid-2" now = arrow.utcnow() start = (now + timedelta(days=3)).date().isoformat() future = (now + timedelta(days=5)).date().isoformat() data = dict(loan_start_date=start, loan_end_date=future) res = _create_loan_action(pid, data, client, json_headers) assert res.status_code in _HTTP_OK brw_req = res.get_json()["metadata"] assert brw_req["status"] == "ON_LOAN" assert "patron_loan" in brw_req patron_loan = brw_req["patron_loan"] assert "pid" in patron_loan assert "loan" in patron_loan loan = patron_loan["loan"] assert patron_loan["pid"] == loan["pid"] assert loan["end_date"] == future loan_pid = loan["pid"] # fetch the loan url = url_for("invenio_records_rest.loanid_item", pid_value=loan_pid) res = client.get(url, headers=json_headers) assert res.status_code in _HTTP_OK loan = res.get_json()["metadata"] # make sure the loan is created with the data from the ILL assert loan["item_pid"] == dict(type=BORROWING_REQUEST_PID_TYPE, value=pid) assert loan["start_date"] == start assert loan["end_date"] == future assert loan["document_pid"] == brw_req["document_pid"] assert loan["patron_pid"] == brw_req["patron_pid"] assert loan["transaction_user_pid"] == str(user.id) # update notes (a random field) brw_req["notes"] = "This is a note" url = url_for(ITEM_ENDPOINT, pid_value=pid) res = client.put(url, headers=json_headers, data=json.dumps(brw_req)) assert res.status_code in _HTTP_OK updated_brw_req = res.get_json()["metadata"] # make sure `patron_loan` system field is preserved assert updated_brw_req["notes"] == "This is a note" assert "patron_loan" in updated_brw_req assert "pid" in patron_loan assert "loan" in patron_loan
def test_ill_brwreqs_list_permissions(client, testdata, json_headers, users): """Test borrowing requests list permissions.""" patron1_brwreq = dict( status="PENDING", document_pid="docid-1", patron_pid="1", provider_pid="ill-provid-1", type="PHYSICAL_COPY", ) patron2_brwreq = dict( status="PENDING", document_pid="docid-1", patron_pid="2", provider_pid="ill-provid-1", type="PHYSICAL_COPY", ) def _test_list(expected_status, pids): """Test get list for given pids.""" q = " OR ".join(["pid:{}".format(pid) for pid in pids]) list_url = url_for(LIST_ENDPOINT, q=q) res = client.get(list_url, headers=json_headers) assert res.status_code in expected_status return res.get_json() # create records list_url = url_for(LIST_ENDPOINT) user_login(client, "admin", users) res = client.post(list_url, headers=json_headers, data=json.dumps(patron1_brwreq)) patron1_brwreq_pid = res.get_json()["metadata"]["pid"] res = client.post(list_url, headers=json_headers, data=json.dumps(patron2_brwreq)) patron2_brwreq_pid = res.get_json()["metadata"]["pid"] all_pids = [patron1_brwreq_pid, patron2_brwreq_pid] # wait for ES current_search.flush_and_refresh(index="ill_borrowing_requests") # test results tests = [ ("admin", _HTTP_OK, all_pids), ("librarian", _HTTP_OK, all_pids), ("patron1", _HTTP_OK, [patron1_brwreq_pid]), ("patron2", _HTTP_OK, [patron2_brwreq_pid]), ] for username, expected_status, expected_pids in tests: user_login(client, username, users) results = _test_list(expected_status, all_pids) assert results["hits"]["total"] == len(expected_pids) found_pids = [ hit["metadata"]["pid"] for hit in results["hits"]["hits"] ] assert set(expected_pids) == set(found_pids) # anonymous user_login(client, "anonymous", users) _test_list([401], [])
def test_brwreq_decline_extension_success(client, testdata, json_headers, users): """Test declone extension success.""" user_login(client, "librarian", users) # create request brwreq, brwreq_pid = _create_on_loan_brwreq_with_pending_extension( "1", client, json_headers) end_date = brwreq["patron_loan"]["loan"]["end_date"] user_login(client, "librarian", users) # decline extension res = _decline_extension_action(brwreq_pid, dict(), client, json_headers) assert res.status_code == 200 patron_loan = res.get_json()["metadata"]["patron_loan"] assert patron_loan["loan"]["state"] == "ITEM_ON_LOAN" assert "extension_count" not in patron_loan["loan"] assert patron_loan["loan"]["end_date"] == end_date assert patron_loan["extension"]["status"] == "DECLINED" # request again should now fail because it has been declined user_login(client, "patron1", users) res = _request_extension_action(brwreq_pid, client, json_headers) assert res.status_code == 400
def test_brwreq_accept_decline_extension_only_librarian( client, testdata, json_headers, users): """Test that only librarian can accept or decline an extension.""" tests = [_accept_extension_action, _decline_extension_action] for action in tests: user_login(client, "librarian", users) # create request brwreq, brwreq_pid = _create_on_loan_brwreq_with_pending_extension( "1", client, json_headers) loan_end_date = arrow.utcnow() + timedelta(days=15) data = dict(loan_end_date=loan_end_date.date().isoformat()) # accept/decline # anonymous, forbidden user_logout(client) res = action(brwreq_pid, data, client, json_headers) assert res.status_code == 401 # patron, forbidden user_login(client, "patron1", users) res = action(brwreq_pid, data, client, json_headers) assert res.status_code == 403 # librarian, success user_login(client, "librarian", users) res = action(brwreq_pid, data, client, json_headers) assert res.status_code == 200
def test_patron_can_request_loan(client, json_headers, users, testdata): """Test that a patron can request a loan.""" url = url_for("invenio_app_ils_circulation.loan_request") user = user_login(client, "patron1", users) params = deepcopy(NEW_LOAN) params["document_pid"] = "docid-3" params["transaction_user_pid"] = str(user.id) res = client.post(url, headers=json_headers, data=json.dumps(params)) assert res.status_code == 202 loan = res.get_json()["metadata"] assert loan["state"] == "PENDING" assert loan["document_pid"] == params["document_pid"] assert loan["transaction_date"]
def test_brwreq_create_loan_fails_on_loan_pid_already_attached( db, client, testdata, json_headers, users): """Test borrowing requests create loan action fails on loan_pid already.""" user_login(client, "librarian", users) # demo data "illbid-2" has the valid state `REQUESTED` pid = "illbid-2" rec = BorrowingRequest.get_record_by_pid(pid) rec.setdefault("patron_loan", {}) rec["patron_loan"]["pid"] = "loanid-3" rec.commit() db.session.commit() # already with a loan pid for some reasons now = arrow.utcnow() future = now + timedelta(days=5) data = dict( loan_start_date=now.date().isoformat(), loan_end_date=future.date().isoformat(), ) _assert_create_loan_action_fails(pid, data, client, json_headers)
def test_patron_can_cancel_loan( client, json_headers, users, testdata, app_config ): """Test that a patron can cancel its own loan.""" url = url_for("invenio_app_ils_circulation.loan_request") user = user_login(client, "patron3", users) params = deepcopy(NEW_LOAN) params["document_pid"] = "docid-3" params["transaction_user_pid"] = str(user.id) # Create a Loan res = client.post(url, headers=json_headers, data=json.dumps(params)) loan = res.get_json() cancel_url = loan["links"]["actions"]["cancel"] meta = loan["metadata"] payload = { "document_pid": meta["document_pid"], "patron_pid": meta["patron_pid"], "transaction_location_pid": meta["transaction_location_pid"], "transaction_user_pid": meta["transaction_user_pid"], "cancel_reason": "USER_CANCEL", } assert res.status_code == 202 # Try to cancel a loan that belongs to patron3 as patron1 user_login(client, "patron1", users) cancel_res = client.post( cancel_url, headers=json_headers, data=json.dumps(payload) ) assert cancel_res.status_code == 403 # Try to cancel a loan that belongs to patron3 as patron3 user_login(client, "patron3", users) cancel_res = client.post( cancel_url, headers=json_headers, data=json.dumps(payload) ) assert cancel_res.status_code == 202
def test_loan_access_permission(client, json_headers, users, testdata): """ Test that a patron should not be able to update their loan; and a fortiori, other people's. """ loan = testdata["loans"][0] user = user_login(client, "patron1", users) url = _url_loan(loan["pid"]) res = client.get(url, headers=json_headers) assert res.status_code == 200 # Can access their own loan metadata = _load_result(res)["metadata"] metadata["transaction_user_pid"] = str(user.id) res = client.put(url, headers=json_headers, data=json.dumps(metadata)) assert res.status_code == 403 # Cannot modify the loan
def login_and_test(username): user = user_login(client, username, users) # Create record id = uuid.uuid4() record = Record.create(access, id_=id) factory = RecordPermission(record, action) if user.has_role("admin"): # super user can do EVERYTHING assert factory.can() elif user.has_role("librarian") and action != "delete": # librarian should be able to update, create, and read everything assert factory.can() else: assert factory.can() if is_allowed else not factory.can()
def test_brwreq_accept_extension_success(client, testdata, json_headers, users): """Test accept extension success.""" user_login(client, "librarian", users) # create request brwreq, brwreq_pid = _create_on_loan_brwreq_with_pending_extension( "1", client, json_headers) loan_end_date = arrow.utcnow() + timedelta(days=15) end_date = loan_end_date.date().isoformat() data = dict(loan_end_date=end_date) user_login(client, "librarian", users) # accept extension res = _accept_extension_action(brwreq_pid, data, client, json_headers) assert res.status_code == 200 patron_loan = res.get_json()["metadata"]["patron_loan"] assert patron_loan["loan"]["state"] == "ITEM_ON_LOAN" assert patron_loan["loan"]["extension_count"] == 1 assert patron_loan["loan"]["end_date"] == end_date assert "status" not in patron_loan["extension"]
def test_brwreq_accept_decline_extension_should_fail_when_loan_not_active( client, testdata, json_headers, users): """Test that accept or decline an extension fails on loan not active.""" tests = [_accept_extension_action, _decline_extension_action] for action in tests: user_login(client, "librarian", users) # create request brwreq, brwreq_pid = _create_on_loan_brwreq_with_pending_extension( "1", client, json_headers) # check-in loan loan_pid = brwreq["patron_loan"]["pid"] url = url_for( "invenio_circulation_loan_actions.loanid_actions", pid_value=loan_pid, action="checkin", ) item_pid = dict(type=BORROWING_REQUEST_PID_TYPE, value=brwreq["pid"]) params = dict( document_pid=brwreq["document_pid"], item_pid=item_pid, patron_pid=brwreq["patron_pid"], transaction_location_pid="locid-1", transaction_user_pid="1", ) res = client.post(url, headers=json_headers, data=json.dumps(params)) assert res.status_code == 202 # accept extension loan_end_date = arrow.utcnow() + timedelta(days=15) data = dict(loan_end_date=loan_end_date.date().isoformat()) res = action(brwreq_pid, data, client, json_headers) assert res.status_code == 400