mirror of
				https://github.com/KevinMidboe/cloudflare-ddns.git
				synced 2025-10-29 17:40:17 +00:00 
			
		
		
		
	
							
								
								
									
										24
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								.drone.yml
									
									
									
									
									
								
							| @@ -1,3 +1,21 @@ | |||||||
|  | --- | ||||||
|  | kind: pipeline | ||||||
|  | type: docker | ||||||
|  | name: Test | ||||||
|  |  | ||||||
|  |  | ||||||
|  | platform: | ||||||
|  |   os: linux | ||||||
|  |   arch: amd64 | ||||||
|  |  | ||||||
|  | steps: | ||||||
|  |   - name: Run unit tests | ||||||
|  |     image: python:3.10-alpine | ||||||
|  |     commands: | ||||||
|  |       - pip install -r requirements.txt | ||||||
|  |       - pip install -r requirements-test.txt | ||||||
|  |       - python3 -m unittest tests/test_*.py -v | ||||||
|  |  | ||||||
| --- | --- | ||||||
| kind: pipeline | kind: pipeline | ||||||
| type: docker | type: docker | ||||||
| @@ -31,6 +49,9 @@ trigger: | |||||||
|   branch: |   branch: | ||||||
|     - main |     - main | ||||||
|  |  | ||||||
|  | depends_on: | ||||||
|  |   - Test | ||||||
|  |  | ||||||
| --- | --- | ||||||
| kind: pipeline | kind: pipeline | ||||||
| type: docker | type: docker | ||||||
| @@ -91,6 +112,7 @@ trigger: | |||||||
|     - main |     - main | ||||||
|  |  | ||||||
| depends_on: | depends_on: | ||||||
|  |   - Test | ||||||
|   - Publish |   - Publish | ||||||
|  |  | ||||||
| volumes: | volumes: | ||||||
| @@ -99,6 +121,6 @@ volumes: | |||||||
|  |  | ||||||
| --- | --- | ||||||
| kind: signature | kind: signature | ||||||
| hmac: d3088aaf784f4eaac3223f43a86a19bfccff416fd854351c527d785002ae2c26 | hmac: 2fb50ffa037eb368bcf6d596ced4c0ef42cfde413781ee39dd42b5f695396132 | ||||||
|  |  | ||||||
| ... | ... | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								.env.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.env.example
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | API_KEY= | ||||||
|  | DDNS_ZONE= | ||||||
|  |  | ||||||
|  | SMS_API_KEY= | ||||||
|  | SMS_RECIPIENT= | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| FROM python:3.10-alpine | FROM python:3.10-alpine | ||||||
|  |  | ||||||
| COPY *.py ./ | COPY requirements.txt . | ||||||
|  | COPY src/*.py ./ | ||||||
|  |  | ||||||
| RUN pip install requests python-dotenv | RUN pip install -r requirements.txt | ||||||
| CMD python3 main.py | CMD python3 main.py | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							| @@ -29,7 +29,7 @@ virtualenv | |||||||
| source -p $(which python3) env/bin/activate | source -p $(which python3) env/bin/activate | ||||||
|  |  | ||||||
| pip install -r requirements.txt | pip install -r requirements.txt | ||||||
| python main.py | python src/main.py | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ## Kubernetes | ## Kubernetes | ||||||
| @@ -46,8 +46,14 @@ metadata: | |||||||
|   namespace: cloudflare-ddns |   namespace: cloudflare-ddns | ||||||
| type: Opaque | type: Opaque | ||||||
| data: | data: | ||||||
|   API_KEY: BASE_64_ENCODED_CLOUDFLARE_API_KEY |   API_KEY: CLOUDFLARE_API_KEY | ||||||
|   DDNS_ZONE: BASE64_ENCODED_CLOUDFLARE_ZONE_ID |   DDNS_ZONE: CLOUDFLARE_ZONE_ID | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | (Optional: receive a SMS from gateway API by appending to secrets data) | ||||||
|  | ```yaml | ||||||
|  |   SMS_API_KEY: GATEWAY_API_API_KEY | ||||||
|  |   SMS_RECIPIENT: PHONE_NUMBER_TO_RECEIVE | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
|   | |||||||
							
								
								
									
										65
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								main.py
									
									
									
									
									
								
							| @@ -1,65 +0,0 @@ | |||||||
| import os |  | ||||||
| import re |  | ||||||
| import requests |  | ||||||
| from bulk_dns_update import updateAllZones, setAPIKey, getDDNSAddresszoneId |  | ||||||
| from notify import notify |  | ||||||
| from dotenv import load_dotenv |  | ||||||
| from logger import logger |  | ||||||
|  |  | ||||||
| load_dotenv() |  | ||||||
|  |  | ||||||
| currentIP = None |  | ||||||
| recordedIP = None |  | ||||||
| DDNS_ZONE = os.getenv('DDNS_ZONE') |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def validIP(ipString): |  | ||||||
|     ipRegex = '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$' |  | ||||||
|     return re.search(ipRegex, ipString) |  | ||||||
|  |  | ||||||
| def publicAddress(): |  | ||||||
|     global currentIP |  | ||||||
|     logger.info('Getting public IP from ifconfg.me...') |  | ||||||
|  |  | ||||||
|     r = requests.get('https://ifconfig.me') |  | ||||||
|     if r.status_code != 200 or not validIP(r.text): |  | ||||||
|         return |  | ||||||
|  |  | ||||||
|     currentIP = r.text |  | ||||||
|     logger.info('Public IP: {}'.format(currentIP)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def cloudflareDDNS(): |  | ||||||
|     global recordedIP |  | ||||||
|     logger.info('Checking IP recorded in Cloudflare...') |  | ||||||
|     ddnsRecord = getDDNSAddresszoneId(DDNS_ZONE) |  | ||||||
|     recordedIP = ddnsRecord['content'] |  | ||||||
|     logger.info('Found ddns recorded IP: {}'.format(recordedIP)) |  | ||||||
|  |  | ||||||
|     if currentIP != recordedIP and validIP(recordedIP): |  | ||||||
|         logger.info('Public IP has changed, updating all A records.') |  | ||||||
|         return True |  | ||||||
|     else: |  | ||||||
|         logger.info('is same, exiting') |  | ||||||
|         return False |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def main(): |  | ||||||
|     apiKey = os.getenv('API_KEY') |  | ||||||
|     if apiKey is None: |  | ||||||
|         raise Exception('In .env file or environment set Cloudflare variable: API_KEY') |  | ||||||
|     if DDNS_ZONE is None: |  | ||||||
|         raise Exception('In .env file or environment; set Cloudflare zone where addr. points to current IP.') |  | ||||||
|  |  | ||||||
|     setAPIKey(apiKey) |  | ||||||
|  |  | ||||||
|     publicAddress() |  | ||||||
|     changed = cloudflareDDNS() |  | ||||||
|  |  | ||||||
|     if changed is True: |  | ||||||
|         notify("IP changed to: {}. Updating all cloudflare zones!".format(currentIP)) |  | ||||||
|         updateAllZones(recordedIP, currentIP) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     main() |  | ||||||
							
								
								
									
										1
									
								
								requirements-test.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								requirements-test.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | responses==0.24.1 | ||||||
							
								
								
									
										2
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | requests==2.31.0 | ||||||
|  | python-dotenv==1.0.1 | ||||||
							
								
								
									
										7
									
								
								src/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | import os | ||||||
|  | import sys | ||||||
|  | PROJECT_PATH = os.getcwd() | ||||||
|  | SOURCE_PATH = os.path.join( | ||||||
|  |     PROJECT_PATH, "src" | ||||||
|  | ) | ||||||
|  | sys.path.append(SOURCE_PATH) | ||||||
| @@ -63,6 +63,11 @@ def getZones(): | |||||||
|     url = 'https://api.cloudflare.com/client/v4/zones' |     url = 'https://api.cloudflare.com/client/v4/zones' | ||||||
|     data = cloudflareRequest(url) |     data = cloudflareRequest(url) | ||||||
| 
 | 
 | ||||||
|  |     if 'success' not in data and 'errors' not in data: | ||||||
|  |         logger.info("Unexpected cloudflare error when getting zones, no response!") | ||||||
|  |         logger.info("data:" + str(data)) | ||||||
|  |         raise Exception('Unexpected Cloudflare error, missing response! Check logs.') | ||||||
|  | 
 | ||||||
|     if data['success'] is False: |     if data['success'] is False: | ||||||
|         logger.info('Request to cloudflare was unsuccessful, error:') |         logger.info('Request to cloudflare was unsuccessful, error:') | ||||||
|         logger.info(data['errors']) |         logger.info(data['errors']) | ||||||
| @@ -77,9 +82,15 @@ def getZones(): | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def getRecordsForZone(zoneId): | def getRecordsForZone(zoneId): | ||||||
|     url = 'https://api.cloudflare.com/client/v4/zones/{}/dns_records?type=A'.format(zoneId) |     url = 'https://api.cloudflare.com/client/v4/zones/{}/dns_records?type=A'.format( | ||||||
|  |         zoneId) | ||||||
|     data = cloudflareRequest(url) |     data = cloudflareRequest(url) | ||||||
| 
 | 
 | ||||||
|  |     if 'success' not in data and 'errors' not in data: | ||||||
|  |         logger.info("Unexpected cloudflare error when getting records, no response!") | ||||||
|  |         logger.info("data:" + str(data)) | ||||||
|  |         raise Exception('Unexpected Cloudflare error, missing response! Check logs.') | ||||||
|  | 
 | ||||||
|     if data['success'] is False: |     if data['success'] is False: | ||||||
|         logger.info('Request from cloudflare was unsuccessful, error:') |         logger.info('Request from cloudflare was unsuccessful, error:') | ||||||
|         logger.info(data['errors']) |         logger.info(data['errors']) | ||||||
| @@ -105,7 +116,8 @@ def getDDNSAddresszoneId(ddnsZone): | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def updateRecord(zoneId, recordId, name, newIP, ttl, proxied): | def updateRecord(zoneId, recordId, name, newIP, ttl, proxied): | ||||||
|     url = 'https://api.cloudflare.com/client/v4/zones/{}/dns_records/{}'.format(zoneId, recordId) |     url = 'https://api.cloudflare.com/client/v4/zones/{}/dns_records/{}'.format( | ||||||
|  |         zoneId, recordId) | ||||||
|     data = { |     data = { | ||||||
|         'type': 'A', |         'type': 'A', | ||||||
|         'name': name, |         'name': name, | ||||||
| @@ -115,7 +127,8 @@ def updateRecord(zoneId, recordId, name, newIP, ttl, proxied): | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     response = cloudflareUpdateRequest(url, data) |     response = cloudflareUpdateRequest(url, data) | ||||||
|     logger.info('\tRecord updated: {}'.format('✅' if response['success'] is True else '❌')) |     logger.info('\tRecord updated: {}'.format( | ||||||
|  |         '✅' if response['success'] is True else '❌')) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def getMatchingRecordsForZone(zoneId, oldIP): | def getMatchingRecordsForZone(zoneId, oldIP): | ||||||
| @@ -131,8 +144,10 @@ def updateZone(zone, oldIP, newIP): | |||||||
|         return |         return | ||||||
| 
 | 
 | ||||||
|     for record in records: |     for record in records: | ||||||
|         logger.info('\tRecord {}: {} -> {}'.format(record['name'], record['content'], newIP)) |         logger.info( | ||||||
|         updateRecord(zone['id'], record['id'], record['name'], newIP, record['ttl'], record['proxied']) |             '\tRecord {}: {} -> {}'.format(record['name'], record['content'], newIP)) | ||||||
|  |         updateRecord(zone['id'], record['id'], record['name'], | ||||||
|  |                      newIP, record['ttl'], record['proxied']) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def updateAllZones(oldIP, newIP): | def updateAllZones(oldIP, newIP): | ||||||
							
								
								
									
										70
									
								
								src/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/main.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | |||||||
|  | import os | ||||||
|  | import re | ||||||
|  | import requests | ||||||
|  | from bulk_dns_update import updateAllZones, setAPIKey, getDDNSAddresszoneId | ||||||
|  | from notify import notify | ||||||
|  | from dotenv import load_dotenv | ||||||
|  | from logger import logger | ||||||
|  |  | ||||||
|  | load_dotenv() | ||||||
|  |  | ||||||
|  | DDNS_ZONE = os.getenv('DDNS_ZONE') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def validIP(ipString): | ||||||
|  |     ipRegex = '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$' | ||||||
|  |     return bool(re.search(ipRegex, str(ipString))) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def publicAddress(): | ||||||
|  |     logger.info('Getting public IP from ifconfg.me...') | ||||||
|  |  | ||||||
|  |     r = requests.get('https://ifconfig.me') | ||||||
|  |     if r.status_code != 200: | ||||||
|  |         return | ||||||
|  |  | ||||||
|  |     ip = r.text | ||||||
|  |     if not validIP(ip): | ||||||
|  |         return | ||||||
|  |  | ||||||
|  |     logger.info('Public IP: {}'.format(ip)) | ||||||
|  |     return ip | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def cloudflareDDNS(): | ||||||
|  |     logger.info('Checking IP recorded in Cloudflare...') | ||||||
|  |     ddnsRecord = getDDNSAddresszoneId(DDNS_ZONE) | ||||||
|  |     ip = ddnsRecord['content'] | ||||||
|  |     logger.info('Found ddns recorded IP: {}'.format(ip)) | ||||||
|  |  | ||||||
|  |     if not validIP(ip): | ||||||
|  |         return | ||||||
|  |  | ||||||
|  |     return ip | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def main(): | ||||||
|  |     apiKey = os.getenv('API_KEY') | ||||||
|  |     if apiKey is None: | ||||||
|  |         raise Exception( | ||||||
|  |             'In .env file or environment set Cloudflare variable: API_KEY') | ||||||
|  |     if DDNS_ZONE is None: | ||||||
|  |         raise Exception( | ||||||
|  |             'In .env file or environment; set Cloudflare zone where addr. points to current IP.') | ||||||
|  |  | ||||||
|  |     setAPIKey(apiKey) | ||||||
|  |  | ||||||
|  |     currentIP = publicAddress() | ||||||
|  |     recordedIP = cloudflareDDNS() | ||||||
|  |  | ||||||
|  |     if currentIP == recordedIP or None in [currentIP, recordedIP]: | ||||||
|  |         logger.info('is same, exiting') | ||||||
|  |         return | ||||||
|  |  | ||||||
|  |     logger.info('Public IP has changed, updating all A records.') | ||||||
|  |     notify("IP changed to: {}. Updating all cloudflare zones!".format(currentIP)) | ||||||
|  |     updateAllZones(recordedIP, currentIP) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     main() | ||||||
							
								
								
									
										2
									
								
								tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | from src.logger import logger | ||||||
|  | logger.setLevel('WARNING') | ||||||
							
								
								
									
										80
									
								
								tests/test_cloudflare_dns_record_ip.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								tests/test_cloudflare_dns_record_ip.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | |||||||
|  | import re | ||||||
|  | import unittest | ||||||
|  | import responses | ||||||
|  | from src.main import cloudflareDDNS | ||||||
|  |  | ||||||
|  | MOCK_IP = '44.208.147.61' | ||||||
|  | CLOUDFLARE_GET_RECORDS_URL = re.compile( | ||||||
|  |     r"https\:\/\/api.cloudflare.com\/client\/v4\/zones\/\w*\/dns_records\?type\=A") | ||||||
|  | CLOUDFLARE_ADDR_RECORD_EXISTS_RESPONSE = { | ||||||
|  |     'success': True, | ||||||
|  |     'result': [{ | ||||||
|  |         'content': MOCK_IP, | ||||||
|  |         'name': 'addr.', | ||||||
|  |         'id': 'id', | ||||||
|  |         'ttl': 86400, | ||||||
|  |         'proxied': True | ||||||
|  |     }] | ||||||
|  | } | ||||||
|  | CLOUDFLARE_ADDR_RECORD_NONEXISTANT_RESPONSE = { | ||||||
|  |     'success': True, | ||||||
|  |     'result': [{ | ||||||
|  |         'content': MOCK_IP, | ||||||
|  |         'name': None, | ||||||
|  |         'id': 'id', | ||||||
|  |         'ttl': 86400, | ||||||
|  |         'proxied': True | ||||||
|  |     }] | ||||||
|  | } | ||||||
|  | CLOUDFLARE_500_RESPONSE = { | ||||||
|  |     'success': False, | ||||||
|  |     'errors': 'someerror' | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestCloudflareDNSRecordIP(unittest.TestCase): | ||||||
|  |  | ||||||
|  |     @responses.activate | ||||||
|  |     def test_successfull_response(self): | ||||||
|  |         responses.add(responses.GET, CLOUDFLARE_GET_RECORDS_URL, | ||||||
|  |                       json=CLOUDFLARE_ADDR_RECORD_EXISTS_RESPONSE, status=200) | ||||||
|  |  | ||||||
|  |         ip = cloudflareDDNS() | ||||||
|  |  | ||||||
|  |         self.assertEqual(MOCK_IP, ip) | ||||||
|  |  | ||||||
|  |     @responses.activate | ||||||
|  |     def test_addr_record_exists(self): | ||||||
|  |         responses.add(responses.GET, CLOUDFLARE_GET_RECORDS_URL, | ||||||
|  |                       json=CLOUDFLARE_ADDR_RECORD_NONEXISTANT_RESPONSE, | ||||||
|  |                       status=200) | ||||||
|  |  | ||||||
|  |         self.assertRaises(Exception, cloudflareDDNS) | ||||||
|  |  | ||||||
|  |     @responses.activate | ||||||
|  |     def test_cloudflare_500_response(self): | ||||||
|  |         responses.add(responses.GET, CLOUDFLARE_GET_RECORDS_URL, | ||||||
|  |                       json=CLOUDFLARE_500_RESPONSE, | ||||||
|  |                       status=500) | ||||||
|  |  | ||||||
|  |         self.assertRaises(Exception, cloudflareDDNS) | ||||||
|  |  | ||||||
|  |     @responses.activate | ||||||
|  |     def test_cloudflare_empty_200_response(self): | ||||||
|  |         responses.add(responses.GET, CLOUDFLARE_GET_RECORDS_URL, | ||||||
|  |                       json={}, | ||||||
|  |                       status=500) | ||||||
|  |  | ||||||
|  |         self.assertRaises(Exception, cloudflareDDNS) | ||||||
|  |  | ||||||
|  |     @responses.activate | ||||||
|  |     def test_cloudflare_empty_500_response(self): | ||||||
|  |         responses.add(responses.GET, CLOUDFLARE_GET_RECORDS_URL, | ||||||
|  |                       json={}, | ||||||
|  |                       status=500) | ||||||
|  |  | ||||||
|  |         self.assertRaises(Exception, cloudflareDDNS) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     unittest.main() | ||||||
							
								
								
									
										33
									
								
								tests/test_ip_address.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								tests/test_ip_address.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | import unittest | ||||||
|  | from src.main import validIP | ||||||
|  |  | ||||||
|  | MOCK_IP = "44.208.147.61" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestIPAddress(unittest.TestCase): | ||||||
|  |     def test_valid_ip(self): | ||||||
|  |         self.assertTrue(validIP(MOCK_IP)) | ||||||
|  |  | ||||||
|  |     def test_invalid_ip(self): | ||||||
|  |         ip = "256.0.0.1" | ||||||
|  |         self.assertFalse(validIP(ip)) | ||||||
|  |  | ||||||
|  |     def test_invalid_format(self): | ||||||
|  |         ip = "192.168.1" | ||||||
|  |         self.assertFalse(validIP(ip)) | ||||||
|  |  | ||||||
|  |     def test_empty_string(self): | ||||||
|  |         ip = "" | ||||||
|  |         self.assertFalse(validIP(ip)) | ||||||
|  |  | ||||||
|  |     def test_error_looking_string(self): | ||||||
|  |         ip = "upstream connect error or disconnect/reset before headers. reset reason: connection timeout." | ||||||
|  |         self.assertFalse(validIP(ip)) | ||||||
|  |  | ||||||
|  |     def test_none(self): | ||||||
|  |         ip = None | ||||||
|  |         self.assertFalse(validIP(ip)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     unittest.main() | ||||||
							
								
								
									
										40
									
								
								tests/test_public_ip.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								tests/test_public_ip.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | import unittest | ||||||
|  | import responses | ||||||
|  | from src.main import publicAddress | ||||||
|  |  | ||||||
|  | MOCK_IP = '44.208.147.61' | ||||||
|  | MOCK_TIMEOUT = 'upstream connect error or disconnect/reset before headers. reset reason: connection timeout.' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestPublicAddress(unittest.TestCase): | ||||||
|  |  | ||||||
|  |     @responses.activate | ||||||
|  |     def test_successfull_response(self): | ||||||
|  |         responses.add(responses.GET, 'https://ifconfig.me', | ||||||
|  |                       body=MOCK_IP, status=200) | ||||||
|  |  | ||||||
|  |         ip = publicAddress() | ||||||
|  |  | ||||||
|  |         self.assertEqual(MOCK_IP, ip) | ||||||
|  |  | ||||||
|  |     @responses.activate | ||||||
|  |     def test_timeout_response(self): | ||||||
|  |         responses.add(responses.GET, 'https://ifconfig.me', | ||||||
|  |                       body=MOCK_TIMEOUT, status=500) | ||||||
|  |  | ||||||
|  |         ip = publicAddress() | ||||||
|  |  | ||||||
|  |         self.assertIsNone(ip) | ||||||
|  |  | ||||||
|  |     @responses.activate | ||||||
|  |     def test_mangled_response(self): | ||||||
|  |         responses.add(responses.GET, 'https://ifconfig.me', | ||||||
|  |                       body='123.22', status=200) | ||||||
|  |  | ||||||
|  |         ip = publicAddress() | ||||||
|  |  | ||||||
|  |         self.assertIsNone(ip) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     unittest.main() | ||||||
		Reference in New Issue
	
	Block a user