Пример #1
0
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)
Пример #2
0
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)
Пример #3
0
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)
Пример #4
0
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
Пример #5
0
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
Пример #6
0
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)
Пример #7
0
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"
Пример #8
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
Пример #9
0
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
Пример #10
0
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)
Пример #11
0
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
Пример #12
0
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)
Пример #13
0
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
Пример #14
0
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
Пример #15
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
    )
Пример #16
0
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
Пример #17
0
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"
Пример #18
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}
Пример #19
0
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"]
    )
Пример #20
0
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)
Пример #21
0
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
Пример #22
0
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"]
Пример #23
0
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
Пример #24
0
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
Пример #25
0
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}
Пример #26
0
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"
Пример #27
0
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"))
Пример #28
0
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