Skip to content

Add unit tests for module admin controller and confirm template#1632

Merged
mambax7 merged 4 commits into
XOOPS:masterfrom
mambax7:master
Feb 26, 2026
Merged

Add unit tests for module admin controller and confirm template#1632
mambax7 merged 4 commits into
XOOPS:masterfrom
mambax7:master

Conversation

@mambax7

@mambax7 mambax7 commented Feb 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add MainControllerTest.php (54 tests) for modules/system/admin/modulesadmin/main.php — covers security/access control, operation routing, input sanitization, confirm/submit/list/install/update/uninstall/display operations, and edge cases
  • Add SystemModulesConfirmTemplateTest.php (39 tests) for system_modules_confirm.tpl — covers template structure, Smarty syntax, conditional logic, hidden fields, form controls, result display, table structure, and regression checks

Both use static code analysis (source file inspection) so they run without database or XOOPS bootstrap.

Based on CodeRabbit-generated tests from PR #1631, adapted for:

  • Correct dirname() path resolution for the project layout
  • XOOPS <{...}> Smarty delimiters
  • Aligned source code formatting ($old = style)
  • Adaptive colspan validation (works pre- and post-PR removed unused code #1631)

Test plan

  • All 93 tests pass locally with PHPUnit 11.5 / PHP 8.4
  • All 191 tests in tests/unit/htdocs/modules/system/ pass together
  • CI passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Added comprehensive unit and template tests for the system modules admin UI and flows (install/update/uninstall, confirmations, routing, security token handling, input/output sanitization, accessibility and edge cases).
    • Added a shared test utility to load and validate source artifacts and updated existing module admin tests to use this loader for more reliable, maintainable test coverage.

Static analysis tests for main.php (54 tests) covering security checks,
operation routing, input sanitization, and all CRUD operations. Template
tests for system_modules_confirm.tpl (39 tests) covering structure,
Smarty syntax, hidden fields, and regression checks.

Based on CodeRabbit suggestions from PR XOOPS#1631, adapted for correct path
resolution, XOOPS Smarty delimiters, and aligned source formatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 26, 2026 02:32
@coderabbitai

coderabbitai Bot commented Feb 26, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@mambax7 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 51 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 ab1bd27 and 2c5d870.

📒 Files selected for processing (1)
  • tests/unit/htdocs/modules/system/SourceFileTestTrait.php

Walkthrough

Adds comprehensive PHPUnit tests for the system modules admin controller and its confirmation template, and introduces a SourceFileTestTrait to centralize locating and loading source files. ModulesAdminTest is updated to use the new trait for source loading instead of manual path resolution.

Changes

Cohort / File(s) Summary
Modules admin controller tests
tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php
New, extensive unit test covering permission checks, security-token handling, operation routing (list/install/order/confirm/submit/display/update/uninstall/etc.), input sanitization, cache/file checks, template assignments, breadcrumb/header/footer calls, and many regression assertions.
Modules confirm template tests
tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php
New template test suite asserting Smarty syntax/structure, form/table layout, conditional branches (confirmation vs results), iteration/escaping of module data, hidden fields and operation/security token presence, button/link classes, and multiple integrity/formatting checks.
Test harness integration
tests/unit/htdocs/modules/system/ModulesAdminTest.php
Replaces manual multi-path file discovery with trait-based loading: now uses SourceFileTestTrait::loadSourceFile(...) and assigns sourceContent to the test’s sourceCode; removes previous filePath resolution logic.
Source file loader trait
tests/unit/htdocs/modules/system/SourceFileTestTrait.php
New trait providing protected string $sourceContent, protected string $filePath, and protected function loadSourceFile(string $relativePath, string $skipMessage = '') which searches repository candidate paths (including htdocs variations), sets $filePath, reads file content into $sourceContent, or skips the test if not found.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and concisely describes the main changes: adding unit tests for the module admin controller (MainControllerTest.php) and confirm template (SystemModulesConfirmTemplateTest.php).
Docstring Coverage ✅ Passed Docstring coverage is 96.94% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 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/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php`:
- Around line 889-900: The test method testFileHasValidPhpSyntax should
defensively handle missing exec() or unavailable PHP binary: check
function_exists('exec') and that PHP_BINARY is defined and executable
(is_executable(PHP_BINARY)) before calling exec(), and if either check fails
call $this->markTestSkipped with an explanatory message; when calling exec, use
escapeshellarg($this->filePath) with PHP_BINARY . ' -l' to run the syntax check
and keep the existing assertions on $returnCode and $output.
- Around line 19-25: Add the required docblock tags to the test class docblock
for MainControllerTest: update the class-level PHPDoc above class
MainControllerTest to include `@category`, `@package`, `@author`, `@copyright`,
`@license`, and `@link` entries (matching project conventions and values used
elsewhere in test files), ensuring the docblock format and tag order follow the
repository's coding guidelines.
- Around line 1128-1159: The extractOperationSection function contains nested
conditionals; refactor it to use early returns to satisfy PHPMD and improve
readability: keep the preg_match check (return '' if not matched), compute
$startPos, then compute $nextCase and $nextBreak and determine $endPos via a
sequence of simple if/return checks (e.g. if both false return '' or handle each
branch with an early return instead of nested if/elses), finally return
substr($this->sourceCode, $startPos, $endPos - $startPos); retain the same
behavior (including adding 6 for "break;") and reference the same symbol name
extractOperationSection to locate the change.

In
`@tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php`:
- Around line 19-25: Add the required PHPDoc tags to the class docblock for
SystemModulesConfirmTemplateTest: include `@category`, `@package`, `@author`,
`@copyright`, `@license`, and `@link`, placing them in the docblock immediately above
the class declaration (SystemModulesConfirmTemplateTest) to comply with the
project's coding guidelines; ensure each tag has appropriate values
(project/category name for `@category/`@package, author name/email for `@author`,
copyright holder and year for `@copyright`, SPDX or full text for `@license`, and a
relevant repository or project URL for `@link`).
- Around line 647-667: The testFooterUsesProperColspan method currently uses an
if/else; refactor to use an early-return to satisfy PHPMD by checking the
single-column case first: after computing $columnCount (via preg_match_all on
$this->templateContent), if $columnCount <= 1 perform the
assertStringNotContainsString('colspan="3"', $this->templateContent) and return;
otherwise perform the assertStringContainsString('colspan="'.$columnCount.'"',
$this->templateContent). Keep the same messages and variables but remove the
else block to flatten the control flow.
- Around line 748-764: Rename the misleading test method
testHiddenFieldsUseProperValueEscaping to something like
testHiddenFieldsUseProperValueSyntax (and update the docblock comment) because
the test only asserts the presence of Smarty variable syntax, not actual HTML
escaping; also update the assertion failure messages ('Should escape oldname in
hidden field value' and 'Should escape newname in hidden field value') to
reflect "Should use proper value syntax" (or similar) and leave the assertions
that check for 'value="<{$row.oldname}>"' and 'value="<{$row.newname}>"' against
$this->templateContent unchanged.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05cdfdd and f9568ad.

📒 Files selected for processing (2)
  • tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php
  • tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php

Comment on lines +889 to +900
public function testFileHasValidPhpSyntax(): void
{
$output = [];
$returnCode = 0;
exec('php -l ' . escapeshellarg($this->filePath) . ' 2>&1', $output, $returnCode);

$this->assertEquals(
0,
$returnCode,
'PHP syntax check failed: ' . implode("\n", $output)
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider defensive handling for exec() availability.

The exec() function may be disabled in certain PHP configurations (e.g., disable_functions in php.ini), and the php binary path isn't guaranteed in all CI environments. Adding a guard prevents unexpected failures.

🛡️ Proposed defensive check
 public function testFileHasValidPhpSyntax(): void
 {
+    if (!function_exists('exec') || !is_callable('exec')) {
+        $this->markTestSkipped('exec() is not available in this environment');
+    }
+
     $output = [];
     $returnCode = 0;
     exec('php -l ' . escapeshellarg($this->filePath) . ' 2>&1', $output, $returnCode);

     $this->assertEquals(
         0,
         $returnCode,
         'PHP syntax check failed: ' . implode("\n", $output)
     );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php`
around lines 889 - 900, The test method testFileHasValidPhpSyntax should
defensively handle missing exec() or unavailable PHP binary: check
function_exists('exec') and that PHP_BINARY is defined and executable
(is_executable(PHP_BINARY)) before calling exec(), and if either check fails
call $this->markTestSkipped with an explanatory message; when calling exec, use
escapeshellarg($this->filePath) with PHP_BINARY . ' -l' to run the syntax check
and keep the existing assertions on $returnCode and $output.

Comment on lines +748 to +764
/**
* Verify that hidden fields use proper value escaping.
*/
public function testHiddenFieldsUseProperValueEscaping(): void
{
$this->assertStringContainsString(
'value="<{$row.oldname}>"',
$this->templateContent,
'Should escape oldname in hidden field value'
);

$this->assertStringContainsString(
'value="<{$row.newname}>"',
$this->templateContent,
'Should escape newname in hidden field value'
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Test name may be misleading regarding "escaping".

The test verifies that the template uses Smarty variable syntax <{$row.oldname}>, but Smarty doesn't inherently escape output—that depends on the Smarty configuration (escape_html modifier or security policy). The test name and assertion messages suggest escaping is validated, which isn't quite accurate.

Consider renaming to testHiddenFieldsUseProperValueSyntax or similar for clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php`
around lines 748 - 764, Rename the misleading test method
testHiddenFieldsUseProperValueEscaping to something like
testHiddenFieldsUseProperValueSyntax (and update the docblock comment) because
the test only asserts the presence of Smarty variable syntax, not actual HTML
escaping; also update the assertion failure messages ('Should escape oldname in
hidden field value' and 'Should escape newname in hidden field value') to
reflect "Should use proper value syntax" (or similar) and leave the assertions
that check for 'value="<{$row.oldname}>"' and 'value="<{$row.newname}>"' against
$this->templateContent unchanged.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds comprehensive static analysis unit tests for the XOOPS module administration system. The tests use source code inspection to verify controller logic and template structure without requiring a database or full XOOPS bootstrap, enabling fast and isolated testing.

Changes:

  • Add 54 unit tests for modules/system/admin/modulesadmin/main.php covering security, operation routing, input sanitization, and all CRUD operations
  • Add 39 unit tests for system_modules_confirm.tpl covering template structure, Smarty syntax, form controls, and security token handling
  • Implement adaptive test logic that works both pre- and post-PR #1631 (which removed Action/Order columns)

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php Comprehensive static analysis tests for module admin controller including permission checks, security token validation, operation routing, input sanitization, and all module operations (list, install, update, uninstall, confirm, submit)
tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php Static analysis tests for confirmation template including HTML structure, Smarty syntax validation, form controls, security token handling, table structure, and adaptive colspan validation for PR #1631 compatibility

Comment thread tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php Outdated
…ication

The path resolution and file loading logic was duplicated across
ModulesAdminTest, MainControllerTest, and SystemModulesConfirmTemplateTest.
Extract into a reusable trait to drop below the 3% duplication threshold.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (3)
tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php (2)

22-28: ⚠️ Potential issue | 🟡 Minor

Add required class PHPDoc metadata tags.

The class-level docblock still misses mandatory tags required by repo standards.

📝 Suggested docblock update
 /**
  * Tests for main.php module administration controller
  *
  * These tests verify the behavior of the module administration interface
  * including operation routing, security token validation, parameter handling,
  * and data preparation for templates.
+ *
+ * `@category`   Test
+ * `@package`    Tests\Unit\System\ModulesAdmin
+ * `@author`     XOOPS Development Team
+ * `@copyright`  2000-2026 XOOPS Project (https://xoops.org)
+ * `@license`    GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
+ * `@link`       https://xoops.org
  */

As per coding guidelines: "Class docblocks must include @category, @package, @author, @copyright, @license, and @link tags."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php`
around lines 22 - 28, The class docblock for MainControllerTest is missing
required PHPDoc metadata; update the class-level comment above class
MainControllerTest to include the mandatory tags `@category`, `@package`, `@author`,
`@copyright`, `@license`, and `@link` with appropriate values (matching project
conventions), ensuring the docblock sits immediately above the "class
MainControllerTest" declaration.

869-874: ⚠️ Potential issue | 🟡 Minor

Guard syntax-lint execution for restricted environments.

exec('php -l ...') is unguarded; environments with disabled exec or non-executable PHP binary will fail this test for infra reasons, not code correctness.

🛡️ Suggested guard
     public function testFileHasValidPhpSyntax(): void
     {
+        if (!function_exists('exec') || !is_callable('exec')) {
+            $this->markTestSkipped('exec() is not available in this environment');
+        }
+        if (!defined('PHP_BINARY') || !is_executable(PHP_BINARY)) {
+            $this->markTestSkipped('PHP_BINARY is not available or not executable');
+        }
+
         $output = [];
         $returnCode = 0;
-        exec('php -l ' . escapeshellarg($this->filePath) . ' 2>&1', $output, $returnCode);
+        exec(PHP_BINARY . ' -l ' . escapeshellarg($this->filePath) . ' 2>&1', $output, $returnCode);

Use this read-only check to verify the current method lacks those guards:

#!/bin/bash
rg -n -C3 'function testFileHasValidPhpSyntax|exec\(|function_exists\(\x27exec\x27\)|PHP_BINARY|is_executable' tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php

Expected result: exec('php -l ...') appears, while guard checks for exec()/PHP_BINARY are absent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php`
around lines 869 - 874, The testFileHasValidPhpSyntax method directly calls
exec('php -l ' . escapeshellarg($this->filePath)) which can fail in restricted
CI/containers; guard the execution by checking function_exists('exec') and
is_executable(PHP_BINARY) (or existence of PHP_BINARY) before running exec, and
call $this->markTestSkipped with a clear message when the checks fail; locate
the exec invocation in testFileHasValidPhpSyntax and add the pre-checks there
(using PHP_BINARY, function_exists('exec'), and is_executable) to avoid false
failures.
tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php (1)

22-27: ⚠️ Potential issue | 🟡 Minor

Add the required class PHPDoc metadata tags.

The class docblock is missing required tags (@category, @author, @link, etc.), which is still a repo rule violation.

📝 Suggested docblock update
 /**
  * Tests for system_modules_confirm.tpl template
  *
  * These tests verify the template structure, Smarty syntax, variable usage,
  * and security token handling in the module confirmation template.
+ *
+ * `@category`   Test
+ * `@package`    Tests\Unit\System\Templates
+ * `@author`     XOOPS Development Team
+ * `@copyright`  2000-2026 XOOPS Project (https://xoops.org)
+ * `@license`    GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
+ * `@link`       https://xoops.org
  */

As per coding guidelines: "Class docblocks must include @category, @package, @author, @copyright, @license, and @link tags."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php`
around lines 22 - 27, Add a complete PHPDoc block above the
SystemModulesConfirmTemplateTest class that includes the required tags:
`@category`, `@package`, `@author`, `@copyright`, `@license`, and `@link`; ensure the
class-level docblock sits immediately before the class declaration for
SystemModulesConfirmTemplateTest and fills each tag with appropriate values
(project/category names, author name/email, copyright holder and year, license
identifier and URL, and a project or repo link) to satisfy the repository
docblock rules.
🤖 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/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php`:
- Around line 737-747: The second assertion in MainControllerTest duplicates the
first and doesn't validate a separate toggle path; update the second
assertStringContainsString on $displaySection to assert the distinct
block-toggle output instead of repeating "setVar('isactive', !\$old)". Replace
the redundant assertion with one that checks for the block-specific toggle
string (e.g. "setVar('blockactive', !\$old)" or the exact template fragment
emitted for the block toggle) using assertStringContainsString($needle,
$displaySection) so both toggle behaviors are verified.
- Around line 783-793: The test asserts currently expect raw-superglobal checks
in the generated $orderSection (e.g., "if (isset(\$_POST['mod']))" and "foreach
(\$_POST['mod'] as \$order)"); update the assertions to instead look for secure
input handling (for example usage of Xmf\Request::getVar('mod', ...) or
Xmf\FilterInput::clean(...)) and that the code iterates the filtered variable
(not $_POST directly). Locate references to $orderSection and any other similar
blocks (also those around lines 947-964) and change the assertions to assert
presence of Xmf\Request::getVar or Xmf\FilterInput::clean and iteration over the
resulting variable name rather than raw $_POST access.

In `@tests/unit/htdocs/modules/system/SourceFileTestTrait.php`:
- Around line 48-54: The candidate path generation loop that iterates $level and
appends $base . '/' . $relativePath (and variant without 'htdocs/') must be made
boundary-safe: compute realpath($base) and realpath($repoRoot) (or __DIR__
anchored repo root) and only add candidate entries when realpath($base) exists
and is a descendant of the repo root to prevent climbing above the repository;
also clamp the traversal by reducing the max $level to a repo-relevant depth
(don’t exceed the repo root) and apply realpath() to the final candidate before
pushing to $candidates so all path resolution uses canonical paths. Ensure the
same changes are applied to the similar block around lines 57-63.
- Around line 69-70: file_get_contents($this->filePath) can return false and
assigning it directly to the typed property $this->sourceContent will cause a
TypeError before the assertion runs; instead, read into a temporary variable
(e.g. $content = file_get_contents($this->filePath)), assert it is not false
(use $this->assertNotFalse($content, '...') or fail with a clear message), then
cast/assign the validated string to $this->sourceContent so the typed property
only ever receives a string.

In
`@tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php`:
- Around line 117-120: The code slices template branches using substr() based on
strpos() results without checking for false; update the extraction around
$modifsStart/$elsePos by first asserting both strpos() calls returned !== false
(e.g., check $modifsStart !== false and $elsePos !== false) before calling
substr(), and throw or fail the test with a clear message if either is false;
apply the same guarding pattern to the other branch-extraction block that uses
strpos() and substr() (the block around the later $modifsStart/$elsePos
variables at the other extraction) so no substr() is invoked with a false
offset.

---

Duplicate comments:
In `@tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php`:
- Around line 22-28: The class docblock for MainControllerTest is missing
required PHPDoc metadata; update the class-level comment above class
MainControllerTest to include the mandatory tags `@category`, `@package`, `@author`,
`@copyright`, `@license`, and `@link` with appropriate values (matching project
conventions), ensuring the docblock sits immediately above the "class
MainControllerTest" declaration.
- Around line 869-874: The testFileHasValidPhpSyntax method directly calls
exec('php -l ' . escapeshellarg($this->filePath)) which can fail in restricted
CI/containers; guard the execution by checking function_exists('exec') and
is_executable(PHP_BINARY) (or existence of PHP_BINARY) before running exec, and
call $this->markTestSkipped with a clear message when the checks fail; locate
the exec invocation in testFileHasValidPhpSyntax and add the pre-checks there
(using PHP_BINARY, function_exists('exec'), and is_executable) to avoid false
failures.

In
`@tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php`:
- Around line 22-27: Add a complete PHPDoc block above the
SystemModulesConfirmTemplateTest class that includes the required tags:
`@category`, `@package`, `@author`, `@copyright`, `@license`, and `@link`; ensure the
class-level docblock sits immediately before the class declaration for
SystemModulesConfirmTemplateTest and fills each tag with appropriate values
(project/category names, author name/email, copyright holder and year, license
identifier and URL, and a project or repo link) to satisfy the repository
docblock rules.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9568ad and d4231ef.

📒 Files selected for processing (4)
  • tests/unit/htdocs/modules/system/ModulesAdminTest.php
  • tests/unit/htdocs/modules/system/SourceFileTestTrait.php
  • tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php
  • tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php

Comment thread tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php Outdated
Comment on lines +783 to +793
$this->assertStringContainsString(
"if (isset(\$_POST['mod']))",
$orderSection,
'Should check for mod POST data'
);

$this->assertStringContainsString(
"foreach (\$_POST['mod'] as \$order)",
$orderSection,
'Should iterate over module order array'
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

These assertions lock in insecure input-handling patterns.

The tests currently enforce raw $_POST checks and variable-variable extraction patterns, which makes secure refactoring harder and conflicts with the project’s input-filtering rules.

As per coding guidelines: "All user input must be filtered using Xmf\Request::getVar() or Xmf\FilterInput::clean() — never access $_GET, $_POST, or $_REQUEST directly."

Also applies to: 947-964

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php`
around lines 783 - 793, The test asserts currently expect raw-superglobal checks
in the generated $orderSection (e.g., "if (isset(\$_POST['mod']))" and "foreach
(\$_POST['mod'] as \$order)"); update the assertions to instead look for secure
input handling (for example usage of Xmf\Request::getVar('mod', ...) or
Xmf\FilterInput::clean(...)) and that the code iterates the filtered variable
(not $_POST directly). Locate references to $orderSection and any other similar
blocks (also those around lines 947-964) and change the assertions to assert
presence of Xmf\Request::getVar or Xmf\FilterInput::clean and iteration over the
resulting variable name rather than raw $_POST access.

Comment thread tests/unit/htdocs/modules/system/SourceFileTestTrait.php
Comment thread tests/unit/htdocs/modules/system/SourceFileTestTrait.php Outdated
Comment on lines +117 to +120
$modifsStart = strpos($this->templateContent, '{if isset($modifs_mods)}');
$elsePos = strpos($this->templateContent, '{else}', $modifsStart);
$modifsBranch = substr($this->templateContent, $modifsStart, $elsePos - $modifsStart);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard strpos() results before slicing template branches.

Both branch-extraction paths compute substr() offsets from strpos() results without fully asserting they are not false. Add explicit assertions first to avoid misleading failures.

🧪 Suggested hardening
         $modifsStart = strpos($this->templateContent, '{if isset($modifs_mods)}');
+        $this->assertNotFalse($modifsStart, 'Should have modifs_mods conditional start');
         $elsePos = strpos($this->templateContent, '{else}', $modifsStart);
+        $this->assertNotFalse($elsePos, 'Should have else branch for modifs_mods conditional');
         $modifsBranch = substr($this->templateContent, $modifsStart, $elsePos - $modifsStart);
@@
         $elsePos = strpos($this->templateContent, '{else}', $formEnd);
         $this->assertNotFalse($elsePos, 'Should have else branch after form');
         $endifPos = strpos($this->templateContent, '{/if}', $elsePos);
+        $this->assertNotFalse($endifPos, 'Should have closing {/if} after else branch');
         $elseBranch = substr($this->templateContent, $elsePos, $endifPos - $elsePos);

Also applies to: 151-152

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php`
around lines 117 - 120, The code slices template branches using substr() based
on strpos() results without checking for false; update the extraction around
$modifsStart/$elsePos by first asserting both strpos() calls returned !== false
(e.g., check $modifsStart !== false and $elsePos !== false) before calling
substr(), and throw or fail the test with a clear message if either is false;
apply the same guarding pattern to the other branch-extraction block that uses
strpos() and substr() (the block around the later $modifsStart/$elsePos
variables at the other extraction) so no substr() is invoked with a false
offset.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

Comment thread tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php Outdated
Comment on lines 18 to +20

require_once __DIR__ . '/SourceFileTestTrait.php';
use Tests\Unit\System\SourceFileTestTrait;

Copilot AI Feb 26, 2026

Copy link

Choose a reason for hiding this comment

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

use import statements must appear before any non-declare/namespace statements. Here require_once precedes use Tests\Unit\System\SourceFileTestTrait;, which will cause a PHP parse error. Move the require_once below the use statements (but still before the class definition), or drop the use import and reference the trait with a fully-qualified name inside the class.

Suggested change
require_once __DIR__ . '/SourceFileTestTrait.php';
use Tests\Unit\System\SourceFileTestTrait;
use Tests\Unit\System\SourceFileTestTrait;
require_once __DIR__ . '/SourceFileTestTrait.php';

Copilot uses AI. Check for mistakes.
Comment thread tests/unit/htdocs/modules/system/SourceFileTestTrait.php Outdated
Comment on lines +18 to +20

require_once dirname(__DIR__) . '/SourceFileTestTrait.php';
use Tests\Unit\System\SourceFileTestTrait;

Copilot AI Feb 26, 2026

Copy link

Choose a reason for hiding this comment

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

use import statements must appear before any non-declare/namespace statements. Here require_once precedes use Tests\Unit\System\SourceFileTestTrait;, which will cause a PHP parse error. Move the require_once below the use statements (but still before the class definition), or drop the use import and reference the trait with a fully-qualified name inside the class.

Suggested change
require_once dirname(__DIR__) . '/SourceFileTestTrait.php';
use Tests\Unit\System\SourceFileTestTrait;
use Tests\Unit\System\SourceFileTestTrait;
require_once dirname(__DIR__) . '/SourceFileTestTrait.php';

Copilot uses AI. Check for mistakes.
public function testIncludesSystemHeader(): void
{
$this->assertStringContainsString(
'{include file="db:system_header.tpl"}',

Copilot AI Feb 26, 2026

Copy link

Choose a reason for hiding this comment

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

These assertions use {...} Smarty tags, but the actual system_modules_confirm.tpl uses XOOPS <{...}> delimiters (e.g. <{include ...}>, <{if ...}>). As written, tests like testIncludesSystemHeader() will fail. Update expected strings/regexes to match the template’s <{...}> syntax (or make the tests accept both syntaxes consistently).

Suggested change
'{include file="db:system_header.tpl"}',
'<{include file="db:system_header.tpl"}>',

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +20

require_once dirname(__DIR__, 2) . '/SourceFileTestTrait.php';
use Tests\Unit\System\SourceFileTestTrait;

Copilot AI Feb 26, 2026

Copy link

Choose a reason for hiding this comment

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

use import statements must appear before any non-declare/namespace statements. Here require_once precedes use Tests\Unit\System\SourceFileTestTrait;, which will cause a PHP parse error. Move the require_once below the use statements (but still before the class definition), or drop the use import and reference the trait with a fully-qualified name inside the class.

Suggested change
require_once dirname(__DIR__, 2) . '/SourceFileTestTrait.php';
use Tests\Unit\System\SourceFileTestTrait;
use Tests\Unit\System\SourceFileTestTrait;
require_once dirname(__DIR__, 2) . '/SourceFileTestTrait.php';

Copilot uses AI. Check for mistakes.
- Bound path resolution to repo root (.git) to prevent directory traversal
- Initialize trait properties with defaults to avoid uninitialized state
- Add PHPDoc class tags (@category, @author, @copyright, @license, @link)
- Guard exec() call, simplify extractOperationSection with match expression
- Remove duplicate assertion, add strpos guards before substr
- Rename legacy POST test with security warning docblock
- Fix method name typo, use early return pattern, rename escaping test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
tests/unit/htdocs/modules/system/SourceFileTestTrait.php (1)

82-83: ⚠️ Potential issue | 🟡 Minor

Handle file_get_contents() failure before assigning to typed string property.

file_get_contents() returns string|false. Assigning false directly to the string-typed $sourceContent property will throw a TypeError in PHP 8.0+ before your assertion executes.

🛠️ Suggested fix
-        $this->sourceContent = file_get_contents($this->filePath);
+        $content = file_get_contents($this->filePath);
+        $this->assertNotFalse($content, 'Failed to read source file: ' . basename($this->filePath));
+        $this->sourceContent = $content;
         $this->assertNotEmpty($this->sourceContent, 'Source file should not be empty');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/htdocs/modules/system/SourceFileTestTrait.php` around lines 82 -
83, file_get_contents($this->filePath) can return false and assigning it
directly to the string-typed property $sourceContent will cause a TypeError
before the assertion runs; change the code to first capture the result into an
untyped local (e.g. $content = file_get_contents($this->filePath)), check that
$content !== false (and if false call $this->fail or an assertion with a clear
message that includes $this->filePath), then assign the validated string to
$this->sourceContent and proceed to $this->assertNotEmpty($this->sourceContent,
'Source file should not be empty').
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/unit/htdocs/modules/system/SourceFileTestTrait.php`:
- Around line 82-83: file_get_contents($this->filePath) can return false and
assigning it directly to the string-typed property $sourceContent will cause a
TypeError before the assertion runs; change the code to first capture the result
into an untyped local (e.g. $content = file_get_contents($this->filePath)),
check that $content !== false (and if false call $this->fail or an assertion
with a clear message that includes $this->filePath), then assign the validated
string to $this->sourceContent and proceed to
$this->assertNotEmpty($this->sourceContent, 'Source file should not be empty').

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4231ef and ab1bd27.

📒 Files selected for processing (3)
  • tests/unit/htdocs/modules/system/SourceFileTestTrait.php
  • tests/unit/htdocs/modules/system/admin/modulesadmin/MainControllerTest.php
  • tests/unit/htdocs/modules/system/templates/SystemModulesConfirmTemplateTest.php

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@mambax7 mambax7 merged commit 05021a7 into XOOPS:master Feb 26, 2026
14 of 15 checks passed
@codecov

codecov Bot commented Feb 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (87765a0) to head (2c5d870).
⚠️ Report is 28 commits behind head on master.

Additional details and impacted files
@@      Coverage Diff       @@
##   master   #1632   +/-   ##
==============================
==============================

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

2 participants