Exemplo n.º 1
0
class TestFakeDatabaseCommand(TestCase):
    def setUp(self):
        self.cmd = FakeDatabaseCommand()
        self.seed = 12345
        self.faker = Faker()
        self.faker.seed(self.seed)

    def test_no_airports_created(self):
        """Make sure we don't create any airports.

        We don't want to create them, because data migrations add some, and in
        the future we want to add them via fixture (see #626)."""
        airports_before = set(Airport.objects.all())
        self.cmd.fake_airports(self.faker)
        airports_after = set(Airport.objects.all())

        self.assertEqual(airports_before, airports_after)

    def test_new_roles_added(self):
        """Make sure we add roles that are hard-coded. They'll end up in
        fixtures in future (see #626)."""
        roles = [
            'helper', 'instructor', 'host', 'learner', 'organizer', 'tutor',
            'debriefed'
        ]
        self.assertFalse(Role.objects.filter(name__in=roles).exists())
        self.cmd.fake_roles(self.faker)

        self.assertEqual(set(roles),
                         set(Role.objects.values_list('name', flat=True)))

    def test_new_tags_added(self):
        """Make sure we add tags that are hard-coded. They'll end up in
        fixtures in future (see #626)."""
        tags = [
            'SWC', 'DC', 'LC', 'WiSE', 'TTT', 'online', 'stalled',
            'unresponsive'
        ]
        self.assertNotEqual(set(tags),
                            set(Tag.objects.values_list('name', flat=True)))

        self.cmd.fake_tags(self.faker)
        self.assertEqual(set(tags),
                         set(Tag.objects.values_list('name', flat=True)))

    def test_database_populated(self):
        """Make sure the database is getting populated."""
        self.assertFalse(Person.objects.exists())
        self.assertFalse(
            Host.objects.exclude(domain='self-organized').exists())
        self.assertFalse(Event.objects.exists())
        self.assertFalse(Task.objects.exists())

        call_command('fake_database', seed=self.seed)

        self.assertTrue(Person.objects.exists())
        self.assertTrue(Host.objects.exclude(domain='self-organized').exists())
        self.assertTrue(Event.objects.exists())
        self.assertTrue(Task.objects.exists())
Exemplo n.º 2
0
class TestFakeDatabaseCommand(TestCase):
    def setUp(self):
        self.cmd = FakeDatabaseCommand()
        self.seed = 12345
        self.faker = Faker()
        self.faker.seed(self.seed)

    def test_no_airports_created(self):
        """Make sure we don't create any airports.

        We don't want to create them, because data migrations add some, and in
        the future we want to add them via fixture (see #626)."""
        airports_before = set(Airport.objects.all())
        self.cmd.fake_airports(self.faker)
        airports_after = set(Airport.objects.all())

        self.assertEqual(airports_before, airports_after)

    def test_new_roles_added(self):
        """Make sure we add roles that are hard-coded. They'll end up in
        fixtures in future (see #626)."""
        roles = ['helper', 'instructor', 'host', 'learner', 'organizer',
                 'tutor', 'debriefed']
        self.assertFalse(Role.objects.filter(name__in=roles).exists())
        self.cmd.fake_roles(self.faker)

        self.assertEqual(set(roles),
                         set(Role.objects.values_list('name', flat=True)))

    def test_new_tags_added(self):
        """Make sure we add tags that are hard-coded. They'll end up in
        fixtures in future (see #626)."""
        tags = ['SWC', 'DC', 'LC', 'WiSE', 'TTT', 'online', 'stalled',
                'unresponsive']
        self.assertNotEqual(set(tags),
                            set(Tag.objects.values_list('name', flat=True)))

        self.cmd.fake_tags(self.faker)
        self.assertEqual(set(tags),
                         set(Tag.objects.values_list('name', flat=True)))

    def test_database_populated(self):
        """Make sure the database is getting populated."""
        self.assertFalse(Person.objects.exists())
        self.assertFalse(Host.objects.exclude(domain='self-organized')
                                     .exists())
        self.assertFalse(Event.objects.exists())
        self.assertFalse(Task.objects.exists())

        call_command('fake_database', seed=self.seed)

        self.assertTrue(Person.objects.exists())
        self.assertTrue(Host.objects.exclude(domain='self-organized').exists())
        self.assertTrue(Event.objects.exists())
        self.assertTrue(Task.objects.exists())
