class TestMockHTTP(AsyncTestCase):

    def setUp(self):
        super(TestMockHTTP, self).setUp()
        self.mock = MockHTTP()
        self.http_client = AsyncHTTPClient(io_loop=self.io_loop)

    def tearDown(self):
        self.mock.shutdown()
        self.http_client.close()
        super(TestMockHTTP, self).tearDown()

    def fetch(self, path, **kwargs):
        """Convenience method to synchronously fetch a url.

        The given path will be appended to the local server's host and
        port.  Any additional kwargs will be passed directly to
        `.AsyncHTTPClient.fetch` (and so could be used to pass
        ``method="POST"``, ``body="..."``, etc).
        """

        self.http_client.fetch(
            HTTPRequest(url=self.mock.get_url(path), **kwargs),
        callback=self.stop)
        return self.wait()

    def test_get_request(self):
        """Tests a get request that expects nothing to return but an 200."""
        
        self.mock.expects(method=GET, path='/index.html')
        resp = self.fetch(method="GET", path="index.html")
        self.assertEqual(resp.code, 200)
        self.assert_(self.mock.verify())
    
    def test_get_request_wrong_url(self):
        """Tests a get request that expects a different URL."""
        
        self.mock.expects(method=GET, path='/index.html')
        resp = self.fetch(method="GET", path="notindex.html")
        self.assertEqual(resp.code, 404)
        self.assertRaises(UnexpectedURLException, self.mock.verify)
    
    def test_get_with_code(self):
        
        self.mock.expects(method=GET, path='/index.html').will(http_code=500)
        resp = self.fetch(method="GET", path="index.html")
        self.assertEqual(resp.code, 500, 'Expected 500 response.')
        self.assert_(self.mock.verify())
    
    def test_get_with_body(self):
        """Tests a get request that returns a different URL."""
        test_body = 'Test response.'
        
        self.mock.expects(method=GET, path='/index.html').will(body=test_body)
        resp = self.fetch(method="GET", path="index.html")
        self.assertEqual(resp.body, test_body)
        self.assert_(self.mock.verify())
    
    def test_get_with_header(self):
        """Tests a get request that includes a custom header."""
        test_header_name = 'Content-Type'
        test_header_contents = 'text/html'
        
        self.mock.expects(method=GET, path='/index.html').will(
            headers={test_header_name: test_header_contents})
        resp = self.fetch(method="GET", path="index.html")
        self.assertTrue(resp.headers[test_header_name].startswith(test_header_contents))
        self.assert_(self.mock.verify())
    
    def test_multiple_get(self):
        """Test getting a URL twice."""
        
        self.mock.expects(method=GET, path='/index.html')
        resp = self.fetch(method="GET", path="index.html")
        self.assertEqual(resp.code, 200)
        resp = self.fetch(method="GET", path="index.html")
        self.assertEqual(resp.code, 200)
        self.assert_(self.mock.verify())
    
    def test_never_get(self):
        """Test a URL that has a 'never' times on it."""
        
        self.mock.expects(method=GET, path='/index.html', times=never)
        resp = self.fetch(method="GET", path="index.html")
        self.assertEqual(resp.code, 404)
        self.assertRaises(UnexpectedURLException, self.mock.verify)
    
    def test_get_once_got_twice(self):
        """Test getting a URL twice that expects to be retrieved once only."""
        
        self.mock.expects(method=GET, path='/index.html', times=once)
        resp = self.fetch(method="GET", path="index.html")
        resp = self.fetch(method="GET", path="index.html")
        self.assertEqual(resp.code, 404)
        self.assertRaises(AlreadyRetrievedURLException, self.mock.verify)
    
    def test_get_once_got_never(self):
        """Test never getting a URL that expects to be retrieved once only."""
        
        self.mock.expects(method=GET, path='/index.html', times=once)
        self.assertRaises(UnretrievedURLException, self.mock.verify)
    
    def test_get_5times_got_never(self):
        """Test never getting a URL that expects to be retrieved 5 times only."""
        
        self.mock.expects(method=GET, path='/index.html', times=5)
        self.assertRaises(UnretrievedURLException, self.mock.verify)   

    def test_get_5times_got_5times(self):
        """Test getting 5 times a URL that expects to be retrieved 5 times"""
        
        self.mock.expects(method=GET, path='/index.html', times=5)
        call = lambda: self.fetch('/index.html')
        [ call() for x in xrange(5) ]
        self.mock.verify()   

    def test_get_at_least_once_got_twice(self):
        """Test getting a URL twice that expects to be retrieved at least once."""
        
        self.mock.expects(method=GET, path='/index.html', times=at_least_once)
        resp = self.fetch(method="GET", path="index.html")
        self.assertEqual(resp.code, 200)
        self.assert_(self.mock.verify())
    
    def test_get_at_least_once_got_never(self):
        """Test never getting a URL that expects to be retrieved at least once."""
        
        self.mock.expects(method=GET, path='/index.html', times=at_least_once)
        self.assertRaises(UnretrievedURLException, self.mock.verify)
    
    def test_get_after(self):
        """Test two URLs that expect to be retrieved in order."""
        test_body = 'Test POST body.\r\n'
        headers = {'content-type': 'text/plain'}
        self.mock.expects(method=GET, path='/index.html', name='url #1')
        self.mock.expects(method=POST, path='/index.html', after='url #1', headers=headers, body=test_body)
        self.fetch(method="GET", path="index.html")
        self.fetch(method="POST", path="index.html", body = test_body, headers=headers)
        self.assert_(self.mock.verify())

    def test_get_after_wrong_order(self):
        """Test two URLs that expect to be retrieved in order, but aren't."""
        test_body = 'Test POST body.\r\n'
        headers = {'content-type': 'text/plain'}
        self.mock.expects(method=GET, path='/index.html', name='url #1')
        self.mock.expects(method=POST, path='/index.html', after='url #1', body=test_body, headers=headers)
        resp = self.fetch('/index.html', method = 'POST', body = test_body, headers = headers)
        self.assertEqual(resp.code, 404)
        self.assertRaises(URLOrderingException, self.mock.verify)
        
    def test_post(self):
        """Tests a POST request."""
        test_body = 'Test POST body.\r\n'
        headers = {'content-type': 'text/plain'}
        self.mock.expects(method=POST, path='/index.html', body=test_body, headers=headers).will(http_code=201)
        resp = self.fetch('/index.html', method = 'POST', body = test_body, headers = headers)
        self.assertEqual(resp.code, 201)
        self.assert_(self.mock.verify())
    
    def test_post_bad_body(self):
        """Tests a POST request that sends the wrong body data."""
        test_body = 'Test POST body.\r\n'
        expected_body = 'Expected POST body.\r\n'
        headers = {'content-type': 'text/plain'}
        self.mock.expects(method=POST, path='/index.html', body=expected_body)
        resp = self.fetch('/index.html', method = 'POST', body = test_body, headers = headers)
        self.assertEqual(resp.code, 404)
        self.assertRaises(WrongBodyException, self.mock.verify)
    
    def test_post_header(self):
        """Tests a POST request with some custom headers."""
        test_body = 'Test POST body.\r\n'
        test_headers = {'content-type': 'application/atom+xml; type=entry',
                        'content-length': str(len(test_body)),
                        'Slug': 'ooze',}
        
        self.mock.expects(method=POST, path='/index.html',
                     body=test_body, headers=test_headers).will(http_code=201)
        resp = self.fetch('/index.html', method = 'POST', body = test_body, headers = test_headers)
        self.assertEqual(resp.code, 201)
        self.assert_(self.mock.verify())

    def test_post_unexpected_header(self):
        """Tests a POST request with an unexpected header."""
        test_body = 'Test POST body.\r\n'
        test_headers = {'content-type': 'application/atom+xml; type=entry',
                        'content-length': str(len(test_body)),
                        'Slug': 'ooze',}
        expected_headers = {'content-type': 'application/atom+xml; type=entry',
                            'content-length': str(len(test_body)),}
        
        self.mock.expects(method=POST, path='/index.html',
                     body=test_body, headers=expected_headers)
        resp = self.fetch('/index.html', method = 'POST', body = test_body, headers = test_headers)
        self.assertEqual(resp.code, 200)
        self.assert_(self.mock.verify())

    def test_post_missing_header(self):
        """Tests a POST request without an expected header."""
        test_body = 'Test POST body.\r\n'
        test_headers = {'content-type': 'application/atom+xml; type=entry',
                        'content-length': str(len(test_body)),}
        expected_headers = {'content-type': 'application/atom+xml; type=entry',
                            'content-length': str(len(test_body)),
                            'Slug': 'ooze',}
        
        self.mock.expects(method=POST, path='/index.html',
                     body=test_body, headers=expected_headers)
        resp = self.fetch('/index.html', method = 'POST', body = test_body, headers = test_headers)
        self.assertEqual(resp.code, 404)
        self.assertRaises(WrongHeaderException, self.mock.verify)
    
    def test_post_unexpected_header_value(self):
        """Tests a POST request with an unexpected header value."""
        test_body = 'Test POST body.\r\n'
        test_headers = {'content-type': 'application/atom+xml; type=entry',
                        'content-length': str(len(test_body)),
                        'Slug': 'ooze',}
        expected_headers = {'content-type': 'application/atom+xml; type=entry',
                        'content-length': str(len(test_body)),
                        'Slug': 'slime',}
        
        self.mock.expects(method=POST, path='/index.html',
                     body=test_body, headers=expected_headers)
        resp = self.fetch('/index.html', method = 'POST', body = test_body, headers = test_headers)
        self.assertEqual(resp.code, 404)
        self.assertRaises(WrongHeaderValueException, self.mock.verify)