Repository: jbradberry/universe

Issue:
Implement the ability to modify, reorder, and delete orders

Discussion:

Patch:
diff --git a/universe/engine.py b/universe/engine.py
index 0737feb..83ffd4a 100644
--- a/universe/engine.py
+++ b/universe/engine.py
@@ -115,7 +115,19 @@ class Manager:
             self.register_entity(entity)
         for entity in self.get_entities('metadata').values():
             entity.validate()
-        self._updates = updates
+
+        for species_id, S in updates.items():
+            if self.get_entity('species', species_id) is None:
+                continue  # FIXME: we should log an error
+            for item in S:
+                actor = self.get_entity('metadata', item['actor_id'])
+                if actor is None:
+                    continue  # FIXME: we should log an error
+                if actor.type not in ('ship', 'planet'):
+                    continue
+                if actor.owner_id != species_id:
+                    continue
+                self._updates.append(item)
 
     def export_data(self):
         return {
@@ -156,13 +168,15 @@ class GameState:
             components.MovementComponent(),
         ])
         Entity.register_manager(self.manager)
+        self.load_data()
 
         self.new = {}
 
+    def load_data(self):
+        self.manager.import_data(self.old, self.updates)
+
     def generate(self):
         self.new_headers()
-
-        self.manager.import_data(self.old, self.updates)
         self.manager.process()
         self.new.update(self.manager.export_data())
 
diff --git a/universe/systems.py b/universe/systems.py
index dba9577..7383d01 100644
--- a/universe/systems.py
+++ b/universe/systems.py
@@ -10,11 +10,40 @@ class UpdateSystem:
         for order in manager.get_entities('orders').values():
             queues[order.actor_id][order.seq] = order
 
-        for action in manager._updates:
-            order = manager.register_entity(action)
-            if order.seq in queues[order.actor_id]:
-                manager.unregister_entity(queues[order.actor_id][order.seq])
-            queues[order.actor_id][order.seq] = order
+        for data in manager._updates:
+            action = data.pop('action')
+            if action == 'create':
+                if data['actor_id'] in queues and data['seq'] in queues[data['actor_id']]:
+                    continue
+                order = manager.register_entity(data)
+                queues[order.actor_id][order.seq] = order
+            elif action == 'reorder':
+                if data['actor_id'] not in queues:
+                    continue
+                if data['seq1'] not in queues[data['actor_id']]:
+                    continue
+                if data['seq2'] not in queues[data['actor_id']]:
+                    continue
+                order1 = queues[data['actor_id']].pop(data['seq1'], None)
+                order2 = queues[data['actor_id']].pop(data['seq2'], None)
+                order1.seq, order2.seq = order2.seq, order1.seq
+                queues[order1.actor_id][order1.seq] = order1
+                queues[order2.actor_id][order2.seq] = order2
+            elif action == 'update':
+                if data['actor_id'] not in queues:
+                    continue
+                if data['seq'] not in queues[data['actor_id']]:
+                    continue
+                order = queues[data['actor_id']][data['seq']]
+                for k, v in data.items():
+                    setattr(order, k, v)
+            elif action == 'delete':
+                if data['actor_id'] not in queues:
+                    continue
+                if data['seq'] not in queues[data['actor_id']]:
+                    continue
+                manager.unregister_entity(queues[data['actor_id']][data['seq']])
+                del queues[data['actor_id']][data['seq']]
 
 
 class MovementSystem:
@@ -29,10 +58,7 @@ class MovementSystem:
             x_t, y_t = move.x_t, move.y_t
 
         # Aim for the midpoint of the 1-light-year sector the goal is in.
-        try:
-            dx, dy = Decimal(x_t).to_integral_value() - move.actor.x, Decimal(y_t).to_integral_value() - move.actor.y
-        except Exception:
-            raise Exception(repr(move.actor.serialize()))
+        dx, dy = Decimal(x_t).to_integral_value() - move.actor.x, Decimal(y_t).to_integral_value() - move.actor.y
 
         D = Decimal(dx ** 2 + dy ** 2).sqrt()
         if D.to_integral_value() <= speed:


Test patch:
diff --git a/tests/test_engine.py b/tests/test_engine.py
index b5432f1..24e7dd9 100644
--- a/tests/test_engine.py
+++ b/tests/test_engine.py
@@ -28,14 +28,22 @@ class EntityTestCase(unittest.TestCase):
             ]
         }
 
-        S = engine.GameState(state, {})
         with self.assertRaises(exceptions.ValidationError) as e:
-            results = S.generate()
+            S = engine.GameState(state, {})
 
         self.assertEqual(str(e.exception), "'owner_id' is not an existing entity.")
 
 
 class PersistenceTestCase(unittest.TestCase):
+    def test_empty_universe(self):
+        state = {'turn': 2500, 'width': 1000, 'entities': []}
+
+        S = engine.GameState(state, {})
+        results = S.generate()
+
+        self.assertEqual(results,
+                         {'turn': 2501, 'width': 1000, 'seq': 0, 'entities': []})
+
     def test_planet(self):
         state = {
             'turn': 2500, 'width': 1000, 'seq': 2,
@@ -167,20 +175,11 @@ class PersistenceTestCase(unittest.TestCase):
 
 
 class MovementTestCase(unittest.TestCase):
-    def test_empty_universe(self):
-        state = {'turn': 2500, 'width': 1000, 'entities': []}
-
-        S = engine.GameState(state, [])
-        results = S.generate()
-
-        self.assertEqual(results,
-                         {'turn': 2501, 'width': 1000, 'seq': 0, 'entities': []})
-
     def test_one_stationary_object(self):
         state = {'turn': 2500, 'width': 1000, 'seq': 1,
                  'entities': [{'pk': 0, 'type': 'ship', 'x': 456, 'y': 337}]}
 
-        S = engine.GameState(state, [])
+        S = engine.GameState(state, {})
         results = S.generate()
 
         self.assertEqual(results['turn'], 2501)
@@ -191,68 +190,16 @@ class MovementTestCase(unittest.TestCase):
         )
 
     def test_one_object_single_turn_move(self):
-        state = {'turn': 2500, 'width': 1000, 'seq': 1,
-                 'entities': [{'pk': 0, 'type': 'ship', 'x': 480, 'y': 235}]}
-        # actual speed is warp**2
-        updates = [{'type': 'movement_order', 'actor_id': 0, 'seq': 0, 'x_t': 422, 'y_t': 210, 'warp': 10}]
-
-        S = engine.GameState(state, updates)
-        results = S.generate()
-
-        self.assertEqual(results['turn'], 2501)
-        self.assertEqual(results['width'], 1000)
-        self.assertEqual(
-            results['entities'],
-            [{'pk': 0, 'type': 'ship', 'x': 422, 'x_prev': 480, 'y': 210, 'y_prev': 235}]
-        )
-
-    def test_multi_turn_single_move(self):
-        state = {'turn': 2500, 'width': 1000, 'seq': 1,
-                 'entities': [{'pk': 0, 'type': 'ship', 'x': 480, 'y': 235}]}
-        # actual speed is warp**2
-        updates = [{'type': 'movement_order', 'actor_id': 0, 'seq': 0, 'x_t': 168, 'y_t': 870, 'warp': 10}]
-
-        S = engine.GameState(state, updates)
-        results = S.generate()
-
-        self.assertEqual(results['turn'], 2501)
-        self.assertEqual(results['width'], 1000)
-        self.assertEqual(
-            results['entities'],
-            [{'pk': 0, 'type': 'ship', 'x': 436, 'x_prev': 480, 'y': 325, 'y_prev': 235},
-             {'pk': 1, 'type': 'movement_order', 'actor_id': 0, 'seq': 0, 'x_t': 168, 'y_t': 870, 'warp': 10}]
-        )
-
-    def test_previously_queued_move(self):
-        state = {
-            'turn': 2500, 'width': 1000, 'seq': 2,
-            'entities': [
-                {'pk': 0, 'type': 'ship', 'x': 480, 'y': 235},
-                {'pk': 1, 'type': 'movement_order', 'actor_id': 0, 'seq': 0, 'x_t': 422, 'y_t': 210, 'warp': 10},
-            ]
-        }
-
-        S = engine.GameState(state, [])
-        results = S.generate()
-
-        self.assertEqual(results['turn'], 2501)
-        self.assertEqual(results['width'], 1000)
-        self.assertEqual(
-            results['entities'],
-            [{'pk': 0, 'type': 'ship', 'x': 422, 'x_prev': 480, 'y': 210, 'y_prev': 235}]
-        )
-
-    def test_replace_queued_move(self):
         state = {
-            'turn': 2500, 'width': 1000, 'seq': 2,
+            'turn': 2500, 'width': 1000, 'seq': 1,
             'entities': [
                 {'pk': 0, 'type': 'ship', 'x': 480, 'y': 235},
-                {'pk': 1, 'type': 'movement_order', 'actor_id': 0, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8},
+                {'pk': 1, 'type': 'movement_order', 'actor_id': 0, 'seq': 0, 'x_t': 422, 'y_t': 210, 'warp': 10}
             ]
         }
