Unit test improvements

This commit is contained in:
Oliver Walters 2021-04-20 16:48:28 +10:00
parent 715d912137
commit debc086f89
2 changed files with 123 additions and 59 deletions

View File

@ -916,7 +916,7 @@ class Build(MPTTModel):
return self.unallocatedQuantity(part, output) == 0 return self.unallocatedQuantity(part, output) == 0
def isFullyAllocated(self, output): def isFullyAllocated(self, output, verbose=False):
""" """
Returns True if the particular build output is fully allocated. Returns True if the particular build output is fully allocated.
""" """
@ -927,14 +927,21 @@ class Build(MPTTModel):
else: else:
bom_items = self.tracked_bom_items bom_items = self.tracked_bom_items
fully_allocated = True
for bom_item in bom_items: for bom_item in bom_items:
part = bom_item.sub_part part = bom_item.sub_part
if not self.isPartFullyAllocated(part, output): if not self.isPartFullyAllocated(part, output):
return False fully_allocated = False
if verbose:
print(f"Part {part} is not fully allocated for output {output}")
else:
break
# All parts must be fully allocated! # All parts must be fully allocated!
return True return fully_allocated
def areUntrackedPartsFullyAllocated(self): def areUntrackedPartsFullyAllocated(self):
""" """

View File

