tests.py 10.5 KB
Newer Older
Ned Batchelder committed
1
"""
2
Test the lms/staticbook views.
Ned Batchelder committed
3 4
"""

5 6 7 8 9
import textwrap

import mock
import requests

Ned Batchelder committed
10
from django.test.utils import override_settings
11
from django.core.urlresolvers import reverse, NoReverseMatch
Ned Batchelder committed
12

13
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
Ned Batchelder committed
14
from student.tests.factories import UserFactory, CourseEnrollmentFactory
Ned Batchelder committed
15
from xmodule.modulestore.tests.factories import CourseFactory
Ned Batchelder committed
16 17 18
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase


19 20
IMAGE_BOOK = ("An Image Textbook", "http://example.com/the_book/")

Ned Batchelder committed
21 22 23 24
PDF_BOOK = {
    "tab_title": "Textbook",
    "title": "A PDF Textbook",
    "chapters": [
25 26 27 28 29 30 31 32 33 34 35
        {"title": "Chapter 1 for PDF", "url": "https://somehost.com/the_book/chap1.pdf"},
        {"title": "Chapter 2 for PDF", "url": "https://somehost.com/the_book/chap2.pdf"},
    ],
}

PORTABLE_PDF_BOOK = {
    "tab_title": "Textbook",
    "title": "A PDF Textbook",
    "chapters": [
        {"title": "Chapter 1 for PDF", "url": "/static/chap1.pdf"},
        {"title": "Chapter 2 for PDF", "url": "/static/chap2.pdf"},
Ned Batchelder committed
36 37 38 39 40 41 42
    ],
}

HTML_BOOK = {
    "tab_title": "Textbook",
    "title": "An HTML Textbook",
    "chapters": [
43 44
        {"title": "Chapter 1 for HTML", "url": "https://somehost.com/the_book/chap1.html"},
        {"title": "Chapter 2 for HTML", "url": "https://somehost.com/the_book/chap2.html"},
Ned Batchelder committed
45 46 47
    ],
}

Will Daly committed
48

49
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
Ned Batchelder committed
50 51 52 53 54
class StaticBookTest(ModuleStoreTestCase):
    """
    Helpers for the static book tests.
    """

55 56 57 58
    def __init__(self, *args, **kwargs):
        super(StaticBookTest, self).__init__(*args, **kwargs)
        self.course = None

Ned Batchelder committed
59 60 61 62
    def make_course(self, **kwargs):
        """
        Make a course with an enrolled logged-in student.
        """
63
        self.course = CourseFactory.create(**kwargs)
Ned Batchelder committed
64
        user = UserFactory.create()
65
        CourseEnrollmentFactory.create(user=user, course_id=self.course.id)
Ned Batchelder committed
66
        self.client.login(username=user.username, password='test')
67 68 69 70 71 72 73 74 75 76 77

    def make_url(self, url_name, **kwargs):
        """
        Make a URL for a `url_name` using keyword args for url slots.

        Automatically provides the course id.

        """
        kwargs['course_id'] = self.course.id
        url = reverse(url_name, kwargs=kwargs)
        return url
Ned Batchelder committed
78 79


80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
class StaticImageBookTest(StaticBookTest):
    """
    Test the image-based static book view.
    """

    def test_book(self):
        # We can access a book.
        with mock.patch.object(requests, 'get') as mock_get:
            mock_get.return_value.text = textwrap.dedent('''\
                <?xml version="1.0"?>
                <table_of_contents>
                <entry page="9" page_label="ix" name="Contents!?"/>
                <entry page="1" page_label="i" name="Preamble">
                    <entry page="4" page_label="iv" name="About the Elephants"/>
                </entry>
                </table_of_contents>
                ''')

            self.make_course(textbooks=[IMAGE_BOOK])
            url = self.make_url('book', book_index=0)
            response = self.client.get(url)

        self.assertContains(response, "Contents!?")
        self.assertContains(response, "About the Elephants")

    def test_bad_book_id(self):
        # A bad book id will be a 404.
        self.make_course(textbooks=[IMAGE_BOOK])
        with self.assertRaises(NoReverseMatch):
            self.make_url('book', book_index='fooey')

    def test_out_of_range_book_id(self):
        self.make_course()
        url = self.make_url('book', book_index=0)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

