Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions includes/class-wpvdb-rest.php
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,43 @@ public static function handle_metadata( \WP_REST_Request $request ) { // phpcs:i
* @return int|\WP_Error Row ID, or WP_Error on validation failure or DB insert failure.
*/
public static function insert_embedding_row( $doc_id, $chunk_id, $chunk_content, $summary, $embedding, $model = '', $doc_type = 'post', $chunk_index = null ) {
/**
* Filters whether a missing or invalid chunk_index is a hard error.
*
* Default false preserves the legacy behavior (warn, then default to 0).
* Return true to reject a null, non-integer, negative, or out-of-range
* chunk_index with a WP_Error instead of silently storing 0, so caller
* regressions fail loudly.
*
* @param bool $strict Whether to reject an invalid chunk_index. Default false.
*/
$strict_chunk_index = (bool) apply_filters( 'wpvdb_strict_chunk_index', false );
$valid_chunk_index = false;
if ( is_int( $chunk_index ) ) {
$valid_chunk_index = $chunk_index >= 0;
} elseif ( is_string( $chunk_index ) && preg_match( '/^\d+$/', $chunk_index ) ) {
$normalized_chunk_index = ltrim( $chunk_index, '0' );
$max_chunk_index = (string) PHP_INT_MAX;
$valid_chunk_index = '' === $normalized_chunk_index
|| strlen( $normalized_chunk_index ) < strlen( $max_chunk_index )
|| (
strlen( $normalized_chunk_index ) === strlen( $max_chunk_index )
&& strcmp( $normalized_chunk_index, $max_chunk_index ) <= 0
);
}
if ( $strict_chunk_index && ! $valid_chunk_index ) {
Logger::error( 'insert_embedding_row rejected invalid chunk_index for doc_id=' . $doc_id );
return new \WP_Error(
'chunk_index_invalid',
'Refused to store an embedding with a missing, non-integer, negative, or out-of-range chunk_index while strict mode is enabled.',
array(
'doc_id' => $doc_id,
'chunk_index' => $chunk_index,
'status' => 400,
)
);
}

// Detect callers using the legacy 5/6/7-arg signature; fall back to 0 for backward
// compatibility but emit a warning so the regression is visible.
if ( null === $chunk_index ) {
Expand Down
8 changes: 4 additions & 4 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
<testsuite name="integration">
<directory>tests/integration</directory>
</testsuite>
<testsuite name="api">
<directory>tests/api</directory>
</testsuite>
</testsuites>
<extensions>
<extension class="WPVDB_Filter_Reset_Hook" file="tests/filter-reset-hook.php" />
</extensions>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">includes</directory>
Expand All @@ -38,4 +38,4 @@
<const name="WP_TESTS_FORCE_KNOWN_BUGS" value="true" />
<server name="SERVER_NAME" value="http://example.org" />
</php>
</phpunit>
</phpunit>
89 changes: 86 additions & 3 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
echo "Using standalone testing mode.\n";

// Mock essential WordPress functions for testing
global $_wp_filters;
$_wp_filters = [];

Comment thread
rbcorrales marked this conversation as resolved.
if ( ! function_exists( 'add_action' ) ) {
function add_action( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
return true;
Expand All @@ -20,10 +23,45 @@ function add_action( $tag, $function_to_add, $priority = 10, $accepted_args = 1

if ( ! function_exists( 'add_filter' ) ) {
function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
global $_wp_filters;
if ( ! isset( $_wp_filters[ $tag ] ) ) {
$_wp_filters[ $tag ] = [];
}
if ( ! isset( $_wp_filters[ $tag ][ $priority ] ) ) {
$_wp_filters[ $tag ][ $priority ] = [];
}
$_wp_filters[ $tag ][ $priority ][] = [
'function' => $function_to_add,
'accepted_args' => $accepted_args,
];
return true;
}
}

if ( ! function_exists( 'remove_filter' ) ) {
function remove_filter( $tag, $function_to_remove, $priority = 10 ) {
global $_wp_filters;
if ( empty( $_wp_filters[ $tag ][ $priority ] ) ) {
return false;
}

foreach ( $_wp_filters[ $tag ][ $priority ] as $index => $filter ) {
if ( $filter['function'] === $function_to_remove ) {
unset( $_wp_filters[ $tag ][ $priority ][ $index ] );
if ( empty( $_wp_filters[ $tag ][ $priority ] ) ) {
unset( $_wp_filters[ $tag ][ $priority ] );
}
if ( empty( $_wp_filters[ $tag ] ) ) {
unset( $_wp_filters[ $tag ] );
}
return true;
}
}

return false;
}
Comment thread
rbcorrales marked this conversation as resolved.
}

if ( ! function_exists( 'do_action' ) ) {
function do_action( $hook_name, ...$args ) {
// Mock implementation
Expand All @@ -32,8 +70,22 @@ function do_action( $hook_name, ...$args ) {
}

if ( ! function_exists( 'apply_filters' ) ) {
function apply_filters( $tag, $value ) {
// Simple mock that just returns the value unchanged
function apply_filters( $tag, $value, ...$args ) {
global $_wp_filters;
if ( empty( $_wp_filters[ $tag ] ) ) {
return $value;
}

ksort( $_wp_filters[ $tag ] );
$filter_args = array_merge( [ $value ], $args );
foreach ( $_wp_filters[ $tag ] as $filters ) {
foreach ( $filters as $filter ) {
$accepted_args = max( 0, (int) $filter['accepted_args'] );
$value = call_user_func_array( $filter['function'], array_slice( $filter_args, 0, $accepted_args ) );
$filter_args[0] = $value;
}
}

return $value;
}
}
Expand Down Expand Up @@ -183,7 +235,24 @@ function has_action( $tag, $function_to_check = false ) {

if ( ! function_exists( 'has_filter' ) ) {
function has_filter( $tag, $function_to_check = false ) {
return false; // Mock - always return false
global $_wp_filters;
if ( empty( $_wp_filters[ $tag ] ) ) {
return false;
}

if ( false === $function_to_check ) {
return true;
}

foreach ( $_wp_filters[ $tag ] as $priority => $filters ) {
foreach ( $filters as $filter ) {
if ( $filter['function'] === $function_to_check ) {
return $priority;
}
}
}

return false;
}
}

Expand Down Expand Up @@ -266,6 +335,18 @@ function stripslashes_from_strings_only( $value ) {
define( 'REST_REQUEST', false );
}

if ( ! function_exists( 'wpvdb_is_sqlite' ) ) {
function wpvdb_is_sqlite() {
return false;
}
}

if ( ! function_exists( 'wpvdb_should_log_to_error_log' ) ) {
function wpvdb_should_log_to_error_log( $level = 'debug', $message = '', $context = array() ) {
return false;
}
}

if ( ! defined( 'OBJECT' ) ) {
define( 'OBJECT', 'OBJECT' );
}
Expand Down Expand Up @@ -435,8 +516,10 @@ public function suppress_errors( $suppress = true ) {
require_once dirname( __DIR__ ) . '/includes/class-wpvdb-providers.php';
require_once dirname( __DIR__ ) . '/includes/class-wpvdb-models.php';
require_once dirname( __DIR__ ) . '/includes/class-wpvdb-settings.php';
require_once dirname( __DIR__ ) . '/includes/class-wpvdb-cache.php';
require_once dirname( __DIR__ ) . '/includes/class-wpvdb-core.php';
require_once dirname( __DIR__ ) . '/includes/class-wpvdb-database.php';
require_once dirname( __DIR__ ) . '/includes/class-wpvdb-rest.php';
require_once dirname( __DIR__ ) . '/includes/class-wpvdb-embedding-enqueuer.php';
require_once dirname( __DIR__ ) . '/includes/class-wpvdb-queue.php';
require_once dirname( __DIR__ ) . '/includes/class-wpvdb-security.php';
41 changes: 41 additions & 0 deletions tests/filter-reset-hook.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php
/**
* PHPUnit hook for resetting standalone WordPress filter mocks.
*
* @package WPVDB
*/

use PHPUnit\Runner\AfterTestHook;
use PHPUnit\Runner\BeforeTestHook;

/**
* Reset the mocked WordPress filter registry around every test.
*/
class WPVDB_Filter_Reset_Hook implements BeforeTestHook, AfterTestHook {

/**
* Reset filters before each test starts.
*
* @param string $test Test name.
*/
public function executeBeforeTest( string $test ): void {
$this->reset_filters();
}

/**
* Reset filters after each test finishes.
*
* @param string $test Test name.
* @param float $time Test runtime.
*/
public function executeAfterTest( string $test, float $time ): void {
$this->reset_filters();
}

/**
* Reset the standalone filter registry.
*/
private function reset_filters(): void {
$GLOBALS['_wp_filters'] = array();
}
}
Loading
Loading