import pytest from flask import url_for, get_flashed_messages import pendulum from uuid import uuid4 from atst.domain.task_orders import TaskOrders from atst.models.task_order import Status as TaskOrderStatus from atst.models import TaskOrder from tests.factories import CLINFactory, PortfolioFactory, TaskOrderFactory, UserFactory from tests.utils import captured_templates def build_pdf_form_data(filename="sample.pdf", object_name=None): return {"pdf-filename": filename, "pdf-object_name": object_name or uuid4()} @pytest.fixture def task_order(): user = UserFactory.create() portfolio = PortfolioFactory.create(owner=user) task_order = TaskOrderFactory.create(portfolio=portfolio) CLINFactory.create(task_order=task_order) return task_order @pytest.fixture def incomplete_to(): user = UserFactory.create() portfolio = PortfolioFactory.create(owner=user) return TaskOrderFactory.create(portfolio=portfolio) @pytest.fixture def completed_task_order(): portfolio = PortfolioFactory.create() task_order = TaskOrderFactory.create( portfolio=portfolio, create_clins=[{"number": "1234567890123456789012345678901234567890123"}], ) return task_order @pytest.fixture def portfolio(): return PortfolioFactory.create() @pytest.fixture def user(): return UserFactory.create() def test_task_orders_form_step_one_add_pdf(client, user_session, portfolio): user_session(portfolio.owner) response = client.get( url_for("task_orders.form_step_one_add_pdf", portfolio_id=portfolio.id) ) assert response.status_code == 200 def test_task_orders_submit_form_step_one_add_pdf(client, user_session, portfolio): user_session(portfolio.owner) response = client.post( url_for("task_orders.submit_form_step_one_add_pdf", portfolio_id=portfolio.id), data=build_pdf_form_data(), ) assert response.status_code == 302 task_order = portfolio.task_orders[0] assert task_order.pdf.filename == "sample.pdf" def test_task_orders_form_step_one_add_pdf_existing_to( client, user_session, task_order ): user_session(task_order.portfolio.owner) response = client.get( url_for("task_orders.form_step_one_add_pdf", task_order_id=task_order.id) ) assert response.status_code == 200 def test_task_orders_submit_form_step_one_add_pdf_existing_to(client, user_session): task_order = TaskOrderFactory.create() user_session(task_order.portfolio.owner) response = client.post( url_for( "task_orders.submit_form_step_one_add_pdf", task_order_id=task_order.id ), data=build_pdf_form_data(), ) assert response.status_code == 302 assert task_order.pdf.filename == "sample.pdf" def test_task_orders_submit_form_step_one_add_pdf_delete_pdf( client, user_session, portfolio ): user_session(portfolio.owner) task_order = TaskOrderFactory.create(portfolio=portfolio) response = client.post( url_for( "task_orders.submit_form_step_one_add_pdf", task_order_id=task_order.id ), data=build_pdf_form_data(filename="", object_name=""), ) assert task_order.pdf is None assert response.status_code == 302 def test_task_orders_submit_form_step_one_validates_filename( app, client, user_session, portfolio ): user_session(portfolio.owner) with captured_templates(app) as templates: client.post( url_for( "task_orders.submit_form_step_one_add_pdf", portfolio_id=portfolio.id ), data={"pdf-filename": "a" * 1024}, follow_redirects=True, ) _, context = templates[-1] assert "filename" in context["form"].pdf.errors def test_task_orders_submit_form_step_one_validates_object_name( app, client, user_session, portfolio ): user_session(portfolio.owner) with captured_templates(app) as templates: client.post( url_for( "task_orders.submit_form_step_one_add_pdf", portfolio_id=portfolio.id ), data={"pdf-object_name": "a" * 41}, follow_redirects=True, ) _, context = templates[-1] assert "object_name" in context["form"].pdf.errors def test_task_orders_form_step_two_add_number(client, user_session, task_order): user_session(task_order.portfolio.owner) response = client.get( url_for("task_orders.form_step_two_add_number", task_order_id=task_order.id) ) assert response.status_code == 200 def test_task_orders_submit_form_step_two_add_number(client, user_session, task_order): user_session(task_order.portfolio.owner) form_data = {"number": "abc-1234567890"} response = client.post( url_for( "task_orders.submit_form_step_two_add_number", task_order_id=task_order.id ), data=form_data, ) assert response.status_code == 302 assert task_order.number == "ABC1234567890" # pragma: allowlist secret def test_task_orders_submit_form_step_two_enforces_unique_number( client, user_session, task_order, session ): number = "1234567890123" dupe_task_order = TaskOrderFactory.create(number=number) portfolio = task_order.portfolio user_session(task_order.portfolio.owner) form_data = {"number": number} session.begin_nested() response = client.post( url_for( "task_orders.submit_form_step_two_add_number", task_order_id=task_order.id ), data=form_data, ) session.rollback() assert response.status_code == 400 def test_task_orders_submit_form_step_two_add_number_existing_to( client, user_session, task_order ): user_session(task_order.portfolio.owner) form_data = {"number": "0000000000000"} original_number = task_order.number response = client.post( url_for( "task_orders.submit_form_step_two_add_number", task_order_id=task_order.id ), data=form_data, ) assert response.status_code == 302 assert task_order.number == "0000000000000" assert task_order.number != original_number def test_task_orders_form_step_three_add_clins(client, user_session, task_order): user_session(task_order.portfolio.owner) response = client.get( url_for("task_orders.form_step_three_add_clins", task_order_id=task_order.id) ) assert response.status_code == 200 def test_task_orders_submit_form_step_three_add_clins(client, user_session, task_order): user_session(task_order.portfolio.owner) form_data = { "clins-0-jedi_clin_type": "JEDI_CLIN_1", "clins-0-number": "1212", "clins-0-start_date": "01/01/2020", "clins-0-end_date": "01/01/2021", "clins-0-obligated_amount": "5000", "clins-0-total_amount": "10000", "clins-1-jedi_clin_type": "JEDI_CLIN_1", "clins-1-number": "1212", "clins-1-start_date": "01/01/2020", "clins-1-end_date": "01/01/2021", "clins-1-obligated_amount": "5000", "clins-1-total_amount": "5000", } response = client.post( url_for( "task_orders.submit_form_step_three_add_clins", task_order_id=task_order.id ), data=form_data, ) assert response.status_code == 302 assert len(task_order.clins) == 2 def test_task_orders_submit_form_step_three_add_clins_existing_to( client, user_session, task_order ): clin_list = [ { "jedi_clin_type": "JEDI_CLIN_1", "number": "12312", "start_date": "01/01/2020", "end_date": "01/01/2021", "obligated_amount": "5000", "total_amount": "10000", }, { "jedi_clin_type": "JEDI_CLIN_1", "number": "12312", "start_date": "01/01/2020", "end_date": "01/01/2021", "obligated_amount": "5000", "total_amount": "10000", }, ] TaskOrders.create_clins(task_order.id, clin_list) assert len(task_order.clins) == 3 user_session(task_order.portfolio.owner) form_data = { "clins-0-jedi_clin_type": "JEDI_CLIN_1", "clins-0-number": "1212", "clins-0-start_date": "01/01/2020", "clins-0-end_date": "01/01/2021", "clins-0-obligated_amount": "5000", "clins-0-total_amount": "10000", } response = client.post( url_for( "task_orders.submit_form_step_three_add_clins", task_order_id=task_order.id ), data=form_data, ) assert response.status_code == 302 assert len(task_order.clins) == 1 def test_task_orders_form_step_four_review(client, user_session, completed_task_order): user_session(completed_task_order.portfolio.owner) response = client.get( url_for( "task_orders.form_step_four_review", task_order_id=completed_task_order.id ) ) assert response.status_code == 200 def test_task_orders_form_step_four_review_incomplete_to( client, user_session, incomplete_to ): user_session(incomplete_to.portfolio.owner) response = client.get( url_for("task_orders.form_step_four_review", task_order_id=incomplete_to.id) ) assert response.status_code == 404 def test_task_orders_form_step_five_confirm_signature( client, user_session, completed_task_order ): user_session(completed_task_order.portfolio.owner) response = client.get( url_for( "task_orders.form_step_five_confirm_signature", task_order_id=completed_task_order.id, ) ) assert response.status_code == 200 def test_task_orders_form_step_five_confirm_signature_incomplete_to( client, user_session, incomplete_to ): user_session(incomplete_to.portfolio.owner) response = client.get( url_for( "task_orders.form_step_five_confirm_signature", task_order_id=incomplete_to.id, ) ) assert response.status_code == 404 def test_task_orders_submit_task_order(client, user_session, task_order): user_session(task_order.portfolio.owner) response = client.post( url_for("task_orders.submit_task_order", task_order_id=task_order.id) ) assert response.status_code == 302 active_start_date = pendulum.today().subtract(days=1) active_task_order = TaskOrderFactory(portfolio=task_order.portfolio) CLINFactory(task_order=active_task_order, start_date=active_start_date) assert active_task_order.status == TaskOrderStatus.UNSIGNED response = client.post( url_for("task_orders.submit_task_order", task_order_id=active_task_order.id) ) assert active_task_order.status == TaskOrderStatus.ACTIVE upcoming_start_date = pendulum.today().add(days=1) upcoming_task_order = TaskOrderFactory(portfolio=task_order.portfolio) CLINFactory(task_order=upcoming_task_order, start_date=upcoming_start_date) assert upcoming_task_order.status == TaskOrderStatus.UNSIGNED response = client.post( url_for("task_orders.submit_task_order", task_order_id=upcoming_task_order.id) ) assert upcoming_task_order.status == TaskOrderStatus.UPCOMING @pytest.mark.parametrize( "to_factory_args,expected_step", [ ({"_pdf": None, "number": "", "clins": []}, "step_1"), ({"number": "", "clins": []}, "step_2"), ({"number": "1234567890123", "clins": []}, "step_3"), ({"number": "1234567890123", "create_clins": [{"number": 1}]}, "step_4"), ], ) def test_task_orders_edit_redirects_to_latest_incomplete_step( client, user_session, portfolio, to_factory_args, expected_step ): task_order = TaskOrderFactory.create(portfolio=portfolio, **to_factory_args) user_session(portfolio.owner) response = client.get(url_for("task_orders.edit", task_order_id=task_order.id)) assert expected_step in response.location def test_can_cancel_edit_and_save_task_order(client, user_session, task_order, session): user_session(task_order.portfolio.owner) response = client.post( url_for("task_orders.cancel_edit", task_order_id=task_order.id, save=True), data={"number": "7896564324567"}, ) assert response.status_code == 302 updated_task_order = session.query(TaskOrder).get(task_order.id) assert updated_task_order.number == "7896564324567" def test_cancel_can_create_new_to(client, user_session, portfolio): user_session(portfolio.owner) response = client.post( url_for("task_orders.cancel_edit", portfolio_id=portfolio.id), data={"number": "7643906432984"}, ) assert response.status_code == 302 def test_cancel_edit_causes_to_to_be_deleted(client, user_session, session): task_order = TaskOrderFactory.create() user_session(task_order.portfolio.owner) response = client.post( url_for("task_orders.cancel_edit", task_order_id=task_order.id, save=False), data={}, ) assert response.status_code == 302 # TO should be deleted assert session.query(TaskOrder).get(task_order.id) is None def test_cancel_edit_and_save_with_invalid_input_does_not_flash( app, client, user_session, session ): task_order = TaskOrderFactory.create() user_session(task_order.portfolio.owner) bad_data = {"clins-0-jedi_clin_type": "foo"} response = client.post( url_for("task_orders.cancel_edit", task_order_id=task_order.id, save=True), data=bad_data, ) assert len(get_flashed_messages()) == 0 @pytest.mark.skip(reason="Reevaluate how form handles invalid data") def test_task_orders_update_invalid_data(client, user_session, portfolio): user_session(portfolio.owner) num_task_orders = len(portfolio.task_orders) response = client.post( url_for("task_orders.update", portfolio_id=portfolio.id), data={"number": ""} ) assert response.status_code == 400 assert num_task_orders == len(portfolio.task_orders) assert "There were some errors" in response.data.decode() @pytest.mark.skip(reason="Update after implementing errors on TO form") def test_task_order_form_shows_errors(client, user_session, task_order): user_session(task_order.portfolio.owner) task_order_data = TaskOrderFactory.dictionary() funding_data = slice_data_for_section(task_order_data, "funding") funding_data = serialize_dates(funding_data) funding_data.update({"clin_01": "one milllllion dollars"}) response = client.post( url_for("task_orders.update", task_order_id=task_order.id), data=funding_data, follow_redirects=False, ) body = response.data.decode() assert "There were some errors" in body assert "Not a valid decimal" in body def test_update_and_render_next_handles_previous_valid_data( client, user_session, task_order ): user_session(task_order.portfolio.owner) form_data = {"number": "0000000000000"} original_number = task_order.number response = client.post( url_for( "task_orders.submit_form_step_two_add_number", task_order_id=task_order.id, previous=True, ), data=form_data, ) assert response.status_code == 302 assert task_order.number == "0000000000000" assert task_order.number != original_number def test_update_and_render_next_handles_previous_invalid_data( client, user_session, task_order ): clin_list = [ { "jedi_clin_type": "JEDI_CLIN_1", "number": "12312", "start_date": "01/01/2020", "end_date": "01/01/2021", "obligated_amount": "5000", "total_amount": "10000", }, ] TaskOrders.create_clins(task_order.id, clin_list) assert len(task_order.clins) == 2 user_session(task_order.portfolio.owner) form_data = { "clins-0-jedi_clin_type": "JEDI_CLIN_1", "clins-0-number": "12312", "clins-0-start_date": "01/01/2020", "clins-0-end_date": "01/01/2021", "clins-0-obligated_amount": "5000", "clins-0-total_amount": "10000", "clins-1-jedi_clin_type": "JEDI_CLIN_1", "clins-1-number": "1212", } response = client.post( url_for( "task_orders.submit_form_step_three_add_clins", task_order_id=task_order.id, previous=True, ), data=form_data, ) assert len(task_order.clins) == 2