Exemplo n.º 3
0
 def setUp(self):
     self.cmd = FakeDatabaseCommand()
     self.seed = 12345
     self.faker = Faker()
     self.faker.seed(self.seed)
Exemplo n.º 4
0
    def setUp(self):
        self.cmd = WebsiteUpdatesCommand()
        self.fake_cmd = FakeDatabaseCommand()
        self.seed = 12345
        self.fake_cmd.faker.seed(self.seed)
        self.fake_cmd.stdout = StringIO()

        self.fake_cmd.fake_organizations()

        self.mocked_event_page = """
<html><head>
<meta name="slug" content="2015-07-13-test" />
<meta name="startdate" content="2015-07-13" />
<meta name="enddate" content="2015-07-14" />
<meta name="country" content="us" />
<meta name="venue" content="Euphoric State University" />
<meta name="address" content="Highway to Heaven 42, Academipolis" />
<meta name="latlng" content="36.998977, -109.045173" />
<meta name="language" content="us" />
<meta name="invalid" content="invalid" />
<meta name="instructor" content="Hermione Granger|Ron Weasley" />
<meta name="helper" content="Peter Parker|Tony Stark|Natasha Romanova" />
<meta name="contact" content="[email protected], [email protected]" />
<meta name="eventbrite" content="10000000" />
<meta name="charset" content="utf-8" />
</head>
<body>
<h1>test</h1>
</body></html>
"""
        self.expected_metadata_parsed = {
            'slug': '2015-07-13-test',
            'language': 'US',
            'start': date(2015, 7, 13),
            'end': date(2015, 7, 14),
            'country': 'US',
            'venue': 'Euphoric State University',
            'address': 'Highway to Heaven 42, Academipolis',
            'latitude': 36.998977,
            'longitude': -109.045173,
            'reg_key': 10000000,
            'instructors': ['Hermione Granger', 'Ron Weasley'],
            'helpers': ['Peter Parker', 'Tony Stark', 'Natasha Romanova'],
            'contact': '[email protected], [email protected]',
        }

        self.date_serialization_tests = [
            # simple tests
            ('', ''),
            ('empty string', 'empty string'),

            # format-matching
            ('2016-04-18', date(2016, 4, 18)),
            ('2016-04-18T16:41:30', datetime(2016, 4, 18, 16, 41, 30)),
            ('2016-04-18T16:41:30.123',
             datetime(2016, 4, 18, 16, 41, 30, 123000)),
            ('16:41:30', time(16, 41, 30)),
            ('16:41:30.123', time(16, 41, 30, 123000)),

            # format not matching (ie. timezone-aware)
            ('2016-04-18T16:41:30+02:00', '2016-04-18T16:41:30+02:00'),
            ('2016-04-18T14:41:30Z', '2016-04-18T14:41:30Z'),
            ('16:41:30+02:00', '16:41:30+02:00'),
            ('14:41:30Z', '14:41:30Z'),
        ]
