not->toBe(''); /* The function must look up the child host_id from data_local using * the new $local_data_id. The pre-fix code did not do this and * relied on $item['host_id'] from the parent row, which is 0 on a * template. */ expect($body) ->toContain('SELECT host_id') ->toContain('FROM data_local') ->toContain('[$local_data_id]'); /* The REPLACE INTO must use the function's own identity tuple * ($data_template_id, $local_data_id, $host_id) for the three * identity columns that previously came from the parent row. */ $replacePos = strpos($body, 'REPLACE INTO data_input_data'); expect($replacePos)->not->toBeFalse('REPLACE INTO data_input_data must remain present'); $insertSlice = substr($body, $replacePos, 800); /* Column count and placeholder count must match. */ expect(substr_count($insertSlice, '?')) ->toBe(7); /* The seven bound values must include the function's own variables, * not just $item[...] readbacks of the parent row. */ expect($insertSlice) ->toContain('$data_template_id,') ->toContain('$local_data_id,') ->toContain('$host_id,'); /* The pre-fix bind list pulled these from $item; that pattern must * not survive in the fixed code for the identity columns. */ expect(strpos($insertSlice, "\$item['local_data_id']"))->toBeFalse( 'must not bind $item[local_data_id] for the child row (template default is 0)' ); expect(strpos($insertSlice, "\$item['host_id']"))->toBeFalse( 'must not bind $item[host_id] for the child row (template default is 0)' ); }); test('api_data_source_duplicate INSERT column count matches placeholder count', function () use ($apiDataSourceSource) { $body = _extract_function_body($apiDataSourceSource, 'api_data_source_duplicate'); expect($body)->not->toBe(''); $insertPos = strpos($body, 'INSERT IGNORE INTO data_input_data'); expect($insertPos)->not->toBeFalse('INSERT IGNORE INTO data_input_data must remain present'); $insertSlice = substr($body, $insertPos, 800); /* Placeholder count must match the seven-column form the caller * already binds. The pre-fix code had four placeholders against * seven bound values; db_execute_prepared() rejects that. */ expect(substr_count($insertSlice, '?')) ->toBe(7); /* All seven columns must be named in the INSERT list. */ foreach (['data_input_field_id', 'data_template_data_id', 'data_template_id', 'local_data_id', 'host_id', 't_value', 'value'] as $col) { expect($insertSlice)->toContain($col); } }); test('1.2.x schema regression check: develop INSERT must not silently drop columns', function () use ($apiDataSourceSource, $templateSource) { /* If anyone re-narrows the develop INSERT/REPLACE column lists back * to the 1.2.x four-column form (data_input_field_id, * data_template_data_id, t_value, value) the column / placeholder * count check above will still pass on length but the cross-source * grammar drifts. Pin the seven-column shape explicitly so a * narrowing change has to delete the columns from the SQL string * rather than just re-shrink the bind array. */ foreach ([$templateSource, $apiDataSourceSource] as $source) { $pos = strpos($source, '(data_input_field_id, data_template_data_id, data_template_id, local_data_id, host_id, t_value, value)'); expect($pos)->not->toBeFalse('seven-column data_input_data write must remain present'); } });