1# -*- coding: utf-8 -*- 2""" 3 test_websupport 4 ~~~~~~~~~~~~~~~ 5 6 Test the Web Support Package 7 8 :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. 9 :license: BSD, see LICENSE for details. 10""" 11 12from sphinxcontrib.websupport import WebSupport 13from sphinxcontrib.websupport.errors import DocumentNotFoundError, \ 14 CommentNotAllowedError, UserNotAuthorizedError 15from sphinxcontrib.websupport.storage import StorageBackend 16from sphinxcontrib.websupport.storage.differ import CombinedHtmlDiff 17try: 18 from sphinxcontrib.websupport.storage.sqlalchemystorage import Session, \ 19 Comment, CommentVote 20 from sphinxcontrib.websupport.storage.sqlalchemy_db import Node 21 sqlalchemy_missing = False 22except ImportError: 23 sqlalchemy_missing = True 24 25import pytest 26from util import rootdir, tempdir 27 28 29@pytest.fixture 30def support(request): 31 settings = { 32 'srcdir': rootdir / 'root', 33 # to use same directory for 'builddir' in each 'support' fixture, using 34 # 'tempdir' (static) value instead of 'tempdir' fixture value. 35 # each test expect result of db value at previous test case. 36 'builddir': tempdir / 'websupport' 37 } 38 marker = request.node.get_closest_marker('support') 39 if marker: 40 settings.update(marker.kwargs) 41 42 support = WebSupport(**settings) 43 yield support 44 45 46with_support = pytest.mark.support 47 48 49class NullStorage(StorageBackend): 50 pass 51 52 53@with_support(storage=NullStorage()) 54def test_no_srcdir(support): 55 # make sure the correct exception is raised if srcdir is not given. 56 with pytest.raises(RuntimeError): 57 support.build() 58 59 60@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy') 61@with_support() 62def test_build(support): 63 support.build() 64 65 66@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy') 67@with_support() 68def test_get_document(support): 69 with pytest.raises(DocumentNotFoundError): 70 support.get_document('nonexisting') 71 72 contents = support.get_document('contents') 73 assert contents['title'] and contents['body'] \ 74 and contents['sidebar'] and contents['relbar'] 75 76 77@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy') 78@with_support() 79def test_comments(support): 80 session = Session() 81 nodes = session.query(Node).all() 82 first_node = nodes[0] 83 second_node = nodes[1] 84 85 # Create a displayed comment and a non displayed comment. 86 comment = support.add_comment('First test comment', 87 node_id=first_node.id, 88 username='user_one') 89 hidden_comment = support.add_comment('Hidden comment', 90 node_id=first_node.id, 91 displayed=False) 92 # Make sure that comments can't be added to a comment where 93 # displayed == False, since it could break the algorithm that 94 # converts a nodes comments to a tree. 95 with pytest.raises(CommentNotAllowedError): 96 support.add_comment('Not allowed', parent_id=str(hidden_comment['id'])) 97 # Add a displayed and not displayed child to the displayed comment. 98 support.add_comment('Child test comment', parent_id=str(comment['id']), 99 username='user_one') 100 support.add_comment('Hidden child test comment', 101 parent_id=str(comment['id']), displayed=False) 102 # Add a comment to another node to make sure it isn't returned later. 103 support.add_comment('Second test comment', 104 node_id=second_node.id, 105 username='user_two') 106 107 # Access the comments as a moderator. 108 data = support.get_data(first_node.id, moderator=True) 109 comments = data['comments'] 110 children = comments[0]['children'] 111 assert len(comments) == 2 112 assert comments[1]['text'] == '<p>Hidden comment</p>\n' 113 assert len(children) == 2 114 assert children[1]['text'] == '<p>Hidden child test comment</p>\n' 115 116 # Access the comments without being a moderator. 117 data = support.get_data(first_node.id) 118 comments = data['comments'] 119 children = comments[0]['children'] 120 assert len(comments) == 1 121 assert comments[0]['text'] == '<p>First test comment</p>\n' 122 assert len(children) == 1 123 assert children[0]['text'] == '<p>Child test comment</p>\n' 124 125 126@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy') 127@with_support() 128def test_user_delete_comments(support): 129 def get_comment(): 130 session = Session() 131 node = session.query(Node).first() 132 session.close() 133 return support.get_data(node.id)['comments'][0] 134 135 comment = get_comment() 136 assert comment['username'] == 'user_one' 137 # Make sure other normal users can't delete someone elses comments. 138 with pytest.raises(UserNotAuthorizedError): 139 support.delete_comment(comment['id'], username='user_two') 140 # Now delete the comment using the correct username. 141 support.delete_comment(comment['id'], username='user_one') 142 comment = get_comment() 143 assert comment['username'] == '[deleted]' 144 assert comment['text'] == '[deleted]' 145 146 147called = False 148 149 150def moderation_callback(comment): 151 global called 152 called = True 153 154 155@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy') 156@with_support(moderation_callback=moderation_callback) 157def test_moderation(support): 158 session = Session() 159 nodes = session.query(Node).all() 160 node = nodes[7] 161 session.close() 162 accepted = support.add_comment('Accepted Comment', node_id=node.id, 163 displayed=False) 164 deleted = support.add_comment('Comment to delete', node_id=node.id, 165 displayed=False) 166 # Make sure the moderation_callback is called. 167 assert called 168 # Make sure the user must be a moderator. 169 with pytest.raises(UserNotAuthorizedError): 170 support.accept_comment(accepted['id']) 171 with pytest.raises(UserNotAuthorizedError): 172 support.delete_comment(deleted['id']) 173 support.accept_comment(accepted['id'], moderator=True) 174 support.delete_comment(deleted['id'], moderator=True) 175 comments = support.get_data(node.id)['comments'] 176 assert len(comments) == 1 177 comments = support.get_data(node.id, moderator=True)['comments'] 178 assert len(comments) == 1 179 180 181@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy') 182@with_support() 183def test_moderator_delete_comments(support): 184 def get_comment(): 185 session = Session() 186 node = session.query(Node).first() 187 session.close() 188 return support.get_data(node.id, moderator=True)['comments'][1] 189 190 comment = get_comment() 191 support.delete_comment(comment['id'], username='user_two', 192 moderator=True) 193 with pytest.raises(IndexError): 194 get_comment() 195 196 197@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy') 198@with_support() 199def test_update_username(support): 200 support.update_username('user_two', 'new_user_two') 201 session = Session() 202 comments = session.query(Comment).\ 203 filter(Comment.username == 'user_two').all() 204 assert len(comments) == 0 205 votes = session.query(CommentVote).\ 206 filter(CommentVote.username == 'user_two').all() 207 assert len(votes) == 0 208 comments = session.query(Comment).\ 209 filter(Comment.username == 'new_user_two').all() 210 assert len(comments) == 1 211 votes = session.query(CommentVote).\ 212 filter(CommentVote.username == 'new_user_two').all() 213 assert len(votes) == 0 214 215 216@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy') 217@with_support() 218def test_proposals(support): 219 session = Session() 220 node = session.query(Node).first() 221 222 data = support.get_data(node.id) 223 224 source = data['source'] 225 proposal = source[:5] + source[10:15] + 'asdf' + source[15:] 226 227 support.add_comment('Proposal comment', 228 node_id=node.id, 229 proposal=proposal) 230 231 232@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy') 233@with_support() 234def test_voting(support): 235 session = Session() 236 nodes = session.query(Node).all() 237 node = nodes[0] 238 239 comment = support.get_data(node.id)['comments'][0] 240 241 def check_rating(val): 242 data = support.get_data(node.id) 243 comment = data['comments'][0] 244 assert comment['rating'] == val, '%s != %s' % (comment['rating'], val) 245 246 support.process_vote(comment['id'], 'user_one', '1') 247 support.process_vote(comment['id'], 'user_two', '1') 248 support.process_vote(comment['id'], 'user_three', '1') 249 check_rating(3) 250 support.process_vote(comment['id'], 'user_one', '-1') 251 check_rating(1) 252 support.process_vote(comment['id'], 'user_one', '0') 253 check_rating(2) 254 255 # Make sure a vote with value > 1 or < -1 can't be cast. 256 with pytest.raises(ValueError): 257 support.process_vote(comment['id'], 'user_one', '2') 258 with pytest.raises(ValueError): 259 support.process_vote(comment['id'], 'user_one', '-2') 260 261 # Make sure past voting data is associated with comments when they are 262 # fetched. 263 data = support.get_data(str(node.id), username='user_two') 264 comment = data['comments'][0] 265 assert comment['vote'] == 1, '%s != 1' % comment['vote'] 266 267 268def test_differ(): 269 source = 'Lorem ipsum dolor sit amet,\nconsectetur adipisicing elit,\n' \ 270 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' 271 prop = 'Lorem dolor sit amet,\nconsectetur nihil adipisicing elit,\n' \ 272 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' 273 differ = CombinedHtmlDiff(source, prop) 274 differ.make_html() 275