Exemplo n.º 5
0
class TestWebsiteUpdatesCommand(TestBase):
    maxDiff = None

    def setUp(self):
        self.cmd = WebsiteUpdatesCommand()
        self.fake_cmd = FakeDatabaseCommand()
        self.seed = 12345
        self.fake_cmd.faker.seed(self.seed)
        self.fake_cmd.stdout = StringIO()

        self.fake_cmd.fake_organizations()

        self.mocked_event_page = """
<html><head>
<meta name="slug" content="2015-07-13-test" />
<meta name="startdate" content="2015-07-13" />
<meta name="enddate" content="2015-07-14" />
<meta name="country" content="us" />
<meta name="venue" content="Euphoric State University" />
<meta name="address" content="Highway to Heaven 42, Academipolis" />
<meta name="latlng" content="36.998977, -109.045173" />
<meta name="language" content="us" />
<meta name="invalid" content="invalid" />
<meta name="instructor" content="Hermione Granger|Ron Weasley" />
<meta name="helper" content="Peter Parker|Tony Stark|Natasha Romanova" />
<meta name="contact" content="[email protected], [email protected]" />
<meta name="eventbrite" content="10000000" />
<meta name="charset" content="utf-8" />
</head>
<body>
<h1>test</h1>
</body></html>
"""
        self.expected_metadata_parsed = {
            'slug': '2015-07-13-test',
            'language': 'US',
            'start': date(2015, 7, 13),
            'end': date(2015, 7, 14),
            'country': 'US',
            'venue': 'Euphoric State University',
            'address': 'Highway to Heaven 42, Academipolis',
            'latitude': 36.998977,
            'longitude': -109.045173,
            'reg_key': 10000000,
            'instructors': ['Hermione Granger', 'Ron Weasley'],
            'helpers': ['Peter Parker', 'Tony Stark', 'Natasha Romanova'],
            'contact': '[email protected], [email protected]',
        }

        self.date_serialization_tests = [
            # simple tests
            ('', ''),
            ('empty string', 'empty string'),

            # format-matching
            ('2016-04-18', date(2016, 4, 18)),
            ('2016-04-18T16:41:30', datetime(2016, 4, 18, 16, 41, 30)),
            ('2016-04-18T16:41:30.123',
             datetime(2016, 4, 18, 16, 41, 30, 123000)),
            ('16:41:30', time(16, 41, 30)),
            ('16:41:30.123', time(16, 41, 30, 123000)),

            # format not matching (ie. timezone-aware)
            ('2016-04-18T16:41:30+02:00', '2016-04-18T16:41:30+02:00'),
            ('2016-04-18T14:41:30Z', '2016-04-18T14:41:30Z'),
            ('16:41:30+02:00', '16:41:30+02:00'),
            ('14:41:30Z', '14:41:30Z'),
        ]

    def test_getting_events(self):
        """Ensure only active events with URL are returned."""
        self.fake_cmd.fake_current_events(count=6, add_tags=False)

        Event.objects.all().update(start=date.today())

        # one active event with URL and one without
        e1, e2 = Event.objects.all()[0:2]
        e1.completed = False  # completed == !active
        e1.url = 'https://swcarpentry.github.io/workshop-template/'
        e1.save()
        e2.completed = False
        e2.url = None
        e2.save()

        # one inactive event with URL and one without
        e3, e4 = Event.objects.all()[2:4]
        e3.completed = True
        e3.url = 'https://datacarpentry.github.io/workshop-template/'
        e3.save()
        e4.completed = True
        e4.url = None
        e4.save()

        # both active but one very old
        e5, e6 = Event.objects.all()[4:6]
        e5.completed = False
        e5.url = 'https://swcarpentry.github.io/workshop-template2/'
        e5.start = date(2014, 1, 1)
        e5.save()
        e6.completed = False
        e6.url = 'https://datacarpentry.github.io/workshop-template2/'
        e6.save()

        # check
        events = set(self.cmd.get_events())
        self.assertEqual({e1, e6}, events)

    def test_parsing_github_url(self):
        """Ensure `parse_github_url()` correctly parses repository URL."""
        url = 'https://github.com/swcarpentry/workshop-template'
        expected = 'swcarpentry', 'workshop-template'
        self.assertEqual(expected, self.cmd.parse_github_url(url))

        with self.assertRaises(WrongWorkshopURL):
            url = 'https://swcarpentry.github.io/workshop-template'
            self.cmd.parse_github_url(url)

    @requests_mock.Mocker()
    def test_getting_event_metadata(self, mock):
        """Ensure metadata are fetched and normalized by `get_event_metadata`."""
        # underlying `fetch_event_metadata` and `parse_metadata_from_event_website`
        # are tested in great detail in `test_util.py`, so here's just a short
        # test
        website_url = 'https://github.com/swcarpentry/workshop-template'
        mock_text = self.mocked_event_page
        mock.get(website_url, text=mock_text, status_code=200)
        # mock placed, let's test `get_event_metadata`

        metadata = self.cmd.get_event_metadata(website_url)
        self.assertEqual(metadata, self.expected_metadata_parsed)

    def test_deserialization_of_string(self):
        "Ensure our datetime matching function works correctly for strings."
        for test, expected in self.date_serialization_tests:
            with self.subTest(test=test):
                self.assertEqual(datetime_match(test), expected)

    def test_deserialization_of_list(self):
        """Ensure our datetime matching function works correctly for lists."""
        tests = self.date_serialization_tests[:]
        tests = list(zip(*tests))  # transpose
        test = list(tests[0])
        expected = list(tests[1])
        self.assertEqual(datetime_decode(test), expected)

    def test_deserialization_of_dict(self):
        """Ensure our datetime matching function works correctly for dicts."""
        test = {k: k for k, v in self.date_serialization_tests}
        expected = {k: v for k, v in self.date_serialization_tests}
        self.assertEqual(datetime_decode(test), expected)

    def test_deserialization_of_nested(self):
        """Ensure our datetime matching function works correctly for nested
        objects/lists."""
        # this test uses simpler format
        dict_test = {'2016-04-18': '2016-04-18'}
        dict_expected = {'2016-04-18': date(2016, 4, 18)}
        test = [dict_test.copy(), dict_test.copy(), dict_test.copy()]
        expected = [
            dict_expected.copy(),
            dict_expected.copy(),
            dict_expected.copy()
        ]
        self.assertEqual(datetime_decode(test), expected)

        test = {'1': test[:]}
        expected = {'1': expected[:]}
        self.assertEqual(datetime_decode(test), expected)

    def test_serialization(self):
        """Ensure serialization uses JSON and works correctly with dates,
        datetimes and times.
        Ensure derialization from JSON works correctly with dates,
        datetimes and times."""
        serialized_json = self.cmd.serialize(self.expected_metadata_parsed)

        self.assertIn('2015-07-13', serialized_json)
        self.assertIn('2015-07-14', serialized_json)
        self.assertIn('2015-07-13-test', serialized_json)
        self.assertIn('-109.045173', serialized_json)
        self.assertIn('36.998977', serialized_json)
        self.assertIn('[email protected], [email protected]',
                      serialized_json)

        deserialized_data = self.cmd.deserialize(serialized_json)
        self.assertEqual(deserialized_data, self.expected_metadata_parsed)

    @unittest.skip('Don\'t know how to test it')
    def test_loading_from_github(self):
        """Not sure how to test it, so for now leaving this blank."""
        # TODO: mock up github response?
        pass

    @requests_mock.Mocker()
    def test_detecting_changes(self, mock):
        """Make sure metadata changes are detected."""
        hash_ = 'abcdefghijklmnopqrstuvwxyz'
        e = Event.objects.create(
            slug='with-changes',
            host=Organization.objects.first(),
            url='https://swcarpentry.github.io/workshop-template/',
            repository_last_commit_hash=hash_,
            repository_metadata='',
            metadata_changed=False)

        branch = MagicMock()
        branch.commit = MagicMock()
        branch.commit.sha = hash_

        changes = self.cmd.detect_changes(branch, e)
        self.assertEqual(changes, [])

        # more real example: hash changed
        hash_ = "zyxwvutsrqponmlkjihgfedcba"
        branch.commit.sha = hash_
        mock_text = self.mocked_event_page
        mock.get(e.url, text=mock_text, status_code=200)
        metadata = self.cmd.empty_metadata()
        metadata['instructors'] = self.expected_metadata_parsed['instructors']
        metadata['latitude'] = self.expected_metadata_parsed['latitude']
        metadata['longitude'] = self.expected_metadata_parsed['longitude']
        e.repository_metadata = self.cmd.serialize(metadata)
        e.save()

        changes = self.cmd.detect_changes(branch, e)
        expected = [
            'Helpers changed',
            'Start date changed',
            'End date changed',
            'Country changed',
            'Venue changed',
            'Address changed',
            'Contact details changed',
            'Eventbrite key changed',
        ]
        self.assertEqual(changes, expected)

    @requests_mock.Mocker()
    def test_initialization(self, mock):
        """Make sure events are initialized to sane values."""
        e = Event.objects.create(
            slug='with-changes',
            host=Organization.objects.first(),
            url='https://swcarpentry.github.io/workshop-template/',
            repository_last_commit_hash='',
            repository_metadata='',
            metadata_changed=False,
            metadata_all_changes='')

        hash_ = 'abcdefghijklmnopqrstuvwxyz'
        branch = MagicMock()
        branch.commit = MagicMock()
        branch.commit.sha = hash_
        mock_text = self.mocked_event_page
        mock.get(e.url, text=mock_text, status_code=200)

        self.cmd.init(branch, e)

        e.refresh_from_db()
        # metadata updated
        self.assertEqual(e.repository_last_commit_hash, hash_)
        self.assertEqual(self.cmd.deserialize(e.repository_metadata),
                         self.expected_metadata_parsed)

        self.assertEqual(e.metadata_all_changes, '')
        self.assertEqual(e.metadata_changed, False)

    @unittest.skip('This command requires internet connection')
    def test_running(self):
        """Test running whole command."""
        call_command('check_for_workshop_websites_updates')
