def test_reset_password(client, user_info): """A user can reset their password using a link sent to their email.""" new_password = '******' assert new_password != user_info['password'] register_user(client, user_info) log_out_current_user(client) with mail.record_messages() as outbox: rv = client.get(url_for('user.password_recovery')) rv = client.post(url_for('user.password_recovery'), data=dict(csrf_token=csrf_token(rv.data), email=user_info['email'], captcha='xyzzy')) message = outbox.pop() assert message.send_to == {user_info['email']} soup = BeautifulSoup(message.html, 'html.parser') token = soup.a['href'].split('/')[-1] rv = client.get(url_for('user.password_reset', token=token), follow_redirects=True) rv = client.post(url_for('do.reset'), data=dict(csrf_token=csrf_token(rv.data), user=get_value(rv.data, 'user'), key=get_value(rv.data, 'key'), password=new_password, confirm=new_password)) log_out_current_user(client) user_info['password'] = new_password log_in_user(client, user_info, expect_success=True)
def test_admin_can_ban_and_unban_user(client, user_info, user2_info): register_user(client, user_info) register_user(client, user2_info) promote_user_to_admin(client, user2_info) username = user_info["username"] rv = client.get(url_for("user.view", user=username)) client.post( url_for("do.ban_user", username=username), data=dict(csrf_token=csrf_token(rv.data)), follow_redirects=True, ) # For now, banning makes you unable to log in. log_out_current_user(client) log_in_user(client, user_info, expect_success=False) log_in_user(client, user2_info) rv = client.get(url_for("user.view", user=username)) client.post( url_for("do.unban_user", username=username), data=dict(csrf_token=csrf_token(rv.data)), follow_redirects=True, ) log_out_current_user(client) log_in_user(client, user_info)
def test_change_password(client, user_info): """A user can change their password and log in with the new password.""" register_user(client, user_info) new_password = "******" + "\N{PARTIAL DIFFERENTIAL}" assert new_password != user_info["password"] rv = client.get(url_for("user.edit_account")) rv = client.post( url_for("do.edit_account"), data=dict( csrf_token=csrf_token(rv.data), oldpassword=user_info["password"], password=new_password, confirm=new_password, ), follow_redirects=True, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) # Try to log in with the old password log_in_user(client, user_info, expect_success=False) new_info = dict(user_info) new_info.update(password=new_password) log_in_user(client, new_info, expect_success=True)
def test_admin_can_ban_email_domain(client, user_info): register_user(client, user_info) promote_user_to_admin(client, user_info) rv = client.get(url_for('admin.domains', domain_type='email')) rv = client.post(url_for('do.ban_domain', domain_type='email'), data=dict(csrf_token=csrf_token(rv.data), domain='spam4u.com'), follow_redirects=True) reply = json.loads(rv.data.decode('utf-8')) assert reply['status'] == 'ok' log_out_current_user(client) rv = client.get(url_for('auth.register')) with mail.record_messages() as outbox: data = dict(csrf_token=csrf_token(rv.data), username='******', password='******', confirm='Safe123#$@lolnot', email_required='*****@*****.**', invitecode='', accept_tos=True, captcha='xyzzy') rv = client.post(url_for('auth.register'), data=data, follow_redirects=True) assert len(outbox) == 0 assert b'do not accept emails' in rv.data assert b'Register' in rv.data assert b'Log out' not in rv.data
def test_admin_can_ban_email_domain(client, user_info, test_config): register_user(client, user_info) promote_user_to_admin(client, user_info) rv = client.get(url_for("admin.domains", domain_type="email")) rv = client.post( url_for("do.ban_domain", domain_type="email"), data=dict(csrf_token=csrf_token(rv.data), domain="spam4u.com"), follow_redirects=True, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) rv = client.get(url_for("auth.register")) with mail.record_messages() as outbox: data = dict( csrf_token=csrf_token(rv.data), username="******", password="******", confirm="Safe123#$@lolnot", email_required="*****@*****.**", invitecode="", accept_tos=True, captcha="xyzzy", ) rv = client.post(url_for("auth.register"), data=data, follow_redirects=True) assert len(outbox) == 0 assert b"do not accept emails" in rv.data assert b"Register" in rv.data assert b"Log out" not in rv.data
def promote_user_to_admin(client, user_info): """Assuming user_info is the info for the logged-in user, promote them to admin and leave them logged in. """ log_out_current_user(client) admin = User.get(fn.Lower(User.name) == user_info['username']) UserMetadata.create(uid=admin.uid, key='admin', value='1') log_in_user(client, user_info)
def test_send_and_receive_pm(client, user_info, user2_info): """Send and receive a private message.""" username = user_info["username"] register_user(client, user_info) register_user(client, user2_info) # User2 sends User a message. rv = client.get(url_for("user.view", user=username)) assert rv.status == "200 OK" client.post( url_for("do.create_sendmsg"), data=dict( csrf_token=csrf_token(rv.data), to=username, subject="Testing", content="Test Content", ), follow_redirects=True, ) # User2 sees the message in Sent Messages. rv = client.get(url_for("messages.view_messages_sent"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present(rv.data, [username, "Testing", "Test Content"]) log_out_current_user(client, verify=True) log_in_user(client, user_info, expect_success=True) # User has one new message. rv = client.get(url_for("home.index"), follow_redirects=True) assert rv.status == "200 OK" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.find(href=url_for("messages.inbox_sort")) assert link.get_text().strip() == "1" # User sees the message on the inbox page. rv = client.get(url_for("messages.inbox_sort"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user2_info["username"], "Testing", "Test Content"] ) # User marks the message as read. soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") tag = soup.find(lambda tag: tag.has_attr("data-mid")) mid = tag.attrs["data-mid"] rv = client.post( url_for("do.read_pm", mid=mid), data=dict(csrf_token=csrf_token(rv.data)), ) # User returns to home page; notifications count now 0. rv = client.get(url_for("home.index"), follow_redirects=True) assert rv.status == "200 OK" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.find(href=url_for("messages.inbox_sort")) assert link.get_text().strip() == "0"
def test_password_required_to_change_recovery_email(client, user_info, test_config): """Changing the password recovery requires the correct password.""" register_user(client, user_info) wrong_password = "******" new_email = "*****@*****.**" assert wrong_password != user_info["password"] assert new_email != user_info["email"] rv = client.get(url_for("user.edit_account")) data = dict( csrf_token=csrf_token(rv.data), email_required=new_email, oldpassword=wrong_password, password="", confirm="", ) # No confirmation email should be sent. with mail.record_messages() as outbox: rv = client.post(url_for("do.edit_account"), data=data, follow_redirects=True) assert len(outbox) == 0 log_out_current_user(client) # Verify password recovery email goes to the right place. with mail.record_messages() as outbox: rv = client.get(url_for("user.password_recovery")) rv = client.post( url_for("user.password_recovery"), data=dict(csrf_token=csrf_token(rv.data), email=new_email, captcha="xyzzy"), ) assert len(outbox) == 0 rv = client.get(url_for("user.password_recovery")) rv = client.post( url_for("user.password_recovery"), data=dict( csrf_token=csrf_token(rv.data), email=user_info["email"], captcha="xyzzy", ), ) assert len(outbox) == 1
def test_resend_registration_email_after_confirmation(client, user_info, test_config): """Registration instructions cannot be resent after confirmation.""" with mail.record_messages() as outbox: rv = client.get(url_for("auth.register")) data = dict( csrf_token=csrf_token(rv.data), username=user_info["username"], password=user_info["password"], confirm=user_info["password"], email_required=user_info["email"], invitecode="", accept_tos=True, captcha="xyzzy", ) rv = client.post(url_for("auth.register"), data=data, follow_redirects=True) assert b"spam" in rv.data # Telling user to go check it. # Find the resend link. soup = BeautifulSoup(rv.data, "html.parser") link = soup.find_all( lambda tag: tag.name == "a" and tag.string == "Resend")[0] url = link["href"] # Find and use the confirmation link. message = outbox[-1] soup = BeautifulSoup(message.html, "html.parser") token = soup.a["href"].split("/")[-1] rv = client.get(url_for("auth.login_with_token", token=token), follow_redirects=True) assert b"Log out" in rv.data log_out_current_user(client, verify=True) # Request the resend form and verify an error message is shown. rv = client.get(url, follow_redirects=True) assert b"The link you used is invalid or has expired" in rv.data assert b"Log out" not in rv.data
def test_reset_password(client, user_info): """A user can reset their password using a link sent to their email.""" new_password = "******" assert new_password != user_info["password"] register_user(client, user_info) log_out_current_user(client) with mail.record_messages() as outbox: rv = client.get(url_for("user.password_recovery")) rv = client.post( url_for("user.password_recovery"), data=dict( csrf_token=csrf_token(rv.data), email=user_info["email"], captcha="xyzzy", ), ) message = outbox.pop() assert message.send_to == {user_info["email"]} soup = BeautifulSoup(message.html, "html.parser") token = soup.a["href"].split("/")[-1] rv = client.get( url_for("user.password_reset", token=token), follow_redirects=True ) rv = client.post( url_for("do.reset"), data=dict( csrf_token=csrf_token(rv.data), user=get_value(rv.data, "user"), key=get_value(rv.data, "key"), password=new_password, confirm=new_password, ), ) log_out_current_user(client) user_info["password"] = new_password log_in_user(client, user_info, expect_success=True)
def test_password_required_to_change_recovery_email(client, user_info): """Changing the password recovery requires the correct password.""" register_user(client, user_info) wrong_password = '******' new_email = '*****@*****.**' assert wrong_password != user_info['password'] assert new_email != user_info['email'] rv = client.get(url_for('user.edit_account')) data = dict(csrf_token=csrf_token(rv.data), email_required=new_email, oldpassword=wrong_password, password='', confirm='') # No confirmation email should be sent. with mail.record_messages() as outbox: rv = client.post(url_for('do.edit_account'), data=data, follow_redirects=True) assert len(outbox) == 0 log_out_current_user(client) # Verify password recovery email goes to the right place. with mail.record_messages() as outbox: rv = client.get(url_for('user.password_recovery')) rv = client.post(url_for('user.password_recovery'), data=dict(csrf_token=csrf_token(rv.data), email=new_email, captcha='xyzzy')) assert len(outbox) == 0 rv = client.get(url_for('user.password_recovery')) rv = client.post(url_for('user.password_recovery'), data=dict(csrf_token=csrf_token(rv.data), email=user_info['email'], captcha='xyzzy')) assert len(outbox) == 1
def test_change_password(client, user_info): """A user can change their password and log in with the new password.""" register_user(client, user_info) new_password = '******' + '\N{PARTIAL DIFFERENTIAL}' assert new_password != user_info['password'] rv = client.get(url_for('user.edit_account')) rv = client.post(url_for('do.edit_account'), data=dict(csrf_token=csrf_token(rv.data), oldpassword=user_info['password'], password=new_password, confirm=new_password), follow_redirects=True) reply = json.loads(rv.data.decode('utf-8')) assert reply['status'] == 'ok' log_out_current_user(client) # Try to log in with the old password log_in_user(client, user_info, expect_success=False) new_info = dict(user_info) new_info.update(password=new_password) log_in_user(client, new_info, expect_success=True)
def test_ban_notification(client, user_info, user2_info): "Notifications are sent for sub bans." config.update_value("site.sub_creation_min_level", 0) receiver, mod = user_info, user2_info register_user(client, receiver) receiver_uid = User.get(User.name == receiver["username"]).uid log_out_current_user(client) register_user(client, mod) mod_uid = User.get(User.name == mod["username"]).uid create_sub(client) log_out_current_user(client) # Receiver blocks mod. log_in_user(client, receiver) rv_index = client.get(url_for("home.index", sub="test")) csrf = csrf_token(rv_index.data) data = { "csrf_token": csrf, "view_messages": "show", "view_content": "hide", } rv = client.post(url_for("do.edit_ignore", uid=mod_uid), data=data, follow_redirects=True) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) # Mod bans receiver. log_in_user(client, mod) data = { "csrf_token": csrf, "user": receiver["username"], "reason": "serious reason", "expires": None, } rv = client.post( url_for("do.ban_user_sub", sub="test"), data=data, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Mod un-bans receiver. rv = client.post( url_for("do.remove_sub_ban", sub="test", user=receiver["username"]), data={"csrf_token": csrf}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) # Receiver checks notifications. They should not be ignored, # because they are mod actions. log_in_user(client, receiver) assert get_notification_count(receiver_uid)["notifications"] == 2 rv = client.get(url_for("messages.view_notifications")) assert b"banned you from" in rv.data assert b"serious reason" in rv.data assert b"unbanned you from" in rv.data # After checking, the notification is marked read. assert get_notification_count(receiver_uid)["notifications"] == 0 # Receiver unblocks mod. data = { "csrf_token": csrf_token(rv.data), "view_messages": "show", "view_content": "show", } rv = client.post(url_for("do.edit_ignore", uid=mod_uid), data=data, follow_redirects=True) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Receiver checks notifications again. rv = client.get(url_for("messages.view_notifications")) assert b"banned you from" in rv.data assert b"serious reason" in rv.data assert b"unbanned you from" in rv.data
def test_reply_notification(client, sub_mod, user_info, user2_info, user3_info): "Notifications are sent for post and comment replies." sender, receiver, mod = user_info, user2_info, user3_info config.update_value("site.sub_creation_min_level", 0) register_user(client, sender) sender_uid = User.get(User.name == sender["username"]).uid if sub_mod == "sender": create_sub(client) log_out_current_user(client) register_user(client, receiver) receiver_uid = User.get(User.name == receiver["username"]).uid if sub_mod == "receiver": create_sub(client) log_out_current_user(client) if sub_mod == "neither": register_user(client, mod) create_sub(client) log_out_current_user(client) # Receiver makes a post that can be replied to log_in_user(client, receiver) rv = client.get(url_for("subs.submit", ptype="text", sub="test")) csrf = csrf_token(rv.data) data = { "csrf_token": csrf, "title": "the title", "ptype": "text", "content": "the content", } rv = client.post( url_for("subs.submit", ptype="text", sub="test"), data=data, follow_redirects=False, ) assert rv.status == "302 FOUND" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.a.get_text() pid = link.split("/")[-1] # Receiver makes a comment that can be replied to. rv = client.get(link, follow_redirects=True) assert b"the title | test" in rv.data data = { "csrf_token": csrf, "post": pid, "parent": "0", "comment": "OP reply", } rv = client.post(url_for("do.create_comment", pid=pid), data=data, follow_redirects=False) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" cid = reply["cid"] # Receiver blocks sender. data = { "csrf_token": csrf, "view_messages": "show", "view_content": "hide", } rv = client.post(url_for("do.edit_ignore", uid=sender_uid), data=data, follow_redirects=True) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) # Sender replies to the post. log_in_user(client, sender) rv = client.get(link, follow_redirects=True) assert b"the title | test" in rv.data data = { "csrf_token": csrf, "post": pid, "parent": "0", "comment": "the comment", } rv = client.post(url_for("do.create_comment", pid=pid), data=data, follow_redirects=True) assert b"the comment" in rv.data # Sender replies to the comment. rv = client.get(link, follow_redirects=True) data = { "csrf_token": csrf_token(rv.data), "post": pid, "parent": cid, "comment": "comment reply", } rv = client.post(url_for("do.create_comment", pid=pid), data=data, follow_redirects=True) log_out_current_user(client) # Depending on who is the mod of the sub, should these notifications # be visible when the receiver has the sender blocked? expected = {"sender": True, "receiver": True, "neither": False}[sub_mod] assert get_notification_count(receiver_uid)["notifications"] == ( 2 if expected else 0) # Receiver checks notifications. log_in_user(client, receiver) rv = client.get(url_for("messages.view_notifications")) assert (b"replied to your post" in rv.data) == expected assert (b"the comment" in rv.data) == expected assert (b"replied to your comment" in rv.data) == expected assert (b"comment reply" in rv.data) == expected # Receiver unblocks sender. data = { "csrf_token": csrf, "view_messages": "show", "view_content": "show", } rv = client.post(url_for("do.edit_ignore", uid=sender_uid), data=data, follow_redirects=True) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Receiver checks notifications. rv = client.get(url_for("messages.view_notifications")) assert b"replied to your post" in rv.data assert b"the comment" in rv.data assert b"replied to your comment" in rv.data assert b"comment reply" in rv.data # Notifications should be marked read. assert get_notification_count(receiver_uid)["notifications"] == 0
def test_save_and_delete_pm(client, user_info, user2_info): """Save and delete a private message.""" username = user_info["username"] register_user(client, user_info) register_user(client, user2_info) # User2 sends User a message. rv = client.get(url_for("user.view", user=username)) assert rv.status == "200 OK" client.post( url_for("do.create_sendmsg"), data=dict( csrf_token=csrf_token(rv.data), to=username, subject="Testing", content="Test Content", ), follow_redirects=True, ) # Switch users. log_out_current_user(client, verify=True) log_in_user(client, user_info, expect_success=True) # Message visible in User's inbox. rv = client.get(url_for("messages.inbox_sort"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user2_info["username"], "Testing", "Test Content"] ) # User saves the message. soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") tag = soup.find(lambda tag: tag.has_attr("data-mid")) mid = tag.attrs["data-mid"] rv = client.post( url_for("do.save_pm", mid=mid), data=dict(csrf_token=csrf_token(rv.data)), follow_redirects=True, ) assert b"error" not in rv.data # Message now no longer appears in inbox. rv = client.get(url_for("messages.inbox_sort"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user2_info["username"], "Testing", "Test Content"], exclude=True ) # Message appears in saved messages. rv = client.get(url_for("messages.view_saved_messages"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user2_info["username"], "Testing", "Test Content"] ) # User deletes message from saved messages. rv = client.post( url_for("do.delete_pm", mid=mid), data=dict(csrf_token=csrf_token(rv.data)), follow_redirects=True, ) assert b"error" not in rv.data # Message is no longer in saved messages. rv = client.get(url_for("messages.view_saved_messages"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user2_info["username"], "Testing", "Test Content"], exclude=True ) # Message is not in User's inbox either. rv = client.get(url_for("messages.inbox_sort"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user2_info["username"], "Testing", "Test Content"], exclude=True )
def test_invite_code_required_for_registration(client, user_info, user2_info): """If invite codes are required, trying to register without one will fail.""" register_user(client, user_info) promote_user_to_admin(client, user_info) # Enable invite codes. rv = client.get(url_for("admin.invitecodes")) data = dict(csrf_token=csrf_token(rv.data), enableinvitecode=True, minlevel=3, maxcodes=10) rv = client.post(url_for("do.use_invite_code"), data=data, follow_redirects=True) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Create an invite code. rv = client.get(url_for("admin.invitecodes")) data = dict(csrf_token=csrf_token(rv.data), code="abcde", uses=10, expires="") client.post(url_for("admin.invitecodes"), data=data, follow_redirects=True) log_out_current_user(client) # Now try to register a new user without an invite code. rv = client.get(url_for("auth.register")) data = dict( csrf_token=csrf_token(rv.data), username=user2_info["username"], password=user2_info["password"], confirm=user2_info["password"], invitecode="", email_optional=user2_info["email"], accept_tos=True, captcha="xyzzy", ) rv = client.post(url_for("auth.register"), data=data, follow_redirects=True) assert b"Invalid invite code" in rv.data # Now try to register a new user with an incorrect invite code. rv = client.get(url_for("auth.register")) data = dict( csrf_token=csrf_token(rv.data), username=user2_info["username"], password=user2_info["password"], confirm=user2_info["password"], invitecode="xyzzy", email_optional=user2_info["email"], accept_tos=True, captcha="xyzzy", ) rv = client.post(url_for("auth.register"), data=data, follow_redirects=True) assert b"Invalid invite code" in rv.data # Now try to register a new user with a valid invite code. rv = client.get(url_for("auth.register")) data = dict( csrf_token=csrf_token(rv.data), username=user2_info["username"], password=user2_info["password"], confirm=user2_info["password"], invitecode="abcde", email_optional=user2_info["email"], accept_tos=True, captcha="xyzzy", ) rv = client.post(url_for("auth.register"), data=data, follow_redirects=True) assert b"Log out" in rv.data
def test_exchange_pm(client, user_info, user2_info): """Send a private message and have an exchange of replies.""" username = user_info["username"] register_user(client, user_info) register_user(client, user2_info) # User2 sends User a message. rv = client.get(url_for("user.view", user=username)) assert rv.status == "200 OK" rv = client.post( url_for("do.create_sendmsg"), data=dict( csrf_token=csrf_token(rv.data), to=username, subject="Testing", content="Test Content", ), follow_redirects=True, ) assert b"error" not in rv.data log_out_current_user(client, verify=True) log_in_user(client, user_info, expect_success=True) # User sees the message on the inbox page. rv = client.get(url_for("messages.inbox_sort"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user2_info["username"], "Testing", "Test Content"] ) # User replies to the message. soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") tag = soup.find(lambda tag: tag.has_attr("data-mid")) mid = tag.attrs["data-mid"] rv = client.post( url_for("do.create_replymsg"), data=dict( csrf_token=csrf_token(rv.data), mid=mid, content="Test reply content" ), ) assert b"error" not in rv.data # User sees the message in Sent Messages. rv = client.get(url_for("messages.view_messages_sent"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user2_info["username"], "Re: Testing", "Test reply content"] ) log_out_current_user(client, verify=True) log_in_user(client, user2_info, expect_success=True) # User2 has one new message. rv = client.get(url_for("home.index"), follow_redirects=True) assert rv.status == "200 OK" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.find(href=url_for("messages.inbox_sort")) assert link.get_text().strip() == "1" # User2 sees the message on the inbox page. rv = client.get(url_for("messages.inbox_sort"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user_info["username"], "Re: Testing", "Test reply content"] ) # User2 replies to the reply. soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") tag = soup.find(lambda tag: tag.has_attr("data-mid")) mid = tag.attrs["data-mid"] rv = client.post( url_for("do.create_replymsg"), data=dict( csrf_token=csrf_token(rv.data), mid=mid, content="Test 2nd reply content" ), ) assert b"error" not in rv.data # User2 sees reply in Sent Messages. rv = client.get(url_for("messages.view_messages_sent"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user_info["username"], "Re: Testing", "Test 2nd reply content"] ) assert b"Re: Re: Testing" not in rv.data log_out_current_user(client, verify=True) log_in_user(client, user_info, expect_success=True) # User now has two new messages. rv = client.get(url_for("home.index"), follow_redirects=True) assert rv.status == "200 OK" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.find(href=url_for("messages.inbox_sort")) assert link.get_text().strip() == "2" # User sees the message on the inbox page. rv = client.get(url_for("messages.inbox_sort"), follow_redirects=True) assert rv.status == "200 OK" assert b"Test 2nd reply content" in rv.data # User uses the mark all as read link. rv = client.post(url_for("do.readall_msgs"), data={}) assert b"error" not in rv.data # User now has no new messages. rv = client.get(url_for("home.index"), follow_redirects=True) assert rv.status == "200 OK" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.find(href=url_for("messages.inbox_sort")) assert link.get_text().strip() == "0"
def test_change_password_recovery_email(client, user_info, test_config): """The user can change their password recovery email.""" register_user(client, user_info) new_email = "*****@*****.**" assert new_email != user_info["email"] rv = client.get(url_for("user.edit_account")) data = dict( csrf_token=csrf_token(rv.data), oldpassword=user_info["password"], password="", confirm="", ) if email_validation_is_required(): data["email_required"] = new_email else: data["email_optional"] = new_email with mail.record_messages() as outbox: rv = client.post(url_for("do.edit_account"), data=data, follow_redirects=True) log_out_current_user(client) if email_validation_is_required(): message = outbox.pop() # Make sure that password recovery emails go to the former address # if the new one has not yet been confirmed. rv = client.get(url_for("user.password_recovery")) rv = client.post( url_for("user.password_recovery"), data=dict(csrf_token=csrf_token(rv.data), email=new_email, captcha="xyzzy"), ) assert len(outbox) == 0 rv = client.get(url_for("user.password_recovery")) rv = client.post( url_for("user.password_recovery"), data=dict( csrf_token=csrf_token(rv.data), email=user_info["email"], captcha="xyzzy", ), ) assert outbox.pop().send_to == {user_info["email"]} # Now click the confirm link. assert message.send_to == {new_email} soup = BeautifulSoup(message.html, "html.parser") token = soup.a["href"].split("/")[-1] rv = client.get(url_for("user.confirm_email_change", token=token), follow_redirects=True) else: assert len(outbox) == 0 # Verify password recovery email goes to the right place. with mail.record_messages() as outbox: rv = client.get(url_for("user.password_recovery")) rv = client.post( url_for("user.password_recovery"), data=dict( csrf_token=csrf_token(rv.data), email=user_info["email"], captcha="xyzzy", ), ) assert len(outbox) == 0 rv = client.get(url_for("user.password_recovery")) rv = client.post( url_for("user.password_recovery"), data=dict(csrf_token=csrf_token(rv.data), email=new_email, captcha="xyzzy"), ) assert outbox.pop().send_to == {new_email}
def test_block_pm(client, user_info, user2_info): """Block and unblock private messages from a user.""" username = user_info["username"] register_user(client, user_info) register_user(client, user2_info) # User2 sends User a message. rv = client.get(url_for("user.view", user=username)) assert rv.status == "200 OK" client.post( url_for("do.create_sendmsg"), data=dict( csrf_token=csrf_token(rv.data), to=username, subject="Testing", content="Test Content", ), follow_redirects=True, ) log_out_current_user(client, verify=True) log_in_user(client, user_info, expect_success=True) # User blocks User2. user2 = User.get(User.name == user2_info["username"]) rv = client.post( url_for("do.ignore_user", uid=user2.uid), data=dict(csrf_token=csrf_token(rv.data)), ) # User doesn't have a notification for blocked message. rv = client.get(url_for("home.index"), follow_redirects=True) assert rv.status == "200 OK" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.find(href=url_for("messages.inbox_sort")) assert link.get_text().strip() == "0" # Message not visible in User's inbox. rv = client.get(url_for("messages.inbox_sort"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user2_info["username"], "Testing", "Test Content"], exclude=True ) # User2 shows up on User's ignore page. rv = client.get(url_for("user.view_ignores")) assert rv.status == "200 OK" assert user2_info["username"].encode("utf-8") in rv.data # User unblocks User2. rv = client.post( url_for("do.ignore_user", uid=user2.uid), data=dict(csrf_token=csrf_token(rv.data)), ) # User now has a notification for message. rv = client.get(url_for("home.index"), follow_redirects=True) assert rv.status == "200 OK" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.find(href=url_for("messages.inbox_sort")) assert link.get_text().strip() == "1" # Message now visible in User's inbox. rv = client.get(url_for("messages.inbox_sort"), follow_redirects=True) assert rv.status == "200 OK" assert substrings_present( rv.data, [user2_info["username"], "Testing", "Test Content"] )
def test_logout_and_login_again(client, user_info): """A logged in user can log out and back in again.""" register_user(client, user_info) assert b"Log out" in client.get(url_for("home.index")).data log_out_current_user(client, verify=True) log_in_user(client, user_info, expect_success=True)
def test_fix_registration_email(client, user_info, user2_info, test_config): """Registered users can fix errors in their email addresses.""" rv = client.get(url_for("auth.register")) data = dict( csrf_token=csrf_token(rv.data), username=user_info["username"], password=user_info["password"], confirm=user_info["password"], email_required=user_info["email"], invitecode="", accept_tos=True, captcha="xyzzy", ) # Register and save the link from the first email. with mail.record_messages() as outbox: rv = client.post(url_for("auth.register"), data=data, follow_redirects=True) assert b"spam" in rv.data # Telling user to go check it. message = outbox.pop() soup = BeautifulSoup(message.html, "html.parser") first_token = soup.a["href"].split("/")[-1] # Find the resend link. soup = BeautifulSoup(rv.data, "html.parser") links = soup.find_all( lambda tag: tag.name == "a" and tag.string == "Resend") url = links[0]["href"] # Request the resend form and verify the registered email is shown. rv = client.get(url) assert b"Resend account confirmation instructions" in rv.data soup = BeautifulSoup(rv.data, "html.parser") tag = soup.find_all(lambda tag: tag.get("name") == "email")[0] assert tag["value"] == user_info["email"] # Ask for emails to be sent to a different address. with mail.record_messages() as outbox: rv = client.post( url, data=dict(csrf_token=csrf_token(rv.data), email=user2_info["email"]), follow_redirects=True, ) assert b"spam" in rv.data # Telling user to go check it. message = outbox.pop() assert message.recipients == [user2_info["email"]] soup = BeautifulSoup(message.html, "html.parser") token = soup.a["href"].split("/")[-1] # Use the confirmation link from the email. rv = client.get(url_for("auth.login_with_token", token=token), follow_redirects=True) assert b"Log out" in rv.data # Verify that the user's confirmed email is the second one. rv = client.get(url_for("user.edit_account")) soup = BeautifulSoup(rv.data, "html.parser") tag = soup.find_all(lambda tag: tag.get("name") == "email_required")[0] assert tag["value"] == user2_info["email"] log_out_current_user(client) # Try to use the first token and verify that it is no longer valid. rv = client.get(url_for("auth.login_with_token", token=first_token), follow_redirects=True) assert b"The link you used is invalid or has expired" in rv.data assert b"Log out" not in rv.data # Try to use the resend form and verify that it no longer works. rv = client.get(url, follow_redirects=True) assert b"The link you used is invalid or has expired" in rv.data assert b"Log out" not in rv.data
def test_mod_post_delete(client, user_info, user2_info, user3_info): config.update_value("site.sub_creation_min_level", 0) user, admin, mod = user_info, user2_info, user3_info register_user(client, admin) promote_user_to_admin(client, admin) log_out_current_user(client) register_user(client, mod) create_sub(client) log_out_current_user(client) register_user(client, user) # User makes several posts. pids = {} posts = [ "delete_user", "delete_admin", "delete_then_undelete_by_admin", "delete_mod", "delete_then_undelete_by_mod", "delete_undelete_by_mod_then_admin", ] rv = client.get(url_for("subs.submit", ptype="text", sub="test")) csrf = csrf_token(rv.data) for post in posts: rv = client.post( url_for("subs.submit", ptype="text", sub="test"), data={ "csrf_token": csrf, "title": post, "ptype": "text", "content": "the content", }, follow_redirects=False, ) assert rv.status == "302 FOUND" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.a.get_text() pids[post] = link.split("/")[-1] # User can see all the posts. rv = client.get(url_for("sub.view_sub", sub="test"), follow_redirects=True) data = rv.data.decode("utf-8") for post in posts: assert post in data # User has a delete link on a single-post page. rv = client.get( url_for("sub.view_post", sub="test", pid=pids["delete_user"]), follow_redirects=True, ) assert len(re.findall("[^n]delete-post", rv.data.decode("utf-8"))) == 1 # User deletes a post. rv = client.post( url_for("do.delete_post"), data={"csrf_token": csrf, "post": pids["delete_user"]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # User can now not see deleted post rv = client.get(url_for("sub.view_sub", sub="test"), follow_redirects=True) data = rv.data.decode("utf-8") for post in posts: if post == "delete_user": assert post not in data else: assert post in data # User can go to deleted post page but there is no delete or undelete link. rv = client.get( url_for("sub.view_post", sub="test", pid=pids["delete_user"]), follow_redirects=True, ) data = rv.data.decode("utf-8") assert "the content" not in data assert len(re.findall("delete-post", data)) == 0 log_out_current_user(client) # Admin sees deleted post content, and no delete or undelete link. log_in_user(client, admin) rv = client.get( url_for("sub.view_post", sub="test", pid=pids["delete_user"]), follow_redirects=True, ) data = rv.data.decode("utf-8") assert "the content" in data assert len(re.findall("delete-post", data)) == 0 # Admin tries to remove user's deleted post. rv = client.post( url_for("do.delete_post"), data={"csrf_token": csrf, "post": pids["delete_user"]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "error" assert reply["error"] == ["Post was already deleted"] # Admin deletes two posts. for post in ["delete_admin", "delete_then_undelete_by_admin"]: rv = client.post( url_for("do.delete_post"), data={"csrf_token": csrf, "post": pids[post], "reason": "admin"}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Admin can still see the post content and now has undelete links. for post in ["delete_admin", "delete_then_undelete_by_admin"]: rv = client.get( url_for("sub.view_post", sub="test", pid=pids[post]), follow_redirects=True ) data = rv.data.decode("utf-8") assert "the content" in data # Check for the css class on the bottombar delete links. assert len(re.findall("undelete-post", data)) == 1 assert len(re.findall("[^n]delete-post", data)) == 0 # Admin undeletes a post. rv = client.post( url_for("do.undelete_post"), data={ "csrf_token": csrf, "post": pids["delete_then_undelete_by_admin"], "reason": "admin", }, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) # Mod can see content of all posts. Mod sees delete links for the # posts which are not deleted, and does not have delete or # undelete links for the deleted posts. log_in_user(client, mod) for post in posts: rv = client.get( url_for("sub.view_post", sub="test", pid=pids[post]), follow_redirects=True ) data = rv.data.decode("utf-8") assert post in data assert "the content" in data if post in ["delete_user", "delete_admin"]: assert len(re.findall("delete-post", data)) == 0 else: assert len(re.findall("undelete-post", data)) == 0 assert len(re.findall("[^n]delete-post", data)) == 1 # Mod tries to remove already deleted posts. for post in ["delete_user", "delete_admin"]: rv = client.post( url_for("do.delete_post"), data={"csrf_token": csrf, "post": pids[post], "reason": "mod"}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "error" assert reply["error"] == ["Post was already deleted"] # Mod can't undelete post deleted by admin. rv = client.post( url_for("do.undelete_post"), data={"csrf_token": csrf, "post": pids["delete_admin"], "reason": "mod"}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "error" assert reply["error"] == ["Not authorized"] # Mod deletes some posts. for post in [ "delete_mod", "delete_then_undelete_by_mod", "delete_undelete_by_mod_then_admin", ]: rv = client.post( url_for("do.delete_post"), data={"csrf_token": csrf, "post": pids[post], "reason": "mod"}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Mod sees content of all posts, and now has two undelete links. for post in posts: rv = client.get( url_for("sub.view_post", sub="test", pid=pids[post]), follow_redirects=True ) data = rv.data.decode("utf-8") assert post in data assert "the content" in data if post in ["delete_user", "delete_admin"]: assert len(re.findall("delete-post", data)) == 0 elif post in [ "delete_mod", "delete_then_undelete_by_mod", "delete_undelete_by_mod_then_admin", ]: assert len(re.findall("[^n]delete-post", data)) == 0 assert len(re.findall("undelete-post", data)) == 1 else: assert len(re.findall("undelete-post", data)) == 0 assert len(re.findall("[^n]delete-post", data)) == 1 # Mod undeletes a post. rv = client.post( url_for("do.undelete_post"), data={ "csrf_token": csrf, "post": pids["delete_then_undelete_by_mod"], "reason": "mod", }, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) log_in_user(client, admin) # Admin has undelete links for the content admin deleted as well as # the content the mod deleted. for post in posts: rv = client.get( url_for("sub.view_post", sub="test", pid=pids[post]), follow_redirects=True ) data = rv.data.decode("utf-8") assert post in data assert "the content" in data if post == "delete_user": assert len(re.findall("delete-post", data)) == 0 elif post in [ "delete_admin", "delete_mod", "delete_undelete_by_mod_then_admin", ]: assert len(re.findall("[^n]delete-post", data)) == 0 assert len(re.findall("undelete-post", data)) == 1 else: assert len(re.findall("undelete-post", data)) == 0 assert len(re.findall("[^n]delete-post", data)) == 1 # Admin can undelete a post deleted by the mod. rv = client.post( url_for("do.undelete_post"), data={ "csrf_token": csrf, "post": pids["delete_undelete_by_mod_then_admin"], "reason": "admin", }, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) log_in_user(client, user) # User can see posts which were deleted by mod or admin. undeleted = [ "delete_then_undelete_by_admin", "delete_then_undelete_by_mod", "delete_undelete_by_mod_then_admin", ] for post in posts: rv = client.get( url_for("sub.view_post", sub="test", pid=pids[post]), follow_redirects=True ) data = rv.data.decode("utf-8") assert post in data if post != "delete_user": assert "the content" in data # User has a delete link for posts which are not deleted, and # no undelete links. if post in undeleted: assert len(re.findall("[^n]delete-post", data)) == 1 else: assert len(re.findall("[^n]delete-post", data)) == 0 assert len(re.findall("undelete-post", data)) == 0 for post in posts: if post not in undeleted: # User can't delete anything which has already been deleted. rv = client.post( url_for("do.delete_post"), data={ "csrf_token": csrf, "post": pids["delete_user"], "reason": "user", }, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "error" assert reply["error"] == ["Post was already deleted"] # User can't undelete any posts. for post in posts: rv = client.post( url_for("do.undelete_post"), data={"csrf_token": csrf, "post": pids[post], "reason": "user"}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "error" if post == "delete_user": assert reply["error"] == ["Can not un-delete a self-deleted post"] elif "undelete" in post: assert reply["error"] == ["Post is not deleted"] else: assert reply["error"] == ["Not authorized"]
def test_comment_sort(client, user_info): "Comments can be sorted by best, top and new." config.update_value("site.sub_creation_min_level", 0) # Create a mod and a sub. mod = user_info register_user(client, mod) create_sub(client) log_out_current_user(client) # Create some users to read and vote. users = [ dict(username=f"user{i}", email=f"user{i}@example.com", password="******") for i in range(10) ] for user in users: register_user(client, user) log_out_current_user(client) # Mod makes a post and adds three comments to it. log_in_user(client, mod) rv = client.get(url_for("subs.submit", ptype="text", sub="test")) csrf = csrf_token(rv.data) data = { "csrf_token": csrf, "title": "the title", "ptype": "text", "content": "the content", } rv = client.post( url_for("subs.submit", ptype="text", sub="test"), data=data, follow_redirects=False, ) assert rv.status == "302 FOUND" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.a.get_text() pid = link.split("/")[-1] rv = client.get(link, follow_redirects=True) assert b"the title | test" in rv.data cids = {} for comment in ["oldest", "middle", "newest"]: data = { "csrf_token": csrf, "post": pid, "parent": "0", "comment": "This comment is: " + comment, } rv = client.post(url_for("do.create_comment", pid=pid), data=data, follow_redirects=False) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" cids[comment] = reply["cid"] log_out_current_user(client) # The other users visit and vote the comments as follows: # oldest - 10 visits, 5 upvotes, 1 downvote # middle - 2 visits, 2 upvotes # newest - 0 visits, 0 upvotes expected_results = { "new": ["newest", "middle", "oldest"], "top": ["oldest", "middle", "newest"], "best": ["middle", "newest", "oldest"], } for num, user in enumerate(users): log_in_user(client, user) # View some comments. if num < 2: views = [cids["oldest"], cids["middle"]] else: views = [cids["oldest"]] rv = client.post( url_for("do.mark_comments_viewed"), data={ "csrf_token": csrf, "cids": json.dumps(views) }, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Upvote some comments. if num < 5: rv = client.post( url_for("do.upvotecomment", cid=cids["oldest"], value="up"), data={"csrf_token": csrf}, ) assert rv.status == "200 OK" if num < 2: rv = client.post( url_for("do.upvotecomment", cid=cids["middle"], value="up"), data={"csrf_token": csrf}, ) assert rv.status == "200 OK" # Downvote a comment if num == 0: rv = client.post( url_for("do.upvotecomment", cid=cids["oldest"], value="down"), data={"csrf_token": csrf}, ) assert rv.status == "200 OK" log_out_current_user(client) for sort, order in expected_results.items(): rv = client.get( url_for("sub.view_post", pid=pid, sub="test", slug="", sort=sort), follow_redirects=True, ) assert rv.status == "200 OK" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") comments = [ c.get_text() for c in soup.find_all("div", class_="content") ] for expected, comment in zip(order, comments): assert comment.find(expected) != -1
def test_post_delete_notification(client, user_info, user2_info, user3_info): "Notifications are sent for post delete and undelete." config.update_value("site.sub_creation_min_level", 0) receiver, admin, mod = user_info, user2_info, user3_info register_user(client, receiver) receiver_uid = User.get(User.name == receiver["username"]).uid log_out_current_user(client) register_user(client, admin) promote_user_to_admin(client, admin) log_out_current_user(client) register_user(client, mod) mod_uid = User.get(User.name == mod["username"]).uid create_sub(client) log_out_current_user(client) # Receiver makes a post. log_in_user(client, receiver) rv_post = client.get(url_for("subs.submit", ptype="text", sub="test")) csrf = csrf_token(rv_post.data) data = { "csrf_token": csrf, "title": "the title", "ptype": "text", "content": "the content", } rv = client.post( url_for("subs.submit", ptype="text", sub="test"), data=data, follow_redirects=False, ) assert rv.status == "302 FOUND" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.a.get_text() pid = link.split("/")[-1] log_out_current_user(client) # Mod deletes the post. log_in_user(client, mod) data = { "csrf_token": csrf, "post": pid, "reason": "serious reason", "send_to_admin": False, } rv = client.post( url_for("do.delete_post"), data=data, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) # Admin un-deletes the post. log_in_user(client, admin) data = { "csrf_token": csrf, "post": pid, "reason": "frivolous reason", } rv = client.post( url_for("do.undelete_post"), data=data, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) # Receiver blocks mod. log_in_user(client, receiver) data = { "csrf_token": csrf, "view_messages": "show", "view_content": "hide", } rv = client.post(url_for("do.edit_ignore", uid=mod_uid), data=data, follow_redirects=True) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Receiver checks notifications. They should not be ignored, # because they are mod actions. assert get_notification_count(receiver_uid)["notifications"] == 2 rv = client.get(url_for("messages.view_notifications")) assert b" deleted your post" in rv.data assert b"un-deleted your post" in rv.data assert b"serious reason" in rv.data assert b"frivolous reason" in rv.data # After checking, the notification is marked read. assert get_notification_count(receiver_uid)["notifications"] == 0 # Receiver unblocks mod. data = { "csrf_token": csrf_token(rv.data), "view_messages": "show", "view_content": "show", } rv = client.post(url_for("do.edit_ignore", uid=mod_uid), data=data, follow_redirects=True) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Receiver checks notifications again. rv = client.get(url_for("messages.view_notifications")) assert b" deleted your post" in rv.data assert b"un-deleted your post" in rv.data assert b"serious reason" in rv.data assert b"frivolous reason" in rv.data
def test_change_password_recovery_email(client, user_info): """The user can change their password recovery email.""" register_user(client, user_info) new_email = '*****@*****.**' assert new_email != user_info['email'] rv = client.get(url_for('user.edit_account')) data = dict(csrf_token=csrf_token(rv.data), oldpassword=user_info['password'], password='', confirm='') if email_validation_is_required(): data['email_required'] = new_email else: data['email_optional'] = new_email with mail.record_messages() as outbox: rv = client.post(url_for('do.edit_account'), data=data, follow_redirects=True) log_out_current_user(client) if email_validation_is_required(): message = outbox.pop() # Make sure that password recovery emails go to the former address # if the new one has not yet been confirmed. rv = client.get(url_for('user.password_recovery')) rv = client.post(url_for('user.password_recovery'), data=dict(csrf_token=csrf_token(rv.data), email=new_email, captcha='xyzzy')) assert len(outbox) == 0 rv = client.get(url_for('user.password_recovery')) rv = client.post(url_for('user.password_recovery'), data=dict(csrf_token=csrf_token(rv.data), email=user_info['email'], captcha='xyzzy')) assert outbox.pop().send_to == {user_info['email']} # Now click the confirm link. assert message.send_to == {new_email} soup = BeautifulSoup(message.html, 'html.parser') token = soup.a['href'].split('/')[-1] rv = client.get(url_for('user.confirm_email_change', token=token), follow_redirects=True) else: assert len(outbox) == 0 # Verify password recovery email goes to the right place. with mail.record_messages() as outbox: rv = client.get(url_for('user.password_recovery')) rv = client.post(url_for('user.password_recovery'), data=dict(csrf_token=csrf_token(rv.data), email=user_info['email'], captcha='xyzzy')) assert len(outbox) == 0 rv = client.get(url_for('user.password_recovery')) rv = client.post(url_for('user.password_recovery'), data=dict(csrf_token=csrf_token(rv.data), email=new_email, captcha='xyzzy')) assert outbox.pop().send_to == {new_email}
def test_mod_comment_delete(client, user_info, user2_info, user3_info): config.update_value("site.sub_creation_min_level", 0) user, admin, mod = user_info, user2_info, user3_info register_user(client, admin) promote_user_to_admin(client, admin) log_out_current_user(client) register_user(client, mod) create_sub(client) log_out_current_user(client) register_user(client, user) # User makes a post. rv = client.get(url_for("subs.submit", ptype="text", sub="test")) csrf = csrf_token(rv.data) data = { "csrf_token": csrf, "title": "the title", "ptype": "text", "content": "the content", } rv = client.post( url_for("subs.submit", ptype="text", sub="test"), data=data, follow_redirects=False, ) assert rv.status == "302 FOUND" soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") link = soup.a.get_text() pid = link.split("/")[-1] # User adds some comments. rv = client.get(link, follow_redirects=True) assert b"the title | test" in rv.data cids = {} comments = [ "delete_user", "delete_admin", "delete_then_undelete_by_admin", "delete_mod", "delete_then_undelete_by_mod", "delete_undelete_by_mod_then_admin", ] for comment in comments: data = { "csrf_token": csrf, "post": pid, "parent": "0", "comment": comment, } rv = client.post( url_for("do.create_comment", pid=pid), data=data, follow_redirects=False ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" cids[comment] = reply["cid"] # User reloads the page and can see all the comments. rv = client.get(link, follow_redirects=True) data = rv.data.decode("utf-8") for comment in comments: assert comment in data # Check for the css class on the bottombar delete links. assert len(re.findall("[^n]delete-comment", data)) == len(comments) rv = client.post( url_for("do.delete_comment"), data={"csrf_token": csrf, "cid": cids["delete_user"]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # User can no longer see deleted comment. rv = client.get(link, follow_redirects=True) data = rv.data.decode("utf-8") for comment in comments: if comment == "delete_user": assert comment not in data else: assert comment in data log_out_current_user(client) # Admin sees deleted comment content, and n-1 delete links. log_in_user(client, admin) rv = client.get(link, follow_redirects=True) data = rv.data.decode("utf-8") for comment in comments: assert comment in data # Check for the css class on the bottombar delete links. assert len(re.findall("[^n]delete-comment", data)) == len(comments) - 1 assert "undelete-comment" not in data # Admin tries to remove user's deleted comment. rv = client.post( url_for("do.delete_comment"), data={"csrf_token": csrf, "cid": cids["delete_user"]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "error" assert reply["error"] == "Comment is already deleted" # Admin removes two comments. for comment in ["delete_admin", "delete_then_undelete_by_admin"]: rv = client.post( url_for("do.delete_comment"), data={"csrf_token": csrf, "cid": cids[comment]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Admin can still see all comment content and now has two undelete links. rv = client.get(link, follow_redirects=True) data = rv.data.decode("utf-8") for comment in comments: assert comment in data # Check for the css class on the bottombar delete links. assert len(re.findall("undelete-comment", data)) == 2 assert len(re.findall("[^n]delete-comment", data)) == len(comments) - 3 # Admin undeletes a comment. rv = client.post( url_for("do.undelete_comment"), data={"csrf_token": csrf, "cid": cids["delete_then_undelete_by_admin"]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) # Mod sees content of deleted and undeleted comments. log_in_user(client, mod) rv = client.get(link, follow_redirects=True) data = rv.data.decode("utf-8") for comment in comments: assert comment in data # Mod should see delete links for undeleted comments. assert len(re.findall("[^n]delete-comment", data)) == len(comments) - 2 assert len(re.findall("undelete-comment", data)) == 0 # Mod tries to remove already deleted comments. for comment in ["delete_user", "delete_admin"]: rv = client.post( url_for("do.delete_comment"), data={"csrf_token": csrf, "cid": cids[comment]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "error" assert reply["error"] == "Comment is already deleted" # Mod tries to undelete comment deleted by admin. rv = client.post( url_for("do.undelete_comment"), data={"csrf_token": csrf, "cid": cids["delete_admin"]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "error" assert reply["error"] == "Not authorized" # Mod removes some comments. for comment in [ "delete_mod", "delete_then_undelete_by_mod", "delete_undelete_by_mod_then_admin", ]: rv = client.post( url_for("do.delete_comment"), data={"csrf_token": csrf, "cid": cids[comment]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Mod sees content of all comments, and now has two undelete links. rv = client.get(link, follow_redirects=True) data = rv.data.decode("utf-8") for comment in comments: assert comment in data # Mod should see delete links for undeleted comments. assert len(re.findall("[^n]delete-comment", data)) == len(comments) - 5 assert len(re.findall("undelete-comment", data)) == 3 # Mod undeletes a comment. rv = client.post( url_for("do.undelete_comment"), data={"csrf_token": csrf, "cid": cids["delete_then_undelete_by_mod"]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) log_in_user(client, admin) # Admin has undelete links for the content admin deleted as well as # the content the mod deleted. assert len(re.findall("undelete-comment", data)) == 3 # Admin can undelete a comment deleted by the mod. rv = client.post( url_for("do.undelete_comment"), data={"csrf_token": csrf, "cid": cids["delete_undelete_by_mod_then_admin"]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) log_in_user(client, user) # User can see comments which are not deleted. undeleted = [ "delete_then_undelete_by_admin", "delete_then_undelete_by_mod", "delete_undelete_by_mod_then_admin", ] rv = client.get(link, follow_redirects=True) data = rv.data.decode("utf-8") for comment in comments: if comment in undeleted: assert comment in data else: assert comment not in data # User should see delete links for undeleted comments. assert len(re.findall("[^n]delete-comment", data)) == len(undeleted) assert len(re.findall("undelete-comment", data)) == 0 # User can't undelete any comment for comment in comments: rv = client.post( url_for("do.undelete_comment"), data={"csrf_token": csrf, "cid": cids[comment]}, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "error" if comment in undeleted: assert reply["error"] == "Comment is not deleted" elif comment == "delete_user": assert reply["error"] == "Can not un-delete a self-deleted comment" else: assert reply["error"] == "Not authorized"
def test_mod_invite_notification(client, user_info, user2_info): "Notifications are sent for mod invites." config.update_value("site.sub_creation_min_level", 0) receiver, mod = user_info, user2_info register_user(client, receiver) receiver_uid = User.get(User.name == receiver["username"]).uid log_out_current_user(client) register_user(client, mod) mod_uid = User.get(User.name == mod["username"]).uid create_sub(client, name="test_janitor") create_sub(client, name="test_mod") # Mod invites receiver as moderator. rv_index = client.get(url_for("home.index", sub="test_mod")) data = { "csrf_token": csrf_token(rv_index.data), "user": receiver["username"], "level": "1", } rv = client.post( url_for("do.inv_mod", sub="test_mod"), data=data, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Mod invites receiver as janitor. data = { "csrf_token": csrf_token(rv_index.data), "user": receiver["username"], "level": "2", } rv = client.post( url_for("do.inv_mod", sub="test_janitor"), data=data, ) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" log_out_current_user(client) # Receiver blocks mod. log_in_user(client, receiver) data = { "csrf_token": csrf_token(rv_index.data), "view_messages": "show", "view_content": "hide", } rv = client.post(url_for("do.edit_ignore", uid=mod_uid), data=data, follow_redirects=True) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Receiver checks notifications. They should not be ignored, # because they are mod actions. assert get_notification_count(receiver_uid)["notifications"] == 2 rv = client.get(url_for("messages.view_notifications")) assert b"invited you to moderate" in rv.data soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8") assert soup.find(href=url_for("sub.edit_sub_mods", sub="test_mod")) assert soup.find(href=url_for("sub.edit_sub_mods", sub="test_janitor")) # After checking, the notification is marked read. assert get_notification_count(receiver_uid)["notifications"] == 0 # Receiver unblocks mod. data = { "csrf_token": csrf_token(rv.data), "view_messages": "show", "view_content": "show", } rv = client.post(url_for("do.edit_ignore", uid=mod_uid), data=data, follow_redirects=True) reply = json.loads(rv.data.decode("utf-8")) assert reply["status"] == "ok" # Receiver checks notifications again. rv = client.get(url_for("messages.view_notifications")) assert b"invited you to moderate" in rv.data assert soup.find(href=url_for("sub.edit_sub_mods", sub="test_mod")) assert soup.find(href=url_for("sub.edit_sub_mods", sub="test_janitor"))
def test_invite_code_required_for_registration(client, user_info, user2_info): "If invite codes are required, trying to register without one will fail." register_user(client, user_info) promote_user_to_admin(client, user_info) # Enable invite codes. rv = client.get(url_for('admin.invitecodes')) data = dict(csrf_token=csrf_token(rv.data), enableinvitecode=True, minlevel=3, maxcodes=10) rv = client.post(url_for('do.use_invite_code'), data=data, follow_redirects=True) reply = json.loads(rv.data.decode('utf-8')) assert reply['status'] == 'ok' # Create an invite code. rv = client.get(url_for('admin.invitecodes')) data = dict(csrf_token=csrf_token(rv.data), code="abcde", uses=10, expires='') rv = client.post(url_for('admin.invitecodes'), data=data, follow_redirects=True) log_out_current_user(client) # Now try to register a new user without an invite code. rv = client.get(url_for('auth.register')) data = dict(csrf_token=csrf_token(rv.data), username=user2_info['username'], password=user2_info['password'], confirm=user2_info['password'], invitecode='', email_optional=user2_info['email'], accept_tos=True, captcha='xyzzy') rv = client.post(url_for('auth.register'), data=data, follow_redirects=True) assert b'Invalid invite code' in rv.data # Now try to register a new user with an incorrect invite code. rv = client.get(url_for('auth.register')) data = dict(csrf_token=csrf_token(rv.data), username=user2_info['username'], password=user2_info['password'], confirm=user2_info['password'], invitecode='xyzzy', email_optional=user2_info['email'], accept_tos=True, captcha='xyzzy') rv = client.post(url_for('auth.register'), data=data, follow_redirects=True) assert b'Invalid invite code' in rv.data # Now try to register a new user with a valid invite code. rv = client.get(url_for('auth.register')) data = dict(csrf_token=csrf_token(rv.data), username=user2_info['username'], password=user2_info['password'], confirm=user2_info['password'], invitecode='abcde', email_optional=user2_info['email'], accept_tos=True, captcha='xyzzy') rv = client.post(url_for('auth.register'), data=data, follow_redirects=True) assert b'Log out' in rv.data