You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_loading.py 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import sys
  2. from os.path import dirname
  3. from django.apps import apps
  4. from django.conf import settings
  5. from django.test import TestCase, override_settings
  6. from oscar.core.loading import (
  7. AppNotFoundError,
  8. ClassNotFoundError,
  9. get_class,
  10. get_class_loader,
  11. get_classes,
  12. get_model,
  13. )
  14. from tests import temporary_python_path
  15. from tests._site.loader import DummyClass
  16. CustomerConfig = get_class("customer.apps", "CustomerConfig")
  17. class TestClassLoading(TestCase):
  18. """
  19. Oscar's class loading utilities
  20. """
  21. def test_load_oscar_classes_correctly(self):
  22. Product, Category = get_classes("catalogue.models", ("Product", "Category"))
  23. self.assertEqual("oscar.apps.catalogue.models", Product.__module__)
  24. self.assertEqual("oscar.apps.catalogue.models", Category.__module__)
  25. def test_load_oscar_class_correctly(self):
  26. Product = get_class("catalogue.models", "Product")
  27. self.assertEqual("oscar.apps.catalogue.models", Product.__module__)
  28. def test_load_oscar_class_from_dashboard_subapp(self):
  29. ReportForm = get_class("dashboard.reports.forms", "ReportForm")
  30. self.assertEqual("oscar.apps.dashboard.reports.forms", ReportForm.__module__)
  31. def test_raise_exception_when_bad_appname_used(self):
  32. with self.assertRaises(AppNotFoundError):
  33. get_classes("fridge.models", ("Product", "Category"))
  34. def test_raise_exception_when_bad_classname_used(self):
  35. with self.assertRaises(ClassNotFoundError):
  36. get_class("catalogue.models", "Monkey")
  37. def test_raise_importerror_if_app_raises_importerror(self):
  38. """
  39. This tests that Oscar doesn't fall back to using the Oscar core
  40. app class if the overriding app class throws an ImportError.
  41. "get_class()" returns None in this case, since there is no such named
  42. class in the core app. We use this fictitious class because classes in
  43. the "models" and "views" modules (along with modules they are related
  44. to) are imported as part of the Django app-loading process, which is
  45. triggered when we override the INSTALLED_APPS setting.
  46. """
  47. installed_apps = list(settings.INSTALLED_APPS)
  48. replaced_app_idx = installed_apps.index(
  49. "tests._site.apps.catalogue.apps.CatalogueConfig"
  50. )
  51. installed_apps[replaced_app_idx] = (
  52. "tests._site.import_error_app.catalogue.apps.CatalogueConfig"
  53. )
  54. with override_settings(INSTALLED_APPS=installed_apps):
  55. with self.assertRaises(ImportError):
  56. get_class("catalogue.import_error_module", "ImportErrorClass")
  57. class ClassLoadingWithLocalOverrideTests(TestCase):
  58. def setUp(self):
  59. self.installed_apps = list(settings.INSTALLED_APPS)
  60. replaced_app_idx = self.installed_apps.index(
  61. "oscar.apps.shipping.apps.ShippingConfig"
  62. )
  63. self.installed_apps[replaced_app_idx] = (
  64. "tests._site.apps.shipping.apps.ShippingConfig"
  65. )
  66. def test_loading_class_defined_in_local_module(self):
  67. with override_settings(INSTALLED_APPS=self.installed_apps):
  68. (Free,) = get_classes("shipping.methods", ("Free",))
  69. self.assertEqual("tests._site.apps.shipping.methods", Free.__module__)
  70. def test_loading_class_which_is_not_defined_in_local_module(self):
  71. with override_settings(INSTALLED_APPS=self.installed_apps):
  72. (FixedPrice,) = get_classes("shipping.methods", ("FixedPrice",))
  73. self.assertEqual("oscar.apps.shipping.methods", FixedPrice.__module__)
  74. def test_loading_class_from_module_not_defined_in_local_app(self):
  75. with override_settings(INSTALLED_APPS=self.installed_apps):
  76. (Repository,) = get_classes("shipping.repository", ("Repository",))
  77. self.assertEqual("oscar.apps.shipping.repository", Repository.__module__)
  78. def test_loading_classes_defined_in_both_local_and_oscar_modules(self):
  79. with override_settings(INSTALLED_APPS=self.installed_apps):
  80. (Free, FixedPrice) = get_classes("shipping.methods", ("Free", "FixedPrice"))
  81. self.assertEqual("tests._site.apps.shipping.methods", Free.__module__)
  82. self.assertEqual("oscar.apps.shipping.methods", FixedPrice.__module__)
  83. def test_loading_classes_with_root_app(self):
  84. import tests._site.shipping
  85. path = dirname(dirname(tests._site.shipping.__file__))
  86. with temporary_python_path([path]):
  87. replaced_app_idx = self.installed_apps.index(
  88. "tests._site.apps.shipping.apps.ShippingConfig"
  89. )
  90. self.installed_apps[replaced_app_idx] = "shipping.apps.ShippingConfig"
  91. with override_settings(INSTALLED_APPS=self.installed_apps):
  92. (Free,) = get_classes("shipping.methods", ("Free",))
  93. self.assertEqual("shipping.methods", Free.__module__)
  94. def test_overriding_view_is_possible_without_overriding_app(self):
  95. # If test fails, it's helpful to know if it's caused by order of
  96. # execution
  97. customer_app_config = CustomerConfig.create("oscar.apps.customer")
  98. customer_app_config.ready()
  99. self.assertEqual(
  100. customer_app_config.summary_view.__module__,
  101. "tests._site.apps.customer.views",
  102. )
  103. self.assertEqual(
  104. apps.get_app_config("customer").summary_view.__module__,
  105. "tests._site.apps.customer.views",
  106. )
  107. class ClassLoadingWithLocalOverrideWithMultipleSegmentsTests(TestCase):
  108. def setUp(self):
  109. self.installed_apps = list(settings.INSTALLED_APPS)
  110. replaced_app_idx = self.installed_apps.index(
  111. "oscar.apps.shipping.apps.ShippingConfig"
  112. )
  113. self.installed_apps[replaced_app_idx] = (
  114. "tests._site.apps.shipping.apps.ShippingConfig"
  115. )
  116. def test_loading_class_defined_in_local_module(self):
  117. with override_settings(INSTALLED_APPS=self.installed_apps):
  118. (Free,) = get_classes("shipping.methods", ("Free",))
  119. self.assertEqual("tests._site.apps.shipping.methods", Free.__module__)
  120. class TestOverridingCoreApps(TestCase):
  121. def test_means_the_overriding_model_is_registered_first(self):
  122. klass = get_model("partner", "StockRecord")
  123. self.assertEqual("tests._site.apps.partner.models", klass.__module__)
  124. class TestAppLabelsForModels(TestCase):
  125. def test_all_oscar_models_have_app_labels(self):
  126. models = apps.get_models()
  127. missing = []
  128. for model in models:
  129. # Ignore non-Oscar models
  130. if "oscar" not in repr(model):
  131. continue
  132. # Don't know how to get the actual model's Meta class. But if
  133. # the parent doesn't have a Meta class, it's doesn't have an
  134. # base in Oscar anyway and is not intended to be overridden
  135. abstract_model = model.__base__
  136. meta_class = getattr(abstract_model, "Meta", None)
  137. if meta_class is None:
  138. continue
  139. if not hasattr(meta_class, "app_label"):
  140. missing.append(model)
  141. if missing:
  142. self.fail("Those models don't have an app_label set: %s" % missing)
  143. class TestDynamicLoadingOn3rdPartyApps(TestCase):
  144. core_app_prefix = "thirdparty_package.apps"
  145. def setUp(self):
  146. self.installed_apps = list(settings.INSTALLED_APPS)
  147. sys.path.append("./tests/_site/")
  148. def tearDown(self):
  149. sys.path.remove("./tests/_site/")
  150. def test_load_core_3rd_party_class_correctly(self):
  151. self.installed_apps.append("thirdparty_package.apps.myapp.apps.MyAppConfig")
  152. with override_settings(INSTALLED_APPS=self.installed_apps):
  153. Cow, Goat = get_classes(
  154. "myapp.models", ("Cow", "Goat"), self.core_app_prefix
  155. )
  156. self.assertEqual("thirdparty_package.apps.myapp.models", Cow.__module__)
  157. self.assertEqual("thirdparty_package.apps.myapp.models", Goat.__module__)
  158. def test_load_overriden_3rd_party_class_correctly(self):
  159. self.installed_apps.append("tests._site.apps.myapp.apps.TestConfig")
  160. with override_settings(INSTALLED_APPS=self.installed_apps):
  161. Cow, Goat = get_classes(
  162. "myapp.models", ("Cow", "Goat"), self.core_app_prefix
  163. )
  164. self.assertEqual("thirdparty_package.apps.myapp.models", Cow.__module__)
  165. self.assertEqual("tests._site.apps.myapp.models", Goat.__module__)
  166. class OverriddenClassLoadingTestCase(TestCase):
  167. def test_non_override_class_loader(self):
  168. from oscar.apps.catalogue.views import ProductDetailView
  169. View = get_class("catalogue.views", "ProductDetailView")
  170. self.assertEqual(View, ProductDetailView)
  171. @override_settings(
  172. OSCAR_DYNAMIC_CLASS_LOADER="tests._site.loader.custom_class_loader"
  173. )
  174. def test_override_class_loader(self):
  175. # Clear lru cache for the class loader
  176. get_class_loader.cache_clear()
  177. View = get_class("catalogue.views", "ProductDetailView")
  178. self.assertEqual(View, DummyClass)
  179. # Clear lru cache for the class loader again
  180. get_class_loader.cache_clear()