Skip to content

Conversation

@superdav42
Copy link
Collaborator

@superdav42 superdav42 commented Sep 9, 2025

fixes #120

Summary by CodeRabbit

  • Bug Fixes

    • Correctly enforces per-role user limits in admin, hiding roles that exceed the limit.
    • Respects “unlimited” when a limit is set to 0.
    • Ensures editable roles remain unchanged for visitors and when no user is logged in.
  • Performance

    • Speeds up role availability checks by using aggregate role counts instead of enumerating users.
  • Tests

    • Adds unit tests covering visitors, admin without login, and over-limit scenarios, with proper site setup and cache resets.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 9, 2025

Warning

Rate limit exceeded

@superdav42 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 11 minutes and 26 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between faaf557 and f918d47.

📒 Files selected for processing (1)
  • inc/limits/class-customer-user-role-limits.php (2 hunks)

Walkthrough

Reworks customer user role limiting to avoid per-role get_users() counts by using count_users() avail_roles data and treating 0 as unlimited. Maintains existing enable/disable checks and preserves roles in frontend/admin-logged-out contexts. Adds PHPUnit tests covering frontend visitors, admin without login, and over-limit behavior on a test site.

Changes

Cohort / File(s) Summary
Role limit logic refactor
inc/limits/class-customer-user-role-limits.php
Replaces per-role get_users() counting with count_users()’s per-role counts; interprets number=0 as unlimited; updates condition to unset roles based on avail_roles count; preserves existing enabled check and else branch behavior.
Unit tests for role filtering
tests/WP_Ultimo/Limits/Customer_User_Role_Limits_Test.php
Adds tests validating unchanged roles for frontend visitors and admin when not logged in; adds test for removing over-limit role on a site with per-role limits; includes test site lifecycle and cache reset.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor WP as WordPress Core
  participant L as Customer_User_Role_Limits
  participant S as Site Limitations
  participant U as Users API

  WP->>L: filter_editable_roles(roles)
  alt No limitations module enabled OR super admin OR frontend/no user context
    L-->>WP: return roles (unchanged)
  else Limits apply
    L->>S: get per-role limits
    L->>U: count_users()
    U-->>L: { avail_roles: {role: count, ...} }
    loop For each role
      alt Limit enabled and number > 0 and count >= number
        L->>L: unset role
      else Limit disabled or unlimited or under limit
        L->>L: keep role
      end
    end
    L-->>WP: return filtered roles
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks (2 passed, 1 warning, 2 inconclusive)

