def test_contributors_ordering(self): contrib_1 = user(save=True) contrib_2 = user(save=True) contrib_3 = user(save=True) rev_1 = revision(creator=contrib_1, save=True) rev_2 = revision(creator=contrib_2, document=rev_1.document, # live in the future to make sure we handle the lack # of microseconds support in Django 1.7 nicely created=rev_1.created + timedelta(seconds=1), save=True) ok_(rev_1.created < rev_2.created) job = DocumentContributorsJob() job_user_pks = [contributor['id'] for contributor in job.fetch(rev_1.document.pk)] # the user with the more recent revision first recent_contributors_pks = [contrib_2.pk, contrib_1.pk] eq_(job_user_pks, recent_contributors_pks) # a third revision should now show up again and # the job's cache is invalidated rev_3 = revision(creator=contrib_3, document=rev_1.document, created=rev_2.created + timedelta(seconds=1), save=True) ok_(rev_2.created < rev_3.created) job_user_pks = [contributor['id'] for contributor in job.fetch(rev_1.document.pk)] # The new revision shows up eq_(job_user_pks, [contrib_3.pk] + recent_contributors_pks)
def test_contributors_ordering(self): contrib_1 = user(save=True) contrib_2 = user(save=True) contrib_3 = user(save=True) rev_1 = revision(creator=contrib_1, save=True) rev_2 = revision( creator=contrib_2, document=rev_1.document, # live in the future to make sure we handle the lack # of microseconds support in Django 1.7 nicely created=rev_1.created + timedelta(seconds=1), save=True) ok_(rev_1.created < rev_2.created) job = DocumentContributorsJob() job_user_pks = [ contributor['id'] for contributor in job.fetch(rev_1.document.pk) ] # the user with the more recent revision first recent_contributors_pks = [contrib_2.pk, contrib_1.pk] eq_(job_user_pks, recent_contributors_pks) # a third revision should now show up again and # the job's cache is invalidated rev_3 = revision(creator=contrib_3, document=rev_1.document, created=rev_2.created + timedelta(seconds=1), save=True) ok_(rev_2.created < rev_3.created) job_user_pks = [ contributor['id'] for contributor in job.fetch(rev_1.document.pk) ] # The new revision shows up eq_(job_user_pks, [contrib_3.pk] + recent_contributors_pks)
def setUp(self): username = "******" password = "******" email = "*****@*****.**" self.user = user(username=username, email=email, password=password, save=True) self.client.login(username=username, password=password) # Give self.user (tester23) keys permissions add_perm = Permission.objects.get(codename="add_key") del_perm = Permission.objects.get(codename="delete_key") self.user.user_permissions.add(add_perm) self.user.user_permissions.add(del_perm) self._cache_bust_user_perms() username2 = "someone" password2 = "somepass" email2 = "*****@*****.**" self.user2 = user(username=username2, email=email2, password=password2, save=True) self.key1 = Key(user=self.user, description="Test Key 1") self.key1.save() self.key2 = Key(user=self.user, description="Test Key 2") self.key2.save() self.key3 = Key(user=self.user2, description="Test Key 3") self.key3.save()
def setUp(self): username = '******' password = '******' email = '*****@*****.**' self.user = user(username=username, email=email, password=password, save=True) self.client.login(username=username, password=password) # Give self.user (tester23) keys permissions add_perm = Permission.objects.get(codename='add_key') del_perm = Permission.objects.get(codename='delete_key') self.user.user_permissions.add(add_perm) self.user.user_permissions.add(del_perm) self._cache_bust_user_perms() username2 = 'someone' password2 = 'somepass' email2 = '*****@*****.**' self.user2 = user(username=username2, email=email2, password=password2, save=True) self.key1 = Key(user=self.user, description='Test Key 1') self.key1.save() self.key2 = Key(user=self.user, description='Test Key 2') self.key2.save() self.key3 = Key(user=self.user2, description='Test Key 3') self.key3.save()
def test_stripe_payment_succeeded_sends_ga_tracking( mock1, track_event_mock_signals, mock2, client, settings ): settings.GOOGLE_ANALYTICS_ACCOUNT = "UA-XXXX-1" settings.GOOGLE_ANALYTICS_TRACKING_RAISE_ERRORS = True mock1.return_value = SimpleNamespace( type="invoice.payment_succeeded", data=SimpleNamespace( object=SimpleNamespace( customer="cus_mock_testuser", created=1583842724, invoice_pdf="https://developer.mozilla.org/mock-invoice-pdf-url", ) ), ) user( save=True, username="******", email="*****@*****.**", stripe_customer_id="cus_mock_testuser", ) response = client.post( reverse("api.v1.stripe_hooks"), content_type="application/json", data={}, ) assert response.status_code == 200 track_event_mock_signals.assert_called_with( CATEGORY_MONTHLY_PAYMENTS, ACTION_SUBSCRIPTION_CREATED, f"{settings.CONTRIBUTION_AMOUNT_USD:.2f}", )
def test_contributors_inactive_or_banned(self): contrib_1 = user(save=True) contrib_2 = user(is_active=False, save=True) contrib_3 = user(save=True) contrib_3_ban = contrib_3.bans.create(by=contrib_1, reason='because reasons') revision_2 = revision(creator=contrib_1, save=True) revision(creator=contrib_2, document=revision_2.document, save=True) revision(creator=contrib_3, document=revision_2.document, save=True) job = DocumentContributorsJob() # setting this to true to be able to test this job.fetch_on_miss = True contributors = job.get(revision_2.document.pk) contrib_ids = [contrib['id'] for contrib in contributors] self.assertIn(contrib_1.id, contrib_ids) self.assertNotIn(contrib_2.id, contrib_ids) self.assertNotIn(contrib_3.id, contrib_ids) # delete the ban again contrib_3_ban.delete() # reloading the document from db to prevent cache doc = Document.objects.get(pk=revision_2.document.pk) # user not in contributors because job invalidation hasn't happened contrib_ids = [contrib['id'] for contrib in job.get(revision_2.document.pk)] self.assertNotIn(contrib_3.id, contrib_ids) # trigger the invalidation manually by saving the document doc.save() doc = Document.objects.get(pk=revision_2.document.pk) contrib_ids = [contrib['id'] for contrib in job.get(revision_2.document.pk)] self.assertIn(contrib_3.id, contrib_ids)
def test_user_exists(self): out = StringIO() user(username='******', save=True) call_command('ihavepower', 'fordprefect', stdout=out) ford = self.user_model.objects.get(username='******') assert ford.is_staff is True assert ford.is_superuser is True
def test_contributors(self): contrib = user(save=True) rev = revision(creator=contrib, save=True) job = DocumentContributorsJob() # setting this to true to be able to test this job.fetch_on_miss = True eq_(contrib.pk, job.get(rev.document.pk)[0]['id'])
def setUp(self): username = "******" password = "******" email = "*****@*****.**" self.user = user(username=username, email=email, password=password, save=True) self.client.login(username=username, password=password)
def user_auth_key(): u = user(username="******", email="*****@*****.**", save=True) key = Key(user=u) secret = key.generate_secret() key.save() return Object(user=u, key=key, secret=secret,)
def test_key_auth_decorator(self): u = user(username="******", email="*****@*****.**", save=True) key = Key(user=u) secret = key.generate_secret() key.save() @accepts_auth_key def fake_view(request, foo, bar): return (foo, bar) cases = ((key.key, secret, True), (key.key, 'FAKE', False), ('FAKE', secret, False), ('FAKE', 'FAKE', False)) for k, s, success in cases: request = HttpRequest() request.user = AnonymousUser() auth = '%s:%s' % (k, s) b64_auth = base64.encodestring(auth) request.META['HTTP_AUTHORIZATION'] = 'Basic %s' % b64_auth foo, bar = fake_view(request, 'foo', 'bar') eq_('foo', foo) eq_('bar', bar) if not success: ok_(not request.user.is_authenticated()) else: ok_(request.user.is_authenticated()) ok_(request.user == u) ok_(request.authkey) ok_(request.authkey == key)
def test_filtered(self): """Make sure watches cull properly by additional filters.""" # A watch with just the filter we're searching for: registered_user = user(email='*****@*****.**', save=True) exact_watch = watch(event_type=TYPE, user=registered_user, save=True) watch_filter(watch=exact_watch, name='color', value=1).save() # A watch with extra filters: extra_watch = watch(event_type=TYPE, email='*****@*****.**', save=True) watch_filter(watch=extra_watch, name='color', value=1).save() watch_filter(watch=extra_watch, name='flavor', value=2).save() # A watch with no row for the filter we're searching on: watch(event_type=TYPE, email='*****@*****.**').save() # A watch with a mismatching filter--shouldn't be found mismatch_watch = watch(event_type=TYPE, email='*****@*****.**', save=True) watch_filter(watch=mismatch_watch, name='color', value=3).save() self._emails_eq(['*****@*****.**', '*****@*****.**', '*****@*****.**'], FilteredEvent(), color=1) # Search on multiple filters to test joining the filters table twice. # We provide values that match for both filters, as [email protected] # suffices to test exclusion. self._emails_eq(['*****@*****.**', '*****@*****.**', '*****@*****.**'], FilteredEvent(), color=1, flavor=2)
def test_simple(self): """Test whether a watch scoped only by event type fires for both anonymous and registered users.""" registered_user = user(email='*****@*****.**', save=True) watch(event_type=TYPE, user=registered_user).save() watch(event_type=TYPE, email='*****@*****.**').save() watch(event_type='something else', email='*****@*****.**').save() self._emails_eq(['*****@*****.**', '*****@*****.**'], SimpleEvent())
def setUp(self): username = '******' password = '******' email = '*****@*****.**' self.user = user(username=username, email=email, password=password, save=True) self.client.login(username=username, password=password)
def watch(save=False, **kwargs): # TODO: better defaults, when there are events available. defaults = {'user': kwargs.get('user') or user(), 'is_active': True} defaults.update(kwargs) w = Watch.objects.create(**defaults) if save: w.save() return w
def test_duplicates(self): """Don't return duplicate email addresses.""" watch(event_type=TYPE, user=user(email='*****@*****.**', save=True), save=True) watch(event_type=TYPE, email='*****@*****.**').save() watch(event_type=TYPE, email='*****@*****.**').save() eq_(3, Watch.objects.all().count()) # We created what we meant to. self._emails_eq(['*****@*****.**'], SimpleEvent())
def setUp(self): username = '******' password = '******' self.email = '*****@*****.**' self.user = user(username=username, email=self.email, password=password, save=True) self.client.login(username=username, password=password) self.user2 = user(username='******', email='*****@*****.**', save=True) self.key1 = Key(user=self.user, description='Test Key 1') self.key1.save() self.key2 = Key(user=self.user, description='Test Key 2') self.key2.save() self.key3 = Key(user=self.user2, description='Test Key 3') self.key3.save()
def test_merging(self): """Test that duplicate emails across multiple events get merged.""" # Remember to keep the emails in order when writing these test cases. # [Ed: But doesn't the SQL query have an ORDER BY?] watch(event_type=TYPE, email='*****@*****.**').save() watch(event_type=TYPE, email='*****@*****.**').save() registered_user = user(email='*****@*****.**', save=True) watch(event_type=ANOTHER_TYPE, user=registered_user).save() self._emails_eq(['*****@*****.**', '*****@*****.**'], EventUnion(SimpleEvent(), AnotherEvent()))
def user_auth_key(): u = user(username="******", email="*****@*****.**", save=True) key = Key(user=u) secret = key.generate_secret() key.save() return Object( user=u, key=key, secret=secret, )
def test_mixed(self): """Make sure having mixed watches claims right ones.""" # Watch before registering. watch(email='*****@*****.**', save=True) watch(email='*****@*****.**', save=True) # Register nobody. user(email='*****@*****.**', save=True) # Then register somebody and watch something after registering. u = user(email='*****@*****.**', save=True) watch(user=u, save=True) claim_watches(u) # Original anonymous watch is claimed. assert not Watch.objects.filter(email='*****@*****.**').exists() eq_(2, Watch.objects.filter(email=None).count()) # No other watches are affected. assert Watch.objects.filter(email='*****@*****.**').exists()
def test_duplicates_case_insensitive(self): """De-duping should compare case-insensitively.""" watch(event_type=TYPE, user=user(email='*****@*****.**', save=True), save=True) watch(event_type=TYPE, email='*****@*****.**').save() watch(event_type=TYPE, email='*****@*****.**').save() eq_(3, Watch.objects.all().count()) # We created what we meant to. addresses = [u.email for u, w in SimpleEvent()._users_watching_by_filter()] eq_(1, len(addresses)) eq_('*****@*****.**', addresses[0].lower())
def test_exclude(self): """Assert the `exclude` arg to fire() excludes the given user.""" SimpleEvent.notify('*****@*****.**').activate().save() registered_user = user(email='*****@*****.**', save=True) SimpleEvent.notify(registered_user).activate().save() SimpleEvent().fire(exclude=registered_user) eq_(1, len(mail.outbox)) first_mail = mail.outbox[0] eq_(['*****@*****.**'], first_mail.to) eq_('Subject!', first_mail.subject)
def test_secret_generation(self): """Generated secret should be saved as a hash and pass a check""" u = user(username="******", email="*****@*****.**", save=True) key = Key(user=u) secret = key.generate_secret() key.save() ok_(key.key) ok_(key.hashed_secret) ok_(len(key.hashed_secret) > 0) ok_(len(secret) > 0) ok_(secret != key.hashed_secret) ok_(not key.check_secret("I AM A FAKE")) ok_(key.check_secret(secret))
def test_content_type(self): """Make sure watches filter properly by content type.""" watch_type = ContentType.objects.get_for_model(Watch) content_type_type = ContentType.objects.get_for_model(ContentType) registered_user = user(email='*****@*****.**', save=True) watch(event_type=TYPE, content_type=content_type_type, user=registered_user).save() watch(event_type=TYPE, content_type=content_type_type, email='*****@*****.**').save() watch(event_type=TYPE, content_type=watch_type, email='*****@*****.**').save() self._emails_eq(['*****@*****.**', '*****@*****.**'], ContentTypeEvent())
def test_contributors_inactive_or_banned(self): contrib_1 = user(save=True) contrib_2 = user(is_active=False, save=True) contrib_3 = user(save=True) contrib_3_ban = contrib_3.bans.create(by=contrib_1, reason='because reasons') revision_2 = revision(creator=contrib_1, save=True) revision(creator=contrib_2, document=revision_2.document, save=True) revision(creator=contrib_3, document=revision_2.document, save=True) job = DocumentContributorsJob() # setting this to true to be able to test this job.fetch_on_miss = True contributors = job.get(revision_2.document.pk) contrib_ids = [contrib['id'] for contrib in contributors] self.assertIn(contrib_1.id, contrib_ids) self.assertNotIn(contrib_2.id, contrib_ids) self.assertNotIn(contrib_3.id, contrib_ids) # delete the ban again contrib_3_ban.delete() # reloading the document from db to prevent cache doc = Document.objects.get(pk=revision_2.document.pk) # user not in contributors because job invalidation hasn't happened contrib_ids = [ contrib['id'] for contrib in job.get(revision_2.document.pk) ] self.assertNotIn(contrib_3.id, contrib_ids) # trigger the invalidation manually by saving the document doc.save() doc = Document.objects.get(pk=revision_2.document.pk) contrib_ids = [ contrib['id'] for contrib in job.get(revision_2.document.pk) ] self.assertIn(contrib_3.id, contrib_ids)
def test_duplicates_case_insensitive(self): """De-duping should compare case-insensitively.""" watch(event_type=TYPE, user=user(email='*****@*****.**', save=True), save=True) watch(event_type=TYPE, email='*****@*****.**').save() watch(event_type=TYPE, email='*****@*****.**').save() eq_(3, Watch.objects.all().count()) # We created what we meant to. addresses = [ u.email for u, w in SimpleEvent()._users_watching_by_filter() ] eq_(1, len(addresses)) eq_('*****@*****.**', addresses[0].lower())
def test_stripe_subscription_canceled_sends_ga_tracking( mock1, track_event_mock_signals, client ): mock1.return_value = SimpleNamespace( type="customer.subscription.deleted", data=SimpleNamespace( object=SimpleNamespace(customer="cus_mock_testuser", id="sub_123456789") ), ) user( save=True, username="******", email="*****@*****.**", stripe_customer_id="cus_mock_testuser", ) response = client.post( reverse("api.v1.stripe_hooks"), content_type="application/json", data={}, ) assert response.status_code == 200 track_event_mock_signals.assert_called_with( CATEGORY_MONTHLY_PAYMENTS, ACTION_SUBSCRIPTION_CANCELED, "webhook" )
def test_key_auth_decorator(self): u = user(username="******", email="*****@*****.**", save=True) key = Key(user=u) secret = key.generate_secret() key.save() @accepts_auth_key def fake_view(request, foo, bar): return (foo, bar) cases = ((key.key, secret, True), (key.key, 'FAKE', False), ('FAKE', secret, False), ('FAKE', 'FAKE', False)) for k, s, success in cases: request = HttpRequest() request.user = AnonymousUser() auth = '%s:%s' % (k, s) b64_auth = base64.encodestring(auth) request.META['HTTP_AUTHORIZATION'] = 'Basic %s' % b64_auth foo, bar = fake_view(request, 'foo', 'bar') eq_('foo', foo) eq_('bar', bar) if not success: ok_(not request.user.is_authenticated()) else: ok_(request.user.is_authenticated()) ok_(request.user == u) ok_(request.authkey) ok_(request.authkey == key) # Test with incorrect auth header request = HttpRequest() request.user = AnonymousUser() request.META['HTTP_AUTHORIZATION'] = "Basic bad_auth_string" # Make a request to the view fake_view(request, 'foo', 'bar') # The user should not be authonticated and no server error should raise ok_(not request.user.is_authenticated())
def test_populated_community_stats(self): for i in range(self.contributors): if i % 2 == 0: locale = 'en-US' else: locale = 'pt-BR' test_user = user(save=True) doc = document(save=True, locale=locale) revision(save=True, creator=test_user, document=doc) update_community_stats() stats = self.cache.get('community_stats') self.assertIsNotNone(stats) self.assertIn('contributors', stats) self.assertIn('locales', stats) self.assertIsInstance(stats['contributors'], long) self.assertIsInstance(stats['locales'], long) self.assertEqual(stats['contributors'], self.contributors) self.assertEqual(stats['locales'], 2)
def test_populated_community_stats(self): for i in range(self.contributors): if i % 2 == 0: locale = "en-US" else: locale = "pt-BR" test_user = user(save=True) doc = document(save=True, locale=locale) revision(save=True, creator=test_user, document=doc) update_community_stats() stats = self.cache.get("community_stats") self.assertIsNotNone(stats) self.assertIn("contributors", stats) self.assertIn("locales", stats) self.assertIsInstance(stats["contributors"], long) self.assertIsInstance(stats["locales"], long) self.assertEqual(stats["contributors"], self.contributors) self.assertEqual(stats["locales"], 2)
def test_stripe_subscription_canceled(mock1, client): mock1.return_value = SimpleNamespace( type="customer.subscription.deleted", data=SimpleNamespace( object=SimpleNamespace(customer="cus_mock_testuser", id="sub_123456789") ), ) testuser = user( save=True, username="******", email="*****@*****.**", stripe_customer_id="cus_mock_testuser", ) UserSubscription.set_active(testuser, "sub_123456789") response = client.post( reverse("api.v1.stripe_hooks"), content_type="application/json", data={}, ) assert response.status_code == 200 (user_subscription,) = UserSubscription.objects.filter(user=testuser) assert user_subscription.canceled
def test_stripe_payment_succeeded_sends_invoice_mail( construct_stripe_event, retrieve_subscription, download_url ): construct_stripe_event.return_value = SimpleNamespace( type="invoice.payment_succeeded", data=SimpleNamespace( object=SimpleNamespace( number="test_invoice_001", total=700, customer="cus_mock_testuser", created=1583842724, invoice_pdf="https://developer.mozilla.org/mock-invoice-pdf-url", ) ), ) retrieve_subscription.return_value = { "next_payment_at": 1583842724, "brand": "MagicCard", } download_url.return_value = bytes("totally not a pdf", "utf-8") testuser = user( save=True, username="******", email="*****@*****.**", stripe_customer_id="cus_mock_testuser", ) response = Client().post( reverse("api.v1.stripe_hooks"), content_type="application/json", data={}, ) assert response.status_code == 200 assert len(mail.outbox) == 1 payment_email = mail.outbox[0] assert payment_email.to == [testuser.email] assert "Receipt" in payment_email.subject assert "Invoice number: test_invoice_001" in payment_email.body assert "You supported MDN with a $7.00 monthly subscription" in payment_email.body assert "manage monthly subscription" in payment_email.body
def test_registered_users_favored(self): """When removing duplicates, make sure registered users are kept in favor of anonymous ones having the same email address.""" def make_anonymous_watches(): for x in xrange(3): watch(event_type=TYPE, email='*****@*****.**').save() # Throw some anonymous watches in there in the hope that they would # come out on top if we didn't purposely favor registered users. # Suggestions on how to make this test more reliable are welcome. make_anonymous_watches() # File the registered watch: watch(event_type=TYPE, user=user(first_name='Jed', email='*****@*****.**', save=True)).save() # A few more anonymous watches in case the BTrees flop in the other # direction: make_anonymous_watches() users_and_watches = list(SimpleEvent()._users_watching_by_filter()) u, w = users_and_watches[0] eq_('Jed', u.first_name)
def _users_watching(self): return [(user(email='*****@*****.**'), watch()), (user(email='*****@*****.**'), watch())]
def test_none(self): """No anonymous watches to claim.""" u = user(email='*****@*****.**', save=True) claim_watches(u)
def test_unique_by_email(self): """Test the routine that sorts through users and watches having the same email addresses.""" # Test the best in a cluster coming first, in the middle, and last. # We mark the correct choices with secret='a'. users_and_watches = [ (user(email='hi'), watch(secret='a')), (user(email='hi'), watch()), (user(), watch(email='hi')), (user(), watch(email='mid')), (user(email='mid'), watch(secret='a')), (user(), watch(email='mid')), (user(), watch(email='lo')), (user(), watch(email='lo')), (user(email='lo'), watch(secret='a')), (user(), watch(email='none', secret='a')), (user(), watch(email='none')), (user(), watch(email='hi', secret='a')), (user(), watch(email='hi')), (user(), watch(email='hi'))] favorites = list(_unique_by_email(users_and_watches)) num_clusters = 5 eq_(num_clusters, len(favorites)) eq_(['a'] * num_clusters, [w.secret for u, w in favorites])
def test_permissions(self): """Ensure that the negative and positive permissions for adding attachments work.""" # Get the negative and positive permissions ct = ContentType.objects.get(app_label='attachments', model='attachment') p1 = Permission.objects.get(codename='disallow_add_attachment', content_type=ct) p2 = Permission.objects.get(codename='add_attachment', content_type=ct) # Create a group with the negative permission. g1, created = Group.objects.get_or_create(name='cannot_attach') g1.permissions = [p1] g1.save() # Create a group with the positive permission. g2, created = Group.objects.get_or_create(name='can_attach') g2.permissions = [p2] g2.save() # User with no explicit permission is allowed u2 = user(username='******', save=True) ok_(allow_add_attachment_by(u2)) # User in group with negative permission is disallowed u3 = user(username='******', save=True) u3.groups = [g1] u3.save() ok_(not allow_add_attachment_by(u3)) # Superusers can do anything, despite group perms u1 = user(username='******', is_superuser=True, save=True) u1.groups = [g1] u1.save() ok_(allow_add_attachment_by(u1)) # User with negative permission is disallowed u4 = user(username='******', save=True) u4.user_permissions.add(p1) u4.save() ok_(not allow_add_attachment_by(u4)) # User with positive permission overrides group u5 = user(username='******', save=True) u5.groups = [g1] u5.user_permissions.add(p2) u5.save() ok_(allow_add_attachment_by(u5)) # Group with positive permission takes priority u6 = user(username='******', save=True) u6.groups = [g1, g2] u6.save() ok_(allow_add_attachment_by(u6)) # positive permission takes priority, period. u7 = user(username='******', save=True) u7.user_permissions.add(p1) u7.user_permissions.add(p2) u7.save() ok_(allow_add_attachment_by(u7))
def test_unsaved_exclude(self): """Excluding an unsaved user should throw a ValueError.""" self.assertRaises(ValueError, SimpleEvent()._users_watching_by_filter, exclude=user())
def test_instance_registered(self): """Watch with a registered user.""" registered_user = user(email='*****@*****.**', save=True) self._test_user_or_email(registered_user)
def _users_watching(self): return [(user(email='*****@*****.**'), watch())]
def test_notify_idempotence(self): """Assure notify() returns an existing watch when possible.""" u = user(save=True) w = FilteredContentTypeEvent.notify(u, color=3, flavor=4) eq_(w.pk, FilteredContentTypeEvent.notify(u, color=3, flavor=4).pk) eq_(1, Watch.objects.all().count())