def test_valid_eox_call_with_validated_date_format(self): test_product = "WS-C3560C-8PC-S" sample_record = """{ "EndOfSvcAttachDate": { "dateFormat": "YYYY-MM-DD", "value": "2017-10-30" }, "EndOfServiceContractRenewal": { "dateFormat": "YYYY-MM-DD", "value": "2021-01-28" }, "ProductBulletinNumber": "EOL10691", "EOLProductID": "WS-C3560C-8PC-S", "LinkToProductBulletinURL": "http://www.cisco.com/c/en/us/products/collateral/switches/catalyst-3560-c-series-switches/eos-eol-notice-c51-736180.html", "EOXExternalAnnouncementDate": { "dateFormat": "YYYY-MM-DD", "value": "2015-10-31" }, "UpdatedTimeStamp": { "dateFormat": "YYYY-MM-DD", "value": "2015-11-03" }, "EOXMigrationDetails": { "MigrationProductInfoURL": "http://www.cisco.com/c/en/us/products/switches/catalyst-3560-cx-series-switches/index.html", "PIDActiveFlag": "Y ", "MigrationStrategy": " ", "MigrationProductName": " ", "MigrationProductId": "WS-C3560CX-8PC-S", "MigrationOption": "Enter PID(s)", "MigrationInformation": "Cisco Catalyst 3560-CX 8 Port PoE IP Base" }, "EndOfRoutineFailureAnalysisDate": { "dateFormat": "YYYY-MM-DD", "value": "2017-10-30" }, "EOXInputType": "ShowEOXByPids", "EndOfSaleDate": { "dateFormat": "YYYY-MM-DD", "value": "2016-10-30" }, "EndOfSWMaintenanceReleases": { "dateFormat": "YYYY-MM-DD", "value": "2017-10-30" }, "EOXInputValue": "WS-C3560C-8PC-S ", "LastDateOfSupport": { "dateFormat": "YYYY-MM-DD", "value": "2021-10-31" }, "ProductIDDescription": "Catalyst 3560C Switch 8 FE PoE, 2 x Dual Uplink, IP Base" }""" eox_db_json = json.loads(sample_record) api_crawler.update_local_db_based_on_record(eox_db_json, True) p = Product.objects.get(product_id=test_product) self.assertEqual(p.end_of_support_date.strftime("%Y-%m-%d"), "2021-10-31")
def test_eox_update_call_with_sample_data(self): app_config = AppSettings() app_config.read_file() app_config.set_cisco_api_enabled(True) app_config.set_periodic_sync_enabled(True) app_config.write_file() eox_sample_response = os.path.join("app", "ciscoeox", "tests", "cisco_eox_sample_response.json") eox_db_json = json.loads(open(eox_sample_response).read()) for record in eox_db_json['EOXRecord']: api_crawler.update_local_db_based_on_record(record, True)
def update_cisco_eox_records(records): """ update given database records from the Cisco EoX v5 API :param records: :return: """ app_config = AppSettings() blacklist_raw_string = app_config.get_product_blacklist_regex() create_missing = app_config.is_auto_create_new_products() # build blacklist from configuration blacklist = [] for e in [e.split(";") for e in blacklist_raw_string.splitlines()]: blacklist += e blacklist = [e for e in blacklist if e != ""] counter = 0 messages = {} for record in records: blacklisted = False for regex in blacklist: try: if re.search(regex, record["EOLProductID"], re.I): blacklisted = True break except: logger.warning("invalid regular expression in blacklist: %s" % regex) if not blacklisted: try: message = cisco_eox_api_crawler.update_local_db_based_on_record(record, create_missing) if message: messages[record["EOLProductID"]] = message except ValidationError as ex: logger.error("invalid data received from Cisco API, cannot save data object for " "'%s' (%s)" % (record, str(ex)), exc_info=True) else: messages[record["EOLProductID"]] = " Product record ignored" counter += 1 return { "count": counter, "messages": messages }
def test_with_valid_new_records_and_create_missing(self): result = api_crawler.update_local_db_based_on_record(valid_eox_record, create_missing=True) assert result is None assert Product.objects.count() == 1, "Product is not created" p = Product.objects.get(product_id="WS-C2960-24T-S") assert p.end_of_new_service_attachment_date == datetime.date(2016, 10, 1) assert p.end_of_sw_maintenance_date == datetime.date(2016, 10, 2) assert p.eox_update_time_stamp == datetime.date(2016, 10, 3) assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4) assert p.end_of_sale_date == datetime.date(2016, 10, 5) assert p.end_of_routine_failure_analysis == datetime.date(2016, 10, 6) assert p.eol_ext_announcement_date == datetime.date(2016, 10, 7) assert p.end_of_support_date == datetime.date(2016, 10, 8) assert p.eol_reference_number == "12345" assert p.eol_reference_url == "http://www.cisco.com/en/US/products/hw/switches/ps628/prod_eol_notice0" \ "900aecd804658c9.html"
def test_migration_options_from_update_local_db_based_on_eox_record(self): mixer.blend("productdb.ProductGroup", name="Catalyst 2960") assert Product.objects.count() == 0 assert ProductMigrationSource.objects.filter( name="Cisco EoX Migration option").count() == 0 # load eox_response with test migration data with open( "app/ciscoeox/tests/data/cisco_eox_reponse_migration_data.json" ) as f: eox_records = json.loads(f.read()) # test with valid migration option result = api_crawler.update_local_db_based_on_record( eox_records["EOXRecord"][0], create_missing=True) assert result is None assert ProductMigrationSource.objects.filter( name="Cisco EoX Migration option").count() == 1 p = Product.objects.get(product_id="WS-C2950T-48-SI-WS", vendor=Vendor.objects.get(id=1)) assert p.has_migration_options() is True assert p.get_product_migration_source_names_set() == [ "Cisco EoX Migration option" ] pmo = p.get_preferred_replacement_option() assert type(pmo) == ProductMigrationOption assert pmo.replacement_product_id == "WS-C2960G-48TC-L" assert pmo.comment == "" assert pmo.migration_product_info_url == "" assert pmo.is_replacement_in_db() is False assert pmo.is_valid_replacement() is True assert pmo.get_valid_replacement_product() is None # create product in database (state of the PMO should change) rep = mixer.blend("productdb.Product", product_id="WS-C2960G-48TC-L", vendor=Vendor.objects.get(id=1)) pmo.refresh_from_db() assert pmo.is_replacement_in_db() is True assert pmo.get_valid_replacement_product().product_id == rep.product_id # test with missing replacement product result = api_crawler.update_local_db_based_on_record( eox_records["EOXRecord"][1], create_missing=True) assert result is None assert ProductMigrationSource.objects.filter( name="Cisco EoX Migration option").count() == 1 p = Product.objects.get(product_id="WS-C2950G-24-EI", vendor=Vendor.objects.get(id=1)) assert p.has_migration_options() is True assert p.get_product_migration_source_names_set() == [ "Cisco EoX Migration option" ] pmo = p.get_preferred_replacement_option() assert type(pmo) == ProductMigrationOption assert pmo.replacement_product_id == "" assert pmo.comment == "No Replacement Available" assert pmo.migration_product_info_url == "http://www.cisco.com/en/US/products/ps10538/index.html" assert pmo.is_replacement_in_db() is False assert pmo.is_valid_replacement() is False assert pmo.get_valid_replacement_product() is None # test with custom migration (no direct link to a product) result = api_crawler.update_local_db_based_on_record( eox_records["EOXRecord"][2], create_missing=True) assert result is None assert ProductMigrationSource.objects.filter( name="Cisco EoX Migration option").count() == 1 p = Product.objects.get(product_id="WS-C2950G-48-EI-WS", vendor=Vendor.objects.get(id=1)) assert p.has_migration_options() is True assert p.get_product_migration_source_names_set() == [ "Cisco EoX Migration option" ] pmo = p.get_preferred_replacement_option() assert type(pmo) == ProductMigrationOption assert pmo.replacement_product_id == "" assert pmo.comment == "Customers are encouraged to migrate to the Cisco 8540 Wireless Controller. Information " \ "about this product can be found at http://www.cisco.com/c/en/us/products/wireless/8540-" \ "wireless-controller/index.html." assert pmo.migration_product_info_url == "http://www.cisco.com/c/en/us/products/wireless/8540-wireless-" \ "controller/index.html" assert pmo.is_replacement_in_db() is False assert pmo.is_valid_replacement() is False assert pmo.get_valid_replacement_product() is None
def test_update_local_db_based_on_record(self): mixer.blend("productdb.ProductGroup", name="Catalyst 2960") assert Product.objects.count() == 0 # test call with invalid data with pytest.raises(KeyError): api_crawler.update_local_db_based_on_record({}) # test call with valid data and create flag result = api_crawler.update_local_db_based_on_record( valid_eox_record, create_missing=True) assert result is None assert Product.objects.count() == 1, "The product should be created" # test call with valid data that already exist in the database result = api_crawler.update_local_db_based_on_record( valid_eox_record, create_missing=True) assert result is None assert Product.objects.count() == 1, "The product should be created" p = Product.objects.get(product_id="WS-C2960-24T-S") assert p.description == "Some description of the product" assert p.end_of_new_service_attachment_date == datetime.date( 2016, 10, 1) assert p.end_of_sw_maintenance_date == datetime.date(2016, 10, 2) assert p.eox_update_time_stamp == datetime.date(2016, 10, 3) assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4) assert p.end_of_sale_date == datetime.date(2016, 10, 5) assert p.end_of_routine_failure_analysis == datetime.date(2016, 10, 6) assert p.eol_ext_announcement_date == datetime.date(2016, 10, 7) assert p.end_of_support_date == datetime.date(2016, 10, 8) assert p.eol_reference_number == "12345" assert p.eol_reference_url == "http://www.cisco.com/en/US/products/hw/switches/ps628/prod_eol_notice0" \ "900aecd804658c9.html" # test call with valid data (updated) p = Product.objects.get(product_id="WS-C2960-24T-S") p.eox_update_time_stamp = datetime.datetime(1999, 1, 1) p.save() result = api_crawler.update_local_db_based_on_record( valid_eox_record, create_missing=True) assert result is None assert Product.objects.count() == 1, "The product was only updated" p = Product.objects.get(product_id="WS-C2960-24T-S") assert p.eox_update_time_stamp == datetime.date( 2016, 10, 3), "update must be processed" assert p.end_of_service_contract_renewal == datetime.date( 2016, 10, 4), "Should be the value prior the update" # test record with invalid URL invalid_record = deepcopy(valid_eox_record) invalid_record["LinkToProductBulletinURL"] = "Not yet provided" invalid_record["EOLProductID"] = "xyz" result = api_crawler.update_local_db_based_on_record( invalid_record, create_missing=True) assert result == "Product Data update failed: invalid EoL reference URL" assert Product.objects.count( ) == 1, "The transaction should rollback to avoid inconsistent entries in the DB" p = Product.objects.get(product_id="WS-C2960-24T-S") assert p.end_of_service_contract_renewal == datetime.date( 2016, 10, 4), "Should be the value prior the update" # test empty/None values in update (should be ignored) p = Product.objects.get(product_id="WS-C2960-24T-S") p.eox_update_time_stamp = datetime.datetime( 1999, 1, 1) # reset the eox timestamp to trigger the update p.save() invalid_record = deepcopy(valid_eox_record) invalid_record["EndOfSWMaintenanceReleases"]["value"] = None invalid_record["EndOfRoutineFailureAnalysisDate"]["value"] = "" invalid_record["EndOfServiceContractRenewal"][ "value"] = "2016-10-30" # value in db at this time: "2016-10-04" result = api_crawler.update_local_db_based_on_record( invalid_record, create_missing=True) assert result is None, "the invalid data within the update should be ignored (no message is thrown)" # verify that the valid values are updated p = Product.objects.get(product_id="WS-C2960-24T-S") assert p.eox_update_time_stamp == datetime.date(2016, 10, 3), \ "should be updated to the value provided by the API" assert p.end_of_routine_failure_analysis == datetime.date(2016, 10, 6), \ "Should be the same value as before" assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 30), \ "Should be the same value as before"
def test_with_valid_new_records(self): result = api_crawler.update_local_db_based_on_record(valid_eox_record) assert result is None assert Product.objects.count( ) == 0, "No product was created, because the created flag was not set"
def update_local_database_records(results, year, records): for record in records: cisco_eox_api_crawler.update_local_db_based_on_record(record, True) results[str(year)] = "success" return results
def test_eox_update_call_with_special_character(self): """ test, that no issue exists when the '%' sign is present in the ProductID :return: """ app_config = AppSettings() app_config.read_file() app_config.set_cisco_api_enabled(True) app_config.set_periodic_sync_enabled(True) app_config.write_file() eox_db_record = """{ "EndOfServiceContractRenewal": { "value": " ", "dateFormat": "YYYY-MM-DD" }, "ProductIDDescription": "^IPX 8 CDP W/E1EC TO UNIVERSAL CDP (IPX 8/16/32)", "ProductBulletinNumber": "LEGACY_ESC_IPX_4", "LastDateOfSupport": { "value": "2003-07-01", "dateFormat": "YYYY-MM-DD" }, "EOXInputValue": "SPA* ", "EOLProductID": "SPARE%", "UpdatedTimeStamp": { "value": "2015-08-23", "dateFormat": "YYYY-MM-DD" }, "EOXInputType": "ShowEOXByPids", "EndOfRoutineFailureAnalysisDate": { "value": " ", "dateFormat": "YYYY-MM-DD" }, "LinkToProductBulletinURL": "http://www.cisco.com/en/US/products/hw/tsd_products_support_end-of-sale_and_end-of-life_products_list.html", "EndOfSvcAttachDate": { "value": " ", "dateFormat": "YYYY-MM-DD" }, "EndOfSaleDate": { "value": "1998-07-02", "dateFormat": "YYYY-MM-DD" }, "EndOfSWMaintenanceReleases": { "value": " ", "dateFormat": "YYYY-MM-DD" }, "EOXExternalAnnouncementDate": { "value": "1998-01-03", "dateFormat": "YYYY-MM-DD" }, "EOXMigrationDetails": { "MigrationStrategy": " ", "MigrationProductId": " ", "MigrationProductInfoURL": " ", "PIDActiveFlag": "Y ", "MigrationProductName": "See Product Bulletin", "MigrationInformation": " ", "MigrationOption": "Enter Product Name(s)" } }""" eox_db_json = json.loads(eox_db_record) api_crawler.update_local_db_based_on_record(eox_db_json, True) p = Product.objects.get(product_id="SPARE%")
def execute_task_to_synchronize_cisco_eox_states(self, ignore_periodic_sync_flag=False): """ This task synchronize the local database with the Cisco EoX API. It executes all configured queries and stores the results in the local database. There are two types of operation: * cisco_eox_api_auto_sync_auto_create_elements is set to true - will create any element which is not part of the blacklist and not in the database * cisco_eox_api_auto_sync_auto_create_elements is set to false - will only update entries, which are already included in the database :return: """ app_config = AppSettings() run_task = app_config.is_periodic_sync_enabled() if run_task or ignore_periodic_sync_flag: logger.info("start sync with Cisco EoX API...") self.update_state(state=TaskState.PROCESSING, meta={ "status_message": "sync with Cisco EoX API..." }) # read configuration for the Cisco EoX API synchronization queries = app_config.get_cisco_eox_api_queries_as_list() blacklist_raw_string = app_config.get_product_blacklist_regex() create_missing = app_config.is_auto_create_new_products() if len(queries) == 0: result = { "status_message": "No Cisco EoX API queries configured." } NotificationMessage.objects.create( title=NOTIFICATION_MESSAGE_TITLE, type=NotificationMessage.MESSAGE_WARNING, summary_message="There are no Cisco EoX API queries configured. Nothing to do.", detailed_message="There are no Cisco EoX API queries configured. Please configure at least on EoX API " "query in the settings or disable the periodic synchronization." ) # update the local database with the Cisco EoX API else: # test Cisco EoX API access test_result = utils.check_cisco_eox_api_access( app_config.get_cisco_api_client_id(), app_config.get_cisco_api_client_secret(), False ) if test_result: # execute all queries from the configuration query_eox_records = {} failed_queries = [] failed_query_msgs = {} successful_queries = [] counter = 1 for query in queries: self.update_state(state=TaskState.PROCESSING, meta={ "status_message": "send query <code>%s</code> to the Cisco EoX API (<strong>%d of " "%d</strong>)..." % (query, counter, len(queries)) }) # wait some time between the query calls time.sleep(int(app_config.get_cisco_eox_api_sync_wait_time())) try: query_eox_records[query] = cisco_eox_api_crawler.get_raw_api_data(api_query=query) successful_queries.append(query) except CiscoApiCallFailed as ex: msg = "Cisco EoX API call failed (%s)" % str(ex) logger.error("Query %s to Cisco EoX API failed (%s)" % (query, msg), exc_info=True) failed_queries.append(query) failed_query_msgs[query] = str(ex) except Exception as ex: msg = "Unexpected Exception, cannot access the Cisco API. Please ensure that the server is " \ "connected to the internet and that the authentication settings are " \ "valid." logger.error("Query %s to Cisco EoX API failed (%s)" % (query, msg), exc_info=True) failed_queries.append(query) failed_query_msgs[query] = str(ex) counter += 1 # build blacklist from configuration blacklist = [] for e in [e.split(";") for e in blacklist_raw_string.splitlines()]: blacklist += e blacklist = [e for e in blacklist if e != ""] # update data in database self.update_state(state=TaskState.PROCESSING, meta={ "status_message": "update database..." }) messages = {} for key in query_eox_records: amount_of_records = len(query_eox_records[key]) self.update_state(state=TaskState.PROCESSING, meta={ "status_message": "update database (query <code>%s</code>, processed <b>0</b> of " "<b>%d</b> results)..." % (key, amount_of_records) }) counter = 0 for record in query_eox_records[key]: if counter % 100 == 0: self.update_state(state=TaskState.PROCESSING, meta={ "status_message": "update database (query <code>%s</code>, processed <b>%d</b> of " "<b>%d</b> results)..." % (key, counter, amount_of_records) }) blacklisted = False for regex in blacklist: try: if re.search(regex, record["EOLProductID"], re.I): blacklisted = True break except: logger.warning("invalid regular expression in blacklist: %s" % regex) if not blacklisted: try: message = cisco_eox_api_crawler.update_local_db_based_on_record(record, create_missing) if message: messages[record["EOLProductID"]] = message except ValidationError as ex: logger.error("invalid data received from Cisco API, cannot save data object for " "'%s' (%s)" % (record, str(ex)), exc_info=True) else: messages[record["EOLProductID"]] = " Product record ignored" counter += 1 # view the queries in the detailed message and all messages (if there are some) detailed_message = "The following queries were executed:<br><ul style=\"text-align: left;\">" for fq in failed_queries: detailed_message += "<li class=\"text-danger\"><code>%s</code> " \ "(failed, %s)</li>" % (fq, failed_query_msgs.get(fq, "unknown")) for sq in successful_queries: detailed_message += "<li><code>%s</code> (<b>affects %d products</b>, " \ "success)</li>" % (sq, len(query_eox_records[sq])) detailed_message += "</ul>" if len(messages) > 0: detailed_message += "<br>The following comment/errors occurred " \ "during the synchronization:<br><ul style=\"text-align: left;\">" for e in messages.keys(): detailed_message += "<li><code>%s</code>: %s</li>" % (e, messages[e]) detailed_message += "</ul>" # show the executed queries in the summary message if len(failed_queries) == 0 and len(successful_queries) != 0: summary_html = "The following queries were successful executed: %s" % ", ".join( ["<code>%s</code>" % query for query in successful_queries] ) NotificationMessage.objects.create( title=NOTIFICATION_MESSAGE_TITLE, type=NotificationMessage.MESSAGE_SUCCESS, summary_message="The synchronization with the Cisco EoX API was successful. " + summary_html, detailed_message=detailed_message ) elif len(failed_queries) != 0 and len(successful_queries) == 0: summary_html = "The following queries failed to execute: %s" % ", ".join( ["<code>%s</code>" % query for query in failed_queries] ) NotificationMessage.objects.create( title=NOTIFICATION_MESSAGE_TITLE, type=NotificationMessage.MESSAGE_ERROR, summary_message="The synchronization with the Cisco EoX API was not successful. " + summary_html, detailed_message=detailed_message ) else: summary_html = "The following queries were successful executed: %s\n<br>The following queries " \ "failed to execute: %s" % ( ", ".join(["<code>%s</code>" % query for query in successful_queries]), ", ".join(["<code>%s</code>" % query for query in failed_queries]) ) NotificationMessage.objects.create( title=NOTIFICATION_MESSAGE_TITLE, type=NotificationMessage.MESSAGE_WARNING, summary_message="The synchronization with the Cisco EoX API was partially " "successful. " + summary_html, detailed_message=detailed_message ) result = {"status_message": "<p style=\"text-align: left;\">" + detailed_message + "</p>"} # if the task was executed eager, set state to SUCCESS (required for testing) if self.request.is_eager: self.update_state(state=TaskState.SUCCESS, meta={"status_message": summary_html}) else: msg = "Cannot contact Cisco EoX API, please verify your internet connection and access " \ "credentials." if not ignore_periodic_sync_flag: NotificationMessage.objects.create( title=NOTIFICATION_MESSAGE_TITLE, type=NotificationMessage.MESSAGE_ERROR, summary_message="The synchronization with the Cisco EoX API was not successful.", detailed_message=msg ) result = { "error_message": msg } else: result = { "status_message": "task not enabled" } # remove in progress flag with the cache cache.delete("CISCO_EOX_API_SYN_IN_PROGRESS") return result
def test_migration_options_from_update_local_db_based_on_eox_record(): mixer.blend("productdb.ProductGroup", name="Catalyst 2960") assert Product.objects.count() == 0 assert ProductMigrationSource.objects.filter(name="Cisco EoX Migration option").count() == 0 # load eox_response with test migration data with open("app/ciscoeox/tests/data/cisco_eox_reponse_migration_data.json") as f: eox_records = json.loads(f.read()) # test with valid migration option result = api_crawler.update_local_db_based_on_record(eox_records["EOXRecord"][0], create_missing=True) assert result["created"] is True assert result["updated"] is True assert ProductMigrationSource.objects.filter(name="Cisco EoX Migration option").count() == 1 p = Product.objects.get(product_id="WS-C2950T-48-SI-WS", vendor=Vendor.objects.get(id=1)) assert p.has_migration_options() is True assert p.get_product_migration_source_names_set() == ["Cisco EoX Migration option"] pmo = p.get_preferred_replacement_option() assert type(pmo) == ProductMigrationOption assert pmo.replacement_product_id == "WS-C2960G-48TC-L" assert pmo.comment == "" assert pmo.migration_product_info_url == "" assert pmo.is_replacement_in_db() is False assert pmo.is_valid_replacement() is True assert pmo.get_valid_replacement_product() is None # create product in database (state of the PMO should change) rep = mixer.blend("productdb.Product", product_id="WS-C2960G-48TC-L", vendor=Vendor.objects.get(id=1)) pmo.refresh_from_db() assert pmo.is_replacement_in_db() is True assert pmo.get_valid_replacement_product().product_id == rep.product_id # test with missing replacement product result = api_crawler.update_local_db_based_on_record(eox_records["EOXRecord"][1], create_missing=True) assert result["created"] is True assert result["updated"] is True assert ProductMigrationSource.objects.filter(name="Cisco EoX Migration option").count() == 1 p = Product.objects.get(product_id="WS-C2950G-24-EI", vendor=Vendor.objects.get(id=1)) assert p.has_migration_options() is True assert p.get_product_migration_source_names_set() == ["Cisco EoX Migration option"] pmo = p.get_preferred_replacement_option() assert type(pmo) == ProductMigrationOption assert pmo.replacement_product_id == "" assert pmo.comment == "No Replacement Available" assert pmo.migration_product_info_url == "http://www.cisco.com/en/US/products/ps10538/index.html" assert pmo.is_replacement_in_db() is False assert pmo.is_valid_replacement() is False assert pmo.get_valid_replacement_product() is None # test with custom migration (no direct link to a product) result = api_crawler.update_local_db_based_on_record(eox_records["EOXRecord"][2], create_missing=True) assert result["created"] is True assert result["updated"] is True assert ProductMigrationSource.objects.filter(name="Cisco EoX Migration option").count() == 1 p = Product.objects.get(product_id="WS-C2950G-48-EI-WS", vendor=Vendor.objects.get(id=1)) assert p.has_migration_options() is True assert p.get_product_migration_source_names_set() == ["Cisco EoX Migration option"] pmo = p.get_preferred_replacement_option() assert type(pmo) == ProductMigrationOption assert pmo.replacement_product_id == "" assert pmo.comment == "Customers are encouraged to migrate to the Cisco 8540 Wireless Controller. Information " \ "about this product can be found at http://www.cisco.com/c/en/us/products/wireless/8540-" \ "wireless-controller/index.html." assert pmo.migration_product_info_url == "http://www.cisco.com/c/en/us/products/wireless/8540-wireless-" \ "controller/index.html" assert pmo.is_replacement_in_db() is False assert pmo.is_valid_replacement() is False assert pmo.get_valid_replacement_product() is None
def test_update_local_db_based_on_record(): mixer.blend("productdb.ProductGroup", name="Catalyst 2960") assert Product.objects.count() == 0 # test call with invalid data with pytest.raises(KeyError): api_crawler.update_local_db_based_on_record({}) # test call with valid data result = api_crawler.update_local_db_based_on_record(valid_eox_record) assert "PID" in result assert result["PID"] == "WS-C2960-24T-S" assert "blacklist" in result assert result["blacklist"] is False assert "created" in result assert result["created"] is False assert "updated" in result assert result["updated"] is False assert "message" in result assert result["message"] is None assert Product.objects.count() == 0, "No product was created, because the created flag was not set" # test call with valid data and create flag result = api_crawler.update_local_db_based_on_record(valid_eox_record, create_missing=True) assert "PID" in result assert result["PID"] == "WS-C2960-24T-S" assert "blacklist" in result assert result["blacklist"] is False assert "created" in result assert result["created"] is True assert "updated" in result assert result["updated"] is True assert "message" in result assert result["message"] is None assert Product.objects.count() == 1, "The product should be created" # test call with valid data that already exist in the database result = api_crawler.update_local_db_based_on_record(valid_eox_record, create_missing=True) assert "PID" in result assert result["PID"] == "WS-C2960-24T-S" assert "blacklist" in result assert result["blacklist"] is False assert "created" in result assert result["created"] is False assert "updated" in result assert result["updated"] is False assert "message" in result assert "update suppressed" in result["message"] assert Product.objects.count() == 1, "The product should be created" p = Product.objects.get(product_id="WS-C2960-24T-S") assert p.description == "Some description of the product" assert p.end_of_new_service_attachment_date == datetime.date(2016, 10, 1) assert p.end_of_sw_maintenance_date == datetime.date(2016, 10, 2) assert p.eox_update_time_stamp == datetime.date(2016, 10, 3) assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4) assert p.end_of_sale_date == datetime.date(2016, 10, 5) assert p.end_of_routine_failure_analysis == datetime.date(2016, 10, 6) assert p.eol_ext_announcement_date == datetime.date(2016, 10, 7) assert p.end_of_support_date == datetime.date(2016, 10, 8) assert p.eol_reference_number == "12345" assert p.eol_reference_url == "http://www.cisco.com/en/US/products/hw/switches/ps628/prod_eol_notice0" \ "900aecd804658c9.html" # test call with valid data (updated) p = Product.objects.get(product_id="WS-C2960-24T-S") p.eox_update_time_stamp = datetime.datetime(1999, 1, 1) p.save() result = api_crawler.update_local_db_based_on_record(valid_eox_record, create_missing=True) assert "PID" in result assert result["PID"] == "WS-C2960-24T-S" assert "blacklist" in result assert result["blacklist"] is False assert "created" in result assert result["created"] is False assert "updated" in result assert result["updated"] is True assert "message" in result assert result["message"] is None assert Product.objects.count() == 1, "The product was only updated" p = Product.objects.get(product_id="WS-C2960-24T-S") assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4), "Should be the value prior the update" # test crash of the update method during update p = Product.objects.get(product_id="WS-C2960-24T-S") p.eox_update_time_stamp = datetime.datetime(1999, 1, 1) # reset the eox timestamp to trigger the update p.save() invalid_record = valid_eox_record.copy() invalid_record["EndOfSWMaintenanceReleases"]["value"] = None invalid_record["EndOfServiceContractRenewal"]["value"] = "2016-10-30" # value in db at this time: "2016-10-04" result = api_crawler.update_local_db_based_on_record(invalid_record, create_missing=True) assert "PID" in result assert result["PID"] == "WS-C2960-24T-S" assert "blacklist" in result assert result["blacklist"] is False assert "created" in result assert result["created"] is False assert "updated" in result assert result["updated"] is True assert "message" in result assert "Update failed: " in result["message"] assert Product.objects.count() == 1, "The transaction should rollback to avoid inconsistent entries in the DB" # verify that the change were not saved p = Product.objects.get(product_id="WS-C2960-24T-S") assert p.eox_update_time_stamp == datetime.date(1999, 1, 1), "Should be the value prior the update" assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4), "Should be the value prior the update" # test crash of the update method during create invalid_record = valid_eox_record.copy() invalid_record["EOLProductID"] = "MyTest123" invalid_record["EndOfRoutineFailureAnalysisDate"]["value"] = None result = api_crawler.update_local_db_based_on_record(invalid_record, create_missing=True) assert "PID" in result assert result["PID"] == "MyTest123" assert "blacklist" in result assert result["blacklist"] is False assert "created" in result assert result["created"] is True assert "updated" in result assert result["updated"] is True assert "message" in result assert "Update failed: " in result["message"] assert Product.objects.count() == 1, "The transaction should rollback to avoid inconsistent entries in the DB" p = Product.objects.get(product_id="WS-C2960-24T-S") assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4), "Should be the value prior the update" # test record with invalid URL invalid_record = valid_eox_record.copy() invalid_record["LinkToProductBulletinURL"] = "Not yet provided" invalid_record["EOLProductID"] = "xyz" result = api_crawler.update_local_db_based_on_record(invalid_record, create_missing=True) assert "PID" in result assert result["PID"] == "xyz" assert "blacklist" in result assert result["blacklist"] is False assert "created" in result assert result["created"] is True assert "updated" in result assert result["updated"] is True assert "message" in result assert "Update failed: " in result["message"] assert Product.objects.count() == 1, "The transaction should rollback to avoid inconsistent entries in the DB" p = Product.objects.get(product_id="WS-C2960-24T-S") assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4), "Should be the value prior the update"