Repository: translate/pootle

Issue:
update_stores logic is no longer called when TP is initialised
This is a regression from #3877 

If this is the desired behaviour ill make the necessary changes to restore it.

The other option is explicitly calling `update_stores` after adding TP - and changing docs to reflect that.

In order to fix i probably need to move the last_sync_revision logic back out of the management command so it can be called from other points in the code more easily

i also think i need to check anywhere that store.update is called and make sure it doesnt expect old last_sync_revision behaviour



Discussion:
I'm trying to think of cases or instance that would have triggered:
1. When a TP was added and files are on disk IIRC that would load units into the TP
2. When a new language is created and no files are on disk it will create the TP from templates.
3. If update_store is called for a language that doesn't have a TP then the TP is created and the stores are loaded.

I think that's the previous behaviour in terms of interaction related to TPs.


Resolving patch:
diff --git a/pootle/apps/pootle_translationproject/models.py b/pootle/apps/pootle_translationproject/models.py
--- a/pootle/apps/pootle_translationproject/models.py
+++ b/pootle/apps/pootle_translationproject/models.py
@@ -297,10 +297,14 @@ def save(self, *args, **kwargs):
 
             self.scan_files()
 
-            if initialize_from_templates:
+            # Create units from disk store
+            for store in self.stores.live().iterator():
+                changed = store.update_from_disk()
+
+                # If there were changes stats will be refreshed anyway - otherwise...
                 # Trigger stats refresh for TP added from UI.
                 # FIXME: This won't be necessary once #3547 is fixed.
-                for store in self.stores.live().iterator():
+                if not changed:
                     store.save(update_cache=True)
 
     def delete(self, *args, **kwargs):


Test patch:
diff --git a/tests/data/po/tutorial/templates/tutorial.pot b/tests/data/po/tutorial/templates/tutorial.pot
new file mode 100644
--- /dev/null
+++ b/tests/data/po/tutorial/templates/tutorial.pot
@@ -0,0 +1,15 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Pootle Tests\n"
+"X-Pootle-Path: /en/tutorial/tutorial.po\n"
+"X-Pootle-Revision: 0\n"
+
+
+#: Hello, world
+msgid "Hello, world"
+msgstr ""
diff --git a/tests/fixtures/models/language.py b/tests/fixtures/models/language.py
--- a/tests/fixtures/models/language.py
+++ b/tests/fixtures/models/language.py
@@ -33,7 +33,7 @@ def _require_language(code, fullname, plurals=2, plural_equation='(n != 1)'):
 
 
 @pytest.fixture
-def english(root):
+def english(root, system):
     """Require the English language."""
     return _require_language('en', 'English')
 
@@ -84,3 +84,18 @@ def russian(english):
 def fish(english):
     """Require the Fish language ><(((º>"""
     return _require_language(code='fish', fullname='Fish')
+
+
+# due to issues with tests not having a clean slate ie:
+# (https://github.com/translate/pootle/issues/3898)
+# please do not use the klingon fixtures 8)
+@pytest.fixture
+def klingon(english):
+    """Require the Klingon language."""
+    return _require_language('kl', 'Klingon')
+
+
+@pytest.fixture
+def klingon_vpw(english):
+    """Require the Klingon language (VPW dialect)."""
+    return _require_language('kl_VPW', 'Klingon vegan peace warriors')
diff --git a/tests/models/revision.py b/tests/models/revision.py
--- a/tests/models/revision.py
+++ b/tests/models/revision.py
@@ -25,7 +25,6 @@ def test_max_revision(af_tutorial_po):
     initial_max_revision = Unit.max_revision()
     initial_revision = Revision.get()
     assert initial_max_revision == initial_revision
-    assert initial_max_revision == 1
 
     # Let's make 10 translation updates, this must also update their
     # revision numbers
diff --git a/tests/models/translationproject.py b/tests/models/translationproject.py
new file mode 100644
--- /dev/null
+++ b/tests/models/translationproject.py
@@ -0,0 +1,81 @@
+import os
+import shutil
+
+import pytest
+
+from django.db import IntegrityError
+
+from pootle_language.models import Language
+from pootle_project.models import Project
+from pootle_translationproject.models import TranslationProject
+
+
+@pytest.mark.django_db
+def test_tp_create_fail(tutorial, english):
+
+    # Trying to create a TP with no Project raises a RelatedObjectDoesNotExist
+    # which can be caught with Project.DoesNotExist
+    with pytest.raises(Project.DoesNotExist):
+        TranslationProject.objects.create()
+
+    # TP needs a lang set too...
+    with pytest.raises(Language.DoesNotExist):
+        TranslationProject.objects.create(project=tutorial)
+
+    # There is already an english tutorial was automagically set up
+    with pytest.raises(IntegrityError):
+        TranslationProject.objects.create(project=tutorial, language=english)
+
+
+@pytest.mark.django_db
+def test_tp_create_no_files(tutorial, fish):
+    # There are no files on disk so TP was not automagically created
+    # and there are no templates loaded
+    tp = TranslationProject.objects.create(project=tutorial, language=fish)
+    assert list(tp.stores.all()) == []
+
+
+def _test_tp_stores_match(tp1, tp2):
+    # For testing tp creation from templates
+    assert tp1.stores.count() == tp2.stores.count()
+    tp1_stores = tp1.stores.all()
+    tp2_stores = tp2.stores.all()
+
+    for i, store1 in enumerate(tp1_stores):
+        store2 = tp2_stores[i]
+        assert store1.units == store2.units
+
+
+@pytest.mark.django_db
+def test_tp_create_templates(tutorial, klingon_vpw, templates):
+    # As there is a tutorial template it will automatically create stores for
+    # our new TP
+    template_tp = TranslationProject.objects.get(
+        language=templates, project=tutorial)
+    tp = TranslationProject.objects.create(
+        project=tutorial, language=klingon_vpw)
+    assert tp.stores.count() == template_tp.stores.count()
+    assert (
+        [(s, t)
+         for s, t
+         in template_tp.stores.first().units.values_list("source_f",
+                                                         "target_f")]
+        == [(s, t)
+            for s, t
+            in tp.stores.first().units.values_list("source_f",
+                                                   "target_f")])
+
+
+@pytest.mark.django_db
+def test_tp_create_with_files(tutorial, english, klingon, settings):
+    # lets add some files by hand
+
+    trans_dir = settings.POOTLE_TRANSLATION_DIRECTORY
+
+    shutil.copytree(
+        os.path.join(trans_dir, "tutorial/en"),
+        os.path.join(trans_dir, "tutorial/kl"))
+
+    TranslationProject.objects.create(project=tutorial, language=klingon)
+
+    shutil.rmtree(os.path.join(trans_dir, "tutorial/kl"))