1#!/usr/bin/env python
2# Copyright 2013 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import json
7import unittest
8
9from compiled_file_system import CompiledFileSystem
10from object_store_creator import ObjectStoreCreator
11from redirector import Redirector
12from test_file_system import TestFileSystem
13from third_party.json_schema_compiler.json_parse import Parse
14
15HOST = 'http://localhost/'
16
17file_system = TestFileSystem({
18  'redirects.json': json.dumps({
19    'foo/...': 'apps/...',
20    '': '/index.html',
21    'home': 'index.html',
22    'index.html': 'http://something.absolute.com/'
23  }),
24  'apps': {
25    'redirects.json': json.dumps({
26      '': '../index.html',
27      'index.html': 'about_apps.html',
28      'foo.html': '/bar.html',
29    })
30  },
31  'extensions': {
32    'redirects.json': json.dumps({
33      'manifest': 'manifest.html',
34      'tabs': 'tabs.html',
35      'dev/...': '...',
36      'a/very/long/dir/chain/...': 'short/...',
37      '_short/...': 'another/long/chain/...',
38      'r1/...': 'r2/r1/...',
39      'r2/r1/...': 'r3/...',
40      'r3/...': 'r4/...',
41      'r5/...': 'r6/...',
42      'nofile1/...': 'nofile2/...',
43      'noredirects1/...': 'noredirects2/...'
44    }),
45    'manifest': {
46      'redirects.json': json.dumps({
47        '': '../manifest.html',
48        'more-info': 'http://lmgtfy.com'
49      }),
50    },
51    'stable': {
52        'redirects.json': json.dumps({
53            'tabs': 'tabs.html'
54        }),
55        'manifest': {
56          'redirects.json': json.dumps({
57            'storage': 'storage.html'
58          })
59        },
60    },
61    'dev': {
62      'redirects.json': json.dumps({
63        'tabs': 'tabs.html',
64        'manifest': 'manifest.html'
65      }),
66      'manifest': {
67        'redirects.json': json.dumps({
68          'storage': 'storage.html'
69        })
70      }
71    },
72    'r4': {
73      'redirects.json': json.dumps({
74        'manifest': 'manifest.html'
75      })
76    },
77    'r6': {
78      'redirects.json': json.dumps({
79        '...': 'directory/...'
80      }),
81      'directory': {
82        'redirects.json': json.dumps({
83          'manifest': 'manifest.html'
84        }),
85        'manifest': 'manifest.html'
86      }
87    },
88    'short': {
89      'redirects.json': json.dumps({
90        'index': 'index.html'
91      })
92    },
93    'another': {
94      'long': {
95        'chain': {
96          'redirects.json': json.dumps({
97            'index': 'index.html'
98          })
99        }
100      }
101    },
102    'nofile': {
103      'redirects.json': json.dumps({
104      })
105    }
106  },
107  'priority': {
108    'redirects.json': json.dumps({
109      'directory/...': 'GOOD/...'
110    }),
111    'directory': {
112      'redirects.json': json.dumps({
113        '...': '../BAD/...'
114      }),
115    }
116  },
117  'relative_directory': {
118    'redirects.json': json.dumps({
119      '...': '../...'
120    })
121  },
122  'infinite_redirect': {
123    'redirects.json': json.dumps({
124      '...': 'loop/...'
125    }),
126    'loop': {
127      'redirects.json': json.dumps({
128        '...': './...'
129      })
130    }
131  },
132  'parent_redirect': {
133    'redirects.json': json.dumps({
134      'a/...': 'b/...'
135    })
136  }
137})
138
139class RedirectorTest(unittest.TestCase):
140  def setUp(self):
141    self._redirector = Redirector(
142        CompiledFileSystem.Factory(ObjectStoreCreator.ForTest()),
143        file_system)
144
145  def testExternalRedirection(self):
146    self.assertEqual(
147        'http://something.absolute.com/',
148        self._redirector.Redirect(HOST, 'index.html'))
149    self.assertEqual(
150        'http://lmgtfy.com',
151        self._redirector.Redirect(HOST, 'extensions/manifest/more-info'))
152
153  def testAbsoluteRedirection(self):
154    self.assertEqual(
155        '/index.html', self._redirector.Redirect(HOST, ''))
156    self.assertEqual(
157        '/bar.html', self._redirector.Redirect(HOST, 'apps/foo.html'))
158
159  def testRelativeRedirection(self):
160    self.assertEqual(
161        'apps/about_apps.html',
162        self._redirector.Redirect(HOST, 'apps/index.html'))
163    self.assertEqual(
164        'extensions/manifest.html',
165        self._redirector.Redirect(HOST, 'extensions/manifest/'))
166    self.assertEqual(
167        'extensions/manifest.html',
168        self._redirector.Redirect(HOST, 'extensions/manifest'))
169    self.assertEqual(
170        'index.html', self._redirector.Redirect(HOST, 'apps/'))
171    self.assertEqual(
172        'index.html', self._redirector.Redirect(HOST, 'home'))
173
174  def testNotFound(self):
175    self.assertEqual(
176        None, self._redirector.Redirect(HOST, 'not/a/real/path'))
177    self.assertEqual(
178        None, self._redirector.Redirect(HOST, 'public/apps/okay.html'))
179
180  def testOldHosts(self):
181    self.assertEqual(
182        'https://developer.chrome.com/',
183        self._redirector.Redirect('http://code.google.com', ''))
184    self.assertEqual(
185        'https://developer.chrome.com/',
186        self._redirector.Redirect('https://code.google.com', ''))
187
188  def testRefresh(self):
189    self._redirector.Refresh().Get()
190
191    expected_paths = set([
192      'redirects.json',
193      'apps/redirects.json',
194      'extensions/redirects.json',
195      'extensions/manifest/redirects.json'
196    ])
197
198    for path in expected_paths:
199      self.assertEqual(
200          Parse(file_system.ReadSingle(path).Get()),
201          # Access the cache's object store to see what files were hit during
202          # the cron run. Returns strings parsed as JSON.
203          # TODO(jshumway): Make a non hack version of this check.
204          self._redirector._cache._file_object_store.Get(
205              path).Get().cache_data)
206
207  def testDirectoryRedirection(self):
208    # Simple redirect.
209    self.assertEqual(
210      'extensions/manifest.html',
211      self._redirector.Redirect(HOST, 'extensions/dev/manifest'))
212
213    # Multiple hops with one file.
214    self.assertEqual(
215      'extensions/r4/manifest.html',
216      self._redirector.Redirect(HOST, 'extensions/r1/manifest'))
217
218    # Multiple hops w/ multiple redirection files.
219    self.assertEqual(
220      'extensions/r6/directory/manifest.html',
221      self._redirector.Redirect(HOST, 'extensions/r5/manifest'))
222
223    # Redirection from root directory redirector.
224    self.assertEqual(
225      'apps/about_apps.html',
226      self._redirector.Redirect(HOST, 'foo/index.html'))
227
228    # Short to long.
229    self.assertEqual(
230      'extensions/short/index.html',
231      self._redirector.Redirect(HOST, 'extensions/a/very/long/dir/chain/index'))
232
233    # Long to short.
234    self.assertEqual(
235      'extensions/another/long/chain/index.html',
236      self._redirector.Redirect(HOST, 'extensions/_short/index'))
237
238    # Directory redirection without a redirects.json in final directory.
239    self.assertEqual(
240      'extensions/noredirects2/file',
241      self._redirector.Redirect(HOST, 'extensions/noredirects1/file'))
242
243    # Directory redirection with redirects.json without rule for the filename.
244    self.assertEqual(
245      'extensions/nofile2/file',
246      self._redirector.Redirect(HOST, 'extensions/nofile1/file'))
247
248    # Relative directory path.
249    self.assertEqual(
250      'index.html',
251      self._redirector.Redirect(HOST, 'relative_directory/home'))
252
253    # Shallower directory redirects have priority.
254    self.assertEqual(
255      'priority/GOOD/index',
256      self._redirector.Redirect(HOST, 'priority/directory/index'))
257
258    # Don't infinitely redirect.
259    self.assertEqual('infinite_redirect/loop/index',
260      self._redirector.Redirect(HOST, 'infinite_redirect/index'))
261
262    # If a parent directory is redirected, redirect children properly.
263    self.assertEqual('parent_redirect/b/c/index',
264      self._redirector.Redirect(HOST, 'parent_redirect/a/c/index'))
265
266
267if __name__ == '__main__':
268  unittest.main()
269