117 118
    def test_bad_page_id(self):
        # A bad page id will cause a 404.        
119 120 121 122
        self.make_course(textbooks=[IMAGE_BOOK])
        with self.assertRaises(NoReverseMatch):
            self.make_url('book', book_index=0, page='xyzzy')

123

Ned Batchelder committed
124 125 126 127 128 129 130
class StaticPdfBookTest(StaticBookTest):
    """
    Test the PDF static book view.
    """

    def test_book(self):
        # We can access a book.
131 132
        self.make_course(pdf_textbooks=[PDF_BOOK])
        url = self.make_url('pdf_book', book_index=0)
Ned Batchelder committed
133 134 135
        response = self.client.get(url)
        self.assertContains(response, "Chapter 1 for PDF")
        self.assertNotContains(response, "options.chapterNum =")
Dave St.Germain committed
136
        self.assertNotContains(response, "page=")
Ned Batchelder committed
137 138 139

    def test_book_chapter(self):
        # We can access a book at a particular chapter.
140 141
        self.make_course(pdf_textbooks=[PDF_BOOK])
        url = self.make_url('pdf_book', book_index=0, chapter=2)
Ned Batchelder committed
142 143
        response = self.client.get(url)
        self.assertContains(response, "Chapter 2 for PDF")
Dave St.Germain committed
144 145
        self.assertContains(response, "file={}".format(PDF_BOOK['chapters'][1]['url']))
        self.assertNotContains(response, "page=")
Ned Batchelder committed
146 147 148

    def test_book_page(self):
        # We can access a book at a particular page.
149 150
        self.make_course(pdf_textbooks=[PDF_BOOK])
        url = self.make_url('pdf_book', book_index=0, page=17)
Ned Batchelder committed
151 152 153
        response = self.client.get(url)
        self.assertContains(response, "Chapter 1 for PDF")
        self.assertNotContains(response, "options.chapterNum =")
Dave St.Germain committed
154
        self.assertContains(response, "page=17")
Ned Batchelder committed
155 156 157

    def test_book_chapter_page(self):
        # We can access a book at a particular chapter and page.
158 159
        self.make_course(pdf_textbooks=[PDF_BOOK])
        url = self.make_url('pdf_book', book_index=0, chapter=2, page=17)
Ned Batchelder committed
160 161
        response = self.client.get(url)
        self.assertContains(response, "Chapter 2 for PDF")
Dave St.Germain committed
162 163
        self.assertContains(response, "file={}".format(PDF_BOOK['chapters'][1]['url']))
        self.assertContains(response, "page=17")
Ned Batchelder committed
164 165

    def test_bad_book_id(self):
166 167 168 169 170 171
        # If the book id isn't an int, we'll get a 404.
        self.make_course(pdf_textbooks=[PDF_BOOK])
        with self.assertRaises(NoReverseMatch):
            self.make_url('pdf_book', book_index='fooey', chapter=1)

    def test_out_of_range_book_id(self):
Ned Batchelder committed
172
        # If we have one book, asking for the second book will fail with a 404.
173 174
        self.make_course(pdf_textbooks=[PDF_BOOK])
        url = self.make_url('pdf_book', book_index=1, chapter=1)
Ned Batchelder committed
175 176 177 178 179
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_no_book(self):
        # If we have no books, asking for the first book will fail with a 404.
180 181
        self.make_course()
        url = self.make_url('pdf_book', book_index=0, chapter=1)
Ned Batchelder committed
182 183 184
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

185 186
    def test_chapter_xss(self):
        # The chapter in the URL used to go right on the page.
187
        self.make_course(pdf_textbooks=[PDF_BOOK])
188 189
        # It's no longer possible to use a non-integer chapter.
        with self.assertRaises(NoReverseMatch):
190
            self.make_url('pdf_book', book_index=0, chapter='xyzzy')
191 192 193

    def test_page_xss(self):
        # The page in the URL used to go right on the page.
