From 74d2f291172c41e1d44ded4a86d1621482c4c6e9 Mon Sep 17 00:00:00 2001 From: peptiderepo Date: Mon, 15 Jun 2026 09:30:05 +0000 Subject: [PATCH] Fix CI round 2: InterpolatedNotPrepared + executor WPCS + test stub class-prv-cost-rollup-query.php: - get_mtd_summary + get_model_breakdown: phpcs:disable/enable block for InterpolatedNotPrepared (single-line ignore doesn't cover multi-line string spans) class-prv-probe-run-executor.php: - Reduced to 290 lines (well under 300) - write_meta array calls: phpcs:disable ArrayItemNoNewLine for compact 2-per-line format; write_io try/catch explicit comment on empty catch - persist_result: expanded insert array to one item per line - Class docblock consolidated; property @var docblocks to one-line format - Closing class brace: removed extra blank line (PSR2 CloseBraceAfterBody) tests/bootstrap.php + test-cost-audit.php: - Added get_row() stub to stdClass_wpdb (PRV_Cost_Rollup_Query uses get_row for get_mtd_summary, not get_var) - Added wpdb_row key to initial state + prv_test_reset() - Test 1: seed wpdb_row with expected row data instead of wpdb_var --- includes/core/class-prv-cost-rollup-query.php | 8 +- .../core/class-prv-probe-run-executor.php | 143 ++++++++---------- tests/bootstrap.php | 3 + tests/unit/test-cost-audit.php | 10 +- 4 files changed, 79 insertions(+), 85 deletions(-) diff --git a/includes/core/class-prv-cost-rollup-query.php b/includes/core/class-prv-cost-rollup-query.php index 220c982..ca2bc01 100644 --- a/includes/core/class-prv-cost-rollup-query.php +++ b/includes/core/class-prv-cost-rollup-query.php @@ -42,10 +42,9 @@ class PRV_Cost_Rollup_Query { $table = PRV_Call_Meta_Table::get_table_name(); $month = gmdate( 'Y-m' ); - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $row = $wpdb->get_row( $wpdb->prepare( - // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "SELECT COALESCE(SUM(cost_usd),0) AS total_cost, COUNT(*) AS total_calls FROM {$table} WHERE DATE_FORMAT(captured_at, %s) = %s", @@ -54,6 +53,7 @@ class PRV_Cost_Rollup_Query { ), ARRAY_A ); + // phpcs:enable $total_cost = (float) ( $row['total_cost'] ?? 0.0 ); $total_calls = (int) ( $row['total_calls'] ?? 0 ); @@ -79,10 +79,9 @@ class PRV_Cost_Rollup_Query { $table = PRV_Call_Meta_Table::get_table_name(); $month = gmdate( 'Y-m' ); - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $rows = $wpdb->get_results( $wpdb->prepare( - // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "SELECT model, COUNT(*) AS calls, COALESCE(SUM(cost_usd),0) AS mtd_cost @@ -96,6 +95,7 @@ class PRV_Cost_Rollup_Query { ), ARRAY_A ); + // phpcs:enable $result = array(); foreach ( (array) $rows as $row ) { diff --git a/includes/core/class-prv-probe-run-executor.php b/includes/core/class-prv-probe-run-executor.php index 60994e0..de95e78 100644 --- a/includes/core/class-prv-probe-run-executor.php +++ b/includes/core/class-prv-probe-run-executor.php @@ -1,36 +1,20 @@ $peptide Peptide config. - * @param string $model Model slug. - * @param string $intent_tpl Intent template. - * @param string $query Rendered query. - * @param int $config_ver Config version. - * @param array &$counts Counts (modified). - * @param array &$model_outcomes Outcomes (modified). - * @param bool &$budget_hit Budget flag (modified). - * @return void Modifies $counts and $model_outcomes in-place. + * @param string $run_id Run UUID. + * @param array $peptide Peptide config. + * @param string $model Model slug. + * @param string $intent_tpl Intent template. + * @param string $query Rendered query string. + * @param int $config_ver Config version. + * @param array &$counts Counts accumulator. + * @param array &$model_outcomes Per-model outcome map. + * @param bool &$budget_hit Budget exhaustion flag. + * @return void */ private function probe_one( string $run_id, @@ -161,22 +143,23 @@ class PRV_Probe_Run_Executor { if ( isset( $model_outcomes[ $model ] ) ) { ++$model_outcomes[ $model ]['errors']; } - // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound, WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine - $call_id = $this->capture->write_meta( array( - 'visibility_row' => null, 'run_id' => $run_id, - 'peptide_slug' => $peptide['slug'], 'model' => $model, - 'intent_label' => $intent_tpl, 'tokens_in' => null, - 'tokens_out' => null, 'cost_usd' => 0.0, - 'latency_ms' => $latency_ms, 'cited' => null, - 'http_status' => $http_status, 'config_version' => $config_ver, - ) ); - // phpcs:enable - if ( $call_id > 0 ) { + // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine + $cid = $this->capture->write_meta( + array( + 'visibility_row' => null, 'run_id' => $run_id, + 'peptide_slug' => $peptide['slug'], 'model' => $model, + 'intent_label' => $intent_tpl, 'tokens_in' => null, + 'tokens_out' => null, 'cost_usd' => 0.0, + 'latency_ms' => $latency_ms, 'cited' => null, + 'http_status' => $http_status, 'config_version' => $config_ver, + ) + ); // phpcs:enable + if ( $cid > 0 ) { try { - $this->capture->write_io( $call_id, $query, '' ); - } catch ( \Throwable $io_err ) { + $this->capture->write_io( $cid, $query, '' ); + } catch ( \Throwable $e2 ) { // Best-effort: I/O capture failure must not propagate (P0-4). - unset( $io_err ); + unset( $e2 ); } } return; @@ -187,22 +170,23 @@ class PRV_Probe_Run_Executor { $this->ledger->update_row_cost( $row_id, $result->get_cost_usd() ); } - // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound, WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine - $call_id = $this->capture->write_meta( array( - 'visibility_row' => $row_id > 0 ? $row_id : null, 'run_id' => $run_id, - 'peptide_slug' => $peptide['slug'], 'model' => $model, - 'intent_label' => $intent_tpl, 'tokens_in' => $result->get_tokens_in(), - 'tokens_out' => $result->get_tokens_out(), 'cost_usd' => $result->get_cost_usd(), - 'latency_ms' => $latency_ms, 'cited' => $result->is_cited() ? 1 : 0, - 'http_status' => $http_status, 'config_version' => $config_ver, - ) ); - // phpcs:enable - if ( $call_id > 0 ) { + // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine + $cid = $this->capture->write_meta( + array( + 'visibility_row' => $row_id > 0 ? $row_id : null, 'run_id' => $run_id, + 'peptide_slug' => $peptide['slug'], 'model' => $model, + 'intent_label' => $intent_tpl, 'tokens_in' => $result->get_tokens_in(), + 'tokens_out' => $result->get_tokens_out(), 'cost_usd' => $result->get_cost_usd(), + 'latency_ms' => $latency_ms, 'cited' => $result->is_cited() ? 1 : 0, + 'http_status' => $http_status, 'config_version' => $config_ver, + ) + ); // phpcs:enable + if ( $cid > 0 ) { try { - $this->capture->write_io( $call_id, $query, $result->get_raw_excerpt() ); - } catch ( \Throwable $io_err ) { + $this->capture->write_io( $cid, $query, $result->get_raw_excerpt() ); + } catch ( \Throwable $e2 ) { // Best-effort: I/O capture failure must not propagate (P0-4). - unset( $io_err ); + unset( $e2 ); } } @@ -226,18 +210,25 @@ class PRV_Probe_Run_Executor { private function persist_result( string $run_id, array $peptide, string $model, string $intent_tpl, PRV_Probe_Result $result, int $config_ver ): int { global $wpdb; $table = PRV_Table_Manager::get_table_name(); - // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine - $wpdb->insert( $table, array( - 'run_id' => $run_id, 'captured_at' => current_time( 'mysql', true ), - 'peptide_slug' => $peptide['slug'], 'peptide_label' => $peptide['label'], - 'model' => $model, 'prompt_intent' => $intent_tpl, - 'cited' => $result->is_cited() ? 1 : 0, 'our_position' => $result->get_our_position(), - 'source_domains' => wp_json_encode( $result->get_source_domains() ), - 'raw_excerpt' => $result->get_raw_excerpt(), - 'cost_usd' => number_format( $result->get_cost_usd(), 8, '.', '' ), - 'config_version' => $config_ver, - ), array( '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%d', '%s', '%s', '%s', '%d' ) ); - // phpcs:enable + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery + $wpdb->insert( + $table, + array( + 'run_id' => $run_id, + 'captured_at' => current_time( 'mysql', true ), + 'peptide_slug' => $peptide['slug'], + 'peptide_label' => $peptide['label'], + 'model' => $model, + 'prompt_intent' => $intent_tpl, + 'cited' => $result->is_cited() ? 1 : 0, + 'our_position' => $result->get_our_position(), + 'source_domains' => wp_json_encode( $result->get_source_domains() ), + 'raw_excerpt' => $result->get_raw_excerpt(), + 'cost_usd' => number_format( $result->get_cost_usd(), 8, '.', '' ), + 'config_version' => $config_ver, + ), + array( '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%d', '%s', '%s', '%s', '%d' ) + ); return (int) $wpdb->insert_id; } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8cb7225..f3e5192 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -50,6 +50,7 @@ $GLOBALS['prv_test_state'] = [ 'wpdb_insert_id' => 1, 'wpdb_results' => [], 'wpdb_var' => null, + 'wpdb_row' => null, 'cron_events' => [], 'remote_posts' => [], 'wpdb_call_meta_rows' => [], @@ -65,6 +66,7 @@ function prv_test_reset(): void { 'wpdb_insert_id' => 1, 'wpdb_results' => [], 'wpdb_var' => null, + 'wpdb_row' => null, 'cron_events' => [], 'remote_posts' => [], 'wpdb_call_meta_rows' => [], @@ -260,6 +262,7 @@ class stdClass_wpdb { public function get_results( string $query, string $output = 'OBJECT' ): array { return is_array( $GLOBALS['prv_test_state']['wpdb_results'] ) ? $GLOBALS['prv_test_state']['wpdb_results'] : []; } + public function get_row( string $query, string $output = 'OBJECT' ): mixed { return $GLOBALS['prv_test_state']['wpdb_row'] ?? null; } public function get_var( string $query ): mixed { return $GLOBALS['prv_test_state']['wpdb_var']; } } diff --git a/tests/unit/test-cost-audit.php b/tests/unit/test-cost-audit.php index 9a7713c..4554265 100644 --- a/tests/unit/test-cost-audit.php +++ b/tests/unit/test-cost-audit.php @@ -14,16 +14,16 @@ echo "=== test-cost-audit ===\n"; prv_test_reset(); // ── Test 1: per-call cost sum reconciles to MTD via PRV_Cost_Rollup_Query ─ -// Seed the wpdb_var mock (returned by get_var) to simulate MTD total. +// Seed get_row mock (used by get_mtd_summary) and get_var (used by PRV_Cost_Ledger). +$GLOBALS['prv_test_state']['wpdb_row'] = array( 'total_cost' => '1.23456789', 'total_calls' => '5' ); $GLOBALS['prv_test_state']['wpdb_var'] = '1.23456789'; -$rollup = new PRV_Cost_Rollup_Query(); +$rollup = new PRV_Cost_Rollup_Query(); $summary = $rollup->get_mtd_summary(); prv_assert( $summary['total_cost'] > 0, 'get_mtd_summary returns positive cost when rows present' ); -// Reconcile: the same wpdb_var stub also underlies PRV_Cost_Ledger. -$ledger = new PRV_Cost_Ledger(); +// Reconcile: PRV_Cost_Ledger reads get_var; confirm it agrees with the seeded mock. +$ledger = new PRV_Cost_Ledger(); $mtd_via_ledger = $ledger->get_month_to_date_usd(); -// Both read the same mock; confirm they agree. prv_assert_equals( (float) '1.23456789', $mtd_via_ledger, 'PRV_Cost_Ledger MTD matches mock value' ); // ── Test 2: project_month_end returns a numeric value ────────────────────