This is what a test using it looks like: +class TestAPIPartipication(TestCaseWithFactory): + + layer = DatabaseFunctionalLayer + + def test_participation_query_limit(self): + # A team with 2 members should only query once for all their + # attributes. + team = self.factory.makeTeam() + with person_logged_in(team.teamowner): + team.addMember(self.factory.makePerson(), team.teamowner) + team.addMember(self.factory.makePerson(), team.teamowner) + webservice = LaunchpadWebServiceCaller() + collector = QueryCollector() + collector.register() + self.addCleanup(collector.unregister) + url = "/~%s/participants" % team.name + logout() + response = webservice.get(url, headers={'User-Agent':'AnonNeedsThis'}) + self.assertEqual(response.status, 200, + "Got %d for url %r with response %r" % ( + response.status, url, response.body)) + self.assertThat(collector, HasQueryCount(LessThan(24))) and it generates this output: running: xvfb-run ./bin/test --subunit -t test_participation_query_limit| testr load ====================================================================== FAIL: lp.registry.tests.test_person.TestAPIPartipication.test_participation_query_limit ---------------------------------------------------------------------- Text attachment: traceback ------------ Traceback (most recent call last): _StringException: Text attachment: queries ------------ (2, 3, 'launchpad-main-master', 'SELECT OAuthConsumer.date_created, OAuthConsumer.disabled, OAuthConsumer.id, OAuthConsumer."key", OAuthConsumer.secret FROM OAuthConsumer WHERE OAuthConsumer."key" IS NULL') (4, 4, 'launchpad-main-master', 'SELECT OAuthConsumer.date_created, OAuthConsumer.disabled, OAuthConsumer.id, OAuthConsumer."key", OAuthConsumer.secret FROM OAuthConsumer WHERE OAuthConsumer."key" = %s') (5, 5, 'launchpad-main-master', 'SELECT OAuthConsumer.date_created, OAuthConsumer.disabled, OAuthConsumer.id, OAuthConsumer."key", OAuthConsumer.secret FROM OAuthConsumer WHERE OAuthConsumer."key" = %s') (8, 9, 'launchpad-main-master', 'INSERT INTO OAuthConsumer (date_created, disabled, "key", secret) VALUES (CURRENT_TIMESTAMP AT TIME ZONE \'UTC\', %s, %s, %s) RETURNING OAuthConsumer.id') (11, 11, 'launchpad-main-master', 'SELECT Person.account, Person.addressline1, Person.addressline2, Person.city, Person.country, Person.creation_comment, Person.creation_rationale, Person.datecreated, Person.defaultmembershipperiod, Person.defaultrenewalperiod, Person.displayname, Person.hide_email_addresses, Person.homepage_content, Person.icon, Person.id, Person.logo, Person.mailing_list_auto_subscribe_policy, Person.merged, Person.mugshot, Person.name, Person.organization, Person.personal_standing, Person.personal_standing_reason, Person.phone, Person.postcode, Person.province, Person.registrant, Person.renewal_policy, Person.subscriptionpolicy, Person.teamdescription, Person.teamowner, Person.verbose_bugnotifications, Person.visibility FROM Person WHERE Person.name = %s AND Person.merged IS NULL ORDER BY person_sort_key(Person.displayname, Person.name)') (12, 13, 'launchpad-main-master', 'SELECT 1 FROM Person WHERE Person.id = %s') (13, 14, 'launchpad-main-master', 'SELECT Account.creation_rationale, Account.date_created, Account.date_status_set, Account.displayname, Account.id, Account.openid_identifier, Account.status, Account.status_comment FROM Account WHERE Account.id = %s LIMIT 1') (16, 16, 'launchpad-main-master', 'SELECT COUNT(*) FROM Person, TeamParticipation WHERE \n Person.id = TeamParticipation.person AND\n TeamParticipation.team = 243653 AND\n TeamParticipation.person != 243653\n AND (1=1)') (18, 19, 'launchpad-main-master', 'SELECT Person.account, Person.addressline1, Person.addressline2, Person.city, Person.country, Person.creation_comment, Person.creation_rationale, Person.datecreated, Person.defaultmembershipperiod, Person.defaultrenewalperiod, Person.displayname, Person.hide_email_addresses, Person.homepage_content, Person.icon, Person.id, Person.logo, Person.mailing_list_auto_subscribe_policy, Person.merged, Person.mugshot, Person.name, Person.organization, Person.personal_standing, Person.personal_standing_reason, Person.phone, Person.postcode, Person.province, Person.registrant, Person.renewal_policy, Person.subscriptionpolicy, Person.teamdescription, Person.teamowner, Person.verbose_bugnotifications, Person.visibility FROM Person, TeamParticipation WHERE \n Person.id = TeamParticipation.person AND\n TeamParticipation.team = 243653 AND\n TeamParticipation.person != 243653\n AND (1=1) ORDER BY person_sort_key(Person.displayname, Person.name) LIMIT 3 OFFSET 0') (20, 21, 'launchpad-main-master', 'SELECT Person.account, Person.addressline1, Person.addressline2, Person.city, Person.country, Person.creation_comment, Person.creation_rationale, Person.datecreated, Person.defaultmembershipperiod, Person.defaultrenewalperiod, Person.displayname, Person.hide_email_addresses, Person.homepage_content, Person.icon, Person.logo, Person.mailing_list_auto_subscribe_policy, Person.merged, Person.mugshot, Person.name, Person.organization, Person.personal_standing, Person.personal_standing_reason, Person.phone, Person.postcode, Person.province, Person.registrant, Person.renewal_policy, Person.subscriptionpolicy, Person.teamdescription, Person.teamowner, Person.verbose_bugnotifications, Person.visibility FROM Person WHERE Person.id = %s') (28, 30, 'launchpad-main-master', 'SELECT KarmaTotalCache.id, KarmaTotalCache.karma_total, KarmaTotalCache.person FROM KarmaTotalCache WHERE KarmaTotalCache.person = %s ORDER BY KarmaTotalCache.id') (31, 32, 'launchpad-main-master', 'SELECT EmailAddress.account, EmailAddress.email, EmailAddress.person, EmailAddress.status FROM EmailAddress WHERE EmailAddress.id = %s') (36, 38, 'launchpad-main-master', 'SELECT Archive.enabled, Archive.authorized_size, Archive.binaries_cached, Archive.buildd_secret, Archive.building_count, Archive.commercial, Archive.date_created, Archive.description, Archive.displayname, Archive.distribution, Archive.external_dependencies, Archive.failed_count, Archive.id, Archive.name, Archive.owner, Archive.package_description_cache, Archive.pending_count, Archive.private, Archive.publish, Archive.purpose, Archive.relative_build_score, Archive.require_virtualized, Archive.signing_key, Archive.sources_cached, Archive.status, Archive.succeeded_count, Archive.total_count FROM Archive WHERE Archive.purpose = %s AND Archive.owner = %s ORDER BY Archive.id LIMIT 1') (41, 42, 'launchpad-main-master', 'SELECT PersonLocation.date_created, PersonLocation.date_last_modified, PersonLocation.id, PersonLocation.last_modified_by, PersonLocation.latitude, PersonLocation.longitude, PersonLocation.person, PersonLocation.time_zone, PersonLocation.visible FROM PersonLocation WHERE PersonLocation.person = %s ORDER BY PersonLocation.id') (45, 46, 'launchpad-main-master', 'SELECT KarmaTotalCache.id, KarmaTotalCache.karma_total, KarmaTotalCache.person FROM KarmaTotalCache WHERE KarmaTotalCache.person = %s ORDER BY KarmaTotalCache.id') (47, 48, 'launchpad-main-master', 'SELECT COUNT(*) FROM SignedCodeOfConduct WHERE SignedCodeOfConduct.active = %s AND SignedCodeOfConduct.owner = %s AND SignedCodeOfConduct.datecreated >= %s') (48, 49, 'launchpad-main-master', 'SELECT ValidPersonCache.id FROM ValidPersonCache WHERE ValidPersonCache.id = %s LIMIT 1') (57, 58, 'launchpad-main-master', 'SELECT KarmaTotalCache.id, KarmaTotalCache.karma_total, KarmaTotalCache.person FROM KarmaTotalCache WHERE KarmaTotalCache.person = %s ORDER BY KarmaTotalCache.id') (58, 59, 'launchpad-main-master', 'SELECT EmailAddress.account, EmailAddress.email, EmailAddress.id, EmailAddress.person, EmailAddress.status FROM EmailAddress WHERE EmailAddress.person = %s AND EmailAddress.status = %s ORDER BY EmailAddress.email') (63, 63, 'launchpad-main-master', 'SELECT Archive.enabled, Archive.authorized_size, Archive.binaries_cached, Archive.buildd_secret, Archive.building_count, Archive.commercial, Archive.date_created, Archive.description, Archive.displayname, Archive.distribution, Archive.external_dependencies, Archive.failed_count, Archive.id, Archive.name, Archive.owner, Archive.package_description_cache, Archive.pending_count, Archive.private, Archive.publish, Archive.purpose, Archive.relative_build_score, Archive.require_virtualized, Archive.signing_key, Archive.sources_cached, Archive.status, Archive.succeeded_count, Archive.total_count FROM Archive WHERE Archive.purpose = %s AND Archive.owner = %s ORDER BY Archive.id LIMIT 1') (66, 66, 'launchpad-main-master', 'SELECT PersonLocation.date_created, PersonLocation.date_last_modified, PersonLocation.id, PersonLocation.last_modified_by, PersonLocation.latitude, PersonLocation.longitude, PersonLocation.person, PersonLocation.time_zone, PersonLocation.visible FROM PersonLocation WHERE PersonLocation.person = %s ORDER BY PersonLocation.id') (68, 68, 'launchpad-main-master', 'SELECT KarmaTotalCache.id, KarmaTotalCache.karma_total, KarmaTotalCache.person FROM KarmaTotalCache WHERE KarmaTotalCache.person = %s ORDER BY KarmaTotalCache.id') (70, 70, 'launchpad-main-master', 'SELECT COUNT(*) FROM SignedCodeOfConduct WHERE SignedCodeOfConduct.active = %s AND SignedCodeOfConduct.owner = %s AND SignedCodeOfConduct.datecreated >= %s') (70, 71, 'launchpad-main-master', 'SELECT ValidPersonCache.id FROM ValidPersonCache WHERE ValidPersonCache.id = %s LIMIT 1') (78, 79, 'launchpad-main-master', 'SELECT KarmaTotalCache.id, KarmaTotalCache.karma_total, KarmaTotalCache.person FROM KarmaTotalCache WHERE KarmaTotalCache.person = %s ORDER BY KarmaTotalCache.id') (79, 80, 'launchpad-main-master', 'SELECT EmailAddress.account, EmailAddress.email, EmailAddress.id, EmailAddress.person, EmailAddress.status FROM EmailAddress WHERE EmailAddress.person = %s AND EmailAddress.status = %s ORDER BY EmailAddress.email') (84, 85, 'launchpad-main-master', 'SELECT Archive.enabled, Archive.authorized_size, Archive.binaries_cached, Archive.buildd_secret, Archive.building_count, Archive.commercial, Archive.date_created, Archive.description, Archive.displayname, Archive.distribution, Archive.external_dependencies, Archive.failed_count, Archive.id, Archive.name, Archive.owner, Archive.package_description_cache, Archive.pending_count, Archive.private, Archive.publish, Archive.purpose, Archive.relative_build_score, Archive.require_virtualized, Archive.signing_key, Archive.sources_cached, Archive.status, Archive.succeeded_count, Archive.total_count FROM Archive WHERE Archive.purpose = %s AND Archive.owner = %s ORDER BY Archive.id LIMIT 1') (87, 87, 'launchpad-main-master', 'SELECT PersonLocation.date_created, PersonLocation.date_last_modified, PersonLocation.id, PersonLocation.last_modified_by, PersonLocation.latitude, PersonLocation.longitude, PersonLocation.person, PersonLocation.time_zone, PersonLocation.visible FROM PersonLocation WHERE PersonLocation.person = %s ORDER BY PersonLocation.id') (89, 90, 'launchpad-main-master', 'SELECT KarmaTotalCache.id, KarmaTotalCache.karma_total, KarmaTotalCache.person FROM KarmaTotalCache WHERE KarmaTotalCache.person = %s ORDER BY KarmaTotalCache.id') (91, 91, 'launchpad-main-master', 'SELECT COUNT(*) FROM SignedCodeOfConduct WHERE SignedCodeOfConduct.active = %s AND SignedCodeOfConduct.owner = %s AND SignedCodeOfConduct.datecreated >= %s') (92, 92, 'launchpad-main-master', 'SELECT ValidPersonCache.id FROM ValidPersonCache WHERE ValidPersonCache.id = %s LIMIT 1') ------------ Text attachment: traceback ------------ Traceback (most recent call last): File "/home/robertc/launchpad/lp-sourcedeps/eggs/testtools-0.9.6dev91-py2.6.egg/testtools/runtest.py", line 144, in _run_user return fn(*args) File "/home/robertc/launchpad/lp-sourcedeps/eggs/testtools-0.9.6dev91-py2.6.egg/testtools/testcase.py", line 442, in _run_test_method testMethod() File "/home/robertc/launchpad/lp-branches/working/lib/lp/registry/tests/test_person.py", line 582, in test_participation_query_limit self.assertThat(collector, HasQueryCount(LessThan(24))) File "/home/robertc/launchpad/lp-sourcedeps/eggs/testtools-0.9.6dev91-py2.6.egg/testtools/testcase.py", line 297, in assertThat % (matchee, matcher, mismatch.describe())) AssertionError: Match failed. Matchee: "" Matcher: HasQueryCount(LessThan(24)) Difference: queries do not match: 24 is >= 31 ------------