def test_exportToStaleBranch(self):
     # Attempting to export to a stale branch marks it for scanning.
     self.useBzrBranches(direct_database=False)
     exporter = ExportTranslationsToBranch(test_args=[])
     exporter.logger = BufferLogger()
     productseries = self.factory.makeProductSeries()
     db_branch, tree = self.create_branch_and_tree(
         product=productseries.product)
     removeSecurityProxy(productseries).translations_branch = db_branch
     db_branch.last_mirrored_id = 'stale-id'
     db_branch.last_scanned_id = db_branch.last_mirrored_id
     self.becomeDbUser('translationstobranch')
     self.assertFalse(db_branch.pending_writes)
     self.assertNotEqual(db_branch.last_mirrored_id,
                         tree.branch.last_revision())
     # The export code works on a Branch from the slave store.  It
     # shouldn't stop the scan request.
     slave_series = ISlaveStore(productseries).get(ProductSeries,
                                                   productseries.id)
     exporter._exportToBranch(slave_series)
     self.assertEqual(db_branch.last_mirrored_id,
                      tree.branch.last_revision())
     self.assertTrue(db_branch.pending_writes)
     matches = MatchesRegex(
         "(.|\n)*WARNING Skipped .* due to stale DB info, and scheduled a "
         "new scan.")
     self.assertThat(exporter.logger.getLogBuffer(), matches)
 def test_exportToStaleBranch(self):
     # Attempting to export to a stale branch marks it for scanning.
     self.useBzrBranches(direct_database=False)
     exporter = ExportTranslationsToBranch(test_args=[])
     exporter.logger = BufferLogger()
     productseries = self.factory.makeProductSeries()
     db_branch, tree = self.create_branch_and_tree(
         product=productseries.product)
     removeSecurityProxy(productseries).translations_branch = db_branch
     db_branch.last_mirrored_id = 'stale-id'
     db_branch.last_scanned_id = db_branch.last_mirrored_id
     self.becomeDbUser('translationstobranch')
     self.assertFalse(db_branch.pending_writes)
     self.assertNotEqual(
         db_branch.last_mirrored_id, tree.branch.last_revision())
     # The export code works on a Branch from the slave store.  It
     # shouldn't stop the scan request.
     slave_series = ISlaveStore(productseries).get(
         ProductSeries, productseries.id)
     exporter._exportToBranch(slave_series)
     self.assertEqual(
         db_branch.last_mirrored_id, tree.branch.last_revision())
     self.assertTrue(db_branch.pending_writes)
     matches = MatchesRegex(
         "(.|\n)*WARNING Skipped .* due to stale DB info, and scheduled a "
         "new scan.")
     self.assertThat(exporter.logger.getLogBuffer(), matches)
 def test_findChangedPOFiles_all(self):
     # If changed_since date is passed in as None, all POFiles are
     # returned.
     pofile = self.factory.makePOFile()
     exporter = ExportTranslationsToBranch(test_args=[])
     self.assertEquals(
         [pofile],
         list(exporter._findChangedPOFiles(
             pofile.potemplate.productseries, changed_since=None)))
 def test_findChangedPOFiles_all(self):
     # If changed_since date is passed in as None, all POFiles are
     # returned.
     pofile = self.factory.makePOFile()
     exporter = ExportTranslationsToBranch(test_args=[])
     self.assertEqual([pofile],
                      list(
                          exporter._findChangedPOFiles(
                              pofile.potemplate.productseries,
                              changed_since=None)))
 def test_sets_bzr_id(self):
     # The script commits to the branch under a user id that mentions
     # the automatic translations exports as well as the Launchpad
     # name of the branch owner.
     self.useBzrBranches(direct_database=False)
     exporter = ExportTranslationsToBranch(test_args=[])
     branch, tree = self.create_branch_and_tree()
     committer = exporter._makeDirectBranchCommit(branch)
     committer.unlock()
     self.assertEqual(
         "Launchpad Translations on behalf of %s" % branch.owner.name,
         committer.getBzrCommitterID())
 def test_sets_bzr_id(self):
     # The script commits to the branch under a user id that mentions
     # the automatic translations exports as well as the Launchpad
     # name of the branch owner.
     self.useBzrBranches(direct_database=False)
     exporter = ExportTranslationsToBranch(test_args=[])
     branch, tree = self.create_branch_and_tree()
     committer = exporter._makeDirectBranchCommit(branch)
     committer.unlock()
     self.assertEqual(
         "Launchpad Translations on behalf of %s" % branch.owner.name,
         committer.getBzrCommitterID())
    def test_findChangedPOFiles_unchanged(self):
        # If a POFile has been changed before changed_since date,
        # it is not returned.
        pofile = self.factory.makePOFile()
        date_in_the_future = (
            datetime.datetime.now(pytz.UTC) + datetime.timedelta(1))

        exporter = ExportTranslationsToBranch(test_args=[])
        self.assertEquals(
            [],
            list(exporter._findChangedPOFiles(
                pofile.potemplate.productseries,
                date_in_the_future)))
    def test_findChangedPOFiles(self):
        # Returns all POFiles changed in a productseries after a certain
        # date.
        date_in_the_past = (
            datetime.datetime.now(pytz.UTC) - datetime.timedelta(1))
        pofile = self.factory.makePOFile()

        exporter = ExportTranslationsToBranch(test_args=[])
        self.assertEquals(
            [pofile],
            list(exporter._findChangedPOFiles(
                pofile.potemplate.productseries,
                changed_since=date_in_the_past)))
    def test_findChangedPOFiles(self):
        # Returns all POFiles changed in a productseries after a certain
        # date.
        date_in_the_past = (datetime.datetime.now(pytz.UTC) -
                            datetime.timedelta(1))
        pofile = self.factory.makePOFile()

        exporter = ExportTranslationsToBranch(test_args=[])
        self.assertEqual([pofile],
                         list(
                             exporter._findChangedPOFiles(
                                 pofile.potemplate.productseries,
                                 changed_since=date_in_the_past)))
    def test_findChangedPOFiles_unchanged(self):
        # If a POFile has been changed before changed_since date,
        # it is not returned.
        pofile = self.factory.makePOFile()
        date_in_the_future = (datetime.datetime.now(pytz.UTC) +
                              datetime.timedelta(1))

        exporter = ExportTranslationsToBranch(test_args=[])
        self.assertEqual([],
                         list(
                             exporter._findChangedPOFiles(
                                 pofile.potemplate.productseries,
                                 date_in_the_future)))
    def test_findChangedPOFiles_unchanged_template_changed(self):
        # If a POFile has been changed before changed_since date,
        # and template has been updated after it, POFile is still
        # considered changed and thus returned.
        pofile = self.factory.makePOFile()
        date_in_the_future = (
            datetime.datetime.now(pytz.UTC) + datetime.timedelta(1))
        date_in_the_far_future = (
            datetime.datetime.now(pytz.UTC) + datetime.timedelta(2))
        pofile.potemplate.date_last_updated = date_in_the_far_future

        exporter = ExportTranslationsToBranch(test_args=[])
        self.assertEquals(
            [pofile],
            list(exporter._findChangedPOFiles(
                pofile.potemplate.productseries,
                date_in_the_future)))
    def test_findChangedPOFiles_unchanged_template_changed(self):
        # If a POFile has been changed before changed_since date,
        # and template has been updated after it, POFile is still
        # considered changed and thus returned.
        pofile = self.factory.makePOFile()
        date_in_the_future = (datetime.datetime.now(pytz.UTC) +
                              datetime.timedelta(1))
        date_in_the_far_future = (datetime.datetime.now(pytz.UTC) +
                                  datetime.timedelta(2))
        pofile.potemplate.date_last_updated = date_in_the_far_future

        exporter = ExportTranslationsToBranch(test_args=[])
        self.assertEqual([pofile],
                         list(
                             exporter._findChangedPOFiles(
                                 pofile.potemplate.productseries,
                                 date_in_the_future)))
    def test_handleUnpushedBranch_mails_branch_owner(self):
        exporter = ExportTranslationsToBranch(test_args=[])
        exporter.logger = BufferLogger()
        productseries = self.factory.makeProductSeries()
        email = self.factory.getUniqueEmailAddress()
        branch_owner = self.factory.makePerson(email=email)
        productseries.translations_branch = self.factory.makeBranch(
            owner=branch_owner)
        exporter._exportToBranch = FakeMethod(failure=NotBranchError("Ow"))
        exporter._sendMail = FakeMethod()

        self.becomeDbUser('translationstobranch')

        exporter._exportToBranches([productseries])

        self.assertEqual(1, exporter._sendMail.call_count)
        (sender, recipients, subject,
         text), kwargs = (exporter._sendMail.calls[-1])
        self.assertIn(config.canonical.noreply_from_address, sender)
        self.assertIn(email, recipients)
        self.assertEqual("Launchpad: translations branch has not been set up.",
                         subject)

        self.assertIn("problem with translations branch synchronization", text)
        self.assertIn(productseries.title, text)
        self.assertIn(productseries.translations_branch.bzr_identity, text)
        self.assertIn('bzr push lp://', text)
    def test_exportToBranches_handles_nonascii_exceptions(self):
        # There's an exception handler in _exportToBranches that must
        # cope well with non-ASCII exception strings.
        productseries = self.factory.makeProductSeries()
        exporter = ExportTranslationsToBranch(test_args=[])
        exporter.logger = BufferLogger()
        boom = u'\u2639'
        exporter._exportToBranch = FakeMethod(failure=GruesomeException(boom))

        self.becomeDbUser('translationstobranch')

        exporter._exportToBranches([productseries])

        self.assertEqual(1, exporter._exportToBranch.call_count)

        message = exporter.logger.getLogBuffer()
        self.assertTrue(message.startswith("ERROR"))
        self.assertTrue("GruesomeException" in message)
    def test_handleUnpushedBranch_has_required_privileges(self):
        # Dealing with an unpushed branch is a special code path that
        # was not exercised by the full-script test.  Ensure that it has
        # the database privileges that it requires.
        exporter = ExportTranslationsToBranch(test_args=[])
        exporter.logger = BufferLogger()
        productseries = self.factory.makeProductSeries()
        email = self.factory.getUniqueEmailAddress()
        branch_owner = self.factory.makePerson(email=email)
        productseries.translations_branch = self.factory.makeBranch(
            owner=branch_owner)
        exporter._exportToBranch = FakeMethod(failure=NotBranchError("Ow"))

        self.becomeDbUser('translationstobranch')

        exporter._handleUnpushedBranch(productseries)

        # _handleUnpushedBranch completes successfully.  There are no
        # database changes still pending in the ORM that are going to
        # fail either.
        transaction.commit()
    def test_handleUnpushedBranch_mails_branch_owner(self):
        exporter = ExportTranslationsToBranch(test_args=[])
        exporter.logger = BufferLogger()
        productseries = self.factory.makeProductSeries()
        email = self.factory.getUniqueEmailAddress()
        branch_owner = self.factory.makePerson(email=email)
        productseries.translations_branch = self.factory.makeBranch(
            owner=branch_owner)
        exporter._exportToBranch = FakeMethod(failure=NotBranchError("Ow"))
        exporter._sendMail = FakeMethod()

        self.becomeDbUser('translationstobranch')

        exporter._exportToBranches([productseries])

        self.assertEqual(1, exporter._sendMail.call_count)
        (sender, recipients, subject, text), kwargs = (
            exporter._sendMail.calls[-1])
        self.assertIn(config.canonical.noreply_from_address, sender)
        self.assertIn(email, recipients)
        self.assertEqual(
            "Launchpad: translations branch has not been set up.", subject)

        self.assertIn(
            "problem with translations branch synchronization", text)
        self.assertIn(productseries.title, text)
        self.assertIn(productseries.translations_branch.bzr_identity, text)
        self.assertIn('bzr push lp://', text)
    def test_handleUnpushedBranch_is_privileged_to_contact_team(self):
        # Notifying a branch owner that is a team can require other
        # database privileges.  The script also has these privileges.
        exporter = ExportTranslationsToBranch(test_args=[])
        exporter.logger = BufferLogger()
        productseries = self.factory.makeProductSeries()
        email = self.factory.getUniqueEmailAddress()
        team_member = self.factory.makePerson(email=email)
        branch_owner = self.factory.makeTeam()
        getUtility(ITeamMembershipSet).new(
            team_member, branch_owner, TeamMembershipStatus.APPROVED,
            branch_owner.teamowner)
        productseries.translations_branch = self.factory.makeBranch(
            owner=branch_owner)
        exporter._exportToBranch = FakeMethod(failure=NotBranchError("Ow"))

        self.becomeDbUser('translationstobranch')

        exporter._handleUnpushedBranch(productseries)

        # _handleUnpushedBranch completes successfully.  There are no
        # database changes still pending in the ORM that are going to
        # fail either.
        transaction.commit()
    def test_exportToBranches_handles_nonascii_exceptions(self):
        # There's an exception handler in _exportToBranches that must
        # cope well with non-ASCII exception strings.
        productseries = self.factory.makeProductSeries()
        exporter = ExportTranslationsToBranch(test_args=[])
        exporter.logger = BufferLogger()
        boom = u'\u2639'
        exporter._exportToBranch = FakeMethod(failure=GruesomeException(boom))

        self.becomeDbUser('translationstobranch')

        exporter._exportToBranches([productseries])

        self.assertEqual(1, exporter._exportToBranch.call_count)

        message = exporter.logger.getLogBuffer()
        self.assertTrue(message.startswith("ERROR"))
        self.assertTrue("GruesomeException" in message)
    def test_handleUnpushedBranch_has_required_privileges(self):
        # Dealing with an unpushed branch is a special code path that
        # was not exercised by the full-script test.  Ensure that it has
        # the database privileges that it requires.
        exporter = ExportTranslationsToBranch(test_args=[])
        exporter.logger = BufferLogger()
        productseries = self.factory.makeProductSeries()
        email = self.factory.getUniqueEmailAddress()
        branch_owner = self.factory.makePerson(email=email)
        productseries.translations_branch = self.factory.makeBranch(
            owner=branch_owner)
        exporter._exportToBranch = FakeMethod(failure=NotBranchError("Ow"))

        self.becomeDbUser('translationstobranch')

        exporter._handleUnpushedBranch(productseries)

        # _handleUnpushedBranch completes successfully.  There are no
        # database changes still pending in the ORM that are going to
        # fail either.
        transaction.commit()
    def test_handleUnpushedBranch_is_privileged_to_contact_team(self):
        # Notifying a branch owner that is a team can require other
        # database privileges.  The script also has these privileges.
        exporter = ExportTranslationsToBranch(test_args=[])
        exporter.logger = BufferLogger()
        productseries = self.factory.makeProductSeries()
        email = self.factory.getUniqueEmailAddress()
        team_member = self.factory.makePerson(email=email)
        branch_owner = self.factory.makeTeam()
        getUtility(ITeamMembershipSet).new(team_member, branch_owner,
                                           TeamMembershipStatus.APPROVED,
                                           branch_owner.teamowner)
        productseries.translations_branch = self.factory.makeBranch(
            owner=branch_owner)
        exporter._exportToBranch = FakeMethod(failure=NotBranchError("Ow"))

        self.becomeDbUser('translationstobranch')

        exporter._handleUnpushedBranch(productseries)

        # _handleUnpushedBranch completes successfully.  There are no
        # database changes still pending in the ORM that are going to
        # fail either.
        transaction.commit()
