update to use request revisions

This commit is contained in:
dandds 2018-08-30 17:16:26 -04:00
parent 56a991a44d
commit cc7ddd9468
13 changed files with 280 additions and 131 deletions

View File

@ -30,20 +30,20 @@ def upgrade():
sa.Column('lname_poc', sa.String(), nullable=True), sa.Column('lname_poc', sa.String(), nullable=True),
sa.Column('jedi_usage', sa.String(), nullable=True), sa.Column('jedi_usage', sa.String(), nullable=True),
sa.Column('start_date', sa.Date(), nullable=True), sa.Column('start_date', sa.Date(), nullable=True),
sa.Column('cloud_native', sa.Boolean(), nullable=True), sa.Column('cloud_native', sa.String(), nullable=True),
sa.Column('dollar_value', sa.Integer(), nullable=True), sa.Column('dollar_value', sa.Integer(), nullable=True),
sa.Column('dod_component', sa.String(), nullable=True), sa.Column('dod_component', sa.String(), nullable=True),
sa.Column('data_transfers', sa.String(), nullable=True), sa.Column('data_transfers', sa.String(), nullable=True),
sa.Column('expected_completion_date', sa.String(), nullable=True), sa.Column('expected_completion_date', sa.String(), nullable=True),
sa.Column('jedi_migration', sa.Boolean(), nullable=True), sa.Column('jedi_migration', sa.String(), nullable=True),
sa.Column('num_software_systems', sa.Integer(), nullable=True), sa.Column('num_software_systems', sa.Integer(), nullable=True),
sa.Column('number_user_sessions', sa.Integer(), nullable=True), sa.Column('number_user_sessions', sa.Integer(), nullable=True),
sa.Column('average_daily_traffic', sa.Integer(), nullable=True), sa.Column('average_daily_traffic', sa.Integer(), nullable=True),
sa.Column('engineering_assessment', sa.Boolean(), nullable=True), sa.Column('engineering_assessment', sa.String(), nullable=True),
sa.Column('technical_support_team', sa.Boolean(), nullable=True), sa.Column('technical_support_team', sa.String(), nullable=True),
sa.Column('estimated_monthly_spend', sa.Integer(), nullable=True), sa.Column('estimated_monthly_spend', sa.Integer(), nullable=True),
sa.Column('average_daily_traffic_gb', sa.Integer(), nullable=True), sa.Column('average_daily_traffic_gb', sa.Integer(), nullable=True),
sa.Column('rationalization_software_systems', sa.Boolean(), nullable=True), sa.Column('rationalization_software_systems', sa.String(), nullable=True),
sa.Column('organization_providing_assistance', sa.String(), nullable=True), sa.Column('organization_providing_assistance', sa.String(), nullable=True),
sa.Column('citizenship', sa.String(), nullable=True), sa.Column('citizenship', sa.String(), nullable=True),
sa.Column('designation', sa.String(), nullable=True), sa.Column('designation', sa.String(), nullable=True),

View File