194
        self.make_course(pdf_textbooks=[PDF_BOOK])
195 196
        # It's no longer possible to use a non-integer page.
        with self.assertRaises(NoReverseMatch):
197
            self.make_url('pdf_book', book_index=0, page='xyzzy')
198

199 200 201 202 203 204 205
    def test_chapter_page_xss(self):
        # The page in the URL used to go right on the page.
        self.make_course(pdf_textbooks=[PDF_BOOK])
        # It's no longer possible to use a non-integer page and a non-integer chapter.
        with self.assertRaises(NoReverseMatch):
            self.make_url('pdf_book', book_index=0, chapter='fooey', page='xyzzy')

206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
    def test_static_url_map_contentstore(self):
        """
        This ensure static  URL mapping is happening properly for
        a course that uses the contentstore
        """
        self.make_course(pdf_textbooks=[PORTABLE_PDF_BOOK])
        url = self.make_url('pdf_book', book_index=0, chapter=1)
        response = self.client.get(url)
        self.assertNotContains(response, 'file={}'.format(PORTABLE_PDF_BOOK['chapters'][0]['url']))
        self.assertContains(response, 'file=/c4x/{0.org}/{0.course}/asset/{1}'.format(
            self.course.location,
            PORTABLE_PDF_BOOK['chapters'][0]['url'].replace('/static/', '')))

    def test_static_url_map_static_asset_path(self):
        """
        Like above, but used when the course has set a static_asset_path
        """
        self.make_course(pdf_textbooks=[PORTABLE_PDF_BOOK], static_asset_path='awesomesauce')
        url = self.make_url('pdf_book', book_index=0, chapter=1)
        response = self.client.get(url)
        self.assertNotContains(response, 'file={}'.format(PORTABLE_PDF_BOOK['chapters'][0]['url']))
        self.assertNotContains(response, 'file=/c4x/{0.org}/{0.course}/asset/{1}'.format(
            self.course.location,
            PORTABLE_PDF_BOOK['chapters'][0]['url'].replace('/static/', '')))
        self.assertContains(response, 'file=/static/awesomesauce/{}'.format(
            PORTABLE_PDF_BOOK['chapters'][0]['url'].replace('/static/', '')))

Ned Batchelder committed
233 234 235 236 237 238 239 240

class StaticHtmlBookTest(StaticBookTest):
    """
    Test the HTML static book view.
    """

    def test_book(self):
        # We can access a book.
241 242
        self.make_course(html_textbooks=[HTML_BOOK])
        url = self.make_url('html_book', book_index=0)
Ned Batchelder committed
243 244 245 246 247 248
        response = self.client.get(url)
        self.assertContains(response, "Chapter 1 for HTML")
        self.assertNotContains(response, "options.chapterNum =")

    def test_book_chapter(self):
        # We can access a book at a particular chapter.
249 250
        self.make_course(html_textbooks=[HTML_BOOK])
        url = self.make_url('html_book', book_index=0, chapter=2)
Ned Batchelder committed
251 252 253 254 255 256
        response = self.client.get(url)
        self.assertContains(response, "Chapter 2 for HTML")
        self.assertContains(response, "options.chapterNum = 2;")

    def test_bad_book_id(self):
        # If we have one book, asking for the second book will fail with a 404.
257 258
        self.make_course(html_textbooks=[HTML_BOOK])
        url = self.make_url('html_book', book_index=1, chapter=1)
Ned Batchelder committed
259 260 261 262 263
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_no_book(self):
        # If we have no books, asking for the first book will fail with a 404.
264 265
        self.make_course()
        url = self.make_url('html_book', book_index=0, chapter=1)
Ned Batchelder committed
266 267
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)
268 269 270

    def test_chapter_xss(self):
        # The chapter in the URL used to go right on the page.
271
        self.make_course(pdf_textbooks=[HTML_BOOK])
272 273
        # It's no longer possible to use a non-integer chapter.
        with self.assertRaises(NoReverseMatch):
274
            self.make_url('html_book', book_index=0, chapter='xyzzy')