learning-unlimited/ESP-Website — comment on commit 5e40a31a7502caef1c20235f2eaf94e1e33c368d
File: esp/esp/qsd/seltests.py line 40
By aseering:

Hm... I see what sounds like a bunch of issues going on here...

The DataTree/GetNode problem is caching; I'm almost (but not quite) 100% certain of it. Maybe try switching to dummy:// caching? Most likely it's the cache not being flushed between tests, or after initialization; Django does flush the database between tests, so GetNode will return a DataTree node that it thinks exists but that doesn't exist in reality.

If switching to dummy:// fixes the problem, take a look at esp.tests.util.CacheFlushTestCase, and at its use in various test suites.

As for something clearing it, yep!, that's Django. See the docs on [TransactionTestCase](http://readthedocs.org/docs/django/en/latest/topics/testing.html#django.test.TransactionTestCase). Note that TRUNCATE is a SQL command; equivalent to DELETE FROM [table]; (with no predicate so all rows are deleted), except it's implemented approximately by simply rm'ing all of the data files associated with the table, so it's very fast.

It also doesn't send the signals that our caching system listens for to indicate that all the rows in the table were modified, so the cache isn't updated to reflect the new (lack of) table contents.

That documentation indicates that TransactionTestCase truncates the table, then reloads all initial data. This is really only sort of true. What it does is that it reloads data *[fixtures](https://docs.djangoproject.com/en/dev/howto/initial-data/)*. However, our initial data is generated by Python code that listens (if I recall correctly) for the signal that's fired when syncdb finishes constructing the initial database. The tests don't fire a similar signal, so our initial data tends to go away.

Note that South [has, and has hacked a solution to](http://johnpaulett.com/2011/03/17/django-13-south/), this same problem, because it stores migration state in the database. Part of why South and tests tend to not get along if things aren't lined up just right.

Also, be careful of playing with psql while tests are running; the output may not be what you expect. See the same docs above on [TransactionTestCase](http://readthedocs.org/docs/django/en/latest/topics/testing.html#django.test.TransactionTestCase), and (more interestingly) on its more-commonly-used sibling [TestCase](https://docs.djangoproject.com/en/dev/topics/testing/#django.test.TestCase). Now you're getting into the realm of transactions: It is by definition impossible to see the state inside a running transaction from anywhere other than the code that's currently executing the transaction.

A transaction is in some sense a private fork of the database right at the point in time when the transaction was opened, that's merged on COMMIT. So if I open up a psql, until I commit (which it may do automatically in an interaction session, I forget the default, but you can force this with an explicit BEGIN statement), I will forever after see the database as it was then, at the point the transaction started. Doesn't matter if someone has gone through and deleted or rewritten every row; that's not how the database looked when I started my transaction so I don't see it.

The flip side applies as well: If someone else starts a transaction, they could rewrite every row in the database; but no other connection can see any of those changes until they commit. Just like I can't see that branch on your laptop until you push it.

Now, TestCase occurs in a transaction; but more than that, it occurs in a transaction that never commits. At the end of the transaction, the whole thing is rolled back. It's therefore impossible to ever see what's going on inside a TestCase from psql, at least without magic that breaks the transaction abstraction barrier (which I'm not sure even exists with regular PostgreSQL?).

(Why does TestCase occur in a transaction? Because it's much faster. Why is it faster? Well, it's expensive to deal with all the locking and concurrency stuff that's needed to push a bunch of changes to a space that many users could be accessing all at once. It's also expensive to guarantee that a set of changes are completely written to disk and not just hanging out in a memory buffer; something else that's guaranteed when a transaction is committed and not before. Skipping both of those steps makes quite a big difference.)

(Also, fun fact: Transactions in PostgreSQL are implemented by a combination of keeping copies of old data around, and taking out locks when data is updated so that it's hard to have two transactions that can't see each other but are trying to write to the same rows. Solving the general problem of merging changes is too hard... So, if you open up a Django or psql shell against the live database and run the right commands and then walk off to class leaving the shell open, you can in fact wedge the entire website while the database waits on you to close the transaction. This has happened at least once or twice... Good reason to use dev servers for everything :-) )

Anyway, that's enough database-ese for me for tonight. Let me know if that helps solve any of the actual problems here.