mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-02-15 17:20:21 -06:00
167 lines
7.2 KiB
Python
167 lines
7.2 KiB
Python
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Test notification_urls validation in Watch and Tag API endpoints.
|
|
Ensures that invalid AppRise URLs are rejected when setting notification_urls.
|
|
|
|
Valid AppRise notification URLs use specific protocols like:
|
|
- posts://example.com - POST to HTTP endpoint
|
|
- gets://example.com - GET to HTTP endpoint
|
|
- mailto://user@example.com - Email
|
|
- slack://token/channel - Slack
|
|
- discord://webhook_id/webhook_token - Discord
|
|
- etc.
|
|
|
|
Invalid notification URLs:
|
|
- https://example.com - Plain HTTPS is NOT a valid AppRise notification protocol
|
|
- ftp://example.com - FTP is NOT a valid AppRise notification protocol
|
|
- Plain URLs without proper AppRise protocol prefix
|
|
"""
|
|
|
|
from flask import url_for
|
|
import json
|
|
|
|
|
|
def test_watch_notification_urls_validation(client, live_server, measure_memory_usage, datastore_path):
|
|
"""Test that Watch PUT/POST endpoints validate notification_urls."""
|
|
api_key = live_server.app.config['DATASTORE'].data['settings']['application'].get('api_access_token')
|
|
|
|
# Test 1: Create a watch with valid notification URLs
|
|
valid_urls = ["posts://example.com/notify1", "posts://example.com/notify2"]
|
|
res = client.post(
|
|
url_for("createwatch"),
|
|
data=json.dumps({
|
|
"url": "https://example.com",
|
|
"notification_urls": valid_urls
|
|
}),
|
|
headers={'content-type': 'application/json', 'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 201, "Should accept valid notification URLs on watch creation"
|
|
watch_uuid = res.json['uuid']
|
|
|
|
# Verify the notification URLs were saved
|
|
res = client.get(
|
|
url_for("watch", uuid=watch_uuid),
|
|
headers={'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 200
|
|
assert set(res.json['notification_urls']) == set(valid_urls), "Valid notification URLs should be saved"
|
|
|
|
# Test 2: Try to create a watch with invalid notification URLs (https:// is not valid)
|
|
invalid_urls = ["https://example.com/webhook"]
|
|
res = client.post(
|
|
url_for("createwatch"),
|
|
data=json.dumps({
|
|
"url": "https://example.com",
|
|
"notification_urls": invalid_urls
|
|
}),
|
|
headers={'content-type': 'application/json', 'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 400, "Should reject https:// notification URLs (not a valid AppRise protocol)"
|
|
assert b"is not a valid AppRise URL" in res.data, "Should provide AppRise validation error message"
|
|
|
|
# Test 2b: Also test other invalid protocols
|
|
invalid_urls_ftp = ["ftp://not-apprise-url"]
|
|
res = client.post(
|
|
url_for("createwatch"),
|
|
data=json.dumps({
|
|
"url": "https://example.com",
|
|
"notification_urls": invalid_urls_ftp
|
|
}),
|
|
headers={'content-type': 'application/json', 'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 400, "Should reject ftp:// notification URLs"
|
|
assert b"is not a valid AppRise URL" in res.data, "Should provide AppRise validation error message"
|
|
|
|
# Test 3: Update watch with valid notification URLs
|
|
new_valid_urls = ["posts://newserver.com"]
|
|
res = client.put(
|
|
url_for("watch", uuid=watch_uuid),
|
|
data=json.dumps({"notification_urls": new_valid_urls}),
|
|
headers={'content-type': 'application/json', 'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 200, "Should accept valid notification URLs on watch update"
|
|
|
|
# Verify the notification URLs were updated
|
|
res = client.get(
|
|
url_for("watch", uuid=watch_uuid),
|
|
headers={'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 200
|
|
assert res.json['notification_urls'] == new_valid_urls, "Valid notification URLs should be updated"
|
|
|
|
# Test 4: Try to update watch with invalid notification URLs (plain https:// not valid)
|
|
invalid_https_url = ["https://example.com/webhook"]
|
|
res = client.put(
|
|
url_for("watch", uuid=watch_uuid),
|
|
data=json.dumps({"notification_urls": invalid_https_url}),
|
|
headers={'content-type': 'application/json', 'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 400, "Should reject https:// notification URLs on watch update"
|
|
assert b"is not a valid AppRise URL" in res.data, "Should provide AppRise validation error message"
|
|
|
|
# Test 5: Update watch with non-list notification_urls (caught by OpenAPI schema validation)
|
|
res = client.put(
|
|
url_for("watch", uuid=watch_uuid),
|
|
data=json.dumps({"notification_urls": "not-a-list"}),
|
|
headers={'content-type': 'application/json', 'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 400, "Should reject non-list notification_urls"
|
|
assert b"Validation failed" in res.data or b"is not of type" in res.data
|
|
|
|
# Test 6: Verify original URLs are preserved after failed update
|
|
res = client.get(
|
|
url_for("watch", uuid=watch_uuid),
|
|
headers={'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 200
|
|
assert res.json['notification_urls'] == new_valid_urls, "URLs should remain unchanged after validation failure"
|
|
|
|
|
|
def test_tag_notification_urls_validation(client, live_server, measure_memory_usage, datastore_path):
|
|
"""Test that Tag PUT endpoint validates notification_urls."""
|
|
from changedetectionio.model import Tag
|
|
|
|
api_key = live_server.app.config['DATASTORE'].data['settings']['application'].get('api_access_token')
|
|
datastore = live_server.app.config['DATASTORE']
|
|
|
|
# Create a tag
|
|
tag_uuid = datastore.add_tag(title="Test Tag")
|
|
assert tag_uuid is not None
|
|
|
|
# Test 1: Update tag with valid notification URLs
|
|
valid_urls = ["posts://example.com/tag-notify"]
|
|
res = client.put(
|
|
url_for("tag", uuid=tag_uuid),
|
|
data=json.dumps({"notification_urls": valid_urls}),
|
|
headers={'content-type': 'application/json', 'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 200, "Should accept valid notification URLs on tag update"
|
|
|
|
# Verify the notification URLs were saved
|
|
tag = datastore.data['settings']['application']['tags'][tag_uuid]
|
|
assert tag['notification_urls'] == valid_urls, "Valid notification URLs should be saved to tag"
|
|
|
|
# Test 2: Try to update tag with invalid notification URLs (https:// not valid)
|
|
invalid_urls = ["https://example.com/webhook"]
|
|
res = client.put(
|
|
url_for("tag", uuid=tag_uuid),
|
|
data=json.dumps({"notification_urls": invalid_urls}),
|
|
headers={'content-type': 'application/json', 'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 400, "Should reject https:// notification URLs on tag update"
|
|
assert b"is not a valid AppRise URL" in res.data, "Should provide AppRise validation error message"
|
|
|
|
# Test 3: Update tag with non-list notification_urls (caught by OpenAPI schema validation)
|
|
res = client.put(
|
|
url_for("tag", uuid=tag_uuid),
|
|
data=json.dumps({"notification_urls": "not-a-list"}),
|
|
headers={'content-type': 'application/json', 'x-api-key': api_key}
|
|
)
|
|
assert res.status_code == 400, "Should reject non-list notification_urls"
|
|
assert b"Validation failed" in res.data or b"is not of type" in res.data
|
|
|
|
# Test 4: Verify original URLs are preserved after failed update
|
|
tag = datastore.data['settings']['application']['tags'][tag_uuid]
|
|
assert tag['notification_urls'] == valid_urls, "URLs should remain unchanged after validation failure"
|