test_models.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. import datetime
  2. from django.test import TestCase
  3. from django.core.exceptions import ValidationError
  4. from ..models import (
  5. Cost, CostUse, Document, Good, GoodUse, Service, ServiceUse)
  6. THREE_YEARS = datetime.timedelta(days=365*3)
  7. class ServiceTests(TestCase):
  8. def setUp(self):
  9. self.doc = Document.objects.create(name='budget')
  10. self.electricity_cost = Cost.objects.create(
  11. name='electricity',
  12. price=10,
  13. document=self.doc,
  14. )
  15. self.server = Good.objects.create(
  16. name="Computer",
  17. price=10,
  18. document=self.doc,
  19. provisioning_duration=THREE_YEARS,
  20. )
  21. def test_get_prices_zero(self):
  22. s = Service.objects.create(name='Foo', document=self.doc)
  23. self.assertEqual(s.get_prices(), {
  24. 'total_recurring_price': 0,
  25. 'unit_recurring_price': 0,
  26. 'unit_goods_value_share': 0,
  27. 'total_goods_value_share': 0,
  28. })
  29. def test_get_prices_w_costs(self):
  30. s = Service.objects.create(name='Foo', document=self.doc)
  31. CostUse.objects.create(
  32. service=s, resource=self.electricity_cost, share=0.4)
  33. self.assertEqual(s.get_prices(), {
  34. 'total_recurring_price': 10,
  35. 'unit_recurring_price': 0,
  36. 'unit_goods_value_share': 0,
  37. 'total_goods_value_share': 0,
  38. })
  39. s.subscriptions_count = 2
  40. s.save()
  41. self.assertEqual(s.get_prices(), {
  42. 'total_recurring_price': 10,
  43. 'unit_recurring_price': 5,
  44. 'unit_goods_value_share': 0,
  45. 'total_goods_value_share': 0,
  46. })
  47. def test_get_prices_w_goods(self):
  48. s = Service.objects.create(
  49. name='Foo', document=self.doc, subscriptions_count=0)
  50. GoodUse.objects.create(
  51. service=s, resource=self.server, share=0.4)
  52. self.assertEqual(s.get_prices(), {
  53. 'total_recurring_price': 10/(365*3)*365.25/12,
  54. 'unit_recurring_price': 0,
  55. 'unit_goods_value_share': 0,
  56. 'total_goods_value_share': 10.0,
  57. })
  58. s.subscriptions_count = 2
  59. s.save()
  60. self.assertEqual(s.get_prices(), {
  61. 'total_recurring_price': 10/(365*3)*365.25/12,
  62. 'unit_recurring_price': 10/(365*3)*365.25/12/2,
  63. 'unit_goods_value_share': 5,
  64. 'total_goods_value_share': 10,
  65. })
  66. class AbstractUseTests(TestCase):
  67. """ Testing AbstractUseTests through CostUse
  68. """
  69. def setUp(self):
  70. self.doc = Document.objects.create(name='budget')
  71. self.hosting_service = Service.objects.create(
  72. name='Physical hosting', document=self.doc)
  73. self.mailbox_service = Service.objects.create(
  74. name='Mailbox', document=self.doc)
  75. self.electricity_cost = Cost.objects.create(
  76. name='electricity',
  77. price=10,
  78. document=self.doc,
  79. capacity_unit='A',
  80. total_capacity=4,
  81. )
  82. self.carrier_connection = Cost.objects.create(
  83. name='carrier connection',
  84. price=100,
  85. document=self.doc,
  86. )
  87. def test_can_add_service_share(self):
  88. use = CostUse(
  89. service=self.hosting_service,
  90. resource=self.electricity_cost,
  91. share=0.4)
  92. use.full_clean()
  93. use.save()
  94. def test_can_add_service_share_with_custom_unity(self):
  95. use = CostUse(
  96. service=self.hosting_service,
  97. resource=self.electricity_cost,
  98. share=2) # means 2 Amps
  99. self.hosting_service.subscriptions_count = 2
  100. self.hosting_service.save()
  101. use.full_clean()
  102. use.save()
  103. self.assertEqual(use.share, 2.0)
  104. self.assertEqual(use.real_share(), 4.0)
  105. self.assertEqual(use.cost_share(), 10)
  106. self.assertEqual(use.unit_cost_share(), 5)
  107. def test_cannot_add_excess_share_one(self):
  108. use = CostUse(
  109. service=self.hosting_service,
  110. resource=self.electricity_cost,
  111. share=40.1)
  112. with self.assertRaises(ValidationError):
  113. use.full_clean()
  114. use.save()
  115. def test_add_several_service_share(self):
  116. u1 = CostUse(
  117. service=self.hosting_service,
  118. resource=self.electricity_cost,
  119. share=0.4)
  120. u1.full_clean()
  121. u1.save()
  122. u2 = CostUse(
  123. service=self.mailbox_service,
  124. resource=self.electricity_cost,
  125. share=0.6)
  126. u2.full_clean()
  127. u2.save()
  128. def test_add_several_service_share_excessive_sum(self):
  129. u1 = CostUse(
  130. service=self.hosting_service,
  131. resource=self.electricity_cost,
  132. share=3)
  133. u1.full_clean()
  134. u1.save()
  135. u2 = CostUse(
  136. service=self.mailbox_service,
  137. resource=self.electricity_cost,
  138. share=1.1)
  139. # Would be 4.1 out of 4 amp...
  140. with self.assertRaises(ValidationError):
  141. u2.full_clean()
  142. u2.save()
  143. def test_modify_service_share_no_error(self):
  144. u1 = CostUse(
  145. service=self.hosting_service,
  146. resource=self.electricity_cost,
  147. share=1)
  148. u1.full_clean()
  149. u1.save()
  150. u1.full_clean()
  151. u1.save()
  152. def test_real_shares(self):
  153. u1 = CostUse.objects.create(
  154. service=self.hosting_service,
  155. resource=self.electricity_cost,
  156. share=1.6)
  157. u2 = CostUse.objects.create(
  158. service=self.hosting_service,
  159. resource=self.electricity_cost,
  160. share=0.6)
  161. self.assertEqual(u1.real_share() + u2.real_share(), 4)
  162. self.assertEqual(u1.share/u2.share, u1.real_share()/u2.real_share())
  163. def test_unit_value_share(self):
  164. self.mailbox_service.subscriptions_count = 2
  165. self.mailbox_service.share = 0.5
  166. self.hosting_service.save()
  167. self.hosting_service.subscriptions_count = 1
  168. self.hosting_service.share = 0.5
  169. self.hosting_service.save()
  170. mailbox_use = CostUse.objects.create(
  171. service=self.mailbox_service,
  172. resource=self.electricity_cost,
  173. share=2)
  174. hosting_use = CostUse.objects.create(
  175. service=self.hosting_service,
  176. resource=self.electricity_cost,
  177. share=2)
  178. self.assertEqual(mailbox_use.value_share(), 5)
  179. self.assertEqual(mailbox_use.unit_value_share(), 2.5)
  180. self.assertEqual(hosting_use.value_share(), 5)
  181. self.assertEqual(hosting_use.unit_value_share(), 5)
  182. def test_used(self):
  183. CostUse.objects.create(
  184. service=self.mailbox_service,
  185. resource=self.electricity_cost,
  186. share=0.5)
  187. self.assertEqual(self.electricity_cost.used(), 0.5)
  188. self.assertEqual(self.electricity_cost.unused(), 3.5)
  189. def test_service_using_service(self):
  190. """
  191. Wifi+VPN is a service, but using VPN access
  192. So there is a service using another service
  193. """
  194. vpn_service = Service.objects.create(
  195. name="VPN",
  196. document=self.doc,
  197. subscriptions_count=20, # includes wifi+vpn subscribers
  198. reusable=True,
  199. )
  200. # both should be auto-set
  201. self.assertEqual(vpn_service.capacity_unit, 'services')
  202. self.assertEqual(vpn_service.total_capacity, 20)
  203. wifi_service = Service.objects.create(
  204. name="Wifi, via VPN",
  205. document=self.doc,
  206. subscriptions_count=2,
  207. reusable=True,
  208. )
  209. self.assertEqual(vpn_service.capacity_unit, 'services')
  210. # To simplify, VPN is only using electricity
  211. CostUse.objects.create(
  212. service=vpn_service,
  213. resource=self.electricity_cost,
  214. share=0.5, # Amp
  215. )
  216. # Wifi is using VPN + a carrier connection
  217. wifi_vpn_use = ServiceUse.objects.create(
  218. service=wifi_service,
  219. resource=vpn_service,
  220. share=2,
  221. )
  222. CostUse.objects.create(
  223. service=wifi_service,
  224. resource=self.carrier_connection,
  225. share=1, # 100%
  226. )
  227. self.assertEqual(wifi_vpn_use.share, 0.5*4*10/20*2)
  228. self.assertEqual(wifi_vpn_use.unit_share(), 0.5*4*10/20)
  229. # VPN this is the only service using electricity
  230. self.assertEqual(wifi_vpn_use.unit_real_share(), 10)
  231. # VPN is now using some gear, with deprecation provisioning
  232. hosting_access_fee = Good.objects.create(
  233. name='hosting access fee', price=360,
  234. provisioning_duration=THREE_YEARS, document=self.doc)
  235. GoodUse.objects.create(
  236. service=vpn_service, resource=hosting_access_fee, share=2)
  237. self.assertEqual(
  238. wifi_service.get_prices()['total_goods_value_share'], 36)
  239. self.assertEqual(
  240. wifi_service.get_prices()['unit_goods_value_share'], 18)
  241. def test_service_using_non_usable_service(self):
  242. serva = Service.objects.create(
  243. name='A', document=self.doc,
  244. subscriptions_count=4,
  245. reusable=False,
  246. )
  247. with self.assertRaises(ValidationError):
  248. su = ServiceUse(
  249. service=self.mailbox_service,
  250. resource=serva,
  251. share=1,
  252. )
  253. su.full_clean()
  254. su.save()
  255. def test_service_using_cyclic_service(self):
  256. """ We should not save any service dependency building a cycle
  257. """
  258. a = Service.objects.create(
  259. name='a', document=self.doc,
  260. reusable=True,
  261. )
  262. b = Service.objects.create(
  263. name='b', document=self.doc,
  264. reusable=True,
  265. )
  266. c = Service.objects.create(
  267. name='c', document=self.doc,
  268. reusable=True,
  269. )
  270. def create_clean(user, used):
  271. new_use = ServiceUse(service=user, resource=used, share=1)
  272. new_use.full_clean()
  273. new_use.save()
  274. create_clean(a, b)
  275. with self.assertRaises(ValidationError):
  276. create_clean(b, a)
  277. create_clean(b, c)
  278. with self.assertRaises(ValidationError):
  279. create_clean(c, a)
  280. class TestDocuments(TestCase):
  281. fixtures = ['full_example.yaml']
  282. _all_models = [Document, Cost, Good, Service, GoodUse, CostUse, ServiceUse]
  283. def _count(self):
  284. return [i.objects.count() for i in self._all_models]
  285. def test_copy(self):
  286. initial_counts = self._count()
  287. old_doc = Document.objects.first()
  288. new_doc = old_doc.copy()
  289. self.assertNotEqual(new_doc.pk, old_doc.pk)
  290. self.assertEqual(self._count(), [i*2 for i in initial_counts])
  291. old_doc.delete()
  292. self.assertEqual(self._count(), initial_counts)