def test_brief_response_can_be_serialized(self): with self.app.app_context(): brief_response = BriefResponse(data={'foo': 'bar'}, brief=self.brief, supplier=self.supplier) db.session.add(brief_response) db.session.commit() with mock.patch('app.models.url_for') as url_for: url_for.side_effect = lambda *args, **kwargs: (args, kwargs) assert brief_response.serialize() == { 'id': brief_response.id, 'briefId': self.brief.id, 'supplierId': 0, 'supplierName': 'Supplier 0', 'createdAt': mock.ANY, 'foo': 'bar', 'links': { 'self': (('.get_brief_response', ), { 'brief_response_id': brief_response.id }), 'brief': (('.get_brief', ), { 'brief_id': self.brief.id }), 'supplier': (('.get_supplier', ), { 'supplier_id': 0 }), } }
def brief_responses(self, app, brief, suppliers): now = pendulum.now('utc') with app.app_context(): db.session.add( BriefResponse( id=1, brief_id=1, created_at=now, data={ 'respondToEmailAddress': '*****@*****.**' }, submitted_at=now, supplier_code=123, updated_at=now ) ) db.session.add( BriefResponse( id=2, brief_id=1, created_at=now, data={}, supplier_code=456, updated_at=now ) ) db.session.commit() yield db.session.query(BriefResponse).all()
def setup_dummy_brief_response(self, brief_id=None, supplier_code=0): with self.app.app_context(): brief_response = BriefResponse( data=example_listings.brief_response_data().example(), supplier_code=supplier_code, brief_id=brief_id or self.brief_id ) db.session.add(brief_response) db.session.commit() return brief_response.id
def brief_response(app): with app.app_context(): db.session.add( BriefResponse(id=1, brief_id=1, supplier_code=1, submitted_at=pendulum.now(), data={})) db.session.commit() yield BriefResponse.query.get(1)
def brief_responses(app, overview_briefs, suppliers): with app.app_context(): now = pendulum.now('utc') db.session.add( BriefResponse(id=1, brief_id=7, data={}, submitted_at=now, supplier_code=2)) db.session.add( BriefResponse(id=2, brief_id=8, data={}, submitted_at=now, supplier_code=2)) db.session.add( BriefResponse(id=3, brief_id=9, data={}, submitted_at=now, supplier_code=2)) db.session.add( BriefResponse(id=4, brief_id=9, data={}, submitted_at=now, supplier_code=2)) db.session.add( BriefResponse(id=5, brief_id=9, data={}, submitted_at=now, supplier_code=2)) db.session.commit() yield db.session.query(BriefResponse).all()
def test_can_not_close_published_opportunity_early_with_multiple_seller_responses( self, overview_briefs, suppliers, lot_slug): lot = lots_service.find(slug=lot_slug).one_or_none() brief = briefs_service.find(lot=lot, status='live').one_or_none() db.session.add( BriefResponse(id=6, brief_id=brief.id, data={}, supplier_code=3)) db.session.commit() can_close = can_close_opportunity_early(brief) assert can_close is False
def brief_responses(app, request, briefs, supplier_user): params = request.param if hasattr(request, 'param') else {} data = params['data'] if 'data' in params else {} with app.app_context(): db.session.add( BriefResponse(id=1, brief_id=1, supplier_code=supplier_user.supplier_code, data=data)) db.session.commit() yield BriefResponse.query.all()
def test_create_a_new_brief_response(self): with self.app.app_context(): brief_response = BriefResponse(data={}, brief=self.brief, supplier=self.supplier) db.session.add(brief_response) db.session.commit() assert brief_response.id is not None assert brief_response.supplier_id == 0 assert brief_response.brief_id == self.brief.id assert isinstance(brief_response.created_at, datetime) assert brief_response.data == {}
def buyer_dashboard_briefs(app, request, buyer_dashboard_users, supplier_user): with app.app_context(): for user in buyer_dashboard_users: for status in ['draft', 'live', 'closed']: brief = Brief( data=COMPLETE_DIGITAL_SPECIALISTS_BRIEF.copy(), framework=Framework.query.filter(Framework.slug == "digital-outcomes-and-specialists").first(), lot=Lot.query.filter(Lot.slug == 'digital-specialists').first(), published_at=None, closed_at=None, questions_closed_at=None, withdrawn_at=None ) db.session.add(brief) db.session.flush() if status == 'live': brief.published_at = utcnow() brief.questions_closed_at = utcnow().add(days=1) brief.closed_at = utcnow().add(days=2) elif status == 'closed': brief.published_at = utcnow().subtract(weeks=1) brief.questions_closed_at = utcnow().subtract(days=2) brief.closed_at = utcnow().subtract(days=1) brief_user = BriefUser( brief_id=brief.id, user_id=user.id ) db.session.add(brief_user) db.session.flush() # Link briefs with responses and work orders when they're live or closed if status == 'live' or status == 'closed': db.session.add(BriefResponse( brief_id=brief.id, supplier_code=supplier_user.supplier_code, data={} )) db.session.add(WorkOrder( brief_id=brief.id, supplier_code=supplier_user.supplier_code, created_at=utcnow(), data={} )) db.session.commit() yield Brief.query.all()
def setup_dummy_brief_response(self, brief_id=None, supplier_id=0, submitted_at=datetime(2016, 1, 2), award_details=None): brief_response = BriefResponse( data=example_listings.brief_response_data().example(), supplier_id=supplier_id, brief_id=brief_id or self.brief_id, submitted_at=submitted_at, award_details=award_details if award_details else {}) db.session.add(brief_response) db.session.commit() return brief_response.id
def test_update_outcome_scenarios( self, other_oc_brief_based, initial_brief_based, other_oc_data, initial_data, put_values, expected_status_code, expected_response_data, ): """ A number of arguments control the background context this test is run in and the parameters PUT to the endpoint. Not all of the combinations make sense together and a caller should not expect a test to pass with a nonsensical combination of arguments :param other_oc_brief_based: whether the "other", existing Outcome should be Brief-based as opposed to Direct Award-based :param initial_brief_based: whether the target Outcome should initially be set up to be Brief-based as opposed to Direct Award-based :param other_oc_data: field values to set up the "other" Outcome with, ``None`` for no "other" Outcome to be created :param initial_data: field values to initially set up the target Outcome with :param put_values: payload dictionary to be PUT to the target endpoint (without the ``outcome`` wrapper) :param expected_status_code: :param expected_response_data: """ user_id = self.setup_dummy_user(id=1, role='buyer') self.setup_dummy_suppliers(3) project = None search = None chosen_archived_service = other_archived_service = None if not (other_oc_brief_based and initial_brief_based): # create required objects for direct award-based Outcome self.setup_dummy_services(3, model=ArchivedService) project = DirectAwardProject( name="Lambay Island", users=[User.query.get(user_id)], ) db.session.add(project) search = DirectAwardSearch( project=project, created_by=user_id, active=True, search_url="http://nothing.nowhere", ) db.session.add(search) for archived_service in db.session.query(ArchivedService).filter( ArchivedService.service_id.in_(( "2000000000", "2000000001", ))).all(): search.archived_services.append(archived_service) chosen_archived_service, other_archived_service = search.archived_services[: 2] # else skip creating these to save time brief = None chosen_brief_response = other_brief_response = None if other_oc_brief_based or initial_brief_based: # create required objects for brief-based Outcome brief = self.setup_dummy_brief(status="closed", user_id=user_id, data={}) chosen_brief_response, other_brief_response = (BriefResponse( brief=brief, supplier_id=i, submitted_at=datetime.datetime.utcnow(), data={}, ) for i in ( 1, 2, )) db.session.add(chosen_brief_response) db.session.add(other_brief_response) # else skip creating these to save time other_outcome = None if other_oc_data is not None: # create "other" Outcome for our target one to potentially clash with other_outcome = Outcome( **({ "brief": brief } if other_oc_brief_based else { "direct_award_project": project }), **( { "result": other_oc_data.get("result", "awarded"), **({ "brief_response": other_brief_response, } if other_oc_brief_based else { "direct_award_search": search, "direct_award_archived_service": other_archived_service, }), } if other_oc_data.get( "result", "awarded") == "awarded" else { "result": other_oc_data["result"] }), **{ k: v for k, v in (other_oc_data or {}).items() if k not in ( "completed_at", "result", ) }, ) if "completed_at" in other_oc_data: other_outcome.completed_at = other_oc_data["completed_at"] db.session.add(other_outcome) # create our target Outcome in its initial state outcome = Outcome( **({ "brief": brief } if initial_brief_based else { "direct_award_project": project }), **( { "result": initial_data.get("result", "awarded"), **({ "brief_response": chosen_brief_response, } if initial_brief_based else { "direct_award_search": search, "direct_award_archived_service": chosen_archived_service, }), } if initial_data.get("result", "awarded") == "awarded" else { "result": initial_data["result"] }), **{ k: v for k, v in (initial_data or {}).items() if k not in ( "completed_at", "result", ) }, ) if "completed_at" in initial_data: # can only set completed_at after other fields have been set outcome.completed_at = initial_data["completed_at"] db.session.add(outcome) # must assign ids before we can lock project db.session.flush() if project: project.locked_at = datetime.datetime.now() # make a concrete note of these so we don't have to fetch them back from the database after the request, # potentially getting back values which have been inadvertantly changed outcome_external_id = outcome.external_id project_external_id = project and project.external_id search_id = search and search.id chosen_archived_service_id = chosen_archived_service and chosen_archived_service.id chosen_archived_service_service_id = chosen_archived_service and chosen_archived_service.service_id brief_id = brief and brief.id chosen_brief_response_id = chosen_brief_response and chosen_brief_response.id audit_event_count = AuditEvent.query.count() db.session.commit() # keep an nice concrete representation for later comparison outcome_serialization_before = outcome.serialize() res = self.client.put( f"/outcomes/{outcome.external_id}", data=json.dumps({ "updated_by": "*****@*****.**", "outcome": put_values, }), content_type="application/json", ) assert res.status_code == expected_status_code response_data = json.loads(res.get_data()) assert response_data == expected_response_data # allow these to be re-used in this session, "refreshed" db.session.add_all(x for x in ( outcome, project, search, chosen_archived_service, ) if x is not None) db.session.expire_all() if res.status_code != 200: # assert change wasn't made, audit event wasn't added assert outcome.serialize() == outcome_serialization_before assert AuditEvent.query.count() == audit_event_count else: # an additional check of values we should be able to figure out the "correct" values for assert response_data == { "outcome": { "id": outcome_external_id, "result": initial_data.get("result", "awarded"), "completed": ( bool(outcome_serialization_before.get("completedAt")) or put_values.get("completed") is True ), "completedAt": ( outcome_serialization_before.get("completedAt") or ( AnyStringMatching(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z") if put_values.get("completed") else None ) ), **({ "resultOfFurtherCompetition": { "brief": { "id": brief_id, }, **({ "briefResponse": { "id": chosen_brief_response_id, }, } if initial_data.get("result", "awarded") == "awarded" else {}), }, } if initial_brief_based else { "resultOfDirectAward": { "project": { "id": project_external_id, }, **({ "search": { "id": search_id, }, "archivedService": { "id": chosen_archived_service_id, "service": { "id": chosen_archived_service_service_id, }, }, } if initial_data.get("result", "awarded") == "awarded" else {}) }, }), **({"award": AnySupersetOf({})} if initial_data.get("result", "awarded") == "awarded" else {}), } } # check changes actually got committed assert response_data == { "outcome": outcome.serialize(), } # check audit event(s) were saved expect_complete_audit_event = put_values.get( "completed") is True and not initial_data.get("completed_at") n_expected_new_audit_events = 2 if expect_complete_audit_event else 1 assert AuditEvent.query.count( ) == audit_event_count + n_expected_new_audit_events # grab those most recent (1 or) 2 audit events from the db, re-sorting them to be in a predictable order - # we don't care whether the complete_outcome or update_outcome comes out of the db first audit_events = sorted( db.session.query(AuditEvent).order_by( desc(AuditEvent.created_at), desc(AuditEvent.id), )[:n_expected_new_audit_events], key=lambda ae: ae.type, reverse=True, ) assert audit_events[0].type == "update_outcome" assert audit_events[0].object is outcome assert audit_events[0].acknowledged is False assert audit_events[0].acknowledged_at is None assert not audit_events[0].acknowledged_by assert audit_events[0].user == "*****@*****.**" assert audit_events[0].data == put_values if expect_complete_audit_event: assert audit_events[1].type == "complete_outcome" assert audit_events[1].created_at == audit_events[ 0].created_at == outcome.completed_at assert audit_events[1].object is outcome assert audit_events[1].acknowledged is False assert audit_events[1].acknowledged_at is None assert not audit_events[1].acknowledged_by assert audit_events[1].user == "*****@*****.**" assert audit_events[1].data == {}
def test_whitespace_is_stripped_from_brief_response_data(self): brief_response = BriefResponse(data={}) brief_response.data = {'foo': ' bar ', 'bar': ['', ' foo']} assert brief_response.data == {'foo': 'bar', 'bar': ['foo']}
def test_nulls_are_removed_from_brief_response_data(self): brief_response = BriefResponse(data={}) brief_response.data = {'foo': 'bar', 'bar': None} assert brief_response.data == {'foo': 'bar'}
def test_foreign_fields_are_removed_from_brief_response_data(self): brief_response = BriefResponse(data={}) brief_response.data = {'foo': 'bar', 'briefId': 5, 'supplierId': 100} assert brief_response.data == {'foo': 'bar'}
def setup_outcomes(self): user_id = self.setup_dummy_user(id=1, role='buyer') self.setup_dummy_suppliers(5) # create required objects for direct award-based Outcome self.setup_dummy_services(5, model=ArchivedService) # # create required objects for direct-award-related Outcomes # projects = tuple( DirectAwardProject( name=name, users=[User.query.get(user_id)], ) for name in ("alumno optimo", "palmam ferenti", "vere dignum et iustum est",) ) db.session.add_all(projects) searches = tuple( DirectAwardSearch( project=project, created_by=user_id, active=True, search_url="http://nothing.nowhere", ) for project in projects ) db.session.add_all(searches) for i, search in enumerate(searches): for archived_service in db.session.query(ArchivedService).filter( ArchivedService.service_id.in_([str(j) for j in range(2000000000 + i, 2000000000 + i + 3)]) ).all(): search.archived_services.append(archived_service) # # create required objects for Brief-related Outcomes # briefs = tuple( self.setup_dummy_brief(status="closed", user_id=user_id, data={}) for _ in range(4) ) db.session.add_all(briefs) # increasingly many BriefResponses for each Brief in `briefs` brief_responses = tuple(BriefResponse( brief=brief, supplier_id=j, submitted_at=datetime.datetime.utcnow(), data={}, ) for j in range(i) for i, brief in enumerate(briefs)) db.session.add_all(brief_responses) outcomes = ( Outcome( external_id=100000000, direct_award_project=searches[0].project, direct_award_search=searches[0], direct_award_archived_service=searches[0].archived_services[0], result="awarded", start_date=datetime.date(2006, 2, 2), end_date=datetime.date(2006, 3, 3), awarding_organisation_name="Omnium Gatherum", award_value=81396, completed_at=datetime.datetime(2005, 10, 10, 10, 10, 10), ), Outcome( external_id=100000005, direct_award_project=searches[0].project, direct_award_search=searches[0], direct_award_archived_service=searches[0].archived_services[1], result="awarded", start_date=datetime.date(2006, 4, 4), awarding_organisation_name="Nisus Formativus", ), Outcome( external_id=100000002, direct_award_project=searches[0].project, result="none-suitable", ), Outcome( external_id=100000011, direct_award_project=searches[1].project, result="none-suitable", ), Outcome( external_id=100000004, direct_award_project=searches[2].project, result="cancelled", completed_at=datetime.datetime(2005, 10, 9, 9, 9, 9), ), Outcome( external_id=100000001, brief=briefs[0], result="cancelled", completed_at=datetime.datetime(2005, 5, 5, 5, 5, 5), ), Outcome( external_id=100000008, brief=briefs[0], result="cancelled", ), Outcome( external_id=100000012, brief=briefs[1], brief_response=briefs[1].brief_responses[0], result="awarded", start_date=datetime.date(2010, 1, 1), end_date=datetime.date(2011, 8, 8), awarding_organisation_name="Viridum Toxicum", award_value=81396, completed_at=datetime.datetime(2005, 11, 11, 11, 11, 11), ), Outcome( external_id=100000006, brief=briefs[1], brief_response=briefs[1].brief_responses[0], result="awarded", award_value=83300, ), Outcome( external_id=100000009, brief=briefs[2], result="none-suitable", completed_at=datetime.datetime(2005, 10, 10, 10, 11, 11), ), Outcome( external_id=100000013, brief=briefs[2], brief_response=briefs[2].brief_responses[0], result="awarded", start_date=datetime.date(2011, 1, 1), end_date=datetime.date(2011, 1, 2), award_value=3072, ), Outcome( external_id=100000003, brief=briefs[2], brief_response=briefs[2].brief_responses[1], result="awarded", ), Outcome( external_id=100000007, brief=briefs[3], result="none-suitable", ), Outcome( external_id=100000010, brief=briefs[3], brief_response=briefs[3].brief_responses[0], result="awarded", start_date=datetime.date(2006, 1, 1), end_date=datetime.date(2008, 1, 1), awarding_organisation_name="Lacus Mortis", award_value=4386035, completed_at=datetime.datetime(2006, 1, 1, 1, 1, 1), ), ) db.session.add_all(outcomes) db.session.commit()
def digital_professional_responses(self, app, briefs, brief_response_data, suppliers): with app.app_context(): brief_response = BriefResponse(id=1, brief_id=1, data=brief_response_data, submitted_at=pendulum.now(), supplier_code=1) brief_response.data['specialistName'] = 'Klay Thompson' db.session.add(brief_response) brief_response = BriefResponse(id=2, brief_id=1, data=brief_response_data, submitted_at=pendulum.now(), supplier_code=1) brief_response.data['specialistName'] = 'steph curry' db.session.add(brief_response) brief_response = BriefResponse(id=3, brief_id=1, data=brief_response_data, submitted_at=pendulum.now(), supplier_code=1) brief_response.data['specialistName'] = 'kevin durant' db.session.add(brief_response) brief_response = BriefResponse(id=4, brief_id=1, data=brief_response_data, submitted_at=pendulum.now(), supplier_code=2) db.session.add(brief_response) brief_response = BriefResponse(id=5, brief_id=1, data=brief_response_data, submitted_at=pendulum.now(), supplier_code=3) db.session.add(brief_response) brief_response = BriefResponse(id=6, brief_id=1, data=brief_response_data, submitted_at=pendulum.now(), supplier_code=4) db.session.add(brief_response) db.session.commit() yield db.session.query(BriefResponse).all()