@ -0,0 +1,28 @@
"""add sequence to request revision
Revision ID: a903ebe91ad5
Revises: 04fe150da553
Create Date: 2018-08-30 13:45:35.561657
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = 'a903ebe91ad5'
down_revision = '04fe150da553'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
db = op.get_bind()
op.add_column('request_revisions', sa.Column('sequence', sa.BigInteger(), nullable=False))
db.execute("CREATE SEQUENCE request_revisions_sequence_seq OWNED BY request_revisions.sequence;")
# ### end Alembic commands ###
def downgrade():
op.drop_column('request_revisions', 'sequence')

View File

@ -4,33 +4,30 @@ from sqlalchemy.sql import text
from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.orm.attributes import flag_modified from sqlalchemy.orm.attributes import flag_modified
from werkzeug.datastructures import FileStorage from werkzeug.datastructures import FileStorage
import pendulum
from atst.database import db from atst.database import db
from atst.domain.authz import Authorization from atst.domain.authz import Authorization
from atst.domain.task_orders import TaskOrders from atst.domain.task_orders import TaskOrders
from atst.domain.workspaces import Workspaces from atst.domain.workspaces import Workspaces
from atst.models.request import Request from atst.models.request import Request
from atst.models.request_revision import RequestRevision
from atst.models.request_status_event import RequestStatusEvent, RequestStatus from atst.models.request_status_event import RequestStatusEvent, RequestStatus
from atst.utils import deep_merge
from .exceptions import NotFoundError, UnauthorizedError from .exceptions import NotFoundError, UnauthorizedError
def deep_merge(source, destination: dict): def create_revision_from_request_body(body):
""" body = {k: v for p in body.values() for k, v in p.items()}
Merge source dict into destination dict recursively. TIMESTAMPS = ["start_date", "date_latest_training"]
""" coerced_timestamps = {
k: pendulum.parse(v)
def _deep_merge(a, b): for k, v in body.items()
for key, value in a.items(): if k in TIMESTAMPS and isinstance(v, str)
if isinstance(value, dict): }
node = b.setdefault(key, {}) body = {**body, **coerced_timestamps}
_deep_merge(value, node) return RequestRevision(**body)
else:
b[key] = value
return b
return _deep_merge(source, dict(destination))
class Requests(object): class Requests(object):
@ -39,7 +36,8 @@ class Requests(object):
@classmethod @classmethod
def create(cls, creator, body): def create(cls, creator, body):
request = Request(creator=creator, body=body) revision = create_revision_from_request_body(body)
request = Request(creator=creator, revisions=[revision])
request = Requests.set_status(request, RequestStatus.STARTED) request = Requests.set_status(request, RequestStatus.STARTED)
db.session.add(request) db.session.add(request)
@ -105,7 +103,10 @@ class Requests(object):
@classmethod @classmethod
def update(cls, request_id, request_delta): def update(cls, request_id, request_delta):
request = Requests._get_with_lock(request_id) request = Requests._get_with_lock(request_id)
request = Requests._merge_body(request, request_delta)
new_body = deep_merge(request_delta, request.body)
revision = create_revision_from_request_body(new_body)
request.revisions.append(revision)
db.session.add(request) db.session.add(request)
db.session.commit() db.session.commit()
@ -129,13 +130,7 @@ class Requests(object):
@classmethod @classmethod
def _merge_body(cls, request, request_delta): def _merge_body(cls, request, request_delta):
request.body = deep_merge(request_delta, request.body) return deep_merge(request_delta, request.body)
# Without this, sqlalchemy won't notice the change to request.body,
# since it doesn't track dictionary mutations by default.
flag_modified(request, "body")
return request
@classmethod @classmethod
def approve_and_create_workspace(cls, request): def approve_and_create_workspace(cls, request):
@ -264,12 +259,7 @@ WHERE requests_with_status.status = :status
if task_order: if task_order:
request.task_order = task_order request.task_order = task_order
request = Requests._merge_body( request = Requests.update(request.id, {"financial_verification": request_data})
request, {"financial_verification": request_data}
)
db.session.add(request)
db.session.commit()
return request return request

View File

@ -16,7 +16,7 @@ class DateField(DateField):
if values: if values:
self.data = values[0] self.data = values[0]
else: else:
self.data = [] self.data = None
class NewlineListField(Field): class NewlineListField(Field):

View File

@ -7,7 +7,23 @@ import pendulum
from atst.models import Base from atst.models import Base
from atst.models.types import Id from atst.models.types import Id
from atst.models.request_status_event import RequestStatus from atst.models.request_status_event import RequestStatus
from atst.utils import first_or_none from atst.utils import deep_merge, first_or_none
def map_properties_to_dict(properties, instance):
return {
field: getattr(instance, field)
for field in properties
if getattr(instance, field) is not None
}
def update_dict_with_properties(instance, body, top_level_key, properties):
new_properties = map_properties_to_dict(properties, instance)
if new_properties:
body[top_level_key] = new_properties
return body
class Request(Base): class Request(Base):
@ -28,7 +44,77 @@ class Request(Base):
task_order_id = Column(ForeignKey("task_order.id")) task_order_id = Column(ForeignKey("task_order.id"))
task_order = relationship("TaskOrder") task_order = relationship("TaskOrder")
revisions = relationship("RequestRevision", back_populates="request") revisions = relationship(
"RequestRevision", back_populates="request", order_by="RequestRevision.sequence"
)
@property
def latest_revision(self):
if self.revisions:
return self.revisions[-1]
else:
return RequestRevision(request=self)
PRIMARY_POC_FIELDS = ["am_poc", "dodid_poc", "email_poc", "fname_poc", "lname_poc"]
DETAILS_OF_USE_FIELDS = [
"jedi_usage",
"start_date",
"cloud_native",
"dollar_value",
"dod_component",
"data_transfers",
"expected_completion_date",
"jedi_migration",
"num_software_systems",
"number_user_sessions",
"average_daily_traffic",
"engineering_assessment",
"technical_support_team",
"estimated_monthly_spend",
"average_daily_traffic_gb",
"rationalization_software_systems",
"organization_providing_assistance",
]
INFORMATION_ABOUT_YOU_FIELDS = [
"citizenship",
"designation",
"phone_number",
"email_request",
"fname_request",
"lname_request",
"service_branch",
"date_latest_training",
]
FINANCIAL_VERIFICATION_FIELDS = [
"pe_id",
"task_order_number",
"fname_co",
"lname_co",
"email_co",
"office_co",
"fname_cor",
"lname_cor",
"email_cor",
"office_cor",
"uii_ids",
"treasury_code",
"ba_code",
]
@property
def body(self):
current = self.latest_revision
body = {}
for top_level_key, properties in [
("primary_poc", Request.PRIMARY_POC_FIELDS),
("details_of_use", Request.DETAILS_OF_USE_FIELDS),
("information_about_you", Request.INFORMATION_ABOUT_YOU_FIELDS),
("financial_verification", Request.FINANCIAL_VERIFICATION_FIELDS),
]:
body = update_dict_with_properties(current, body, top_level_key, properties)
return body
@property @property
def status(self): def status(self):
@ -40,7 +126,7 @@ class Request(Base):
@property @property
def annual_spend(self): def annual_spend(self):
monthly = self.body.get("details_of_use", {}).get("estimated_monthly_spend", 0) monthly = self.latest_revision.estimated_monthly_spend or 0
return monthly * 12 return monthly * 12
@property @property

View File

@ -1,5 +1,15 @@
import pendulum import pendulum
from sqlalchemy import Column, func, ForeignKey, String, Boolean, Integer, Date from sqlalchemy import (
Column,
func,
ForeignKey,
String,
Boolean,
Integer,
Date,
BigInteger,
Sequence,
)
from sqlalchemy.types import DateTime from sqlalchemy.types import DateTime
from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
@ -15,6 +25,9 @@ class RequestRevision(Base, TimestampsMixin):
id = Id() id = Id()
request_id = Column(ForeignKey("requests.id"), nullable=False) request_id = Column(ForeignKey("requests.id"), nullable=False)
request = relationship("Request", back_populates="revisions") request = relationship("Request", back_populates="revisions")
sequence = Column(
BigInteger, Sequence("request_revisions_sequence_seq"), nullable=False
)
# primary_poc # primary_poc
am_poc = Column(Boolean, default=False) am_poc = Column(Boolean, default=False)
@ -26,20 +39,20 @@ class RequestRevision(Base, TimestampsMixin):
# details_of_use # details_of_use
jedi_usage = Column(String) jedi_usage = Column(String)
start_date = Column(Date()) start_date = Column(Date())
cloud_native = Column(Boolean) cloud_native = Column(String)
dollar_value = Column(Integer) dollar_value = Column(Integer)
dod_component = Column(String) dod_component = Column(String)
data_transfers = Column(String) data_transfers = Column(String)
expected_completion_date = Column(String) expected_completion_date = Column(String)
jedi_migration = Column(Boolean) jedi_migration = Column(String)
num_software_systems = Column(Integer) num_software_systems = Column(Integer)
number_user_sessions = Column(Integer) number_user_sessions = Column(Integer)
average_daily_traffic = Column(Integer) average_daily_traffic = Column(Integer)
engineering_assessment = Column(Boolean) engineering_assessment = Column(String)
technical_support_team = Column(Boolean) technical_support_team = Column(String)
estimated_monthly_spend = Column(Integer) estimated_monthly_spend = Column(Integer)
average_daily_traffic_gb = Column(Integer) average_daily_traffic_gb = Column(Integer)
rationalization_software_systems = Column(Boolean) rationalization_software_systems = Column(String)
organization_providing_assistance = Column(String) organization_providing_assistance = Column(String)
# information_about_you # information_about_you
@ -66,13 +79,3 @@ class RequestRevision(Base, TimestampsMixin):
uii_ids = Column(String) uii_ids = Column(String)
treasury_code = Column(String) treasury_code = Column(String)
ba_code = Column(String) ba_code = Column(String)
_BOOLS = ["am_poc", "jedi_migration", "engineering_assessment", "technical_support_team", "rationalization_software_systems", "cloud_native"]
_TIMESTAMPS = ["start_date", "date_latest_training"]
@classmethod
def create_from_request_body(cls, request, **body):
coerced_bools = {k: v == "yes" for k,v in body.items() if k in RequestRevision._BOOLS}
coerced_timestamps = {k: pendulum.parse(v) for k,v in body.items() if k in RequestRevision._TIMESTAMPS}
body = {**body, **coerced_bools, **coerced_timestamps}
return RequestRevision(request=request, **body)

View File

@ -1,2 +1,20 @@
def first_or_none(predicate, lst): def first_or_none(predicate, lst):
return next((x for x in lst if predicate(x)), None) return next((x for x in lst if predicate(x)), None)
def deep_merge(source, destination: dict):
"""
Merge source dict into destination dict recursively.
"""
def _deep_merge(a, b):
for key, value in a.items():
if isinstance(value, dict):
node = b.setdefault(key, {})
_deep_merge(value, node)
else:
b[key] = value
return b
return _deep_merge(source, dict(destination))

View File

@ -12,6 +12,7 @@ from tests.factories import (
UserFactory, UserFactory,
RequestStatusEventFactory, RequestStatusEventFactory,
TaskOrderFactory, TaskOrderFactory,
RequestRevisionFactory,
) )
@ -20,10 +21,11 @@ def new_request(session):
return RequestFactory.create() return RequestFactory.create()
def test_can_get_request(new_request): def test_can_get_request():
request = Requests.get(new_request.creator, new_request.id) factory_req = RequestFactory.create()
request = Requests.get(factory_req.creator, factory_req.id)
assert request.id == new_request.id assert request.id == factory_req.id
def test_nonexistent_request_raises(): def test_nonexistent_request_raises():
@ -37,28 +39,30 @@ def test_new_request_has_started_status():
assert request.status == RequestStatus.STARTED assert request.status == RequestStatus.STARTED
def test_auto_approve_less_than_1m(new_request): def test_auto_approve_less_than_1m():
new_request.body = {"details_of_use": {"dollar_value": 999999}} new_request = RequestFactory.create(initial_revision={"dollar_value": 999999})
request = Requests.submit(new_request) request = Requests.submit(new_request)
assert request.status == RequestStatus.PENDING_FINANCIAL_VERIFICATION assert request.status == RequestStatus.PENDING_FINANCIAL_VERIFICATION
def test_dont_auto_approve_if_dollar_value_is_1m_or_above(new_request): def test_dont_auto_approve_if_dollar_value_is_1m_or_above():
new_request.body = {"details_of_use": {"dollar_value": 1000000}} new_request = RequestFactory.create(initial_revision={"dollar_value": 1000000})
request = Requests.submit(new_request) request = Requests.submit(new_request)
assert request.status == RequestStatus.PENDING_CCPO_APPROVAL assert request.status == RequestStatus.PENDING_CCPO_APPROVAL
def test_dont_auto_approve_if_no_dollar_value_specified(new_request): def test_dont_auto_approve_if_no_dollar_value_specified():
new_request.body = {"details_of_use": {}} new_request = RequestFactory.create(initial_revision={})
request = Requests.submit(new_request) request = Requests.submit(new_request)
assert request.status == RequestStatus.PENDING_CCPO_APPROVAL assert request.status == RequestStatus.PENDING_CCPO_APPROVAL
def test_should_allow_submission(new_request): def test_should_allow_submission():
new_request = RequestFactory.create()
assert Requests.should_allow_submission(new_request) assert Requests.should_allow_submission(new_request)
RequestStatusEventFactory.create( RequestStatusEventFactory.create(
@ -66,7 +70,8 @@ def test_should_allow_submission(new_request):
) )
assert Requests.should_allow_submission(new_request) assert Requests.should_allow_submission(new_request)
del new_request.body["details_of_use"] # new, blank revision
RequestRevisionFactory.create(request=new_request)
assert not Requests.should_allow_submission(new_request) assert not Requests.should_allow_submission(new_request)

View File

@ -2,9 +2,11 @@ import random
import string import string
import factory import factory
from uuid import uuid4 from uuid import uuid4
import datetime
from atst.forms.data import SERVICE_BRANCHES from atst.forms.data import SERVICE_BRANCHES
from atst.models.request import Request from atst.models.request import Request
from atst.models.request_revision import RequestRevision
from atst.models.request_status_event import RequestStatusEvent, RequestStatus from atst.models.request_status_event import RequestStatusEvent, RequestStatus
from atst.models.pe_number import PENumber from atst.models.pe_number import PENumber
from atst.models.task_order import TaskOrder from atst.models.task_order import TaskOrder
@ -42,6 +44,13 @@ class RequestStatusEventFactory(factory.alchemy.SQLAlchemyModelFactory):
sequence = 1 sequence = 1
class RequestRevisionFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = RequestRevision
id = factory.Sequence(lambda x: uuid4())
class RequestFactory(factory.alchemy.SQLAlchemyModelFactory): class RequestFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta: class Meta:
model = Request model = Request
@ -51,48 +60,58 @@ class RequestFactory(factory.alchemy.SQLAlchemyModelFactory):
RequestStatusEventFactory, "request", new_status=RequestStatus.STARTED RequestStatusEventFactory, "request", new_status=RequestStatus.STARTED
) )
creator = factory.SubFactory(UserFactory) creator = factory.SubFactory(UserFactory)
body = factory.LazyAttribute(lambda r: RequestFactory.build_request_body(r.creator)) revisions = factory.LazyAttribute(
lambda r: [RequestFactory.create_initial_revision(r)]
)
class Params:
initial_revision = None
@classmethod @classmethod
def build_request_body(cls, user, dollar_value=1000000): def create_initial_revision(cls, request, dollar_value=1000000):
return { user = request.creator
"primary_poc": { default_data = dict(
"am_poc": False, am_poc=False,
"dodid_poc": user.dod_id, dodid_poc=user.dod_id,
"email_poc": user.email, email_poc=user.email,
"fname_poc": user.first_name, fname_poc=user.first_name,
"lname_poc": user.last_name, lname_poc=user.last_name,
}, jedi_usage="adf",
"details_of_use": { start_date=datetime.datetime(2018, 8, 8, tzinfo=datetime.timezone.utc),
"jedi_usage": "adf", cloud_native="yes",
"start_date": "2018-08-08", dollar_value=dollar_value,
"cloud_native": "yes", dod_component=SERVICE_BRANCHES[2][1],
"dollar_value": dollar_value, data_transfers="Less than 100GB",
"dod_component": SERVICE_BRANCHES[2][1], expected_completion_date="Less than 1 month",
"data_transfers": "Less than 100GB", jedi_migration="yes",
"expected_completion_date": "Less than 1 month", num_software_systems=1,
"jedi_migration": "yes", number_user_sessions=2,
"num_software_systems": 1, average_daily_traffic=1,
"number_user_sessions": 2, engineering_assessment="yes",
"average_daily_traffic": 1, technical_support_team="yes",
"engineering_assessment": "yes", estimated_monthly_spend=100,
"technical_support_team": "yes", average_daily_traffic_gb=4,
"estimated_monthly_spend": 100, rationalization_software_systems="yes",
"average_daily_traffic_gb": 4, organization_providing_assistance="In-house staff",
"rationalization_software_systems": "yes", citizenship="United States",
"organization_providing_assistance": "In-house staff", designation="military",
}, phone_number="1234567890",
"information_about_you": { email_request=user.email,
"citizenship": "United States", fname_request=user.first_name,
"designation": "military", lname_request=user.last_name,
"phone_number": "1234567890", service_branch=SERVICE_BRANCHES[1][1],
"email_request": user.email, date_latest_training=datetime.datetime(
"fname_request": user.first_name, 2018, 8, 6, tzinfo=datetime.timezone.utc
"lname_request": user.last_name, ),
"service_branch": SERVICE_BRANCHES[1][1], )
"date_latest_training": "2018-08-06",
}, data = (
} request.initial_revision
if request.initial_revision is not None
else default_data
)
return RequestRevisionFactory.build(**data)
class PENumberFactory(factory.alchemy.SQLAlchemyModelFactory): class PENumberFactory(factory.alchemy.SQLAlchemyModelFactory):

View File

@ -2,9 +2,7 @@ from tests.factories import RequestFactory, UserFactory
MOCK_USER = UserFactory.build() MOCK_USER = UserFactory.build()
MOCK_REQUEST = RequestFactory.build( MOCK_REQUEST = RequestFactory.build(creator=MOCK_USER)
creator=MOCK_USER.id, body={"financial_verification": {"pe_id": "0203752A"}}
)
DOD_SDN_INFO = {"first_name": "ART", "last_name": "GARFUNKEL", "dod_id": "5892460358"} DOD_SDN_INFO = {"first_name": "ART", "last_name": "GARFUNKEL", "dod_id": "5892460358"}
DOD_SDN = f"CN={DOD_SDN_INFO['last_name']}.{DOD_SDN_INFO['first_name']}.G.{DOD_SDN_INFO['dod_id']},OU=OTHER,OU=PKI,OU=DoD,O=U.S. Government,C=US" DOD_SDN = f"CN={DOD_SDN_INFO['last_name']}.{DOD_SDN_INFO['first_name']}.G.{DOD_SDN_INFO['dod_id']},OU=OTHER,OU=PKI,OU=DoD,O=U.S. Government,C=US"

View File

@ -35,7 +35,7 @@ class TestPENumberInForm:
return user return user
def submit_data(self, client, user, data, extended=False): def submit_data(self, client, user, data, extended=False):
request = RequestFactory.create(creator=user, body=MOCK_REQUEST.body) request = RequestFactory.create(creator=user)
url_kwargs = {"request_id": request.id} url_kwargs = {"request_id": request.id}
if extended: if extended:
url_kwargs["extended"] = True url_kwargs["extended"] = True
@ -58,7 +58,7 @@ class TestPENumberInForm:
user = self._set_monkeypatches(monkeypatch) user = self._set_monkeypatches(monkeypatch)
data = dict(self.required_data) data = dict(self.required_data)
data["pe_id"] = MOCK_REQUEST.body["financial_verification"]["pe_id"] data["pe_id"] = "0101110F"
response = self.submit_data(client, user, data) response = self.submit_data(client, user, data)
@ -95,7 +95,7 @@ class TestPENumberInForm:
user_session(user) user_session(user)
data = dict(self.required_data) data = dict(self.required_data)
data["pe_id"] = MOCK_REQUEST.body["financial_verification"]["pe_id"] data["pe_id"] = "0101110F"
data["task_order_number"] = "1234" data["task_order_number"] = "1234"
response = self.submit_data(client, user, data) response = self.submit_data(client, user, data)
@ -112,7 +112,7 @@ class TestPENumberInForm:
user_session(user) user_session(user)
data = dict(self.required_data) data = dict(self.required_data)
data["pe_id"] = MOCK_REQUEST.body["financial_verification"]["pe_id"] data["pe_id"] = "0101110F"
data["task_order_number"] = MockEDAClient.MOCK_CONTRACT_NUMBER data["task_order_number"] = MockEDAClient.MOCK_CONTRACT_NUMBER
response = self.submit_data(client, user, data) response = self.submit_data(client, user, data)

View File

@ -1,5 +1,5 @@
import re import re
from tests.factories import RequestFactory, UserFactory from tests.factories import RequestFactory, UserFactory, RequestRevisionFactory
from atst.domain.roles import Roles from atst.domain.roles import Roles
from atst.domain.requests import Requests from atst.domain.requests import Requests
from urllib.parse import urlencode from urllib.parse import urlencode
@ -75,10 +75,12 @@ def test_nonexistent_request(client, user_session):
assert response.status_code == 404 assert response.status_code == 404
def test_creator_info_is_autopopulated(monkeypatch, client, user_session): def test_creator_info_is_autopopulated_for_existing_request(
monkeypatch, client, user_session
):
user = UserFactory.create() user = UserFactory.create()
user_session(user) user_session(user)
request = RequestFactory.create(creator=user, body={"information_about_you": {}}) request = RequestFactory.create(creator=user, initial_revision={})
response = client.get("/requests/new/2/{}".format(request.id)) response = client.get("/requests/new/2/{}".format(request.id))
body = response.data.decode() body = response.data.decode()
@ -104,7 +106,7 @@ def test_non_creator_info_is_not_autopopulated(monkeypatch, client, user_session
user = UserFactory.create() user = UserFactory.create()
creator = UserFactory.create() creator = UserFactory.create()
user_session(user) user_session(user)
request = RequestFactory.create(creator=creator, body={"information_about_you": {}}) request = RequestFactory.create(creator=creator, initial_revision={})
response = client.get("/requests/new/2/{}".format(request.id)) response = client.get("/requests/new/2/{}".format(request.id))
body = response.data.decode() body = response.data.decode()
@ -116,7 +118,7 @@ def test_non_creator_info_is_not_autopopulated(monkeypatch, client, user_session
def test_am_poc_causes_poc_to_be_autopopulated(client, user_session): def test_am_poc_causes_poc_to_be_autopopulated(client, user_session):
creator = UserFactory.create() creator = UserFactory.create()
user_session(creator) user_session(creator)
request = RequestFactory.create(creator=creator, body={}) request = RequestFactory.create(creator=creator, initial_revision={})
client.post( client.post(
"/requests/new/3/{}".format(request.id), "/requests/new/3/{}".format(request.id),
headers={"Content-Type": "application/x-www-form-urlencoded"}, headers={"Content-Type": "application/x-www-form-urlencoded"},
@ -129,7 +131,7 @@ def test_am_poc_causes_poc_to_be_autopopulated(client, user_session):
def test_not_am_poc_requires_poc_info_to_be_completed(client, user_session): def test_not_am_poc_requires_poc_info_to_be_completed(client, user_session):
creator = UserFactory.create() creator = UserFactory.create()
user_session(creator) user_session(creator)
request = RequestFactory.create(creator=creator, body={}) request = RequestFactory.create(creator=creator, initial_revision={})
response = client.post( response = client.post(
"/requests/new/3/{}".format(request.id), "/requests/new/3/{}".format(request.id),
headers={"Content-Type": "application/x-www-form-urlencoded"}, headers={"Content-Type": "application/x-www-form-urlencoded"},
@ -142,7 +144,7 @@ def test_not_am_poc_requires_poc_info_to_be_completed(client, user_session):
def test_not_am_poc_allows_user_to_fill_in_poc_info(client, user_session): def test_not_am_poc_allows_user_to_fill_in_poc_info(client, user_session):
creator = UserFactory.create() creator = UserFactory.create()
user_session(creator) user_session(creator)
request = RequestFactory.create(creator=creator, body={}) request = RequestFactory.create(creator=creator, initial_revision={})
poc_input = { poc_input = {
"am_poc": "no", "am_poc": "no",
"fname_poc": "test", "fname_poc": "test",
@ -177,13 +179,11 @@ def test_poc_autofill_checks_information_about_you_form_first(client, user_sessi
user_session(creator) user_session(creator)
request = RequestFactory.create( request = RequestFactory.create(
creator=creator, creator=creator,
body={ initial_revision=dict(
"information_about_you": { fname_request="Alice",
"fname_request": "Alice", lname_request="Adams",
"lname_request": "Adams", email_request="alice.adams@mail.mil",
"email_request": "alice.adams@mail.mil", ),
}
},
) )
poc_input = {"am_poc": "yes"} poc_input = {"am_poc": "yes"}
client.post( client.post(

View File

@ -15,7 +15,8 @@ def screens(app):
def test_stepthrough_request_form(user_session, screens, client): def test_stepthrough_request_form(user_session, screens, client):
user = UserFactory.create() user = UserFactory.create()
user_session(user) user_session(user)
mock_request = RequestFactory.stub() mock_request = RequestFactory.create()
mock_body = mock_request.body
def post_form(url, redirects=False, data=""): def post_form(url, redirects=False, data=""):
return client.post( return client.post(
@ -33,6 +34,7 @@ def test_stepthrough_request_form(user_session, screens, client):
# destination url # destination url
prelim_resp = post_form(req_url, data=data) prelim_resp = post_form(req_url, data=data)
response = post_form(req_url, True, data=data) response = post_form(req_url, True, data=data)
assert prelim_resp.status_code == 302
return (prelim_resp.headers.get("Location"), response) return (prelim_resp.headers.get("Location"), response)
# GET the initial form # GET the initial form
@ -44,7 +46,7 @@ def test_stepthrough_request_form(user_session, screens, client):
for i in range(1, len(screens)): for i in range(1, len(screens)):
# get appropriate form data to POST for this section # get appropriate form data to POST for this section
section = screens[i - 1]["section"] section = screens[i - 1]["section"]
post_data = urlencode(mock_request.body[section]) post_data = urlencode(mock_body[section])
effective_url, resp = take_a_step(i, req=req_id, data=post_data) effective_url, resp = take_a_step(i, req=req_id, data=post_data)
req_id = effective_url.split("/")[-1] req_id = effective_url.split("/")[-1]
@ -55,7 +57,7 @@ def test_stepthrough_request_form(user_session, screens, client):
# at this point, the real request we made and the mock_request bodies # at this point, the real request we made and the mock_request bodies
# should be equivalent # should be equivalent
assert Requests.get(user, req_id).body == mock_request.body assert Requests.get(user, req_id).body == mock_body
# finish the review and submit step # finish the review and submit step
client.post( client.post(