Fix chart data not showing due to incorrect bounds check#232
Fix chart data not showing due to incorrect bounds check#232parhumm merged 10 commits intodevelopmentfrom
Conversation
The condition `$offset <= $this->points` was allowing negative offsets and offsets equal to points (out of bounds) while potentially dropping valid data. Changed to `$offset >= 0 && $offset < $this->points` to properly validate array indices. This fixes the issue where users see only a few records in charts despite having hundreds in the database. Ref: Ticket #14684
The condition `$offset <= $this->points` was allowing negative offsets and offsets equal to points (out of bounds) while potentially dropping valid data. Changed to `$offset >= 0 && $offset < $this->points` to properly validate array indices. This fixes the issue where users see only a few records in charts despite having hundreds in the database. Fixes #231
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughTightens bounds checking in DataBuckets::addRow() and adds multiple end-to-end Playwright tests plus test-only MU-plugins to validate DataBuckets accuracy, calendar-extension fallback, chart SQL expression whitelist, exclusion filters, and custom-DB behavior; also updates MU-plugin manifest and an admin option description. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
…p-slimstat/wp-slimstat into fix/issue-14684-chart-data-not-showing
…ulation Validates the DataBuckets::addRow() off-by-one fix (DataBuckets.php:209) and confirms the slimstat_custom_wpdb filter causes no read/write split. - custom-db-simulator-mu-plugin.php: simulates external DB addon filter; uses $GLOBALS['wpdb'] to avoid PHP 8.2 fatal with same-named parameter - setup.ts: register mu-plugin in MU_PLUGIN_MANIFEST - databuckets-custom-db-accuracy.spec.ts: 3 tests covering default DB, custom DB filter active, and boundary edge case Refs #231 / Support #14684
- custom-db-simulator-mu-plugin: reset wpdb->result via Reflection after clone to prevent "mysqli_result already closed" fatal (HTTP 500) caused by shallow clone sharing the protected result property with global wpdb - databuckets spec Test 5: rewrite to accurately test what the code does — Chart.php always reads from global wpdb (wp_slim_stats), not wp_slimstat::$wpdb, so the custom DB filter affects write paths only; test now confirms the AJAX handler survives the filter active and data is still accurate All 6 tests now pass (19.5s): Tests 1-6 PASS Source: Support #14684 / GitHub #231
E2E Test Results — DataBuckets Chart Accuracy + Calendar Extension Fallback + SQL Expression ValidationBranch: Suite 1:
|
| # | Test | Result | Output |
|---|---|---|---|
| 1 | Direct AJAX: weekly chart v1 sum equals inserted record count | ✅ PASS | DB=9, chart.v1 sum=9, labels=4 |
| 2 | Server-rendered data-data v1 sum ≥ inserted record count |
✅ PASS | DB=9, server-rendered sum=9 |
| 3 | Records outside AJAX range excluded from chart | ✅ PASS | total DB=8, in-range=5, chart.v1 sum=5 |
| 4 | Empty range — zero records, no crash, sum=0 | ✅ PASS | chart.v1 sum=0, labels=4 |
| 5 | Chart AJAX survives with slimstat_custom_wpdb filter active |
✅ PASS | custom DB filter active, chart reads default DB: sum=5, labels=4 |
| 6 | DataBuckets off-by-one regression (phantom bucket boundary) | ✅ PASS | DB=7, chart.v1 sum=7, labels=4, v1 array length=4 |
| 7 🆕 | Chart AJAX with MONTHLY granularity: v1 sum equals inserted record count | ✅ PASS | monthly: DB=7, chart.v1 sum=7, labels=2 |
Suite 2: databuckets-no-calendar-ext.spec.ts
Result: ✅ 4/4 passed (11.2s)
| # | Test | Result | Output |
|---|---|---|---|
| 1 | Chart AJAX with WEEK granularity returns HTTP 200 when jddayofweek() would throw |
✅ PASS | HTTP 200, success=true (no jddayofweek() fatal) |
| 2 | Chart v1 sum matches DB row count — DAY_NAMES week boundaries are correct | ✅ PASS | DB=9, chart.v1 sum=9, labels=4 |
| 3 | Chart works correctly with start_of_week = 0 (Sunday) |
✅ PASS | start_of_week=0 (Sunday), chart.v1 sum=4, labels=4 |
| 4 | Corrupted start_of_week option falls back to Monday — no fatal |
✅ PASS | corrupted start_of_week=99, fell back safely, labels=4 |
Suite 3: chart-sql-expression-validation.spec.ts 🆕
Result: ✅ 4/4 passed (22.5s)
| # | Test | Result | Output |
|---|---|---|---|
| 1 | COUNT(id) — default free-plugin expression passes whitelist |
✅ PASS | accepted |
| 2 | COUNT( DISTINCT ip ) — default free-plugin expression passes whitelist |
✅ PASS | accepted |
| 3 | COUNT(*) + 1 — arithmetic expression rejected by whitelist |
✅ PASS | rejected: "Invalid SQL expression in chart data. Only whitelisted aggregate functions on valid columns are allowed." |
| 4 | SQL injection attempt rejected by whitelist | ✅ PASS | rejected: "Invalid SQL expression in chart data. Only whitelisted aggregate functions on valid columns are allowed." |
What is tested
Bug fix (DataBuckets.php:209) (Suite 1, Tests 1–7): Directly targets the phantom bucket scenario — records near the range boundary that would silently fall into datasets[points] with no label under the old $offset <= $this->points condition. With the fix ($offset >= 0 && $offset < $this->points), the chart sum equals the DB count for both WEEK (Tests 1, 3, 6) and MONTH (Test 7) granularities.
Calendar extension fallback (Suite 2): Validates PR #229's fix — DataBuckets::initSeqWeek() uses self::DAY_NAMES[$startOfWeek] ?? 'Monday' instead of jddayofweek(). The calendar-ext-simulator mu-plugin defines SlimStat\Helpers\jddayofweek() to throw a RuntimeException, simulating a server without PHP's ext-calendar. All 4 tests confirm the fixed code never calls the stub.
SQL expression whitelist (Suite 3): Validates Chart.php::validateSqlExpression() — the strict whitelist accepts the two default free-plugin expressions (COUNT(id), COUNT( DISTINCT ip )) and rejects arithmetic expressions and injection payloads with success: false before any SQL executes.
Custom DB compatibility (Suite 1, Test 5): The slimstat_custom_wpdb filter no longer crashes the chart AJAX. Chart.php → Query.php → global $wpdb always reads from wp_slim_stats regardless of the filter.
There was a problem hiding this comment.
🧹 Nitpick comments (4)
tests/e2e/helpers/custom-db-simulator-mu-plugin.php (2)
14-17: Use Yoda condition for consistency.The comparison should use Yoda-style to match the project's PHP coding guidelines.
♻️ Proposed fix
add_filter('slimstat_custom_wpdb', function ($current_wpdb) { - if (get_option('slimstat_test_use_custom_db') !== 'yes') { + if ('yes' !== get_option('slimstat_test_use_custom_db')) { return $current_wpdb; }As per coding guidelines: "Use Yoda Conditions:
if ( 'value' === $var )"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/helpers/custom-db-simulator-mu-plugin.php` around lines 14 - 17, Change the conditional inside the anonymous function passed to add_filter('slimstat_custom_wpdb') to use a Yoda condition: invert the comparison so the literal 'yes' is on the left and the result of get_option('slimstat_test_use_custom_db') is on the right (i.e., use 'yes' === get_option(...)); update the early-return branch accordingly in that closure.
1-12: Add GPL license header to this new PHP file.Per coding guidelines, all new PHP files should include a GPL license header. Consider adding the standard WordPress GPL header block.
📝 Suggested header addition
<?php +/* + * Plugin Name: Custom DB Simulator (E2E Test Only) + * Description: Simulates the slimstat_custom_wpdb filter for E2E testing. + * License: GPL-2.0-or-later + * License URI: https://www.gnu.org/licenses/gpl-2.0.html + */ /** * MU-Plugin: Custom DB Simulator *🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/helpers/custom-db-simulator-mu-plugin.php` around lines 1 - 12, Add the standard WordPress GPL license header block to the top of the new MU-Plugin file (the comment beginning "MU-Plugin: Custom DB Simulator" in custom-db-simulator-mu-plugin.php). Insert the canonical PHP GPL header (short copyright line, Copyright holder/year, a brief description, "This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License" and the license URL) immediately above or replacing the existing file comment so the file complies with coding guidelines.tests/e2e/databuckets-custom-db-accuracy.spec.ts (2)
167-176: Minor: Timestamps are computed at module load time.The range timestamps (
now,rangeStart,rangeEnd,week*Ts) are computed when the file loads rather than when tests execute. If there's significant delay between loading and execution, the "now" could be slightly stale. This is unlikely to cause issues given the 3-week range buffer, but worth noting.If flaky behavior ever occurs with time-sensitive tests, consider moving these calculations into a
beforeAllhook or computing them fresh in each test.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/databuckets-custom-db-accuracy.spec.ts` around lines 167 - 176, Timestamps like now, rangeStart, rangeEnd, week1Ts, week2Ts, week3Ts are computed at module load time which can drift before test execution; move their calculations into a dynamic setup (e.g., a beforeAll hook or compute inside each test) so values are fresh when tests run, update any test usages to reference the new variables (or call a helper that returns them) so week, now and the week*Ts are derived at runtime rather than module import time.
405-409: Assertion may be overly permissive for the off-by-one fix validation.The comment states "v1 array length should match labels length" but the assertion uses
toBeLessThanOrEqual. Based on theDataBuckets::initSeqcode (which creates labels and datasets in lock-step), after the fix they should always be equal. A stricter assertion would more precisely validate the fix.♻️ Suggested stricter assertion
// Verify no extra phantom datasets beyond label count const labels: string[] = json?.data?.data?.labels ?? []; const v1Array: number[] = json?.data?.data?.datasets?.v1 ?? []; // After the fix, v1 array length should match labels length - expect(v1Array.length).toBeLessThanOrEqual(labels.length); + expect(v1Array.length).toBe(labels.length);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/databuckets-custom-db-accuracy.spec.ts` around lines 405 - 409, The test currently asserts v1Array.length is <= labels.length but should assert exact equality to validate the off-by-one fix; update the assertion in tests/e2e/databuckets-custom-db-accuracy.spec.ts to compare v1Array.length === labels.length (referencing the labels and v1Array variables and the DataBuckets::initSeq behavior) so the test fails if datasets and labels are not created in lock-step.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/e2e/databuckets-custom-db-accuracy.spec.ts`:
- Around line 167-176: Timestamps like now, rangeStart, rangeEnd, week1Ts,
week2Ts, week3Ts are computed at module load time which can drift before test
execution; move their calculations into a dynamic setup (e.g., a beforeAll hook
or compute inside each test) so values are fresh when tests run, update any test
usages to reference the new variables (or call a helper that returns them) so
week, now and the week*Ts are derived at runtime rather than module import time.
- Around line 405-409: The test currently asserts v1Array.length is <=
labels.length but should assert exact equality to validate the off-by-one fix;
update the assertion in tests/e2e/databuckets-custom-db-accuracy.spec.ts to
compare v1Array.length === labels.length (referencing the labels and v1Array
variables and the DataBuckets::initSeq behavior) so the test fails if datasets
and labels are not created in lock-step.
In `@tests/e2e/helpers/custom-db-simulator-mu-plugin.php`:
- Around line 14-17: Change the conditional inside the anonymous function passed
to add_filter('slimstat_custom_wpdb') to use a Yoda condition: invert the
comparison so the literal 'yes' is on the left and the result of
get_option('slimstat_test_use_custom_db') is on the right (i.e., use 'yes' ===
get_option(...)); update the early-return branch accordingly in that closure.
- Around line 1-12: Add the standard WordPress GPL license header block to the
top of the new MU-Plugin file (the comment beginning "MU-Plugin: Custom DB
Simulator" in custom-db-simulator-mu-plugin.php). Insert the canonical PHP GPL
header (short copyright line, Copyright holder/year, a brief description, "This
program is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License" and the license URL) immediately above
or replacing the existing file comment so the file complies with coding
guidelines.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 769509a4-d61e-498c-a3f6-fa00e879b5ad
📒 Files selected for processing (3)
tests/e2e/databuckets-custom-db-accuracy.spec.tstests/e2e/helpers/custom-db-simulator-mu-plugin.phptests/e2e/helpers/setup.ts
Tests for bot, CPT, permalink, and user exclusion filters. Confirms issue #233: CPT exclusion requires cpt: prefix.
The ignore_content_types description now explains that custom post types require the cpt: prefix (e.g., cpt:product). Without this, users entering bare type names see exclusion silently fail. Fixes #233
Validates that DataBuckets::initSeqWeek() uses DAY_NAMES[] instead of jddayofweek(), which would fatal on servers without PHP ext-calendar. - 4 tests: HTTP 200, sum accuracy, Sunday start-of-week, corrupted SOW fallback - calendar-ext-simulator mu-plugin: throws RuntimeException from SlimStat\Helpers namespace, simulating ext-calendar absence via PHP namespace shadowing - Uses nonce-helper AJAX endpoint for reliable nonce generation (avoids scraping slimstat_chart_vars which can fail with unusual start_of_week) - beforeEach clears chart transients to prevent cached results across tests
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
tests/e2e/exclusion-filters.spec.ts (1)
126-126: Prefer polling the DB over fixed 4s sleeps.These waits are both the slowest part of the file and still timing-sensitive under CI variance.
expect.poll()againstgetRecentStatByResource()/getStatCount()will make the assertions deterministic.⏱️ Example refactor
- await anonPage.waitForTimeout(4000); - - const stat = await getRecentStatByResource(slug); - expect(stat).toBeNull(); + await expect.poll(() => getRecentStatByResource(slug)).toBeNull();- await anonPage.waitForTimeout(4000); - const countAfter = await getStatCount(); - expect(countAfter).toBe(countBefore); + await expect.poll(() => getStatCount()).toBe(countBefore);Also applies to: 158-158, 191-191, 216-216, 249-249
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/exclusion-filters.spec.ts` at line 126, Replace fixed sleeps (await anonPage.waitForTimeout(4000)) with polling assertions using expect.poll() against the helper functions (getRecentStatByResource() or getStatCount()) so tests wait until the DB reaches the expected state; locate each occurrence of await anonPage.waitForTimeout (lines where 4000 is used) and change to an expect.poll(() => getRecentStatByResource(...)) or expect.poll(() => getStatCount(...)).toHave... with a reasonable timeout and interval so the assertion becomes deterministic under CI variance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/e2e/exclusion-filters.spec.ts`:
- Around line 175-200: The test "Bot excluded when ignore_bots is on with
Googlebot UA" currently calls installHeaderInjector() but only calls
uninstallHeaderInjector() on the happy path; wrap the browser interactions and
assertions in a try/finally so resources and test-only globals are always
cleaned up: call installHeaderInjector() before the try, then in finally ensure
anonPage.close() and anonCtx.close() are invoked if they were created and always
call uninstallHeaderInjector(); reference the existing symbols anonCtx
(browser.newContext), anonPage (anonCtx.newPage / goto), getStatCount, and
uninstallHeaderInjector to locate and implement the safe cleanup block.
- Around line 32-52: The MU plugin registers the 'product' CPT but never flushes
rewrite rules, so pretty-permalink routes like /product/{slug}/ may 404; update
the MU plugin content (CPT_MU_PLUGIN_CONTENT) or the installation helper
(installCptMuPlugin) to flush rewrite rules after registering the post type
(e.g., call flush_rewrite_rules() at the end of the init callback or trigger a
rewrite flush after writing the MU plugin) so the /product/ routes exist before
the tests that visit those URLs.
---
Nitpick comments:
In `@tests/e2e/exclusion-filters.spec.ts`:
- Line 126: Replace fixed sleeps (await anonPage.waitForTimeout(4000)) with
polling assertions using expect.poll() against the helper functions
(getRecentStatByResource() or getStatCount()) so tests wait until the DB reaches
the expected state; locate each occurrence of await anonPage.waitForTimeout
(lines where 4000 is used) and change to an expect.poll(() =>
getRecentStatByResource(...)) or expect.poll(() => getStatCount(...)).toHave...
with a reasonable timeout and interval so the assertion becomes deterministic
under CI variance.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5abdfeed-1a21-4549-984f-bb6e10d20853
📒 Files selected for processing (2)
admin/config/index.phptests/e2e/exclusion-filters.spec.ts
…rage Closes gaps identified in PR #232 impact analysis: - Test 7 in databuckets-custom-db-accuracy.spec.ts verifies MONTHLY granularity chart sum equals DB count (the offset formula for MONTH differs from WEEK and was previously untested). - New chart-sql-expression-validation.spec.ts (4 tests) verifies that valid default expressions pass the validateSqlExpression() whitelist and that arithmetic/injection payloads are rejected with success:false.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/e2e/databuckets-custom-db-accuracy.spec.ts`:
- Around line 130-141: The helper readServerRenderedChart is currently reading
the first '[id^="slimstat_chart_data_"]' on the page which can return the wrong
widget; modify readServerRenderedChart to accept a container selector or element
id (e.g., readServerRenderedChart(containerSelector: string)) and query the
'[id^="slimstat_chart_data_"]' inside that specific report container so it
targets the chart under test; update the tests that call readServerRenderedChart
(including where clearStatsTable() is used and the calls in the region
referenced) to pass the specific report container selector for the
server-rendered pageviews chart and change the assertion from chartSum >=
dbCount to chartSum === dbCount to assert exact equality after
clearStatsTable().
In `@tests/e2e/databuckets-no-calendar-ext.spec.ts`:
- Around line 120-125: The anchor timestamp is wrong (1738454400 is 2025-02-02
UTC) and makes week1Ts a Sunday; replace the hardcoded epoch with an explicit
UTC date calculation so the suite anchors to Monday 2026-02-02; update week1Ts,
week2Ts, week3Ts, rangeStart, and rangeEnd to derive from a Date.UTC or
equivalent for 2026-02-02T00:00:00Z (then add 7*86400 seconds per week) so the
variables week1Ts, week2Ts, week3Ts, rangeStart, and rangeEnd reflect the
intended Monday-based 3-week window.
In `@tests/e2e/helpers/calendar-ext-simulator-mu-plugin.php`:
- Around line 1-19: Add the repo-standard GPLv2+ header to the top of this new
MU-plugin file (the PHP file that defines SlimStat\Helpers\jddayofweek stub) by
inserting the project's canonical GPLv2-or-later comment block immediately after
the opening <?php (and before the existing file docblock), including copyright
holder/year and the standard "This program is free software..." text and license
URL; ensure the header matches other PHP files in the repo so the file is
clearly licensed GPLv2+.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: afb69a58-6714-4a33-bbb6-6989b504a61d
📒 Files selected for processing (5)
tests/e2e/chart-sql-expression-validation.spec.tstests/e2e/databuckets-custom-db-accuracy.spec.tstests/e2e/databuckets-no-calendar-ext.spec.tstests/e2e/helpers/calendar-ext-simulator-mu-plugin.phptests/e2e/helpers/setup.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/e2e/helpers/setup.ts
| async function readServerRenderedChart(page: import('@playwright/test').Page): Promise<any> { | ||
| const raw = await page.evaluate(() => { | ||
| const el = document.querySelector('[id^="slimstat_chart_data_"]'); | ||
| return el ? el.getAttribute('data-data') : null; | ||
| }); | ||
| if (!raw) return null; | ||
| try { | ||
| return JSON.parse(raw); | ||
| } catch { | ||
| return null; | ||
| } | ||
| } |
There was a problem hiding this comment.
Scope the server-rendered assertion to the actual chart under test.
readServerRenderedChart() grabs the first [id^="slimstat_chart_data_"] element on the page, and Test 2 only checks chartSum >= dbCount. That can pass on the wrong widget or on leaked rows without ever proving the server-rendered pageviews chart is correct. Please target the specific report container and, after clearStatsTable(), tighten the expectation to the exact count instead of >=.
Also applies to: 232-255
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/e2e/databuckets-custom-db-accuracy.spec.ts` around lines 130 - 141, The
helper readServerRenderedChart is currently reading the first
'[id^="slimstat_chart_data_"]' on the page which can return the wrong widget;
modify readServerRenderedChart to accept a container selector or element id
(e.g., readServerRenderedChart(containerSelector: string)) and query the
'[id^="slimstat_chart_data_"]' inside that specific report container so it
targets the chart under test; update the tests that call readServerRenderedChart
(including where clearStatsTable() is used and the calls in the region
referenced) to pass the specific report container selector for the
server-rendered pageviews chart and change the assertion from chartSum >=
dbCount to chartSum === dbCount to assert exact equality after
clearStatsTable().
| // 3-week window anchored to a known Monday (2026-02-02) | ||
| const week1Ts = 1738454400; // 2026-02-02 00:00 UTC (Monday) | ||
| const week2Ts = week1Ts + 7 * 86400; // 2026-02-09 | ||
| const week3Ts = week1Ts + 14 * 86400; // 2026-02-16 | ||
| const rangeStart = week1Ts; | ||
| const rangeEnd = week1Ts + 21 * 86400; // 2026-02-23 |
There was a problem hiding this comment.
The weekly anchor is Sunday, February 2, 2025 UTC — not Monday, February 2, 2026 UTC.
1738454400 resolves to 2025-02-02T00:00:00Z, so week1Ts is a Sunday and the suite no longer matches the documented Monday-based fixture. That changes the bucket layout being exercised and makes the test data harder to reason about.
🛠️ Derive the anchor from an explicit UTC date
-// 3-week window anchored to a known Monday (2026-02-02)
-const week1Ts = 1738454400; // 2026-02-02 00:00 UTC (Monday)
-const week2Ts = week1Ts + 7 * 86400; // 2026-02-09
-const week3Ts = week1Ts + 14 * 86400; // 2026-02-16
-const rangeStart = week1Ts;
-const rangeEnd = week1Ts + 21 * 86400; // 2026-02-23
+// 3-week window anchored to a known Monday (2026-02-02)
+const week1Ts = Date.parse('2026-02-02T00:00:00Z') / 1000;
+const week2Ts = week1Ts + 7 * 86400;
+const week3Ts = week1Ts + 14 * 86400;
+const rangeStart = week1Ts;
+const rangeEnd = week1Ts + 21 * 86400;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 3-week window anchored to a known Monday (2026-02-02) | |
| const week1Ts = 1738454400; // 2026-02-02 00:00 UTC (Monday) | |
| const week2Ts = week1Ts + 7 * 86400; // 2026-02-09 | |
| const week3Ts = week1Ts + 14 * 86400; // 2026-02-16 | |
| const rangeStart = week1Ts; | |
| const rangeEnd = week1Ts + 21 * 86400; // 2026-02-23 | |
| // 3-week window anchored to a known Monday (2026-02-02) | |
| const week1Ts = Date.parse('2026-02-02T00:00:00Z') / 1000; | |
| const week2Ts = week1Ts + 7 * 86400; | |
| const week3Ts = week1Ts + 14 * 86400; | |
| const rangeStart = week1Ts; | |
| const rangeEnd = week1Ts + 21 * 86400; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/e2e/databuckets-no-calendar-ext.spec.ts` around lines 120 - 125, The
anchor timestamp is wrong (1738454400 is 2025-02-02 UTC) and makes week1Ts a
Sunday; replace the hardcoded epoch with an explicit UTC date calculation so the
suite anchors to Monday 2026-02-02; update week1Ts, week2Ts, week3Ts,
rangeStart, and rangeEnd to derive from a Date.UTC or equivalent for
2026-02-02T00:00:00Z (then add 7*86400 seconds per week) so the variables
week1Ts, week2Ts, week3Ts, rangeStart, and rangeEnd reflect the intended
Monday-based 3-week window.
| <?php | ||
| /** | ||
| * MU-Plugin: Calendar Extension Simulator | ||
| * | ||
| * Defines jddayofweek() in the SlimStat\Helpers namespace to throw a RuntimeException, | ||
| * simulating an environment where PHP's ext-calendar extension is not loaded. | ||
| * | ||
| * How PHP namespace resolution works here: | ||
| * When DataBuckets::initSeqWeek() calls jddayofweek() (without a leading backslash), | ||
| * PHP looks for SlimStat\Helpers\jddayofweek() first, before falling back to the | ||
| * global ext-calendar function. Our stub intercepts that lookup and throws — identical | ||
| * to what a server with no ext-calendar would do. | ||
| * | ||
| * After the v5.4.3 fix, initSeqWeek() uses self::DAY_NAMES[] instead of jddayofweek(), | ||
| * so this stub is never called and the chart loads cleanly. | ||
| * | ||
| * Used exclusively by E2E tests for the calendar-extension-fallback spec. | ||
| * Remove this file (or uninstall via setup.ts) to disable. | ||
| */ |
There was a problem hiding this comment.
Add the repo-standard GPL header to this new MU-plugin.
This helper is a new PHP file, but it still lacks the required GPLv2+ license header.
As per coding guidelines, "Add GPL license headers to all new PHP files; ensure all code/assets are GPLv2+ or GPL-compatible".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/e2e/helpers/calendar-ext-simulator-mu-plugin.php` around lines 1 - 19,
Add the repo-standard GPLv2+ header to the top of this new MU-plugin file (the
PHP file that defines SlimStat\Helpers\jddayofweek stub) by inserting the
project's canonical GPLv2-or-later comment block immediately after the opening
<?php (and before the existing file docblock), including copyright holder/year
and the standard "This program is free software..." text and license URL; ensure
the header matches other PHP files in the repo so the file is clearly licensed
GPLv2+.
- Add try/finally to Test 3 (bot exclusion) so uninstallHeaderInjector()
always runs even if an assertion throws
- Add flush_rewrite_rules() to CPT mu-plugin init callback so /product/{slug}/
pretty permalinks resolve immediately without a 404
- Replace all waitForTimeout(4000) with expect.poll() across all 5 tests;
inclusion tests poll until the record appears, exclusion tests poll to
confirm the count/resource stays at the expected value
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/e2e/exclusion-filters.spec.ts (1)
75-83: UnusedpostIdreturn value.
createProductPost()returns the inserted post ID, but callers on lines 117 and 151 assign it topostIdwithout using it. Either remove the assignment or add assertions to verify the correct post was created.♻️ Option 1: Remove unused assignment
// Create product post via direct DB insert const slug = `e2e-cpt-excl-${Date.now()}`; - const postId = await createProductPost('E2E CPT Exclusion Test', slug); + await createProductPost('E2E CPT Exclusion Test', slug);Also applies to: 115-117, 149-151
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/exclusion-filters.spec.ts` around lines 75 - 83, The helper createProductPost returns the inserted post ID but the callers that call createProductPost (assigning to postId) never use that value; either stop assigning the unused return or actually assert the created post ID/row exists. Update the test calls that invoke createProductPost (remove the "const postId = " assignment) or replace the assignment with an assertion that validates the insert (e.g., check result.insertId is a number or query wp_posts by ID/title) and keep createProductPost as-is to return the ID for verification.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/e2e/exclusion-filters.spec.ts`:
- Around line 249-251: The test currently hardcodes credentials in calls to
adminPage.fill('#user_login', 'parhumm') and adminPage.fill('#user_pass',
'testpass123'); replace these literals with environment-driven values (e.g.,
read from process.env.TEST_ADMIN_USER and process.env.TEST_ADMIN_PASS or your
test runner's config) and use those variables when calling adminPage.fill and
adminPage.click; update any test setup to fail fast with a clear error if the
env vars are missing and add entries for TEST_ADMIN_USER / TEST_ADMIN_PASS to
your test README or .env.example.
---
Nitpick comments:
In `@tests/e2e/exclusion-filters.spec.ts`:
- Around line 75-83: The helper createProductPost returns the inserted post ID
but the callers that call createProductPost (assigning to postId) never use that
value; either stop assigning the unused return or actually assert the created
post ID/row exists. Update the test calls that invoke createProductPost (remove
the "const postId = " assignment) or replace the assignment with an assertion
that validates the insert (e.g., check result.insertId is a number or query
wp_posts by ID/title) and keep createProductPost as-is to return the ID for
verification.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 703b0549-e7d2-4e18-9d99-8a05b347dae4
📒 Files selected for processing (1)
tests/e2e/exclusion-filters.spec.ts
| await adminPage.fill('#user_login', 'parhumm'); | ||
| await adminPage.fill('#user_pass', 'testpass123'); | ||
| await adminPage.click('#wp-submit'); |
There was a problem hiding this comment.
Extract hardcoded credentials to environment variables.
Embedding parhumm / testpass123 directly in source makes tests non-portable and risks accidental credential exposure if this pattern spreads. Use environment variables for test credentials.
🛠️ Proposed fix
- await adminPage.fill('#user_login', 'parhumm');
- await adminPage.fill('#user_pass', 'testpass123');
+ await adminPage.fill('#user_login', process.env.WP_ADMIN_USER ?? 'admin');
+ await adminPage.fill('#user_pass', process.env.WP_ADMIN_PASS ?? 'password');Then document the required environment variables in the test README or .env.example.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/e2e/exclusion-filters.spec.ts` around lines 249 - 251, The test
currently hardcodes credentials in calls to adminPage.fill('#user_login',
'parhumm') and adminPage.fill('#user_pass', 'testpass123'); replace these
literals with environment-driven values (e.g., read from
process.env.TEST_ADMIN_USER and process.env.TEST_ADMIN_PASS or your test
runner's config) and use those variables when calling adminPage.fill and
adminPage.click; update any test setup to fail fast with a clear error if the
env vars are missing and add entries for TEST_ADMIN_USER / TEST_ADMIN_PASS to
your test README or .env.example.
Human QA Checklist — PR #232
Setup
Change 1 —
|
…lidation 5 tests verifying the DataBuckets bounds fix (PR #232) addresses the "wrong stats" report where daily/weekly charts showed zero for today.
Summary
Fixes chart data not showing for users with hundreds of records in the database, but only a few visible in the chart. Root cause: an off-by-one bounds check in
DataBuckets::addRow()silently discarded records into a phantom bucket.Closes #231
Source: Support #14684
Changes
Bug fix —
src/Helpers/DataBuckets.php:209What was wrong:
$offset <= $this->pointsallowed$offset == $this->points(one past the last valid index). Records that hashed to this offset were accumulated into$datasets[points]— a bucket with no corresponding label. The chart renderer silently dropped them, so those records were invisible in the UI despite existing in the database.Fix:
$offset >= 0 && $offset < $this->points— only valid indices [0, points-1] are accepted.Note on -1: The
shiftDatasets()method uses -1 as a sentinel for partial-period data. The old code unintentionally accepted all negative offsets too; the new lower bound >= 0 correctly excludes them.E2E test suite —
tests/e2e/databuckets-custom-db-accuracy.spec.ts(new)6 tests, all passing (19.5s):
Test helper —
tests/e2e/helpers/custom-db-simulator-mu-plugin.php(new)Simulates the
slimstat_custom_wpdbfilter. Includes a fix for a shallow-clone pitfall: afterclone $GLOBALS["wpdb"], the protected$resultproperty (amysqli_resultresource) is reset viaReflectionProperty. Without this, both the clone and the original share the same closed result object, causing a fatal on the next query.Type of change
Summary by CodeRabbit
Bug Fixes
Documentation
Tests