#!/usr/bin/python -S
#
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Commit translations to translations_branch where requested.

This script needs to be run on the codehosting server, so that it can
access hosted branches.

Besides committing to branches, the script updates Branch records in the
database, to let the branch scanner know that the branches' contents
have been updated.  For the rest, the script talks to the slave store.
"""

__metaclass__ = type
__all__ = []

import _pythonpath

from lp.translations.scripts.translations_to_branch import (
    ExportTranslationsToBranch,
    )


if __name__ == '__main__':
    script = ExportTranslationsToBranch(
        'translations-export-to-branch', dbuser='******')
    script.config_name = 'translations_export_to_branch'
    script.lock_and_run()
    def test_exportToBranches_handles_unpushed_branches(self):
        # bzrlib raises NotBranchError when accessing a nonexistent
        # branch.  The exporter deals with that by calling
        # _handleUnpushedBranch.
        exporter = ExportTranslationsToBranch(test_args=[])
        exporter.logger = BufferLogger()
        productseries = self.factory.makeProductSeries()
        productseries.translations_branch = self.factory.makeBranch()

        self.becomeDbUser('translationstobranch')

        # _handleUnpushedBranch is called if _exportToBranch raises
        # NotBranchError.
        exporter._handleUnpushedBranch = FakeMethod()
        exporter._exportToBranch = FakeMethod(failure=NotBranchError("No!"))
        exporter._exportToBranches([productseries])
        self.assertEqual(1, exporter._handleUnpushedBranch.call_count)

        # This does not happen if the export succeeds.
        exporter._handleUnpushedBranch = FakeMethod()
        exporter._exportToBranch = FakeMethod()
        exporter._exportToBranches([productseries])
        self.assertEqual(0, exporter._handleUnpushedBranch.call_count)

        # Nor does it happen if the export fails in some other way.
        exporter._handleUnpushedBranch = FakeMethod()
        exporter._exportToBranch = FakeMethod(failure=IndexError("Ayyeee!"))
        exporter._exportToBranches([productseries])
        self.assertEqual(0, exporter._handleUnpushedBranch.call_count)
    def test_exportToBranches_handles_unpushed_branches(self):
        # bzrlib raises NotBranchError when accessing a nonexistent
        # branch.  The exporter deals with that by calling
        # _handleUnpushedBranch.
        exporter = ExportTranslationsToBranch(test_args=[])
        exporter.logger = BufferLogger()
        productseries = self.factory.makeProductSeries()
        productseries.translations_branch = self.factory.makeBranch()

        self.becomeDbUser('translationstobranch')

        # _handleUnpushedBranch is called if _exportToBranch raises
        # NotBranchError.
        exporter._handleUnpushedBranch = FakeMethod()
        exporter._exportToBranch = FakeMethod(failure=NotBranchError("No!"))
        exporter._exportToBranches([productseries])
        self.assertEqual(1, exporter._handleUnpushedBranch.call_count)

        # This does not happen if the export succeeds.
        exporter._handleUnpushedBranch = FakeMethod()
        exporter._exportToBranch = FakeMethod()
        exporter._exportToBranches([productseries])
        self.assertEqual(0, exporter._handleUnpushedBranch.call_count)

        # Nor does it happen if the export fails in some other way.
        exporter._handleUnpushedBranch = FakeMethod()
        exporter._exportToBranch = FakeMethod(failure=IndexError("Ayyeee!"))
        exporter._exportToBranches([productseries])
        self.assertEqual(0, exporter._handleUnpushedBranch.call_count)
#!/usr/bin/python -S
#
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Commit translations to translations_branch where requested.

This script needs to be run on the codehosting server, so that it can
access hosted branches.

Besides committing to branches, the script updates Branch records in the
database, to let the branch scanner know that the branches' contents
have been updated.  For the rest, the script talks to the slave store.
"""

__metaclass__ = type
__all__ = []

import _pythonpath

from lp.translations.scripts.translations_to_branch import (
    ExportTranslationsToBranch, )

if __name__ == '__main__':
    script = ExportTranslationsToBranch('translations-export-to-branch',
                                        dbuser='******')
    script.config_name = 'translations_export_to_branch'
    script.lock_and_run()