20, 'step' => 60, 'steps' => 5, 'rows' => 1, 'timespan' => 300], ['local_data_id' => 10, 'step' => 30, 'steps' => 2, 'rows' => 500000000, 'timespan' => 30000000000], ['local_data_id' => 20, 'step' => 60, 'steps' => 1, 'rows' => 500000000, 'timespan' => 30000000000], ]; expect(rrdtool_function_get_resstep_under_test([10, 20, 10], 1, 2))->toBe(60) ->and($GLOBALS['resolution_step_queries'])->toHaveCount(1) ->and($GLOBALS['resolution_step_queries'][0][0])->toContain('dtd.local_data_id IN (?, ?)') ->and($GLOBALS['resolution_step_queries'][0][1])->toBe([10, 20]); }); test('step mode preserves caller data source order and skips expired archives', function () { $GLOBALS['resolution_step_rows'] = [ ['local_data_id' => 4, 'step' => 120, 'steps' => 1, 'rows' => 500000000, 'timespan' => 60000000000], ['local_data_id' => 3, 'step' => 30, 'steps' => 1, 'rows' => 1, 'timespan' => 30], ]; expect(rrdtool_function_get_resstep_under_test([3, 4], 1, 2, 'step'))->toBe(120); }); test('negative graph times are normalized before retention matching', function () { $GLOBALS['resolution_step_rows'] = [ ['local_data_id' => 7, 'step' => 60, 'steps' => 1, 'rows' => 1000, 'timespan' => 60000], ]; expect(rrdtool_function_get_resstep_under_test(7, -300, -1))->toBe(60); }); test('empty and unmatched data source sets return zero', function () { expect(rrdtool_function_get_resstep_under_test([], 1, 2))->toBe(0) ->and($GLOBALS['resolution_step_queries'])->toBe([]); $GLOBALS['resolution_step_rows'] = [ ['local_data_id' => 8, 'step' => 60, 'steps' => 1, 'rows' => 1, 'timespan' => 60], ]; expect(rrdtool_function_get_resstep_under_test(8, 1, 2))->toBe(0); });