My Django webapps are multi-player strategy games that involve a player proceeding through a sequence of pages and submitting a ModelForm on each page. They are usually back-and-forth games, so Player 1 makes a decision while Player 2 sees a waiting screen, then vice versa, etc. The games can have a variable number of players.
I have implemented tests using Django's HTTP test client, where the tests are designed to be written from the perspective of an individual user, with conditional logic that branches whether the client plays as P1 or P2, and with the waiting screen abstracted away:
def play(self):
self.submit(views.Introduction)
self.submit(views.Question1, {'answer': random.choice([True,False])})
if self.player.id_in_group == 1:
self.submit(views.Send, {"sent_amount": 4})
else:
self.submit(views.SendBack, {'sent_back_amount': 8})
self.submit(views.Results)
self.submit(views.Question2, dict(feedback=4))
Then, to simulate N players, I instantiate N threads, each executing the code above.
This approach works on Postgres, but not with SQLite, which for external reasons is the engine we need to use for local development. I get: OperationalError: Database is locked
Is there some way to avoid the locks? Like a task queue, or sleeping until the SQlite DB is unlocked? Or somehow looping through the test clients in random order and executing a few lines from each at a given time? It's OK if we slow down the performance a bit on SQLite.
Note: I could write the code of multiple players inline so that they all execute in 1 thread, but (1) I find that code harder to read, (2) it is artificial because it hardcodes an exact order in which actions will be executed, and (3) it doesn't scale well when, say, we change a 10-player game to a 20-player game.