0\) \{.*?\n\t\}\n/s', $source, $block); test('the batched update_data_query_cache() loop body is present in lib/data_query.php', function () use ($block) { expect($block)->not->toBeEmpty(); }); $namespace = 'CactiTest\\UpdateDataQueryCacheBatching'; // eval() here only wraps PHP regex-extracted from this repo's own // lib/data_query.php (not external/user input) into a throwaway, namespaced // function. Test-only. Guarded by function_exists() so re-running this file // within the same process is safe. if (!function_exists($namespace . '\\run')) { eval(" namespace $namespace; function db_fetch_cell_prepared(\$sql, \$params = []) { return 5; // the poller_id every data_source in this scenario shares } function update_data_source_data_query_cache(\$id, \$host_id, \$data_query_id, \$index) { \$GLOBALS['__changed_check_calls'][] = \$id; return in_array(\$id, \$GLOBALS['__changed_ids'], true); } function update_poller_cache(\$data_source, \$commit, \$poller_id) { \$GLOBALS['__update_poller_cache_calls'][] = [\$data_source['id'], \$commit, \$poller_id]; return [\"tuple_{\$data_source['id']}\"]; } function poller_update_poller_cache_from_buffer(\$ids, \$items, \$poller_id) { \$GLOBALS['__flush_calls'][] = [\$ids, \$items, \$poller_id]; } function run(int \$host_id, int \$data_query_id, array \$data_sources) { {$block[0]} } "); } $run = $namespace . '\\run'; beforeEach(function () { $GLOBALS['__changed_check_calls'] = []; $GLOBALS['__update_poller_cache_calls'] = []; $GLOBALS['__flush_calls'] = []; }); test('every changed row calls update_poller_cache() with commit=false, not commit=true', function () use ($run) { $GLOBALS['__changed_ids'] = [1, 2, 3]; $dataSources = [ ['id' => 1, 'snmp_index' => '1'], ['id' => 2, 'snmp_index' => '2'], ['id' => 3, 'snmp_index' => '3'], ]; $run(10, 20, $dataSources); expect($GLOBALS['__update_poller_cache_calls'])->toHaveCount(3); foreach ($GLOBALS['__update_poller_cache_calls'] as $call) { [, $commit, $poller_id] = $call; expect($commit)->toBeFalse() ->and($poller_id)->toBe(5); } }); test('poller_update_poller_cache_from_buffer() is called exactly once for all changed rows, not once per row', function () use ($run) { $GLOBALS['__changed_ids'] = [1, 2, 3]; $dataSources = [ ['id' => 1, 'snmp_index' => '1'], ['id' => 2, 'snmp_index' => '2'], ['id' => 3, 'snmp_index' => '3'], ]; $run(10, 20, $dataSources); expect($GLOBALS['__flush_calls'])->toHaveCount(1); [$ids, $items, $poller_id] = $GLOBALS['__flush_calls'][0]; expect($ids)->toBe([1, 2, 3]) ->and($items)->toBe(['tuple_1', 'tuple_2', 'tuple_3']) ->and($poller_id)->toBe(5); }); test('unchanged rows are excluded from both the poller_items buffer and the flush id list', function () use ($run) { $GLOBALS['__changed_ids'] = [2]; $dataSources = [ ['id' => 1, 'snmp_index' => '1'], ['id' => 2, 'snmp_index' => '2'], ['id' => 3, 'snmp_index' => '3'], ]; $run(10, 20, $dataSources); // update_data_source_data_query_cache() is still called for every row // (that check is unrelated to batching), but only row 2 changed expect($GLOBALS['__changed_check_calls'])->toBe([1, 2, 3]) ->and($GLOBALS['__update_poller_cache_calls'])->toHaveCount(1); [$ids, $items] = $GLOBALS['__flush_calls'][0]; expect($ids)->toBe([2]) ->and($items)->toBe(['tuple_2']); }); test('no rows changed means no flush at all', function () use ($run) { $GLOBALS['__changed_ids'] = []; $dataSources = [ ['id' => 1, 'snmp_index' => '1'], ]; $run(10, 20, $dataSources); expect($GLOBALS['__update_poller_cache_calls'])->toBe([]) ->and($GLOBALS['__flush_calls'])->toBe([]); });