@ -19,6 +19,18 @@ class BuildTest(TestCase):
def setUp(self): def setUp(self):
""" """
Initialize data to use for these tests. Initialize data to use for these tests.
The base Part 'assembly' has a BOM consisting of three parts:
- 5 x sub_part_1
- 3 x sub_part_2
- 2 x sub_part_3 (trackable)
We will build 10x 'assembly' parts, in two build outputs:
- 3 x output_1
- 7 x output_2
""" """
# Create a base "Part" # Create a base "Part"
@ -41,17 +53,31 @@ class BuildTest(TestCase):
component=True component=True
) )
self.sub_part_3 = Part.objects.create(
name="Widget C",
description="A widget",
component=True,
trackable=True
)
# Create BOM item links for the parts # Create BOM item links for the parts
BomItem.objects.create( BomItem.objects.create(
part=self.assembly, part=self.assembly,
sub_part=self.sub_part_1, sub_part=self.sub_part_1,
quantity=10 quantity=5
) )
BomItem.objects.create( BomItem.objects.create(
part=self.assembly, part=self.assembly,
sub_part=self.sub_part_2, sub_part=self.sub_part_2,
quantity=25 quantity=3
)
# sub_part_3 is trackable!
BomItem.objects.create(
part=self.assembly,
sub_part=self.sub_part_3,
quantity=2
) )
# Create a "Build" object to make 10x objects # Create a "Build" object to make 10x objects
@ -64,14 +90,14 @@ class BuildTest(TestCase):
# Create some build output (StockItem) objects # Create some build output (StockItem) objects
self.output_1 = StockItem.objects.create( self.output_1 = StockItem.objects.create(
part=self.assembly, part=self.assembly,
quantity=5, quantity=3,
is_building=True, is_building=True,
build=self.build build=self.build
) )
self.output_2 = StockItem.objects.create( self.output_2 = StockItem.objects.create(
part=self.assembly, part=self.assembly,
quantity=5, quantity=7,
is_building=True, is_building=True,
build=self.build, build=self.build,
) )
@ -82,10 +108,12 @@ class BuildTest(TestCase):
self.stock_2_1 = StockItem.objects.create(part=self.sub_part_2, quantity=5000) self.stock_2_1 = StockItem.objects.create(part=self.sub_part_2, quantity=5000)
self.stock_3_1 = StockItem.objects.create(part=self.sub_part_3, quantity=1000)
def test_init(self): def test_init(self):
# Perform some basic tests before we start the ball rolling # Perform some basic tests before we start the ball rolling
self.assertEqual(StockItem.objects.count(), 5) self.assertEqual(StockItem.objects.count(), 6)
# Build is PENDING # Build is PENDING
self.assertEqual(self.build.status, status.BuildStatus.PENDING) self.assertEqual(self.build.status, status.BuildStatus.PENDING)
@ -144,84 +172,113 @@ class BuildTest(TestCase):
quantity=99 quantity=99
) )
def allocate_stock(self, q11, q12, q21, output): def allocate_stock(self, output, allocations):
# Assign stock to this build """
Allocate stock to this build, against a particular output
if q11 > 0: Args:
output - StockItem object (or None)
allocations - Map of {StockItem: quantity}
"""
for item, quantity in allocations.items():
BuildItem.objects.create( BuildItem.objects.create(
build=self.build, build=self.build,
stock_item=self.stock_1_1, stock_item=item,
quantity=q11, quantity=quantity,
install_into=output install_into=output
) )
if q12 > 0:
BuildItem.objects.create(
build=self.build,
stock_item=self.stock_1_2,
quantity=q12,
install_into=output
)
if q21 > 0:
BuildItem.objects.create(
build=self.build,
stock_item=self.stock_2_1,
quantity=q21,
install_into=output,
)
# Attempt to create another identical BuildItem
b = BuildItem(
build=self.build,
stock_item=self.stock_2_1,
quantity=q21
)
with self.assertRaises(ValidationError):
b.clean()
def test_partial_allocation(self): def test_partial_allocation(self):
""" """
Partially allocate against output 1 Test partial allocation of stock
""" """
self.allocate_stock(50, 50, 200, self.output_1) # Fully allocate tracked stock against build output 1
self.allocate_stock(
self.output_1,
{
self.stock_3_1: 6,
}
)
self.assertTrue(self.build.isFullyAllocated(self.output_1)) self.assertTrue(self.build.isFullyAllocated(self.output_1))
# Partially allocate tracked stock against build output 2
self.allocate_stock(
self.output_2,
{
self.stock_3_1: 1,
}
)
self.assertFalse(self.build.isFullyAllocated(self.output_2)) self.assertFalse(self.build.isFullyAllocated(self.output_2))
self.assertTrue(self.build.isPartFullyAllocated(self.sub_part_1, self.output_1))
self.assertTrue(self.build.isPartFullyAllocated(self.sub_part_2, self.output_1))
self.assertFalse(self.build.isPartFullyAllocated(self.sub_part_1, self.output_2))
self.assertFalse(self.build.isPartFullyAllocated(self.sub_part_2, self.output_2))
# Check that the part has been allocated # Partially allocate untracked stock against build
self.assertEqual(self.build.allocatedQuantity(self.sub_part_1, self.output_1), 100) self.allocate_stock(
None,
{
self.stock_1_1: 1,
self.stock_2_1: 1
}
)
self.build.unallocateStock(output=self.output_1) self.assertFalse(self.build.isFullyAllocated(None, verbose=True))
self.assertEqual(BuildItem.objects.count(), 0)
# Check that the part has been unallocated unallocated = self.build.unallocatedParts(None)
self.assertEqual(self.build.allocatedQuantity(self.sub_part_1, self.output_1), 0)
self.assertEqual(len(unallocated), 2)
self.allocate_stock(
None,
{
self.stock_1_2: 100,
}
)
self.assertFalse(self.build.isFullyAllocated(None, verbose=True))
unallocated = self.build.unallocatedParts(None)
self.assertEqual(len(unallocated), 1)
self.build.unallocateUntracked()
unallocated = self.build.unallocatedParts(None)
self.assertEqual(len(unallocated), 2)
self.assertFalse(self.build.areUntrackedPartsFullyAllocated())
# Now we "fully" allocate the untracked untracked items
self.allocate_stock(
None,
{
self.stock_1_1: 50,
self.stock_2_1: 50,
}
)
self.assertTrue(self.build.areUntrackedPartsFullyAllocated())
def test_auto_allocate(self): def test_auto_allocate(self):
""" """
Test auto-allocation functionality against the build outputs Test auto-allocation functionality against the build outputs.
Note: auto-allocations only work for un-tracked stock!
""" """
allocations = self.build.getAutoAllocations(self.output_1) allocations = self.build.getAutoAllocations()
self.assertEqual(len(allocations), 1) self.assertEqual(len(allocations), 1)
self.build.autoAllocate(self.output_1) self.build.autoAllocate()
self.assertEqual(BuildItem.objects.count(), 1) self.assertEqual(BuildItem.objects.count(), 1)
# Check that one part has been fully allocated to the build output # Check that one un-tracked part has been fully allocated to the build
self.assertTrue(self.build.isPartFullyAllocated(self.sub_part_2, self.output_1)) self.assertTrue(self.build.isPartFullyAllocated(self.sub_part_2, None))
# But, the *other* build output has not been allocated against self.assertFalse(self.build.isPartFullyAllocated(self.sub_part_1, None))
self.assertFalse(self.build.isPartFullyAllocated(self.sub_part_2, self.output_2))
def test_cancel(self): def test_cancel(self):
""" """