-        updates = [{'type': 'movement_order', 'actor_id': 0, 'seq': 0, 'x_t': 422, 'y_t': 210, 'warp': 10}]
+        # actual speed is warp**2
 
-        S = engine.GameState(state, updates)
+        S = engine.GameState(state, {})
         results = S.generate()
 
         self.assertEqual(results['turn'], 2501)
@@ -262,25 +209,25 @@ class MovementTestCase(unittest.TestCase):
             [{'pk': 0, 'type': 'ship', 'x': 422, 'x_prev': 480, 'y': 210, 'y_prev': 235}]
         )
 
-    def test_add_to_queued_moves(self):
+    def test_multi_turn_single_move(self):
         state = {
-            'turn': 2500, 'width': 1000, 'seq': 2,
+            'turn': 2500, 'width': 1000, 'seq': 1,
             'entities': [
                 {'pk': 0, 'type': 'ship', 'x': 480, 'y': 235},
-                {'pk': 1, 'type': 'movement_order', 'actor_id': 0, 'seq': 0, 'x_t': 422, 'y_t': 210, 'warp': 10},
+                {'pk': 1, 'type': 'movement_order', 'actor_id': 0, 'seq': 0, 'x_t': 168, 'y_t': 870, 'warp': 10}
             ]
         }
-        updates = [{'type': 'movement_order', 'actor_id': 0, 'seq': 1, 'x_t': 637, 'y_t': 786, 'warp': 8}]
+        # actual speed is warp**2
 
-        S = engine.GameState(state, updates)
+        S = engine.GameState(state, {})
         results = S.generate()
 
         self.assertEqual(results['turn'], 2501)
         self.assertEqual(results['width'], 1000)
         self.assertEqual(
             results['entities'],
-            [{'pk': 0, 'type': 'ship', 'x': 422, 'x_prev': 480, 'y': 210, 'y_prev': 235},
-             {'pk': 2, 'type': 'movement_order', 'actor_id': 0, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8}]
+            [{'pk': 0, 'type': 'ship', 'x': 436, 'x_prev': 480, 'y': 325, 'y_prev': 235},
+             {'pk': 1, 'type': 'movement_order', 'actor_id': 0, 'seq': 0, 'x_t': 168, 'y_t': 870, 'warp': 10}]
         )
 
     def test_slow_motion(self):
@@ -312,7 +259,7 @@ class MovementTestCase(unittest.TestCase):
             ]
         }
 
-        S = engine.GameState(state, [])
+        S = engine.GameState(state, {})
         results = S.generate()
 
         self.assertEqual(results['turn'], 2501)
@@ -334,7 +281,7 @@ class MovementTestCase(unittest.TestCase):
             ]
         }
 
-        S = engine.GameState(state, [])
+        S = engine.GameState(state, {})
         results = S.generate()
 
         self.assertEqual(results['turn'], 2501)
@@ -402,7 +349,7 @@ class MovementTestCase(unittest.TestCase):
             ]
         }
 
-        S = engine.GameState(state, [])
+        S = engine.GameState(state, {})
         results = S.generate()
 
         self.assertEqual(results['turn'], 2501)
@@ -424,7 +371,7 @@ class MovementTestCase(unittest.TestCase):
             ]
         }
 