Exemplo n.º 6
0
 def setUp(self):
     self.cmd = FakeDatabaseCommand()
     self.seed = 12345
     self.faker = Faker()
     self.faker.seed(self.seed)
Exemplo n.º 7
0
    def setUp(self):
        self.cmd = WebsiteUpdatesCommand()
        self.fake_cmd = FakeDatabaseCommand()
        self.seed = 12345
        Faker.seed(self.seed)
        self.fake_cmd.stdout = StringIO()

        self.fake_cmd.fake_organizations()

        self.mocked_event_page = """
<html><head>
<meta name="slug" content="2015-07-13-test" />
<meta name="startdate" content="2015-07-13" />
<meta name="enddate" content="2015-07-14" />
<meta name="country" content="us" />
<meta name="venue" content="Euphoric State University" />
<meta name="address" content="Highway to Heaven 42, Academipolis" />
<meta name="latlng" content="36.998977, -109.045173" />
<meta name="language" content="us" />
<meta name="invalid" content="invalid" />
<meta name="instructor" content="Hermione Granger|Ron Weasley" />
<meta name="helper" content="Peter Parker|Tony Stark|Natasha Romanova" />
<meta name="contact" content="[email protected]|[email protected]" />
<meta name="eventbrite" content="10000000" />
<meta name="charset" content="utf-8" />
</head>
<body>
<h1>test</h1>
</body></html>
"""
        self.expected_metadata_parsed = {
            "slug": "2015-07-13-test",
            "language": "US",
            "start": date(2015, 7, 13),
            "end": date(2015, 7, 14),
            "country": "US",
            "venue": "Euphoric State University",
            "address": "Highway to Heaven 42, Academipolis",
            "latitude": 36.998977,
            "longitude": -109.045173,
            "reg_key": 10000000,
            "instructors": ["Hermione Granger", "Ron Weasley"],
            "helpers": ["Peter Parker", "Tony Stark", "Natasha Romanova"],
            "contact": ["*****@*****.**", "*****@*****.**"],
        }

        self.date_serialization_tests = [
            # simple tests
            ("", ""),
            ("empty string", "empty string"),
            # format-matching
            ("2016-04-18", date(2016, 4, 18)),
            ("2016-04-18T16:41:30", datetime(2016, 4, 18, 16, 41, 30)),
            ("2016-04-18T16:41:30.123",
             datetime(2016, 4, 18, 16, 41, 30, 123000)),
            ("16:41:30", time(16, 41, 30)),
            ("16:41:30.123", time(16, 41, 30, 123000)),
            # format not matching (ie. timezone-aware)
            ("2016-04-18T16:41:30+02:00", "2016-04-18T16:41:30+02:00"),
            ("2016-04-18T14:41:30Z", "2016-04-18T14:41:30Z"),
            ("16:41:30+02:00", "16:41:30+02:00"),
            ("14:41:30Z", "14:41:30Z"),
        ]
