From dffb0b98bfbfcd11e60186414b9f93d0cb7e4eb3 Mon Sep 17 00:00:00 2001 From: Anna Manko Date: Tue, 17 Mar 2020 17:56:15 +0200 Subject: [PATCH] Added document types and a documentOf option, tests for them. --- src/openprocurement/api/models.py | 5 ++ .../tender/belowthreshold/tests/award.py | 4 + .../belowthreshold/tests/award_blanks.py | 78 +++++++++++++++++- .../tender/belowthreshold/tests/base.py | 9 +++ .../tender/belowthreshold/tests/document.py | 4 + .../belowthreshold/tests/document_blanks.py | 79 +++++++++++++++++++ .../tender/cfaselectionua/tests/award.py | 8 +- .../tender/cfaselectionua/tests/document.py | 4 + .../tests/stage1/document.py | 5 +- .../tests/stage1/document_blanks.py | 79 +++++++++++++++++++ .../tests/stage2/document.py | 6 +- src/openprocurement/tender/core/models.py | 6 +- .../tender/openuadefense/tests/document.py | 2 + 13 files changed, 282 insertions(+), 7 deletions(-) diff --git a/src/openprocurement/api/models.py b/src/openprocurement/api/models.py index 568b8e4a9a..ca075350a2 100644 --- a/src/openprocurement/api/models.py +++ b/src/openprocurement/api/models.py @@ -520,6 +520,11 @@ class Options: "registerExtract", "registerFiscal", "winningBid", + "contractTemplate", + "contractSchema", + "contractForm", + "contractData", + "contractProforma", ] ) title = StringType(required=True) # A title of the document. diff --git a/src/openprocurement/tender/belowthreshold/tests/award.py b/src/openprocurement/tender/belowthreshold/tests/award.py index 5e4aff11f8..248d9e5c51 100644 --- a/src/openprocurement/tender/belowthreshold/tests/award.py +++ b/src/openprocurement/tender/belowthreshold/tests/award.py @@ -64,6 +64,8 @@ # TenderAwardDocumentResourceTest not_found_award_document, create_tender_award_document, + create_tender_award_with_the_invalid_document_type, + put_tender_json_award_document_of_document, put_tender_award_document, patch_tender_award_document, create_award_document_bot, @@ -95,7 +97,9 @@ class TenderAwardComplaintResourceTestMixin(object): class TenderAwardDocumentResourceTestMixin(object): test_not_found_award_document = snitch(not_found_award_document) test_create_tender_award_document = snitch(create_tender_award_document) + test_create_tender_award_with_the_invalid_document_type = snitch(create_tender_award_with_the_invalid_document_type) test_put_tender_award_document = snitch(put_tender_award_document) + test_put_tender_json_award_document_of_document = snitch(put_tender_json_award_document_of_document) test_patch_tender_award_document = snitch(patch_tender_award_document) test_create_award_document_bot = snitch(create_award_document_bot) test_patch_not_author = snitch(patch_not_author) diff --git a/src/openprocurement/tender/belowthreshold/tests/award_blanks.py b/src/openprocurement/tender/belowthreshold/tests/award_blanks.py index f040dc25c6..f418a2d398 100644 --- a/src/openprocurement/tender/belowthreshold/tests/award_blanks.py +++ b/src/openprocurement/tender/belowthreshold/tests/award_blanks.py @@ -4,10 +4,11 @@ from webtest import AppError import mock import dateutil.parser - +import re +import ast from openprocurement.api.utils import get_now from openprocurement.tender.belowthreshold.tests.base import ( - test_organization, test_draft_claim, test_claim, test_cancellation + test_organization, test_draft_claim, test_claim, test_cancellation, test_tender_document_data ) @@ -591,6 +592,79 @@ def create_tender_award_no_scale(self): self.assertNotIn("scale", response.json["data"]["suppliers"][0]) +def create_tender_award_with_the_invalid_document_type(self): + document_data = test_tender_document_data + document_data["url"] = self.generate_docservice_url() + document_data["hash"] = "md5:" + "0" * 32 + document_data["documentType"] = "smth" + + response = self.app.post( + "/tenders/{}/awards/{}/documents?acc_token={}".format(self.tender_id, self.award_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + self.assertEqual(u"name.doc", response.json["data"]["title"]) + response = self.app.patch_json( + "/tenders/{}/awards/{}/documents/{}?acc_token={}".format(self.tender_id, self.award_id,doc_id, self.tender_token), + {"data": {"documentType": "smth"}}, + status=422, + ) + self.assertEqual(response.status, "422 Unprocessable Entity") + self.assertEqual(response.content_type, "application/json") + response_doctype_dict = re.findall(r"\[.*\]",response.json["errors"][0]["description"][0])[0] + response_doctype_dict = ast.literal_eval(response_doctype_dict) + response_doctype_dict = [n.strip() for n in response_doctype_dict] + self.assertListEqual( + response_doctype_dict, + ["tenderNotice","awardNotice","contractNotice","notice","biddingDocuments","technicalSpecifications", + "evaluationCriteria","clarifications","shortlistedFirms","riskProvisions","billOfQuantity","bidders", + "conflictOfInterest","debarments","evaluationReports","winningBid","complaints","contractSigned", + "contractArrangements","contractSchedule","contractAnnexe","contractGuarantees","subContract", + "eligibilityCriteria","contractProforma","commercialProposal","qualificationDocuments", + "eligibilityDocuments","registerExtract","registerFiscal","winningBid","contractTemplate", + "contractSchema","contractForm","contractData","contractProforma"]) + + +def put_tender_json_award_document_of_document(self): + response = self.app.post( + "/tenders/{}/awards/{}/documents?acc_token={}".format(self.tender_id, self.award_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + document_id = response.json["data"]["id"] + response = self.app.patch_json( + "/tenders/{}/awards/{}/documents/{}?acc_token={}".format(self.tender_id, self.award_id,document_id, self.tender_token), + {"data": { + "title": u"укр.doc", + "url": self.generate_docservice_url(), + "hash": "md5:" + "0" * 32, + "format": "application/msword", + "documentOf": "document", + "relatedItem": "0"*32, + }}, status=422 + ) + self.assertEqual(response.status, "422 Unprocessable Entity") + self.assertEqual(response.content_type, "application/json") + self.assertEqual( + response.json["errors"], + [ + { + u"location": u"body", + u"name": u"relatedItem", + u"description": [ + + u'relatedItem should be one of documents' + ] + } + ] + ) + + # TenderLotAwardResourceTest diff --git a/src/openprocurement/tender/belowthreshold/tests/base.py b/src/openprocurement/tender/belowthreshold/tests/base.py index 01b66ab0c8..ad2bfb6a25 100644 --- a/src/openprocurement/tender/belowthreshold/tests/base.py +++ b/src/openprocurement/tender/belowthreshold/tests/base.py @@ -84,6 +84,15 @@ "procurementMethodType": "belowThreshold", "milestones": test_milestones, } + +test_tender_document_data = { + "url": "http://ds.prozorro.local/get/b97562e3f33c493297fd14dd6d8c50f0?KeyID=a8968c46&Signature=3OV7QC7f%2ByfcGTvpy0tf%2FaM%2BFRI6kkg1ImfEJlfAx5qi%2FLY7IIj7TFqtxgaPrzdd%2BWIOCe3O5Q7WhXkOdCB9CQ%3D%3D", + "documentType":"tenderNotice", + "title": "Notice.pdf", + "hash": "md5:00000000000000000000000000000000", + "format": "application/pdf" +} + if SANDBOX_MODE: test_tender_data["procurementMethodDetails"] = "quick, accelerator=1440" test_features_tender_data = test_tender_data.copy() diff --git a/src/openprocurement/tender/belowthreshold/tests/document.py b/src/openprocurement/tender/belowthreshold/tests/document.py index 9f6960bae1..28d68918a9 100644 --- a/src/openprocurement/tender/belowthreshold/tests/document.py +++ b/src/openprocurement/tender/belowthreshold/tests/document.py @@ -11,6 +11,8 @@ create_tender_document, put_tender_document, patch_tender_document, + create_document_with_the_invalid_document_type, + put_tender_json_document_of_document, # TenderDocumentWithDSResourceTest create_tender_document_error, create_tender_document_json_invalid, @@ -33,6 +35,8 @@ class TenderDocumentWithDSResourceTestMixin(object): test_create_tender_document_json_invalid = snitch(create_tender_document_json_invalid) test_create_tender_document_json = snitch(create_tender_document_json) test_put_tender_document_json = snitch(put_tender_document_json) + test_put_tender_json_document_of_document = snitch(put_tender_json_document_of_document) + test_create_document_with_the_invalid_document_type = snitch(create_document_with_the_invalid_document_type) class TenderDocumentResourceTest(TenderContentWebTest, TenderDocumentResourceTestMixin): diff --git a/src/openprocurement/tender/belowthreshold/tests/document_blanks.py b/src/openprocurement/tender/belowthreshold/tests/document_blanks.py index ae12c06efb..1c3a45d5b7 100644 --- a/src/openprocurement/tender/belowthreshold/tests/document_blanks.py +++ b/src/openprocurement/tender/belowthreshold/tests/document_blanks.py @@ -2,8 +2,13 @@ from email.header import Header # TenderDocumentResourceTest +import re +import ast from mock import patch +from copy import deepcopy from openprocurement.tender.core.tests.base import bad_rs_request, srequest +from openprocurement.tender.belowthreshold.tests.base import test_tender_document_data +from openprocurement.api.models import Document as BaseDocument def not_found(self): @@ -888,3 +893,77 @@ def lot_patch_tender_document_json_items_none(self): errors = {error["name"]: error["description"] for error in response.json["errors"]} self.assertEqual(errors["documents"][0], {"relatedItem": ["relatedItem should be one of items"]}) + + +def put_tender_json_document_of_document(self): + document_data = deepcopy(test_tender_document_data) + document_data["url"] = self.generate_docservice_url() + document_data["hash"] = "md5:" + "0" * 32 + document_data["documentType"] = "tenderNotice" + + response = self.app.post_json("/tenders/{}/documents?acc_token={}".format( + self.tender_id, self.tender_token),{"data":document_data}, status=201) + + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + document_id = response.json["data"]["id"] + + response = self.app.post_json( + "/tenders/{}/documents?acc_token={}".format(self.tender_id, self.tender_token), + { + "data": { + "title": u"укр.doc", + "url": self.generate_docservice_url(), + "hash": "md5:" + "0" * 32, + "format": "application/msword", + "documentOf": "document", + "relatedItem": document_id, + } + }, + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + + response = self.app.post_json( + "/tenders/{}/documents?acc_token={}".format(self.tender_id, self.tender_token), + {"data": { + "title": u"укр.doc", + "url": self.generate_docservice_url(), + "hash": "md5:" + "0" * 32, + "format": "application/msword", + "documentOf": "document", + "relatedItem": "0"*32, + }}, status=422 + ) + self.assertEqual(response.status, "422 Unprocessable Entity") + self.assertEqual(response.content_type, "application/json") + self.assertEqual( + response.json["errors"], + [ + { + u"location": u"body", + u"name": u"relatedItem", + u"description": [ + + u'relatedItem should be one of documents' + ] + } + ] + ) + +def create_document_with_the_invalid_document_type(self): + """ + A test checks if errors raise in case of processing document with the invalid document type (documentType field). + """ + document_data = deepcopy(test_tender_document_data) + document_data["url"] = self.generate_docservice_url() + document_data["hash"] = "md5:" + "0" * 32 + document_data["documentType"] = "smth" + + response = self.app.post_json("/tenders/{}/documents?acc_token={}".format( + self.tender_id, self.tender_token),{"data":document_data}, status=422) + self.assertEqual(response.status, "422 Unprocessable Entity") + self.assertEqual(response.content_type, "application/json") + response_doctype_dict = re.findall(r"\[.*\]",response.json["errors"][0]["description"][0])[0] + response_doctype_dict = ast.literal_eval(response_doctype_dict) + response_doctype_dict = [n.strip() for n in response_doctype_dict] diff --git a/src/openprocurement/tender/cfaselectionua/tests/award.py b/src/openprocurement/tender/cfaselectionua/tests/award.py index 094debba84..2f847685dd 100644 --- a/src/openprocurement/tender/cfaselectionua/tests/award.py +++ b/src/openprocurement/tender/cfaselectionua/tests/award.py @@ -3,7 +3,11 @@ from copy import deepcopy from openprocurement.api.tests.base import snitch -from openprocurement.tender.belowthreshold.tests.award_blanks import patch_tender_lot_award_lots_none +from openprocurement.tender.belowthreshold.tests.award_blanks import ( + patch_tender_lot_award_lots_none, + put_tender_json_award_document_of_document, + create_tender_award_with_the_invalid_document_type, +) from openprocurement.tender.cfaselectionua.adapters.configurator import TenderCfaSelectionUAConfigurator from openprocurement.tender.cfaselectionua.tests.base import ( TenderContentWebTest, @@ -53,6 +57,8 @@ class TenderAwardResourceTestMixin(object): class TenderAwardDocumentResourceTestMixin(object): test_not_found_award_document = snitch(not_found_award_document) test_create_tender_award_document = snitch(create_tender_award_document) + test_create_tender_award_with_the_invalid_document_type = snitch(create_tender_award_with_the_invalid_document_type) + test_put_tender_json_award_document_of_document = snitch(put_tender_json_award_document_of_document) test_put_tender_award_document = snitch(put_tender_award_document) test_patch_tender_award_document = snitch(patch_tender_award_document) test_create_award_document_bot = snitch(create_award_document_bot) diff --git a/src/openprocurement/tender/cfaselectionua/tests/document.py b/src/openprocurement/tender/cfaselectionua/tests/document.py index d247628e5e..0feca6c1f7 100644 --- a/src/openprocurement/tender/cfaselectionua/tests/document.py +++ b/src/openprocurement/tender/cfaselectionua/tests/document.py @@ -10,6 +10,8 @@ create_tender_document_json_invalid, create_tender_document_json, put_tender_document_json, + put_tender_json_document_of_document, + create_document_with_the_invalid_document_type, ) from openprocurement.tender.cfaselectionua.tests.document_blanks import ( @@ -34,6 +36,8 @@ class TenderDocumentWithDSResourceTestMixin(object): test_create_tender_document_json_invalid = snitch(create_tender_document_json_invalid) test_create_tender_document_json = snitch(create_tender_document_json) test_put_tender_document_json = snitch(put_tender_document_json) + test_put_tender_json_document_of_document = snitch(put_tender_json_document_of_document) + test_create_document_with_the_invalid_document_type = snitch(create_document_with_the_invalid_document_type) class TenderDocumentResourceTest(TenderContentWebTest, TenderDocumentResourceTestMixin): diff --git a/src/openprocurement/tender/competitivedialogue/tests/stage1/document.py b/src/openprocurement/tender/competitivedialogue/tests/stage1/document.py index 1845a6349f..c9385e5739 100644 --- a/src/openprocurement/tender/competitivedialogue/tests/stage1/document.py +++ b/src/openprocurement/tender/competitivedialogue/tests/stage1/document.py @@ -12,6 +12,8 @@ from openprocurement.tender.competitivedialogue.tests.stage1.document_blanks import ( put_tender_document, patch_tender_document, + put_tender_json_document_of_document, + create_document_with_the_invalid_document_type, ) # _____________________________________________________________________ @@ -61,7 +63,8 @@ class DialogUADocumentResourceTest(BaseCompetitiveDialogUAContentWebTest, Tender test_put_tender_document = snitch(put_tender_document) test_patch_tender_document = snitch(patch_tender_document) - + test_put_tender_json_document_of_document =snitch(put_tender_json_document_of_document) + test_create_document_with_the_invalid_document_type = snitch(create_document_with_the_invalid_document_type) class DialogUADocumentWithDSResourceTest(DialogUADocumentResourceTest): docservice = True diff --git a/src/openprocurement/tender/competitivedialogue/tests/stage1/document_blanks.py b/src/openprocurement/tender/competitivedialogue/tests/stage1/document_blanks.py index 0d21682b24..82eb5ab5bf 100644 --- a/src/openprocurement/tender/competitivedialogue/tests/stage1/document_blanks.py +++ b/src/openprocurement/tender/competitivedialogue/tests/stage1/document_blanks.py @@ -1,5 +1,9 @@ # -*- coding: utf-8 -*- from email.header import Header +import re +import ast +from openprocurement.tender.belowthreshold.tests.base import test_tender_document_data +from openprocurement.api.models import Document as BaseDocument # DialogEUDocumentResourceTest @@ -244,3 +248,78 @@ def patch_tender_document(self): self.assertEqual(response.content_type, "application/json") self.assertEqual(doc_id, response.json["data"]["id"]) self.assertEqual("document description", response.json["data"]["description"]) + + +def put_tender_json_document_of_document(self): + response = self.app.post( + "/tenders/{}/documents?acc_token={}".format(self.tender_id, self.tender_token), + upload_files=[("file", str(Header(u"укр.doc", "utf-8")), "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + old_doc_id = response.json["data"]["id"] + + response = self.app.post( + "/tenders/{}/documents?acc_token={}".format(self.tender_id, self.tender_token), + upload_files=[("file", str(Header(u"укр.doc", "utf-8")), "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + self.assertEqual(u"укр.doc", response.json["data"]["title"]) + + response = self.app.patch_json( + "/tenders/{}/documents/{}?acc_token={}".format(self.tender_id, doc_id, self.tender_token), + {"data": {"documentOf": "document", "relatedItem": doc_id}}, + status=200, + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + response = self.app.patch_json( + "/tenders/{}/documents/{}?acc_token={}".format(self.tender_id, doc_id, self.tender_token), + {"data": {"documentOf": "document", "relatedItem": "0"*32,}}, + status=422, + ) + self.assertEqual(response.status, "422 Unprocessable Entity") + self.assertEqual(response.content_type, "application/json") + self.assertEqual( + response.json["errors"], + [ + { + u"location": u"body", + u"name": u"relatedItem", + u"description": [ + + u'relatedItem should be one of documents' + ] + } + ] + ) + + +def create_document_with_the_invalid_document_type(self): + """ + A test checks if errors raise in case of processing document with the invalid document type (documentType field). + """ + response = self.app.post( + "/tenders/{}/documents?acc_token={}".format(self.tender_id, self.tender_token), + upload_files=[("file", str(Header(u"укр.doc", "utf-8")), "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + self.assertEqual(u"укр.doc", response.json["data"]["title"]) + + # Try connect document with lot, without description in params + response = self.app.patch_json( + "/tenders/{}/documents/{}?acc_token={}".format(self.tender_id, doc_id, self.tender_token), + {"data": {"documentType": "smth"}}, + status=422, + ) + self.assertEqual(response.status, "422 Unprocessable Entity") + self.assertEqual(response.content_type, "application/json") + response_doctype_dict = re.findall(r"\[.*\]",response.json["errors"][0]["description"][0])[0] + response_doctype_dict = ast.literal_eval(response_doctype_dict) + response_doctype_dict = [n.strip() for n in response_doctype_dict] diff --git a/src/openprocurement/tender/competitivedialogue/tests/stage2/document.py b/src/openprocurement/tender/competitivedialogue/tests/stage2/document.py index c808b634a3..e4933e3e6d 100644 --- a/src/openprocurement/tender/competitivedialogue/tests/stage2/document.py +++ b/src/openprocurement/tender/competitivedialogue/tests/stage2/document.py @@ -12,6 +12,8 @@ from openprocurement.tender.competitivedialogue.tests.stage1.document_blanks import ( put_tender_document, patch_tender_document, + put_tender_json_document_of_document, + create_document_with_the_invalid_document_type, ) # _____________________________________________________________________ @@ -48,7 +50,7 @@ class TenderStage2EUDocumentResourceTest(BaseCompetitiveDialogEUStage2ContentWeb test_put_tender_document = snitch(put_tender_document) test_patch_tender_document = snitch(patch_tender_document) - + test_create_document_with_the_invalid_document_type = snitch(create_document_with_the_invalid_document_type) class TenderStage2DocumentWithDSResourceTest(TenderStage2EUDocumentResourceTest, TenderDocumentResourceTestMixin): docservice = True @@ -65,7 +67,7 @@ class TenderStage2UADocumentResourceTest(BaseCompetitiveDialogUAStage2ContentWeb test_put_tender_document = snitch(put_tender_document) test_patch_tender_document = snitch(patch_tender_document) - + test_put_tender_json_document_of_document = snitch(put_tender_json_document_of_document) class TenderStage2UADocumentWithDSResourceTest(TenderStage2UADocumentResourceTest, TenderDocumentResourceTestMixin): docservice = True diff --git a/src/openprocurement/tender/core/models.py b/src/openprocurement/tender/core/models.py index f4ff649235..3196cf1d16 100644 --- a/src/openprocurement/tender/core/models.py +++ b/src/openprocurement/tender/core/models.py @@ -148,7 +148,9 @@ def export_loop(self, model_instance, field_converter, role=None, print_none=Fal class Document(BaseDocument): - documentOf = StringType(required=True, choices=["tender", "item", "lot"], default="tender") + documentOf = StringType(required=True, choices=[ + "tender", "item", "lot", "document"], default="tender") + def validate_relatedItem(self, data, relatedItem): if not relatedItem and data.get("documentOf") in ["item", "lot"]: @@ -160,6 +162,8 @@ def validate_relatedItem(self, data, relatedItem): raise ValidationError(u"relatedItem should be one of lots") if data.get("documentOf") == "item" and relatedItem not in [i.id for i in tender.items if i]: raise ValidationError(u"relatedItem should be one of items") + if data.get("documentOf") == "document" and relatedItem not in [i.id for i in tender.documents]: + raise ValidationError(u"relatedItem should be one of documents") class ConfidentialDocument(Document): diff --git a/src/openprocurement/tender/openuadefense/tests/document.py b/src/openprocurement/tender/openuadefense/tests/document.py index 669aebc733..3bddd0afe0 100644 --- a/src/openprocurement/tender/openuadefense/tests/document.py +++ b/src/openprocurement/tender/openuadefense/tests/document.py @@ -13,6 +13,7 @@ create_tender_document_json_invalid, create_tender_document_json, put_tender_document_json, + create_document_with_the_invalid_document_type, ) from openprocurement.tender.openuadefense.tests.base import BaseTenderUAContentWebTest @@ -33,6 +34,7 @@ class TenderDocumentWithDSResourceTest(TenderDocumentResourceTest): test_create_tender_document_json_invalid = snitch(create_tender_document_json_invalid) test_create_tender_document_json = snitch(create_tender_document_json) test_put_tender_document_json = snitch(put_tender_document_json) + test_create_document_with_the_invalid_document_type = snitch(create_document_with_the_invalid_document_type) def suite():