❌ Failed checks (1 warning, 2 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title “More faster user query count” is grammatically awkward, vague, and fails to succinctly convey the primary change of optimizing the role-filtering logic by switching from get_users to count_users and adding context checks. Revise the title to a concise, descriptive phrase that reflects the main change, for example “Optimize filter_editable_roles with count_users and skip front-end contexts.”
Description Check ❓ Inconclusive The description only states “fixes #120” without any summary of the implemented changes, making it too terse to convey what the pull request actually does. Expand the description to outline the key updates, such as replacing get_users calls with count_users, adding early returns for front-end/visitor contexts, and including new PHPUnit tests.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues Check ✅ Passed The changes replace costly per-role get_users calls with a single count_users invocation, respect numeric per-role limits, and include tests that verify unaltered roles for visitors and admins as well as proper removal when over limit, thereby satisfying issue #120’s requirements.
Out of Scope Changes Check ✅ Passed All modifications are confined to the customer user role limits class and its corresponding test suite, with no unrelated files or features altered outside the scope of issue #120.

Poem

I thump my paws at queries slow,
Now counts arrive in one swift flow.
Front burrow calm, no loops to fear,
Back burrow trims when limits near.
Roles hop light, the load made slim—
A speedy warren, neat and trim. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-slow-user-query

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
inc/limits/class-customer-user-role-limits.php (1)

96-112: Harden against non-object $limit to avoid TypeError in property_exists().

$users_limitation->{$role} may be absent or null; property_exists() requires object|string.

Use an object check:

if (is_object($limit) && property_exists($limit, 'enabled') && $limit->enabled) {
    // ...
} else {
    unset($roles[$role]);
}
🧹 Nitpick comments (4)
inc/limits/class-customer-user-role-limits.php (1)

103-105: Memoize count_users() per request to cut redundant work.

Static caching avoids repeated queries if the filter runs multiple times in one request.

-				if ( ! isset($user_count)) {
-					$user_count = count_users();
-				}
+				static $user_count = null;
+				if (null === $user_count) {
+					$user_count = count_users();
+				}
tests/WP_Ultimo/Limits/Customer_User_Role_Limits_Test.php (3)

29-41: Don’t create a site in every test; move site creation to a class-level setup and target the correct blog when clearing meta.

Current setUp() leaks sites across tests and clears meta on the current blog, not the test site.

@@
-		// Create a test site
-		self::$test_site = wu_create_site(
-			[
-				'title'       => 'Test Site',
-				'domain'      => 'test-site5.example.com',
-				'template_id' => 1,
-				'type'        => Site_Type::CUSTOMER_OWNED,
-			]
-		);
-		// Remove any pre-existing site limitations
-		$blog_id = get_current_blog_id();
-		delete_metadata('blog', $blog_id, 'wu_limitations');
+		// Remove any pre-existing site limitations on current and test blog
+		delete_metadata('blog', get_current_blog_id(), 'wu_limitations');
+		if (self::$test_site) {
+			delete_metadata('blog', self::$test_site->get_id(), 'wu_limitations');
+		}

Add a class-level setup once (place after the property declaration):

+	public static function wpSetUpBeforeClass($factory): void {
+		self::$test_site = wu_create_site(
+			[
+				'title'       => 'Test Site',
+				'domain'      => 'test-site5.example.com',
+				'template_id' => 1,
+				'type'        => Site_Type::CUSTOMER_OWNED,
+			]
+		);
+	}

47-53: Use wpTearDownAfterClass() so cleanup reliably runs.

tear_down_after_class() may not be invoked depending on the PHPUnit/WP versions in CI.

-	public static function tear_down_after_class() {
-		parent::tear_down_after_class();
+	public static function wpTearDownAfterClass(): void {
+		parent::wpTearDownAfterClass();

55-73: Add coverage for “users module enabled” on frontend/anonymous to lock in the fast-path behavior.

The current tests pass because the module is disabled; they won’t catch regressions where a frontend visitor still triggers count logic.

Add tests like:

public function test_frontend_visitor_returns_original_when_users_module_enabled(): void {
	$instance = Customer_User_Role_Limits::get_instance();
	wp_set_current_user(0);
	if (function_exists('set_current_screen')) {
		set_current_screen('front');
	}

	$roles = ['subscriber' => ['name' => 'Subscriber']];
	// Enable users module with some limit
	add_metadata('blog', get_current_blog_id(), 'wu_limitations', [
		'users' => [
			'enabled' => true,
			'limit'   => ['subscriber' => ['enabled' => true, 'number' => 1]],
		],
	], true);

	$this->assertSame($roles, $instance->filter_editable_roles($roles));
}

public function test_admin_not_logged_in_returns_original_when_users_module_enabled(): void {
	$instance = Customer_User_Role_Limits::get_instance();
	wp_set_current_user(0);
	if (function_exists('set_current_screen')) {
		set_current_screen('dashboard');
	}
	add_metadata('blog', get_current_blog_id(), 'wu_limitations', [
		'users' => [
			'enabled' => true,
			'limit'   => ['subscriber' => ['enabled' => true, 'number' => 1]],
		],
	], true);

	$this->assertSame(['subscriber' => ['name' => 'Subscriber']], $instance->filter_editable_roles(['subscriber' => ['name' => 'Subscriber']]));
}

Also applies to: 75-92

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 010e6cc and faaf557.

📒 Files selected for processing (2)
  • inc/limits/class-customer-user-role-limits.php (1 hunks)
  • tests/WP_Ultimo/Limits/Customer_User_Role_Limits_Test.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/WP_Ultimo/Limits/Customer_User_Role_Limits_Test.php (3)
inc/objects/class-limitations.php (1)
  • Limitations (24-489)
inc/functions/site.php (1)
  • wu_create_site (135-160)
inc/limits/class-customer-user-role-limits.php (2)
  • Customer_User_Role_Limits (21-150)
  • filter_editable_roles (85-116)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: cypress (8.4, chrome)
  • GitHub Check: cypress (8.2, firefox)
  • GitHub Check: cypress (7.4, firefox)
  • GitHub Check: cypress (8.0, chrome)
  • GitHub Check: PHP 8.3
  • GitHub Check: PHP 8.1
  • GitHub Check: PHP 8.2
  • GitHub Check: PHP 8.4

@superdav42 superdav42 merged commit da8aa04 into main Sep 9, 2025
19 checks passed
@superdav42 superdav42 deleted the fix-slow-user-query branch September 23, 2025 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

slow query limiting users

2 participants