Exemplo n.º 8
0
    def setUp(self):
        self.cmd = WebsiteUpdatesCommand()
        self.fake_cmd = FakeDatabaseCommand()
        self.seed = 12345
        self.fake_cmd.faker.seed(self.seed)
        self.fake_cmd.stdout = StringIO()

        self.fake_cmd.fake_organizations()

        self.mocked_event_page = """
<html><head>
<meta name="slug" content="2015-07-13-test" />
<meta name="startdate" content="2015-07-13" />
<meta name="enddate" content="2015-07-14" />
<meta name="country" content="us" />
<meta name="venue" content="Euphoric State University" />
<meta name="address" content="Highway to Heaven 42, Academipolis" />
<meta name="latlng" content="36.998977, -109.045173" />
<meta name="language" content="us" />
<meta name="invalid" content="invalid" />
<meta name="instructor" content="Hermione Granger|Ron Weasley" />
<meta name="helper" content="Peter Parker|Tony Stark|Natasha Romanova" />
<meta name="contact" content="[email protected], [email protected]" />
<meta name="eventbrite" content="10000000" />
<meta name="charset" content="utf-8" />
</head>
<body>
<h1>test</h1>
</body></html>
"""
        self.expected_metadata_parsed = {
            'slug': '2015-07-13-test',
            'language': 'US',
            'start': date(2015, 7, 13),
            'end': date(2015, 7, 14),
            'country': 'US',
            'venue': 'Euphoric State University',
            'address': 'Highway to Heaven 42, Academipolis',
            'latitude': 36.998977,
            'longitude': -109.045173,
            'reg_key': 10000000,
            'instructors': ['Hermione Granger', 'Ron Weasley'],
            'helpers': ['Peter Parker', 'Tony Stark', 'Natasha Romanova'],
            'contact': '[email protected], [email protected]',
        }

        self.date_serialization_tests = [
            # simple tests
            ('', ''),
            ('empty string', 'empty string'),

            # format-matching
            ('2016-04-18', date(2016, 4, 18)),
            ('2016-04-18T16:41:30', datetime(2016, 4, 18, 16, 41, 30)),
            ('2016-04-18T16:41:30.123',
             datetime(2016, 4, 18, 16, 41, 30, 123000)),
            ('16:41:30', time(16, 41, 30)),
            ('16:41:30.123', time(16, 41, 30, 123000)),

            # format not matching (ie. timezone-aware)
            ('2016-04-18T16:41:30+02:00', '2016-04-18T16:41:30+02:00'),
            ('2016-04-18T14:41:30Z', '2016-04-18T14:41:30Z'),
            ('16:41:30+02:00', '16:41:30+02:00'),
            ('14:41:30Z', '14:41:30Z'),
        ]
