Browse Source

Added a test runner and better tests / Updated admin options

master
David Winterbottom 15 years ago
parent
commit
df9e2b5ec8

+ 2
- 1
README.md View File

@@ -50,7 +50,8 @@ There is a shortcut script for dropping all of oscar's apps and rerunning `syncd
50 50
     ./reset_oscar_tables.sh
51 51
     
52 52
 Run tests using:
53
-    ./manage.py test oscar
53
+    ./run_tests.sh
54
+This specifies a sqlite3 database to use for testing and filters out the useless output.
54 55
     
55 56
 You can also use the functionality from (django-test-extensions)[https://github.com/garethr/django-test-extensions/] which 
56 57
 is one of the installed app	

+ 19
- 4
oscar/basket/abstract_models.py View File

@@ -8,7 +8,8 @@ class AbstractBasket(models.Model):
8 8
     """
9 9
     Basket object
10 10
     """
11
-    owner = models.ForeignKey(User, related_name='baskets')
11
+    # Baskets can be anonymously owned
12
+    owner = models.ForeignKey(User, related_name='baskets', null=True)
12 13
     OPEN, MERGED, SUBMITTED = ("Open", "Merged", "Submitted")
13 14
     STATUS_CHOICES = (
14 15
         (OPEN, _("Open - currently active")),
@@ -25,19 +26,31 @@ class AbstractBasket(models.Model):
25 26
     def is_empty(self):
26 27
         return self.get_num_lines() == 0
27 28
     
29
+    def add_product(self, item, quantity=1):
30
+        """
31
+        Convenience method for adding a line
32
+        """
33
+        self.lines.create(basket=self, product=item, quantity=quantity)
34
+    
28 35
     def get_num_lines(self):
29 36
         """
30 37
         Returns number of lines within this basket
31 38
         """
32
-        return len(self.lines.all())
39
+        return self.lines.all().count()
33 40
     
34 41
     def get_num_items(self):
35
-        return reduce(lambda num,line: num+line.quantity, self.lines)
42
+        """
43
+        Returns the number of items in the basket
44
+        """
45
+        return reduce(lambda num,line: num+line.quantity, self.lines.all(), 0)
36 46
     
37 47
     def __unicode__(self):
38 48
         return "%s basket (owner: %s)" % (self.status, self.owner)
39 49
     
40 50
 class AbstractLine(models.Model):
51
+    """
52
+    A line of a basket (product and a quantity)
53
+    """
41 54
     basket = models.ForeignKey('basket.Basket', related_name='lines')
42 55
     product = models.ForeignKey('product.Item')
43 56
     quantity = models.PositiveIntegerField(default=1)
@@ -53,10 +66,12 @@ class AbstractLine(models.Model):
53 66
         """
54 67
         attribute_string = "_".join([attribute.get_hash() for attributes in self.attributes])
55 68
         return zlib.crc32(attribute_string)
56
-        
57 69
     
58 70
     class Meta:
59 71
         abstract = True
72
+        
73
+    def __unicode__(self):
74
+        return "%s, Product '%s', quantity %d" % (self.basket, self.product, self.quantity)
60 75
     
61 76
 class AbstractLineAttribute(models.Model):
62 77
     line = models.ForeignKey('basket.Line', related_name='attributes')

+ 26
- 6
oscar/basket/tests.py View File

@@ -1,16 +1,36 @@
1 1
 import unittest
2 2
 
3
-from django.test.utils import setup_test_environment
4
-setup_test_environment()
5
-
6 3
 from django.test import TestCase
7 4
 from oscar.basket.models import * 
5
+from oscar.product.models import Item, ItemClass
8 6
 
9 7
 class BasketTest(unittest.TestCase):
8
+    
9
+    def setUp(self):
10
+        self.basket = Basket.objects.create()
11
+        
12
+        # Create a dummy product
13
+        ic = ItemClass.objects.create(name='Dummy class')
14
+        self.dummy_product = Item.objects.create(title='Dummy product', item_class=ic)
15
+    
10 16
     def test_empty_baskets_have_zero_lines(self):
11
-        b = Basket()
12
-        self.assertTrue(b.get_num_lines() == 0)
13
-
17
+        self.assertTrue(Basket().get_num_lines() == 0)
18
+        
19
+    def test_new_baskets_are_empty(self):
20
+        self.assertTrue(Basket().is_empty())
21
+        
22
+    def test_basket_have_with_one_line(self):
23
+        line = Line.objects.create(basket=self.basket, product=self.dummy_product)
24
+        self.assertTrue(self.basket.get_num_lines() == 1)
25
+        
26
+    def test_add_product_creates_line(self):
27
+        self.basket.add_product(self.dummy_product)
28
+        self.assertTrue(self.basket.get_num_lines() == 1)
29
+        
30
+    def test_adding_multiproduct_line_returns_correct_number_of_items(self):
31
+        self.basket.add_product(self.dummy_product, 10)
32
+        self.assertEqual(self.basket.get_num_items(), 10)
33
+        
14 34
 if __name__ == '__main__':
15 35
     from django.test.utils import setup_test_environment
16 36
     setup_test_environment()

+ 4
- 3
oscar/image/abstract_models.py View File

@@ -1,17 +1,18 @@
1 1
 """
2
-Abstract models for the product images app
2
+Abstract models for product images
3 3
 """
4 4
 
5 5
 from django.db import models
6 6
 from django.utils.translation import ugettext as _
7 7
 
8
-
9 8
 class AbstractImage(models.Model):
10 9
     """
11 10
     An image of a product  
12 11
     """
13 12
     product = models.ForeignKey('product.Item', related_name='images')
14
-    path = models.ImageField(upload_to='product-images/%Y/%m/')
13
+    # Namespacing path with app name to avoid clashes with other apps
14
+    path_to_original = models.ImageField(upload_to='product-images/%Y/%m/')
15
+    path_to_thumbnail = models.ImageField(upload_to='product-images/%Y/%m/')
15 16
     # Use display_order to determine which is the "primary" image
16 17
     display_order = models.PositiveIntegerField()
17 18
     date_created = models.DateTimeField(auto_now_add=True)

+ 1
- 1
oscar/order/abstract_models.py View File

@@ -93,7 +93,7 @@ class AbstractBatchLineEvent(models.Model):
93 93
         verbose_name_plural = _("Batch line events")
94 94
         
95 95
     def __unicode__(self):
96
-        return "Order #%d, batch #%d, line %d: %d items %s" % (
96
+        return "Order #%d, batch #%d, line %s: %d items %s" % (
97 97
             self.line.batch.order.number, self.line.batch.id, self.line.line_id, self.quantity, self.event_type)
98 98
 
99 99
 class AbstractBatchLineEventType(models.Model):

+ 5
- 1
oscar/product/abstract_models.py View File

@@ -10,6 +10,7 @@ class AbstractItemClass(models.Model):
10 10
     class Meta:
11 11
         abstract = True
12 12
         ordering = ['name']
13
+        verbose_name_plural = "Item classes"
13 14
 
14 15
     def __unicode__(self):
15 16
         return self.name
@@ -26,7 +27,10 @@ class AbstractItem(models.Model):
26 27
     # children would be "Green fleece - size L".
27 28
     #
28 29
     # No canonical product should have a stock record as they cannot be bought.
29
-    parent = models.ForeignKey('self', blank=True, null=True)
30
+    parent = models.ForeignKey('self', blank=True, null=True, 
31
+        help_text="""Only choose a parent product if this is a 'variant' of a canonical product.  For example 
32
+                     if this is a size 4 of a particular t-shirt.  Leave blank if there is only one version of this
33
+                     product""")
30 34
     title = models.CharField(_('name'), max_length=255)
31 35
     description = models.TextField(_('description'), blank=True, null=True)
32 36
     item_class = models.ForeignKey('product.ItemClass', verbose_name=_('item class'))

+ 1
- 1
oscar/stock/abstract_models.py View File

@@ -30,7 +30,7 @@ class AbstractStockRecord(models.Model):
30 30
     price_incl_tax = models.DecimalField(decimal_places=2, max_digits=12)
31 31
     price_excl_tax = models.DecimalField(decimal_places=2, max_digits=12)
32 32
     num_in_stock = models.IntegerField()
33
-    num_allocated = models.IntegerField()
33
+    num_allocated = models.IntegerField(default=0)
34 34
     
35 35
     class Meta:
36 36
         abstract = True

+ 4
- 1
oscar/stock/admin.py View File

@@ -1,5 +1,8 @@
1 1
 from django.contrib import admin
2 2
 from oscar.stock.models import *
3 3
 
4
+class StockRecordAdmin(admin.ModelAdmin):
5
+    exclude = ('num_allocated',)
6
+
4 7
 admin.site.register(Partner)
5
-admin.site.register(StockRecord)
8
+admin.site.register(StockRecord, StockRecordAdmin)

+ 1
- 22
oscar/tests.py View File

@@ -1,23 +1,2 @@
1
-"""
2
-This file demonstrates two different styles of tests (one doctest and one
3
-unittest). These will both pass when you run "manage.py test".
4
-
5
-Replace these with more appropriate tests for your application.
6
-"""
7
-
8
-from django.test import TestCase
9
-
10
-class SimpleTest(TestCase):
11
-    def test_basic_addition(self):
12
-        """
13
-        Tests that 1 + 1 always equals 2.
14
-        """
15
-        self.failUnlessEqual(1 + 1, 2)
16
-
17
-__test__ = {"doctest": """
18
-Another way to test that 1 + 1 is equal to 2.
19
-
20
->>> 1 + 1 == 2
21
-True
22
-"""}
1
+from oscar.basket.tests import *
23 2
 

+ 2
- 1
reset_oscar_tables.sh View File

@@ -1,7 +1,8 @@
1 1
 #!/bin/bash
2 2
 # For dropping and recreating all the oscar tables
3 3
 # @todo rewrite this a manage.py command
4
+echo "Dropping tables"
4 5
 ./manage.py sqlclear payment offer order basket stock image product | \
5 6
 	awk 'BEGIN {print "set foreign_key_checks=0;"} {print $0}' | \
6 7
     ./manage.py dbshell && \
7
-    ./manage.py syncdb
8
+    ./manage.py syncdb

+ 5
- 0
run_tests.sh View File

@@ -0,0 +1,5 @@
1
+#!/bin/bash
2
+# Test runnner script that specifies correct settings file
3
+# and filters out useless output
4
+time ./manage.py test oscar --settings=test_settings -v 1 --failfast | \
5
+	grep -v "^\(Installing\|Creating\)"

+ 19
- 0
test_settings.py View File

@@ -0,0 +1,19 @@
1
+"""
2
+Special settings file for use when testing.  This specifies a SQLite 
3
+database to use when running tests.
4
+
5
+Just make sure you run the tests and specify this file:
6
+> ./manage.py test -settings=test_settings
7
+"""
8
+from settings import *
9
+
10
+DATABASES = {
11
+    'default': {
12
+        'ENGINE': 'sqlite3', 
13
+        'NAME': ':memory:', 
14
+        'USER': '',                      # Not used with sqlite3.
15
+        'PASSWORD': '',                  # Not used with sqlite3.
16
+        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
17
+        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
18
+    }
19
+}

Loading…
Cancel
Save