From 3d63dcde46ed2f3dbf5e9c6a95cd87571a95451c Mon Sep 17 00:00:00 2001 From: Oleksiy Veretiuk Date: Tue, 12 Dec 2017 16:43:48 +0200 Subject: [PATCH 1/4] Add validation for dgfID --- openprocurement/auctions/dgf/models.py | 9 +++++---- openprocurement/auctions/dgf/validation.py | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/openprocurement/auctions/dgf/models.py b/openprocurement/auctions/dgf/models.py index a23b5e9b..3ff2aa3a 100644 --- a/openprocurement/auctions/dgf/models.py +++ b/openprocurement/auctions/dgf/models.py @@ -329,7 +329,7 @@ class Options: 'invalid': view_role, 'edit_pending.verification': whitelist(), 'edit_invalid': whitelist(), - 'convoy': whitelist('status', 'items', 'documents') + 'convoy': whitelist('status', 'items', 'documents', 'dgfID') } awards = ListType(ModelType(Award), default=list()) @@ -407,9 +407,10 @@ def validate_value(self, data, value): raise ValidationError(u"currency should be only UAH") def validate_dgfID(self, data, dgfID): - if not dgfID: - if (data.get('revisions')[0].date if data.get('revisions') else get_now()) > DGF_ID_REQUIRED_FROM: - raise ValidationError(u'This field is required.') + if data['status'] not in ['draft', 'pending.verification', 'invalid']: + if not dgfID: + if (data.get('revisions')[0].date if data.get('revisions') else get_now()) > DGF_ID_REQUIRED_FROM: + raise ValidationError(u'This field is required.') def validate_dgfDecisionID(self, data, dgfDecisionID): if not dgfDecisionID: diff --git a/openprocurement/auctions/dgf/validation.py b/openprocurement/auctions/dgf/validation.py index f8de9443..0f827e54 100644 --- a/openprocurement/auctions/dgf/validation.py +++ b/openprocurement/auctions/dgf/validation.py @@ -31,6 +31,10 @@ def validate_patch_auction_data(request): request.errors.add('body', 'items', 'This field is not required.') request.errors.status = 422 return + elif request.context.dgfID: + request.errors.add('body', 'dgfID', 'This field is not required.') + request.errors.status = 422 + return request.validated['data'] = {'status': new_status} request.context.status = new_status return From ad87b8ea083a6a9d3a42774c2a7272ef7dd38f42 Mon Sep 17 00:00:00 2001 From: "oleksiy.veretiu" Date: Wed, 13 Dec 2017 08:34:40 +0000 Subject: [PATCH 2/4] Expand tests --- openprocurement/auctions/dgf/tests/tender.py | 45 +++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/openprocurement/auctions/dgf/tests/tender.py b/openprocurement/auctions/dgf/tests/tender.py index e84acb45..d51ec6f3 100644 --- a/openprocurement/auctions/dgf/tests/tender.py +++ b/openprocurement/auctions/dgf/tests/tender.py @@ -675,6 +675,7 @@ def test_create_auction_draft(self): def test_create_auction_draft_with_registry(self): data = self.initial_data.copy() items = data.pop('items') + dgf_id = data.pop('dgfID') data.update({'status': 'draft', 'merchandisingObject': uuid4().hex}) response = self.app.post_json('/auctions', {'data': data}) self.assertEqual(response.status, '201 Created') @@ -698,10 +699,11 @@ def test_create_auction_draft_with_registry(self): self.app.authorization = ('Basic', ('convoy', '')) response = self.app.patch_json('/auctions/{}'.format(auction['id']), - {'data': {'items': items}}) + {'data': {'items': items, 'dgfID': dgf_id}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.content_type, 'application/json') self.assertEqual(len(response.json['data']['items']), len(items)) + self.assertEqual(response.json['data']['dgfID'], dgf_id) response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'status': 'active.tendering'}}) self.assertEqual(response.status, '200 OK') @@ -1285,9 +1287,11 @@ def test_convoy_change_status(self): u'location': u'body', u'name': u'items'}]) - # Create auction with merchandisingObject and without items - data.update({'merchandisingObject': uuid4().hex}) + # Create auction with merchandisingObject and without items and without dgfID + data = self.initial_data.copy() + data.update({'status': 'draft', 'merchandisingObject': uuid4().hex}) items = data.pop('items') + dgf_id = data.pop('dgfID') response = self.app.post_json('/auctions', {'data': data}) self.assertEqual(response.status, '201 Created') auction = response.json['data'] @@ -1310,18 +1314,21 @@ def test_convoy_change_status(self): self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['data'], auction) - # Switch auction status 'pending.verification' -> 'active.tendering' via convoy without items + # Switch auction status 'pending.verification' -> 'active.tendering' via convoy without items and dgfID response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'status': 'active.tendering'}}, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [ {u'description': [u"This field is required."], - u'location': u'body', u'name': u'items'}]) + u'location': u'body', u'name': u'items'}, + {u'description': [u"This field is required."], + u'location': u'body', u'name': u'dgfID'}]) - # Add items via convoy - response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'items': items}}) + # Add items and dgfID via convoy + response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'items': items, 'dgfID': dgf_id}}) self.assertEqual(response.status, '200 OK') self.assertEqual(len(response.json['data']['items']), len(items)) + self.assertEqual(response.json['data']['dgfID'], dgf_id) # Switch auction status 'pending.verification' -> 'active.tendering' via convoy response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'status': 'active.tendering'}}) @@ -1386,6 +1393,30 @@ def test_convoy_change_status(self): self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['data'], auction) + # Create auction with merchandisingObject and without items and with dgfID + self.app.authorization = ('Basic', ('broker', '')) + data = self.initial_data.copy() + data.update({'status': 'draft', 'merchandisingObject': uuid4().hex}) + data.pop('items') + response = self.app.post_json('/auctions', {'data': data}) + self.assertEqual(response.status, '201 Created') + auction = response.json['data'] + owner_token = response.json['access']['token'] + + response = self.app.get('/auctions/{}'.format(auction['id'])) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.content_type, 'application/json') + self.assertEqual(response.json['data'], auction) + + # Switch auction status 'draft' -> 'pending.verification' via owner with dgfID + response = self.app.patch_json('/auctions/{}?acc_token={}'.format(auction['id'], owner_token), {'data': {'status': 'pending.verification'}}, + status=422) + self.assertEqual(response.status, '422 Unprocessable Entity') + self.assertEqual(response.json['status'], 'error') + self.assertEqual(response.json['errors'], [ + {u'description': u"This field is not required.", + u'location': u'body', u'name': u'dgfID'}]) + class AuctionProcessTest(BaseAuctionWebTest): #setUp = BaseWebTest.setUp From 59dbe848cfb6ebb72c4e605295b29565adb73883 Mon Sep 17 00:00:00 2001 From: "oleksiy.veretiu" Date: Wed, 13 Dec 2017 08:54:30 +0000 Subject: [PATCH 3/4] Resolve discussions --- openprocurement/auctions/dgf/models.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openprocurement/auctions/dgf/models.py b/openprocurement/auctions/dgf/models.py index 3ff2aa3a..62259392 100644 --- a/openprocurement/auctions/dgf/models.py +++ b/openprocurement/auctions/dgf/models.py @@ -407,10 +407,9 @@ def validate_value(self, data, value): raise ValidationError(u"currency should be only UAH") def validate_dgfID(self, data, dgfID): - if data['status'] not in ['draft', 'pending.verification', 'invalid']: - if not dgfID: - if (data.get('revisions')[0].date if data.get('revisions') else get_now()) > DGF_ID_REQUIRED_FROM: - raise ValidationError(u'This field is required.') + if not dgfID and data['status'] not in ['draft', 'pending.verification', 'invalid']: + if (data.get('revisions')[0].date if data.get('revisions') else get_now()) > DGF_ID_REQUIRED_FROM: + raise ValidationError(u'This field is required.') def validate_dgfDecisionID(self, data, dgfDecisionID): if not dgfDecisionID: From 0a97f7d63eab1aa2a36706ee4e5c9d98b2aa6afd Mon Sep 17 00:00:00 2001 From: Oleksiy Veretiuk Date: Mon, 18 Dec 2017 20:56:01 +0200 Subject: [PATCH 4/4] Use snitch function --- openprocurement/auctions/dgf/tests/tender.py | 235 +------------------ 1 file changed, 10 insertions(+), 225 deletions(-) diff --git a/openprocurement/auctions/dgf/tests/tender.py b/openprocurement/auctions/dgf/tests/tender.py index d51ec6f3..39e12070 100644 --- a/openprocurement/auctions/dgf/tests/tender.py +++ b/openprocurement/auctions/dgf/tests/tender.py @@ -7,6 +7,7 @@ from openprocurement.api.utils import ROUTE_PREFIX from openprocurement.api.models import get_now, SANDBOX_MODE, TZ +from openprocurement.auctions.core.tests.base import snitch from openprocurement.auctions.dgf.models import DGFOtherAssets, DGFFinancialAssets, DGF_ID_REQUIRED_FROM from openprocurement.auctions.dgf.tests.base import ( test_auction_data, @@ -68,6 +69,15 @@ def test_edit_role(self): class AuctionResourceTest(BaseWebTest): + + from openprocurement.auctions.core.tests.blanks.tender_blanks import ( + create_auction_draft_with_registry, + convoy_change_status + ) + + test_01_create_auction_draft_with_registry = snitch(create_auction_draft_with_registry) + test_02_convoy_change_status = snitch(convoy_change_status) + initial_data = test_auction_data initial_organization = test_organization @@ -672,51 +682,6 @@ def test_create_auction_draft(self): auction = response.json['data'] self.assertEqual(auction['status'], 'active.tendering') - def test_create_auction_draft_with_registry(self): - data = self.initial_data.copy() - items = data.pop('items') - dgf_id = data.pop('dgfID') - data.update({'status': 'draft', 'merchandisingObject': uuid4().hex}) - response = self.app.post_json('/auctions', {'data': data}) - self.assertEqual(response.status, '201 Created') - self.assertEqual(response.content_type, 'application/json') - auction = response.json['data'] - self.assertEqual(auction['status'], 'draft') - - response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'value': {'amount': 100}}}, status=403) - self.assertEqual(response.status, '403 Forbidden') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json['status'], 'error') - self.assertEqual(response.json['errors'], [ - {u'description': u"Can't update auction in current (draft) status", u'location': u'body', u'name': u'data'} - ]) - - response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'status': 'pending.verification'}}) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - auction = response.json['data'] - self.assertEqual(auction['status'], 'pending.verification') - - self.app.authorization = ('Basic', ('convoy', '')) - response = self.app.patch_json('/auctions/{}'.format(auction['id']), - {'data': {'items': items, 'dgfID': dgf_id}}) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(len(response.json['data']['items']), len(items)) - self.assertEqual(response.json['data']['dgfID'], dgf_id) - - response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'status': 'active.tendering'}}) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - auction = response.json['data'] - self.assertEqual(auction['status'], 'active.tendering') - - response = self.app.get('/auctions/{}'.format(auction['id'])) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - auction = response.json['data'] - self.assertEqual(auction['status'], 'active.tendering') - def test_create_auction(self): response = self.app.get('/auctions') self.assertEqual(response.status, '200 OK') @@ -1237,186 +1202,6 @@ def test_auction_Administrator_change(self): self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['data']['mode'], u'test') - def test_convoy_change_status(self): - # Check auctions list count - self.app.authorization = ('Basic', ('broker', '')) - response = self.app.get('/auctions') - self.assertEqual(response.status, '200 OK') - self.assertEqual(len(response.json['data']), 0) - - # Create auction without merchandisingObject - data = self.initial_data.copy() - data.update({'status': 'draft'}) - response = self.app.post_json('/auctions', {'data': data}) - self.assertEqual(response.status, '201 Created') - auction = response.json['data'] - owner_token = response.json['access']['token'] - - response = self.app.get('/auctions/{}'.format(auction['id'])) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json['data'], auction) - - # Switch auction status 'draft' -> 'pending.verification' via owner - # without merchandisingObject - response = self.app.patch_json('/auctions/{}?acc_token={}'.format(auction['id'], owner_token), {'data': {'status': 'pending.verification'}}, status=422) - self.assertEqual(response.status, '422 Unprocessable Entity') - self.assertEqual(response.json['status'], 'error') - self.assertEqual(response.json['errors'], [{u'description': u"Can't switch auction to status (pending.verification) without merchandisingObject", u'location': u'body', u'name': u'data'}]) - - # Create auction with items - data.update({'merchandisingObject': uuid4().hex}) - response = self.app.post_json('/auctions', {'data': data}) - self.assertEqual(response.status, '201 Created') - auction = response.json['data'] - owner_token = response.json['access']['token'] - - response = self.app.get('/auctions/{}'.format(auction['id'])) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json['data'], auction) - - # Switch auction status 'draft' -> 'pending.verification' via owner - # with items - response = self.app.patch_json('/auctions/{}?acc_token={}'.format(auction['id'], owner_token), - {'data': {'status': 'pending.verification'}}, status=422) - self.assertEqual(response.status, '422 Unprocessable Entity') - self.assertEqual(response.json['status'], 'error') - self.assertEqual(response.json['errors'], [ - {u'description': u"This field is not required.", - u'location': u'body', u'name': u'items'}]) - - - # Create auction with merchandisingObject and without items and without dgfID - data = self.initial_data.copy() - data.update({'status': 'draft', 'merchandisingObject': uuid4().hex}) - items = data.pop('items') - dgf_id = data.pop('dgfID') - response = self.app.post_json('/auctions', {'data': data}) - self.assertEqual(response.status, '201 Created') - auction = response.json['data'] - owner_token = response.json['access']['token'] - - response = self.app.get('/auctions/{}'.format(auction['id'])) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json['data'], auction) - - # Switch auction status 'draft' -> 'pending.verification' via owner - response = self.app.patch_json('/auctions/{}?acc_token={}'.format(auction['id'], owner_token), {'data': {'status': 'pending.verification'}}) - self.assertEqual(response.status, '200 OK') - auction = response.json['data'] - self.assertEqual(auction['status'], 'pending.verification') - - self.app.authorization = ('Basic', ('convoy', '')) - response = self.app.get('/auctions/{}'.format(auction['id'])) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json['data'], auction) - - # Switch auction status 'pending.verification' -> 'active.tendering' via convoy without items and dgfID - response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'status': 'active.tendering'}}, status=422) - self.assertEqual(response.status, '422 Unprocessable Entity') - self.assertEqual(response.json['status'], 'error') - self.assertEqual(response.json['errors'], [ - {u'description': [u"This field is required."], - u'location': u'body', u'name': u'items'}, - {u'description': [u"This field is required."], - u'location': u'body', u'name': u'dgfID'}]) - - # Add items and dgfID via convoy - response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'items': items, 'dgfID': dgf_id}}) - self.assertEqual(response.status, '200 OK') - self.assertEqual(len(response.json['data']['items']), len(items)) - self.assertEqual(response.json['data']['dgfID'], dgf_id) - - # Switch auction status 'pending.verification' -> 'active.tendering' via convoy - response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'status': 'active.tendering'}}) - self.assertEqual(response.status, '200 OK') - auction = response.json['data'] - self.assertEqual(auction['status'], 'active.tendering') - - response = self.app.get('/auctions/{}'.format(auction['id'])) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json['data'], auction) - - # Switch auction status 'active.tendering' -> 'invalid' via convoy - response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'status': 'invalid'}}, status=403) - self.assertEqual(response.status, '403 Forbidden') - self.assertEqual(response.json['status'], 'error') - self.assertEqual(response.json['errors'], [{u'description': u"Can't update auction in current (active.tendering) status", u'location': u'body', u'name': u'data'}]) - - # Create auction with merchandisingObject - self.app.authorization = ('Basic', ('broker', '')) - response = self.app.post_json('/auctions', {'data': data}) - self.assertEqual(response.status, '201 Created') - auction = response.json['data'] - owner_token = response.json['access']['token'] - - response = self.app.get('/auctions/{}'.format(auction['id'])) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json['data'], auction) - - # Switch auction status 'draft' -> 'pending.verification' via owner - response = self.app.patch_json('/auctions/{}?acc_token={}'.format(auction['id'], owner_token), {'data': {'status': 'pending.verification'}}) - self.assertEqual(response.status, '200 OK') - auction = response.json['data'] - self.assertEqual(auction['status'], 'pending.verification') - - self.app.authorization = ('Basic', ('convoy', '')) - response = self.app.get('/auctions/{}'.format(auction['id'])) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json['data'], auction) - - # Switch auction status 'pending.verification' -> 'invalid' via convoy - response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'status': 'invalid'}}) - self.assertEqual(response.status, '200 OK') - auction = response.json['data'] - self.assertEqual(auction['status'], 'invalid') - - response = self.app.get('/auctions/{}'.format(auction['id'])) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json['data'], auction) - - # Switch auction status 'invalid' -> 'active.tendering' via convoy - response = self.app.patch_json('/auctions/{}'.format(auction['id']), {'data': {'status': 'active.tendering'}}, status=403) - self.assertEqual(response.status, '403 Forbidden') - self.assertEqual(response.json['status'], 'error') - self.assertEqual(response.json['errors'], [{u'description': u"Can't update auction in current (invalid) status", u'location': u'body', u'name': u'data'}]) - - response = self.app.get('/auctions/{}'.format(auction['id'])) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json['data'], auction) - - # Create auction with merchandisingObject and without items and with dgfID - self.app.authorization = ('Basic', ('broker', '')) - data = self.initial_data.copy() - data.update({'status': 'draft', 'merchandisingObject': uuid4().hex}) - data.pop('items') - response = self.app.post_json('/auctions', {'data': data}) - self.assertEqual(response.status, '201 Created') - auction = response.json['data'] - owner_token = response.json['access']['token'] - - response = self.app.get('/auctions/{}'.format(auction['id'])) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json['data'], auction) - - # Switch auction status 'draft' -> 'pending.verification' via owner with dgfID - response = self.app.patch_json('/auctions/{}?acc_token={}'.format(auction['id'], owner_token), {'data': {'status': 'pending.verification'}}, - status=422) - self.assertEqual(response.status, '422 Unprocessable Entity') - self.assertEqual(response.json['status'], 'error') - self.assertEqual(response.json['errors'], [ - {u'description': u"This field is not required.", - u'location': u'body', u'name': u'dgfID'}]) - class AuctionProcessTest(BaseAuctionWebTest): #setUp = BaseWebTest.setUp