Exemplo n.º 9
0
class TestWebsiteUpdatesCommand(TestBase):
    maxDiff = None

    def setUp(self):
        self.cmd = WebsiteUpdatesCommand()
        self.fake_cmd = FakeDatabaseCommand()
        self.seed = 12345
        self.fake_cmd.faker.seed(self.seed)
        self.fake_cmd.stdout = StringIO()

        self.fake_cmd.fake_organizations()

        self.mocked_event_page = """
<html><head>
<meta name="slug" content="2015-07-13-test" />
<meta name="startdate" content="2015-07-13" />
<meta name="enddate" content="2015-07-14" />
<meta name="country" content="us" />
<meta name="venue" content="Euphoric State University" />
<meta name="address" content="Highway to Heaven 42, Academipolis" />
<meta name="latlng" content="36.998977, -109.045173" />
<meta name="language" content="us" />
<meta name="invalid" content="invalid" />
<meta name="instructor" content="Hermione Granger|Ron Weasley" />
<meta name="helper" content="Peter Parker|Tony Stark|Natasha Romanova" />
<meta name="contact" content="[email protected], [email protected]" />
<meta name="eventbrite" content="10000000" />
<meta name="charset" content="utf-8" />
</head>
<body>
<h1>test</h1>
</body></html>
"""
        self.expected_metadata_parsed = {
            'slug': '2015-07-13-test',
            'language': 'US',
            'start': date(2015, 7, 13),
            'end': date(2015, 7, 14),
            'country': 'US',
            'venue': 'Euphoric State University',
            'address': 'Highway to Heaven 42, Academipolis',
            'latitude': 36.998977,
            'longitude': -109.045173,
            'reg_key': 10000000,
            'instructors': ['Hermione Granger', 'Ron Weasley'],
            'helpers': ['Peter Parker', 'Tony Stark', 'Natasha Romanova'],
            'contact': '[email protected], [email protected]',
        }

        self.date_serialization_tests = [
            # simple tests
            ('', ''),
            ('empty string', 'empty string'),

            # format-matching
            ('2016-04-18', date(2016, 4, 18)),
            ('2016-04-18T16:41:30', datetime(2016, 4, 18, 16, 41, 30)),
            ('2016-04-18T16:41:30.123',
             datetime(2016, 4, 18, 16, 41, 30, 123000)),
            ('16:41:30', time(16, 41, 30)),
            ('16:41:30.123', time(16, 41, 30, 123000)),

            # format not matching (ie. timezone-aware)
            ('2016-04-18T16:41:30+02:00', '2016-04-18T16:41:30+02:00'),
            ('2016-04-18T14:41:30Z', '2016-04-18T14:41:30Z'),
            ('16:41:30+02:00', '16:41:30+02:00'),
            ('14:41:30Z', '14:41:30Z'),
        ]

    def test_getting_events(self):
        """Ensure only active events with URL are returned."""
        self.fake_cmd.fake_current_events(count=6, add_tags=False)

        Event.objects.all().update(start=date.today())

        # one active event with URL and one without
        e1, e2 = Event.objects.all()[0:2]
        e1.completed = False  # completed == !active
        e1.url = 'https://swcarpentry.github.io/workshop-template/'
        e1.save()
        e2.completed = False
        e2.url = None
        e2.save()

        # one inactive event with URL and one without
        e3, e4 = Event.objects.all()[2:4]
        e3.completed = True
        e3.url = 'https://datacarpentry.github.io/workshop-template/'
        e3.save()
        e4.completed = True
        e4.url = None
        e4.save()

        # both active but one very old
        e5, e6 = Event.objects.all()[4:6]
        e5.completed = False
        e5.url = 'https://swcarpentry.github.io/workshop-template2/'
        e5.start = date(2014, 1, 1)
        e5.save()
        e6.completed = False
        e6.url = 'https://datacarpentry.github.io/workshop-template2/'
        e6.save()

        # check
        events = set(self.cmd.get_events())
        self.assertEqual({e1, e6}, events)

    def test_parsing_github_url(self):
        """Ensure `parse_github_url()` correctly parses repository URL."""
        url = 'https://github.com/swcarpentry/workshop-template'
        expected = 'swcarpentry', 'workshop-template'
        self.assertEqual(expected, self.cmd.parse_github_url(url))

        with self.assertRaises(WrongWorkshopURL):
            url = 'https://swcarpentry.github.io/workshop-template'
            self.cmd.parse_github_url(url)

    @requests_mock.Mocker()
    def test_getting_event_metadata(self, mock):
        """Ensure metadata are fetched and normalized by `get_event_metadata`."""
        # underlying `fetch_event_metadata` and `parse_metadata_from_event_website`
        # are tested in great detail in `test_util.py`, so here's just a short
        # test
        website_url = 'https://github.com/swcarpentry/workshop-template'
        mock_text = self.mocked_event_page
        mock.get(website_url, text=mock_text, status_code=200)
        # mock placed, let's test `get_event_metadata`

        metadata = self.cmd.get_event_metadata(website_url)
        self.assertEqual(metadata, self.expected_metadata_parsed)

    def test_deserialization_of_string(self):
        "Ensure our datetime matching function works correctly for strings."
        for test, expected in self.date_serialization_tests:
            with self.subTest(test=test):
                self.assertEqual(datetime_match(test), expected)

    def test_deserialization_of_list(self):
        """Ensure our datetime matching function works correctly for lists."""
        tests = self.date_serialization_tests[:]
        tests = list(zip(*tests))  # transpose
        test = list(tests[0])
        expected = list(tests[1])
        self.assertEqual(datetime_decode(test), expected)

    def test_deserialization_of_dict(self):
        """Ensure our datetime matching function works correctly for dicts."""
        test = {k: k for k, v in self.date_serialization_tests}
        expected = {k: v for k, v in self.date_serialization_tests}
        self.assertEqual(datetime_decode(test), expected)

    def test_deserialization_of_nested(self):
        """Ensure our datetime matching function works correctly for nested
        objects/lists."""
        # this test uses simpler format
        dict_test = {'2016-04-18': '2016-04-18'}
        dict_expected = {'2016-04-18': date(2016, 4, 18)}
        test = [dict_test.copy(), dict_test.copy(), dict_test.copy()]
        expected = [dict_expected.copy(), dict_expected.copy(),
                    dict_expected.copy()]
        self.assertEqual(datetime_decode(test), expected)

        test = {'1': test[:]}
        expected = {'1': expected[:]}
        self.assertEqual(datetime_decode(test), expected)

    def test_serialization(self):
        """Ensure serialization uses JSON and works correctly with dates,
        datetimes and times.
        Ensure derialization from JSON works correctly with dates,
        datetimes and times."""
        serialized_json = self.cmd.serialize(self.expected_metadata_parsed)

        self.assertIn('2015-07-13', serialized_json)
        self.assertIn('2015-07-14', serialized_json)
        self.assertIn('2015-07-13-test', serialized_json)
        self.assertIn('-109.045173', serialized_json)
        self.assertIn('36.998977', serialized_json)
        self.assertIn('[email protected], [email protected]',
                      serialized_json)

        deserialized_data = self.cmd.deserialize(serialized_json)
        self.assertEqual(deserialized_data, self.expected_metadata_parsed)

    @unittest.skip('Don\'t know how to test it')
    def test_loading_from_github(self):
        """Not sure how to test it, so for now leaving this blank."""
        # TODO: mock up github response?
        pass

    @requests_mock.Mocker()
    def test_detecting_changes(self, mock):
        """Make sure metadata changes are detected."""
        hash_ = 'abcdefghijklmnopqrstuvwxyz'
        e = Event.objects.create(
            slug='with-changes', host=Organization.objects.first(),
            url='https://swcarpentry.github.io/workshop-template/',
            repository_last_commit_hash=hash_,
            repository_metadata='',
            metadata_changed=False)

        branch = MagicMock()
        branch.commit = MagicMock()
        branch.commit.sha = hash_

        changes = self.cmd.detect_changes(branch, e)
        self.assertEqual(changes, [])

        # more real example: hash changed
        hash_ = "zyxwvutsrqponmlkjihgfedcba"
        branch.commit.sha = hash_
        mock_text = self.mocked_event_page
        mock.get(e.url, text=mock_text, status_code=200)
        metadata = self.cmd.empty_metadata()
        metadata['instructors'] = self.expected_metadata_parsed['instructors']
        metadata['latitude'] = self.expected_metadata_parsed['latitude']
        metadata['longitude'] = self.expected_metadata_parsed['longitude']
        e.repository_metadata = self.cmd.serialize(metadata)
        e.save()

        changes = self.cmd.detect_changes(branch, e)
        expected = [
            'Helpers changed',
            'Start date changed',
            'End date changed',
            'Country changed',
            'Venue changed',
            'Address changed',
            'Contact details changed',
            'Eventbrite key changed',
        ]
        self.assertEqual(changes, expected)

    @requests_mock.Mocker()
    def test_initialization(self, mock):
        """Make sure events are initialized to sane values."""
        e = Event.objects.create(
            slug='with-changes', host=Organization.objects.first(),
            url='https://swcarpentry.github.io/workshop-template/',
            repository_last_commit_hash='', repository_metadata='',
            metadata_changed=False, metadata_all_changes='')

        hash_ = 'abcdefghijklmnopqrstuvwxyz'
        branch = MagicMock()
        branch.commit = MagicMock()
        branch.commit.sha = hash_
        mock_text = self.mocked_event_page
        mock.get(e.url, text=mock_text, status_code=200)

        self.cmd.init(branch, e)

        e.refresh_from_db()
        # metadata updated
        self.assertEqual(e.repository_last_commit_hash, hash_)
        self.assertEqual(self.cmd.deserialize(e.repository_metadata),
                         self.expected_metadata_parsed)

        self.assertEqual(e.metadata_all_changes, '')
        self.assertEqual(e.metadata_changed, False)

    @unittest.skip('This command requires internet connection')
    def test_running(self):
        """Test running whole command."""
        call_command('check_for_workshop_websites_updates')