Compare commits

..

5 Commits

12 changed files with 34 additions and 41 deletions

View File

@@ -2,7 +2,7 @@
# Read more https://github.com/dgtlmoon/changedetection.io/wiki
__version__ = '0.45.23'
__version__ = '0.45.24'
from changedetectionio.strtobool import strtobool
from json.decoder import JSONDecodeError

View File

@@ -679,7 +679,10 @@ def changedetection_app(config=None, datastore_o=None):
if request.method == 'POST' and form.validate():
extra_update_obj = {}
extra_update_obj = {
'consecutive_filter_failures': 0,
'last_error' : False
}
if request.args.get('unpause_on_save'):
extra_update_obj['paused'] = False

View File

@@ -5,6 +5,7 @@ from changedetectionio.notification import (
default_notification_title,
)
# Equal to or greater than this number of FilterNotFoundInResponse exceptions will trigger a filter-not-found notification
_FILTER_FAILURE_THRESHOLD_ATTEMPTS_DEFAULT = 6
DEFAULT_SETTINGS_HEADERS_USERAGENT='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36'

View File

@@ -2,13 +2,12 @@
import time
from flask import url_for
from . util import live_server_setup
from .util import live_server_setup, wait_for_all_checks
def test_basic_auth(client, live_server):
live_server_setup(live_server)
# Give the endpoint time to spin up
time.sleep(1)
# Add our URL to the import page
test_url = url_for('test_basicauth_method', _external=True).replace("//","//myuser:mypass@")
@@ -19,8 +18,8 @@ def test_basic_auth(client, live_server):
follow_redirects=True
)
assert b"1 Imported" in res.data
wait_for_all_checks(client)
time.sleep(1)
# Check form validation
res = client.post(
url_for("edit_page", uuid="first"),
@@ -29,7 +28,7 @@ def test_basic_auth(client, live_server):
)
assert b"Updated watch." in res.data
time.sleep(1)
wait_for_all_checks(client)
res = client.get(
url_for("preview_page", uuid="first"),
follow_redirects=True

View File

@@ -100,7 +100,7 @@ def test_check_ldjson_price_autodetect(client, live_server):
# Accept it
uuid = extract_UUID_from_client(client)
time.sleep(1)
client.get(url_for('price_data_follower.accept', uuid=uuid, follow_redirects=True))
wait_for_all_checks(client)

View File

@@ -62,9 +62,6 @@ def test_check_basic_change_detection_functionality(client, live_server):
# Make a change
set_modified_response()
res = urlopen(url_for('test_endpoint', _external=True))
assert b'which has this one new line' in res.read()
# Force recheck
res = client.get(url_for("form_watch_checknow"), follow_redirects=True)
assert b'1 watches queued for rechecking.' in res.data

View File

@@ -3,7 +3,7 @@
import time
from flask import url_for
from .util import live_server_setup
from .util import live_server_setup, wait_for_all_checks
import pytest
@@ -27,9 +27,6 @@ def set_html_response():
def test_check_encoding_detection(client, live_server):
set_html_response()
# Give the endpoint time to spin up
time.sleep(1)
# Add our URL to the import page
test_url = url_for('test_endpoint', content_type="text/html", _external=True)
client.post(
@@ -39,7 +36,7 @@ def test_check_encoding_detection(client, live_server):
)
# Give the thread time to pick it up
time.sleep(2)
wait_for_all_checks(client)
res = client.get(
url_for("preview_page", uuid="first"),
@@ -56,9 +53,6 @@ def test_check_encoding_detection(client, live_server):
def test_check_encoding_detection_missing_content_type_header(client, live_server):
set_html_response()
# Give the endpoint time to spin up
time.sleep(1)
# Add our URL to the import page
test_url = url_for('test_endpoint', _external=True)
client.post(
@@ -67,8 +61,7 @@ def test_check_encoding_detection_missing_content_type_header(client, live_serve
follow_redirects=True
)
# Give the thread time to pick it up
time.sleep(2)
wait_for_all_checks(client)
res = client.get(
url_for("preview_page", uuid="first"),

View File

@@ -29,6 +29,7 @@ def test_check_extract_text_from_diff(client, live_server):
# Load in 5 different numbers/changes
last_date=""
for n in range(5):
time.sleep(1)
# Give the thread time to pick it up
print("Bumping snapshot and checking.. ", n)
last_date = str(time.time())

View File

@@ -21,10 +21,11 @@ def set_response_with_filter():
f.write(test_return_data)
return None
def run_filter_test(client, content_filter):
def run_filter_test(client, live_server, content_filter):
# Response WITHOUT the filter ID element
set_original_response()
# Give the endpoint time to spin up
time.sleep(1)
# cleanup for the next
client.get(
url_for("form_delete", uuid="all"),
@@ -79,6 +80,7 @@ def run_filter_test(client, content_filter):
"include_filters": content_filter,
"fetch_backend": "html_requests"})
# A POST here will also reset the filter failure counter (filter_failure_notification_threshold_attempts)
res = client.post(
url_for("edit_page", uuid="first"),
data=notification_form_data,
@@ -91,20 +93,21 @@ def run_filter_test(client, content_filter):
# Now the notification should not exist, because we didnt reach the threshold
assert not os.path.isfile("test-datastore/notification.txt")
# -2 because we would have checked twice above (on adding and on edit)
# recheck it up to just before the threshold, including the fact that in the previous POST it would have rechecked (and incremented)
for i in range(0, App._FILTER_FAILURE_THRESHOLD_ATTEMPTS_DEFAULT-2):
res = client.get(url_for("form_watch_checknow"), follow_redirects=True)
client.get(url_for("form_watch_checknow"), follow_redirects=True)
wait_for_all_checks(client)
assert not os.path.isfile("test-datastore/notification.txt"), f"test-datastore/notification.txt should not exist - Attempt {i}"
time.sleep(2) # delay for apprise to fire
assert not os.path.isfile("test-datastore/notification.txt"), f"test-datastore/notification.txt should not exist - Attempt {i} when threshold is {App._FILTER_FAILURE_THRESHOLD_ATTEMPTS_DEFAULT}"
# We should see something in the frontend
res = client.get(url_for("index"))
assert b'Warning, no filters were found' in res.data
# One more check should trigger it (see -2 above)
client.get(url_for("form_watch_checknow"), follow_redirects=True)
wait_for_all_checks(client)
# One more check should trigger the _FILTER_FAILURE_THRESHOLD_ATTEMPTS_DEFAULT threshold
client.get(url_for("form_watch_checknow"), follow_redirects=True)
wait_for_all_checks(client)
time.sleep(2) # delay for apprise to fire
# Now it should exist and contain our "filter not found" alert
assert os.path.isfile("test-datastore/notification.txt")
@@ -149,13 +152,9 @@ def test_setup(live_server):
live_server_setup(live_server)
def test_check_include_filters_failure_notification(client, live_server):
set_original_response()
wait_for_all_checks(client)
run_filter_test(client, '#nope-doesnt-exist')
run_filter_test(client, live_server,'#nope-doesnt-exist')
def test_check_xpath_filter_failure_notification(client, live_server):
set_original_response()
time.sleep(1)
run_filter_test(client, '//*[@id="nope-doesnt-exist"]')
run_filter_test(client, live_server, '//*[@id="nope-doesnt-exist"]')
# Test that notification is never sent

View File

@@ -69,6 +69,7 @@ def test_rss_and_token(client, live_server):
wait_for_all_checks(client)
set_modified_response()
time.sleep(1)
client.get(url_for("form_watch_checknow"), follow_redirects=True)
wait_for_all_checks(client)
@@ -87,7 +88,7 @@ def test_rss_and_token(client, live_server):
assert b"Access denied, bad token" not in res.data
assert b"Random content" in res.data
res = client.get(url_for("form_delete", uuid="all"), follow_redirects=True)
client.get(url_for("form_delete", uuid="all"), follow_redirects=True)
def test_basic_cdata_rss_markup(client, live_server):
#live_server_setup(live_server)

View File

@@ -348,7 +348,7 @@ class update_worker(threading.Thread):
# Send notification if we reached the threshold?
threshold = self.datastore.data['settings']['application'].get('filter_failure_notification_threshold_attempts',
0)
logger.error(f"Filter for {uuid} not found, consecutive_filter_failures: {c}")
logger.warning(f"Filter for {uuid} not found, consecutive_filter_failures: {c}")
if threshold > 0 and c >= threshold:
if not self.datastore.data['watching'][uuid].get('notification_muted'):
self.send_filter_failure_notification(uuid)
@@ -362,7 +362,6 @@ class update_worker(threading.Thread):
# Yes fine, so nothing todo, don't continue to process.
process_changedetection_results = False
changed_detected = False
self.datastore.update_watch(uuid=uuid, update_obj={'last_error': False})
except content_fetchers.exceptions.BrowserConnectError as e:
self.datastore.update_watch(uuid=uuid,
update_obj={'last_error': e.msg

View File

@@ -1,7 +1,7 @@
# Used by Pyppeteer
pyee
eventlet==0.35.2 # related to dnspython fixes
eventlet>=0.36.1 # fixes SSL error on Python 3.12
feedgen~=0.9
flask-compress
# 0.6.3 included compatibility fix for werkzeug 3.x (2.x had deprecation of url handlers)
@@ -29,7 +29,7 @@ chardet>2.3.0
wtforms~=3.0
jsonpath-ng~=1.5.3
dnspython==2.6.1
dnspython==2.6.1 # related to eventlet fixes
# jq not available on Windows so must be installed manually