-        S = engine.GameState(state, [])
+        S = engine.GameState(state, {})
         results = S.generate()
 
         self.assertEqual(results['turn'], 2501)
@@ -448,7 +395,7 @@ class MovementTestCase(unittest.TestCase):
             ]
         }
 
-        S = engine.GameState(state, [])
+        S = engine.GameState(state, {})
         results = S.generate()
 
         self.assertEqual(results['turn'], 2501)
diff --git a/tests/test_systems.py b/tests/test_systems.py
index 052b19b..fc098d3 100644
--- a/tests/test_systems.py
+++ b/tests/test_systems.py
@@ -1,10 +1,445 @@
 import unittest
 
-from universe import engine
-from universe import systems
+from universe import engine, systems
 
 
-class PopulationTestCase(unittest.TestCase):
+class UpdateTestCase(unittest.TestCase):
+    def test_create(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 2,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 1, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+            ]
+        }
+        updates = {
+            0: [
+                {'action': 'create', 'type': 'movement_order', 'actor_id': 1, 'seq': 0,
+                 'x_t': 637, 'y_t': 786, 'warp': 8}
+            ],
+        }
+
+        S = engine.GameState(state, updates)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 1)
+        self.assertEqual(
+            orders[2].serialize(),
+            {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0,
+             'x_t': 637, 'y_t': 786, 'warp': 8}
+        )
+
+    def test_create_with_existing(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 3,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 1, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+                {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8}
+            ]
+        }
+        updates = {
+            0: [
+                {'action': 'create', 'type': 'movement_order', 'actor_id': 1, 'seq': 1,
+                 'x_t': 422, 'y_t': 210, 'warp': 10}
+            ],
+        }
+
+        S = engine.GameState(state, updates)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 2)
+        self.assertEqual(
+            orders[3].serialize(),
+            {'pk': 3, 'type': 'movement_order', 'actor_id': 1, 'seq': 1,
+             'x_t': 422, 'y_t': 210, 'warp': 10}
+        )
+
+    def test_create_with_conflict(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 3,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 1, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+                {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8}
+            ]
+        }
+        updates = {
+            0: [
+                {'action': 'create', 'type': 'movement_order', 'actor_id': 1, 'seq': 0,
+                 'x_t': 422, 'y_t': 210, 'warp': 10}
+            ],
+        }
+
+        S = engine.GameState(state, updates)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 1)
+        self.assertEqual(
+            orders[2].serialize(),
+            {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0,
+             'x_t': 637, 'y_t': 786, 'warp': 8}
+        )
+
+    def test_reorder(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 4,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 1, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+                {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8},
+                {'pk': 3, 'type': 'movement_order', 'actor_id': 1, 'seq': 1, 'x_t': 422, 'y_t': 210, 'warp': 10}
+            ]
+        }
+        updates = {
+            0: [
+                {'action': 'reorder', 'actor_id': 1, 'seq1': 0, 'seq2': 1}
+            ],
+        }
+
+        S = engine.GameState(state, updates)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 2)
+        self.assertEqual(
+            orders[2].serialize(),
+            {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 1, 'x_t': 637, 'y_t': 786, 'warp': 8}
+        )
+        self.assertEqual(
+            orders[3].serialize(),
+            {'pk': 3, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 422, 'y_t': 210, 'warp': 10}
+        )
+
+    def test_reorder_does_not_exist(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 4,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 1, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+                {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8},
+                {'pk': 3, 'type': 'movement_order', 'actor_id': 1, 'seq': 1, 'x_t': 422, 'y_t': 210, 'warp': 10}
+            ]
+        }
+        updates = {
+            0: [
+                {'action': 'reorder', 'actor_id': 1, 'seq1': 0, 'seq2': 2}
+            ],
+        }
+
+        S = engine.GameState(state, updates)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 2)
+        self.assertEqual(
+            orders[2].serialize(),
+            {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8}
+        )
+        self.assertEqual(
+            orders[3].serialize(),
+            {'pk': 3, 'type': 'movement_order', 'actor_id': 1, 'seq': 1, 'x_t': 422, 'y_t': 210, 'warp': 10}
+        )
+
+    def test_update(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 3,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 1, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+                {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8},
+            ]
+        }
+        updates = {
+            0: [
+                {'action': 'update', 'type': 'movement_order', 'actor_id': 1, 'seq': 0,
+                 'x_t': 422, 'y_t': 210, 'warp': 10}
+            ]
+        }
+
+        S = engine.GameState(state, updates)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 1)
+        self.assertEqual(
+            orders[2].serialize(),
+            {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 422, 'y_t': 210, 'warp': 10}
+        )
+
+    def test_update_does_not_exist(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 3,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 1, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+                {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8},
+            ]
+        }
+        updates = {
+            0: [
+                {'action': 'update', 'type': 'movement_order', 'actor_id': 1, 'seq': 1,
+                 'x_t': 422, 'y_t': 210, 'warp': 10}
+            ]
+        }
+
+        S = engine.GameState(state, updates)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 1)
+        self.assertEqual(
+            orders[2].serialize(),
+            {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8}
+        )
+
+    def test_delete(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 3,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 1, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+                {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8},
+            ]
+        }
+        updates = {
+            0: [
+                {'action': 'delete', 'actor_id': 1, 'seq': 0}
+            ]
+        }
+
+        S = engine.GameState(state, updates)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 0)
+
+    def test_delete_does_not_exist(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 3,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 1, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+                {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8},
+            ]
+        }
+        updates = {
+            0: [
+                {'action': 'delete', 'actor_id': 1, 'seq': 1}
+            ]
+        }
+
+        S = engine.GameState(state, updates)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 1)
+        self.assertEqual(
+            orders[2].serialize(),
+            {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8}
+        )
+
+    def test_issued_by_non_existent_species(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 3,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 1, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+                {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8},
+            ]
+        }
+        updates = {
+            1: [
+                {'action': 'delete', 'actor_id': 1, 'seq': 0}
+            ]
+        }
+
+        S = engine.GameState(state, updates)
+        self.assertEqual(len(S.manager._updates), 0)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 1)
+        self.assertEqual(
+            orders[2].serialize(),
+            {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8}
+        )
+
+    def test_issued_for_non_existent_entity(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 3,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 1, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+                {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8},
+            ]
+        }
+        updates = {
+            0: [
+                {'action': 'delete', 'actor_id': 3, 'seq': 0}
+            ]
+        }
+
+        S = engine.GameState(state, updates)
+        self.assertEqual(len(S.manager._updates), 0)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 1)
+        self.assertEqual(
+            orders[2].serialize(),
+            {'pk': 2, 'type': 'movement_order', 'actor_id': 1, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8}
+        )
+
+    def test_issued_by_wrong_species(self):
+        state = {
+            'turn': 2500, 'width': 1000, 'seq': 4,
+            'entities': [
+                {
+                    'pk': 0,
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {
+                    'pk': 1,
+                    'type': 'species',
+                    'name': 'Romulan',
+                    'plural_name': 'Romulans',
+                    'growth_rate': 15,
+                    'gravity_immune': True,
+                    'temperature_immune': True,
+                    'radiation_immune': True,
+                },
+                {'pk': 2, 'type': 'ship', 'x': 480, 'y': 235, 'owner_id': 0},
+                {'pk': 3, 'type': 'movement_order', 'actor_id': 2, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8},
+            ]
+        }
+        updates = {
+            1: [
+                {'action': 'delete', 'actor_id': 2, 'seq': 0}
+            ]
+        }
+
+        S = engine.GameState(state, updates)
+        self.assertEqual(len(S.manager._updates), 0)
+        systems.UpdateSystem().process(S.manager)
+
+        orders = S.manager.get_entities('orders')
+        self.assertEqual(len(orders), 1)
+        self.assertEqual(
+            orders[3].serialize(),
+            {'pk': 3, 'type': 'movement_order', 'actor_id': 2, 'seq': 0, 'x_t': 637, 'y_t': 786, 'warp': 8}
+        )
+
+
+class PopulationGrowthTestCase(unittest.TestCase):
     def test_habitability_growth(self):
         state = {
             'turn': 2500,