--- /dev/null
+import json
+import subprocess
+from unittest import mock
+
+import pytest
+
+from .utils import create_user, create_user_and_song
+
+import littlesongplace as lsp
+
+SUBDATA = json.dumps({
+ "endpoint": "https://example.com",
+ "expirationTime": None,
+ "keys": {"auth": "auth_str", "p256dh": "p256dh_str"}
+})
+
+@pytest.fixture
+def subid(client, user):
+ response = client.post(
+ "/push-notifications/subscribe",
+ headers={"Content-Type": "application/json"},
+ data=SUBDATA)
+ yield response.json["subid"]
+
+@pytest.fixture
+def vapid_keys(app):
+ with open(lsp.datadir.get_vapid_public_key_path(), "w") as keyfile:
+ keyfile.write("vapid-public-key")
+ with open(lsp.datadir.get_vapid_private_key_path(), "w") as keyfile:
+ keyfile.write("vapid-private-key")
+
+################################################################################
+# Subscribe
+
+def test_subscribe_new(client, user):
+ response = client.post(
+ "/push-notifications/subscribe",
+ headers={"Content-Type": "application/json"},
+ data=SUBDATA)
+ assert response.json["status"] == "success"
+ assert response.json["subid"] == 1
+
+def test_subscribe_update_existing(client, user):
+ # Create new
+ for i in range(1, 4):
+ response = client.post(
+ "/push-notifications/subscribe",
+ headers={"Content-Type": "application/json"},
+ data=SUBDATA)
+ assert response.json["subid"] == i
+
+ # Update subid 1
+ response = client.post(
+ "/push-notifications/subscribe?subid=1",
+ headers={"Content-Type": "application/json"},
+ data=SUBDATA)
+
+ assert response.json["status"] == "success"
+ assert response.json["subid"] == 1
+
+def test_subscribe_not_logged_in(client):
+ response = client.post(
+ "/push-notifications/subscribe",
+ headers={"Content-Type": "application/json"},
+ data=SUBDATA)
+ assert response.status_code == 302
+ assert response.headers["Location"] == "/login"
+
+def test_subscribe_other_users_subid(client, user):
+ # Create subid1 for user
+ response = client.post(
+ "/push-notifications/subscribe",
+ headers={"Content-Type": "application/json"},
+ data=SUBDATA)
+
+ # Try to modify as user2
+ create_user(client, "user2", login=True)
+ response = client.post(
+ "/push-notifications/subscribe?subid=1",
+ headers={"Content-Type": "application/json"},
+ data=SUBDATA)
+
+ # Subid ignored, created new one
+ assert response.json["subid"] == 2
+
+def test_subscribe_other_users_subid(client, user):
+ # Create subid1 for user, requesting invalid subid
+ response = client.post(
+ "/push-notifications/subscribe?subid=100",
+ headers={"Content-Type": "application/json"},
+ data=SUBDATA)
+
+ # Subid ignored, created new one
+ assert response.json["subid"] == 1
+
+################################################################################
+# VAPID Key
+
+def test_vapid_public_key(client, vapid_keys):
+ response = client.get("/push-notifications/vapid-public-key")
+ assert response.json["status"] == "ok"
+ assert response.json["public_key"] == "vapid-public-key"
+
+def test_vapid_public_key_no_key(client):
+ response = client.get("/push-notifications/vapid-public-key")
+ assert response.json["status"] == "error"
+
+################################################################################
+# Get Settings
+
+def test_get_settings_defaults(client, user, subid):
+ response = client.get(f"/push-notifications/settings?subid={subid}")
+ assert response.json["comments"] == False
+ assert response.json["songs"] == False
+
+def test_get_settings_not_logged_in(client):
+ response = client.get("/push-notifications/settings?subid=1")
+ assert response.status_code == 302
+ assert response.headers["Location"] == "/login"
+
+def test_get_settings_invalid_subid(client, user):
+ response = client.get("/push-notifications/settings?subid=1")
+ assert response.status_code == 404
+
+def test_get_settings_other_users_subid(client, user, subid):
+ create_user(client, "user2", login=True)
+ response = client.get(f"/push-notifications/settings?subid={subid}")
+ assert response.status_code == 404
+
+################################################################################
+# Update Settings
+
+def test_update_settings_ok(client, user, subid):
+ # Update
+ response = client.post(
+ "/push-notifications/settings",
+ headers={"Content-Type": "application/json"},
+ data=json.dumps({"subid": subid, "comments": True, "songs": True}))
+ assert response.json["status"] == "success"
+
+ # Verify settings applied
+ response = client.get(f"/push-notifications/settings?subid={subid}")
+ assert response.json["comments"] == True
+ assert response.json["songs"] == True
+
+def test_update_settings_not_logged_in(client):
+ response = client.post(
+ "/push-notifications/settings",
+ headers={"Content-Type": "application/json"},
+ data=json.dumps({"subid": 1, "comments": True, "songs": True}))
+ assert response.status_code == 302
+ assert response.headers["Location"] == "/login"
+
+def test_update_settings_invalid_subid(client, user):
+ response = client.post(
+ "/push-notifications/settings",
+ headers={"Content-Type": "application/json"},
+ data=json.dumps({"subid": 1, "comments": True, "songs": True}))
+ assert response.status_code == 404
+
+def test_update_settings_other_users_subid(client, user, subid):
+ create_user(client, "user2", login=True)
+ response = client.post(
+ "/push-notifications/settings",
+ headers={"Content-Type": "application/json"},
+ data=json.dumps({"subid": subid, "comments": True, "songs": True}))
+ assert response.status_code == 404
+
+def test_update_settings_missing_value(client, user, subid):
+ response = client.post(
+ "/push-notifications/settings",
+ headers={"Content-Type": "application/json"},
+ data=json.dumps({"subid": subid, "comments": True})) # Missing songs
+ assert response.status_code == 400
+
+ response = client.post(
+ "/push-notifications/settings",
+ headers={"Content-Type": "application/json"},
+ data=json.dumps({"subid": subid, "songs": True})) # Missing comments
+ assert response.status_code == 400
+
+################################################################################
+# Notification delivery
+
+def _enable_notifications(client, subid, **kwargs):
+ response = client.post(
+ "/push-notifications/settings",
+ headers={"Content-Type": "application/json"},
+ data=json.dumps(dict(subid=subid, **kwargs)))
+ assert response.status_code == 200
+
+@mock.patch("pywebpush.webpush")
+def test_notification_for_new_songs_enabled(pushmock, app, client, user, subid, vapid_keys):
+ _enable_notifications(client, subid, comments=False, songs=True)
+ create_user_and_song(client, "user2")
+
+ with app.app_context():
+ lsp.push_notifications.notify_new_songs_cmd([], standalone_mode=False)
+ lsp.push_notifications.wait_all()
+
+ pushmock.assert_called_once_with(
+ json.loads(SUBDATA),
+ '{"title": "New song from user2", "body": null, "url": "/"}',
+ vapid_private_key="vapid-private-key",
+ vapid_claims={"sub": "mailto:littlesongplace@gmail.com"})
+
+@mock.patch("pywebpush.webpush")
+def test_notification_for_new_songs_disabled(pushmock, app, client, user, subid, vapid_keys):
+ _enable_notifications(client, subid, comments=False, songs=False)
+ create_user_and_song(client, "user2")
+
+ with app.app_context():
+ lsp.push_notifications.notify_new_songs_cmd([], standalone_mode=False)
+ lsp.push_notifications.wait_all()
+
+ pushmock.assert_not_called()
+
+# notification for activity when enabled
+# no notification for activity when disabled
+
+# notification for jam when enabled
+# no notification for jam when disabled
+
+
+
+################################################################################
+# Subscription expiry
+
+# @mock.patch("pywebpush.webpush")
+# def test_subscription_deleted_on_410(client, user, subid):
+# ...
+
+# subscription deleted on 410
+# subscription not deleted on 500