def test_release_no_drive(self, mock_cepath, mock_move, mock_make_archive,
                              mock_copy, mock_makedirs, mock_getpass,
                              mock_session, mock_upload, mock_rmtree,
                              mock_isdir):
        '''
        Test that release() correctly prepares a release
        that does not upload files to Google Drive.
        '''
        # Mock functions called by release() to simulate an actual call
        args = copy.deepcopy(standard_args)
        args['DriveReleaseFiles'] = []

        release_data = helpers.mock_release_data(args)
        mock_session.return_value.get.return_value.content = release_data

        # Test without DriveReleaseFiles
        tools.release(**args)
        # Check that no file operations occur when no files are specified for release
        # to Google Drive.
        mock_copy.assert_not_called()
        mock_makedirs.assert_not_called()
        mock_rmtree.assert_not_called()
        mock_make_archive.assert_not_called()
        mock_move.assert_not_called()
        mock_upload.assert_not_called()
    def check_failure(self, changes, expected_error):
        '''
        Check that release() fails with `expected_error` when
        its arguments deviate from standard ones as specified in `changes`.
        '''
        test_args = copy.deepcopy(standard_args)

        for changed_arg in changes.keys():
            test_args[changed_arg] = changes[changed_arg]

        with self.assertRaises(expected_error):
            tools.release(**test_args)
    def test_release_unintended_inputs(self, mock_move, mock_make_archive,
                                       mock_copy, mock_makedirs, mock_getpass,
                                       mock_session, mock_upload, mock_rmtree,
                                       mock_isdir):
        '''
        Test that release() responds as expected to 
        unintended inputs.
        '''
        # Mock functions called by release() to simulate an actual call
        mock_getpass.return_value = 'test_token'
        args = copy.deepcopy(standard_args)

        release_data = helpers.mock_release_data(args)
        mock_session.return_value.get.return_value.content = release_data
        mock_session.return_value.post.side_effect = fx.post_side_effect

        args = copy.deepcopy(standard_args)

        # Inappropriate local_release argument and DriveReleaseFiles is empty.
        test_args = copy.deepcopy(args)
        test_args['DriveReleaseFiles'] = []
        test_args['local_release'] = 1
        try:
            tools.release(**test_args)
        except:
            self.fail("Running release() with an invalid local_release "
                      "argument and without files to be released to Google "
                      "Drive shouldn't raise an error!")

        # local_release is of an inappropriate type
        self.check_failure({'local_release': 1}, AttributeError)
        # local_release doesn't contain a /release/ subdirectory. Caps matter!
        self.check_failure({'local_release': 'root/Release/folder'},
                           ReleaseError)

        # org/repo referss to a nonexistent GitHub repository
        self.check_failure({'org': 'orgg'}, requests.exceptions.HTTPError)
        self.check_failure({'repo': 1}, requests.exceptions.HTTPError)

        # Some error-inducing DriveReleaseFiles arguments
        self.check_failure({'DriveReleaseFiles': True}, TypeError)
        self.check_failure({'DriveReleaseFiles': [1, 2, 3]}, AttributeError)
    def check_release(self, args, mock_session, mock_upload, mock_copy,
                      mock_make_archive, mock_move):
        '''
        This helper method checks that test.release() works correctly
        with a given collection of arguments and a system configuration
        mocked by its assorted mock_* arguments.  
        '''

        for mock_object in [
                mock_session, mock_upload, mock_copy, mock_make_archive,
                mock_move
        ]:
            mock_object.reset_mock()

        tools.release(**args)

        mock_session.return_value.post.assert_called_once()
        post_args = mock_session.return_value.post.call_args

        # Check that the first positional argument of session.post() is
        # the url to which we desire to make our release.
        desired_release_url = \
            'https://test_token:@api.github.com/repos/org/repo/releases'
        self.assertEqual(post_args[0][0], desired_release_url)

        # Check that the correct data was passed to session.post()
        data = post_args[1]['data']
        self.assertTrue(re.search('"body": ""', data))
        self.assertTrue(re.search('"name": "%s"' % args['vers'], data))
        self.assertTrue(re.search('"target_commitish": "%s"' % \
                                  args['target_commitish'], data))
        self.assertTrue(re.search('"tag_name": "%s"' % args['vers'], data))
        self.assertTrue(re.search('"prerelease": false', data))
        self.assertTrue(re.search('"draft": false', data))

        # Check that release() called upload_asset() with the correct arguments
        mock_upload.assert_called_once()
        # We expect test_ID to be the release ID given the mocked-up return value
        # of session.get()
        self.assertEqual(mock_upload.call_args[1]['release_id'], 'test_ID')

        # Check that release() prepared files for release correctly,
        # If we are zipping before releasing, then we should move
        # our release files into an intermediate folder to be zipped.
        if args['zip_release']:
            base = 'release_content'
        # Otherwise, we should move them directly into our
        # local_release directory.
        else:
            base = args['local_release']

        drive_files = args['DriveReleaseFiles']
        if isinstance(drive_files, basestring):
            drive_files = [drive_files]

        for filename in drive_files:
            mock_copy.assert_any_call(filename, '%s/%s' % (base, filename))

        # ...that it zipped them if zip_release == True, ...
        if args['zip_release']:
            mock_make_archive.assert_called_with('release_content', 'zip',
                                                 'release_content')
            # ...and that it moved them to the local_release directory
            mock_move.assert_called_with(
                'release_content.zip',
                '%s/release.zip' % args['local_release'])
        else:
            mock_make_archive.assert_not_called()

        # Check that the assets listed in the file whose path is
        # passed to upload_asset() are those specified by release()'s
        # DriveReleaseFiles argument.
        if args['DriveReleaseFiles']:
            with open('assets_listing.txt', 'rU') as assets:
                lines = assets.readlines()

            for line in lines:
                if not line == lines[0]:
                    # Extract the filename from the line of the assets listing
                    filename = os.path.basename(line.strip())
                    # Check that the filename is listed.
                    self.assertIn(filename, args['DriveReleaseFiles'])

            os.remove('assets_listing.txt')