Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package openQA for openSUSE:Factory checked in at 2025-09-14 18:50:21 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/openQA (Old) and /work/SRC/openSUSE:Factory/.openQA.new.1977 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "openQA" Sun Sep 14 18:50:21 2025 rev:746 rq:1304465 version:5.1757696527.61d51d58 Changes: -------- --- /work/SRC/openSUSE:Factory/openQA/openQA.changes 2025-09-12 21:10:58.018862572 +0200 +++ /work/SRC/openSUSE:Factory/.openQA.new.1977/openQA.changes 2025-09-14 18:50:57.880080797 +0200 @@ -1,0 +2,10 @@ +Fri Sep 12 19:18:15 UTC 2025 - [email protected] + +- Update to version 5.1757696527.61d51d58: + * Avoid partitioning in raid0 device + * Avoid "Server returned …" error if there is an actual error message + * Highlight invalid/problematic group config fields in UI when applying + * Improve validation of cleanup configuration + * AMQP: include list of failed modules in job.done messages + +------------------------------------------------------------------- Old: ---- openQA-5.1757597587.61c22a78.obscpio New: ---- openQA-5.1757696527.61d51d58.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ openQA-client-test.spec ++++++ --- /var/tmp/diff_new_pack.187OqO/_old 2025-09-14 18:50:58.840121028 +0200 +++ /var/tmp/diff_new_pack.187OqO/_new 2025-09-14 18:50:58.844121195 +0200 @@ -18,7 +18,7 @@ %define short_name openQA-client Name: %{short_name}-test -Version: 5.1757597587.61c22a78 +Version: 5.1757696527.61d51d58 Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ openQA-devel-test.spec ++++++ --- /var/tmp/diff_new_pack.187OqO/_old 2025-09-14 18:50:58.872122368 +0200 +++ /var/tmp/diff_new_pack.187OqO/_new 2025-09-14 18:50:58.872122368 +0200 @@ -18,7 +18,7 @@ %define short_name openQA-devel Name: %{short_name}-test -Version: 5.1757597587.61c22a78 +Version: 5.1757696527.61d51d58 Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ openQA-test.spec ++++++ --- /var/tmp/diff_new_pack.187OqO/_old 2025-09-14 18:50:58.896123374 +0200 +++ /var/tmp/diff_new_pack.187OqO/_new 2025-09-14 18:50:58.900123542 +0200 @@ -18,7 +18,7 @@ %define short_name openQA Name: %{short_name}-test -Version: 5.1757597587.61c22a78 +Version: 5.1757696527.61d51d58 Release: 0 Summary: Test package for openQA License: GPL-2.0-or-later ++++++ openQA-worker-test.spec ++++++ --- /var/tmp/diff_new_pack.187OqO/_old 2025-09-14 18:50:58.924124548 +0200 +++ /var/tmp/diff_new_pack.187OqO/_new 2025-09-14 18:50:58.928124715 +0200 @@ -18,7 +18,7 @@ %define short_name openQA-worker Name: %{short_name}-test -Version: 5.1757597587.61c22a78 +Version: 5.1757696527.61d51d58 Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ openQA.spec ++++++ --- /var/tmp/diff_new_pack.187OqO/_old 2025-09-14 18:50:58.964126223 +0200 +++ /var/tmp/diff_new_pack.187OqO/_new 2025-09-14 18:50:58.964126223 +0200 @@ -99,7 +99,7 @@ %define devel_requires %devel_no_selenium_requires chromedriver Name: openQA -Version: 5.1757597587.61c22a78 +Version: 5.1757696527.61d51d58 Release: 0 Summary: The openQA web-frontend, scheduler and tools License: GPL-2.0-or-later ++++++ openQA-5.1757597587.61c22a78.obscpio -> openQA-5.1757696527.61d51d58.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1757597587.61c22a78/assets/assetpack.def new/openQA-5.1757696527.61d51d58/assets/assetpack.def --- old/openQA-5.1757597587.61c22a78/assets/assetpack.def 2025-09-11 15:33:07.000000000 +0200 +++ new/openQA-5.1757696527.61d51d58/assets/assetpack.def 2025-09-12 19:02:07.000000000 +0200 @@ -127,6 +127,7 @@ < ../node_modules/datatables.net-bs5/js/dataTables.bootstrap5.min.js < ../node_modules/timeago/jquery.timeago.js < javascripts/index.js +< javascripts/render.js < javascripts/admintable.js < javascripts/admin_user.js < javascripts/admin_api_keys.js @@ -157,7 +158,6 @@ < ../node_modules/anser/lib/index.js ! test_result.js -< javascripts/render.js < javascripts/test_result.js < javascripts/needlediff.js < javascripts/running.js diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1757597587.61c22a78/assets/javascripts/job_templates.js new/openQA-5.1757696527.61d51d58/assets/javascripts/job_templates.js --- old/openQA-5.1757597587.61c22a78/assets/javascripts/job_templates.js 2025-09-11 15:33:07.000000000 +0200 +++ new/openQA-5.1757696527.61d51d58/assets/javascripts/job_templates.js 2025-09-12 19:02:07.000000000 +0200 @@ -505,6 +505,46 @@ form.find('.properties-status').html(result); } +// adds/removes "is-invalid"/"invalid-feedback" classes/elements within the specified form for the specified response +// returns the overall error with mentionings of internal field names replaced with labels from the form +function updateValidation(form, response) { + const E = createElement; + const errorsByField = response?.errors_by_field ?? {}; + const warningsByField = response?.warnings_by_field ?? {}; + const elements = Array.from(form.elements); + const labels = elements.filter(e => e.labels?.length > 0).map(e => [`'${e.name}'`, `"${e.labels[0].innerText}"`]); + const applyLabels = msg => labels.reduce((msg, label) => msg.replace(...label), msg); + const overallError = typeof response.error === 'string' ? applyLabels(response.error) : undefined; + elements.forEach(element => { + const fieldName = element.name; + if (fieldName.length === 0) { + return; + } + const errors = errorsByField[fieldName]; + const warnings = warningsByField[fieldName]; + const hasErrors = Array.isArray(errors) && errors.length > 0; + const hasWarnings = Array.isArray(warnings) && warnings.length > 0; + const parentElement = element.parentElement; + let feedbackElement = parentElement.querySelector('.invalid-feedback'); + element.classList[hasErrors || hasWarnings ? 'add' : 'remove']('is-invalid'); + element.classList[hasWarnings ? 'add' : 'remove']('is-invalid-non-critical'); + if (hasErrors || hasWarnings) { + if (feedbackElement === null) { + feedbackElement = E('div', [], {class: 'invalid-feedback'}); + parentElement.appendChild(feedbackElement); + } else { + feedbackElement.innerHTML = ''; + } + const addBadge = (className, msg) => feedbackElement.appendChild(E('span', [msg], {class: className})); + hasErrors && errors.map(applyLabels).forEach(addBadge.bind(undefined, 'badge text-bg-danger')); + hasWarnings && warnings.map(applyLabels).forEach(addBadge.bind(undefined, 'badge text-bg-warning')); + } else if (feedbackElement !== null) { + parentElement.removeChild(feedbackElement); + } + }); + return overallError; +} + function submitProperties(form) { var editorForm = $(form); editorForm.find('.buttons').hide(); @@ -523,9 +563,19 @@ }); }) .then(({response, json}) => { - if (!response.ok || json.error) - throw `Server returned ${response.status}: ${response.statusText}\n${json.error || ''}`; - showSubmitResults(editorForm, '<i class="fa fa-save"></i> Changes applied'); + const collapse = document.getElementById('show-advanced-cleanup-settings-button'); + if (json?.error?.includes('_jobs') && collapse.getAttribute('aria-expanded') !== 'true') { + collapse.click(); + } + const overallError = updateValidation(form, json); + if (overallError) throw overallError; + if (!response.ok) throw `Server returned ${response.status}: ${response.statusText}`; + const warnings = json?.warnings_by_field; + const remark = + typeof warnings === 'object' && Object.keys(warnings).length > 0 + ? ', but <strong>there are warnings</strong> (see highlighted fields)' + : ''; + showSubmitResults(editorForm, `<i class="fa fa-save"></i> Changes applied${remark}`); // show new name var newJobName = $('#editor-name').val(); @@ -538,10 +588,6 @@ $('td.prio input').attr('placeholder', defaultPrio); }) .catch(error => { - const collapse = document.getElementById('show-advanced-cleanup-settings-button'); - if (error.includes('_jobs') && collapse.getAttribute('aria-expanded') !== 'true') { - collapse.click(); - } showSubmitResults( editorForm, `<i class="fa fa-exclamation-circle"></i> Unable to apply changes: <strong>${error}</strong>` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1757597587.61c22a78/assets/stylesheets/forms.scss new/openQA-5.1757696527.61d51d58/assets/stylesheets/forms.scss --- old/openQA-5.1757597587.61c22a78/assets/stylesheets/forms.scss 2025-09-11 15:33:07.000000000 +0200 +++ new/openQA-5.1757696527.61d51d58/assets/stylesheets/forms.scss 2025-09-12 19:02:07.000000000 +0200 @@ -129,3 +129,10 @@ background-color: $default-bg-color-dark; } } + +// formatting for validation warnings +.is-invalid-non-critical { + border-color: var(--bs-warning) !important; + background-image: none !important; + padding-right: 0.5rem !important; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1757597587.61c22a78/contrib/ay-openqa-worker.xml.erb new/openQA-5.1757696527.61d51d58/contrib/ay-openqa-worker.xml.erb --- old/openQA-5.1757597587.61c22a78/contrib/ay-openqa-worker.xml.erb 2025-09-11 15:33:07.000000000 +0200 +++ new/openQA-5.1757696527.61d51d58/contrib/ay-openqa-worker.xml.erb 2025-09-12 19:02:07.000000000 +0200 @@ -3,19 +3,19 @@ <profile xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns"> <general> <mode> - <confirm config:type="boolean">false</confirm> + <confirm t="boolean">false</confirm> </mode> </general> <networking> <!-- okurz: 2023-06-13: For multiple network interfaces by default the last is configured, prefer the first --> - <interfaces config:type="list"> + <interfaces t="list"> <interface> <bootproto>dhcp</bootproto> <name>eth0</name> <startmode>auto</startmode> </interface> </interfaces> - <keep_install_network config:type="boolean">true</keep_install_network> + <keep_install_network t="boolean">true</keep_install_network> <routing t="map"> <ipv4_forward t="boolean">true</ipv4_forward> <ipv6_forward t="boolean">true</ipv6_forward> @@ -23,7 +23,7 @@ </networking> <% if os_release[:id] == 'opensuse-leap' %> <add-on> - <add_on_others config:type="list"> + <add_on_others t="list"> <listentry> <media_url>https://download.opensuse.org/distribution/leap/<%= os_release[:version] %>/repo/oss</media_url> <alias>repo-oss</alias> @@ -49,14 +49,14 @@ <% end %> <software> <% if os_release[:id] == 'opensuse-leap' %> - <products config:type="list"> + <products t="list"> <product>Leap</product> </products> <% end %> - <patterns config:type="list"> + <patterns t="list"> <pattern>kvm_server</pattern> </patterns> - <packages config:type="list"> + <packages t="list"> <package>openssh</package> <package>sudo</package> <package>salt-minion</package> @@ -67,16 +67,16 @@ <hwclock>UTC</hwclock> <timezone>Etc/UTC</timezone> </timezone> - <partitioning config:type="list"> + <partitioning t="list"> <drive> + <initialize t="boolean">true</initialize> <%# wipe out partition table before AutoYaST starts partition calculation %> <% disk = disks.sort_by { |d| d[:size] }.first %> <%# find the [smallest] disk for main OS installation %> <device><%= disk[:udev_names][0] %></device> <%# print the disk device name %> - <initialize config:type="boolean">true</initialize> - <partitions config:type="list"> + <partitions t="list"> <partition> <mount>/</mount> <size>max</size> - <filesystem config:type="symbol">btrfs</filesystem> + <filesystem t="symbol">btrfs</filesystem> </partition> <partition> <mount>swap</mount> @@ -89,12 +89,12 @@ <% if raidarray.length > 1 %> <%# if there is more than one disk remaining on the system, combine them into a raid array %> <% for d in 0..raidarray.length - 1 %> <drive> + <initialize t="boolean">true</initialize> <%# wipe out partition table before AutoYaST starts partition calculation %> <type t="symbol">CT_DISK</type> <device><%= raidarray[d][:udev_names][0] %></device> <%# print full device name such as /dev/nvme0n1 %> - <disklabel>none</disklabel> - <partitions config:type="list"> + <partitions t="list"> <partition> - <raid_name>/dev/md/openqa</raid_name> + <raid_name>/dev/md/openqa</raid_name> <%# device node used within automount systemd service var-lib-automount %> <size>max</size> </partition> </partitions> @@ -104,12 +104,6 @@ <drive> <type t="symbol">CT_MD</type> <device>/dev/md/openqa</device> - <partitions config:type="list"> - <partition> - <mount>/var/lib/openqa</mount> - <size>max</size> - </partition> - </partitions> <raid_options> <raid_type>raid0</raid_type> <device_order t="list"> @@ -123,27 +117,37 @@ <% else %> <%# maybe we have only one disk left on the system %> <% if raidarray.length != 0 %> <drive> - <type t="symbol">CT_DISK</type> + <initialize t="boolean">true</initialize> <%# wipe out partition table %> <device><%= raidarray[0][:udev_names][0] %></device> <%# print full device name such as /dev/nvme0n1 %> - <disklabel>gpt</disklabel> - <use>all</use> <partitions t="list"> <partition> + <%# create raid device even with a single disk to comply with systemd automount service %> + <raid_name>/dev/md/openqa</raid_name> <size>max</size> - <filesystem t="symbol">ext4</filesystem> - <mount>/var/lib/openqa</mount> </partition> </partitions> + <use>all</use> + </drive> + <drive> + <type t="symbol">CT_MD</type> + <device>/dev/md/openqa</device> + <raid_options> + <raid_type>raid0</raid_type> + <device_order t="list"> + <device><%= raidarray[0][:udev_names][0] %></device> + </device_order> + </raid_options> + <use>all</use> </drive> <% end %> <%# end inner if %> <% end %> <%# end outer if-else %> </partitioning> <scripts> - <post-scripts config:type="list"> + <post-scripts t="list"> <script> <filename>setup.sh</filename> <interpreter>shell</interpreter> - <debug config:type="boolean">true</debug> + <debug t="boolean">true</debug> <source><![CDATA[ echo 'roles: worker' > /etc/salt/grains ]]></source> @@ -151,13 +155,13 @@ </post-scripts> </scripts> <firewall> - <enable_firewall config:type="boolean">true</enable_firewall> - <start_firewall config:type="boolean">true</start_firewall> + <enable_firewall t="boolean">true</enable_firewall> + <start_firewall t="boolean">true</start_firewall> <default_zone>public</default_zone> - <zones config:type="list"> + <zones t="list"> <zone> <name>public</name> - <services config:type="list"> + <services t="list"> <service>ssh</service> </services> </zone> @@ -165,7 +169,7 @@ </firewall> <services-manager t="map"> <services> - <enable config:type="list"> + <enable t="list"> <service>sshd</service> <service>chronyd</service> </enable> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1757597587.61c22a78/lib/OpenQA/WebAPI/Controller/API/V1/JobGroup.pm new/openQA-5.1757696527.61d51d58/lib/OpenQA/WebAPI/Controller/API/V1/JobGroup.pm --- old/openQA-5.1757597587.61c22a78/lib/OpenQA/WebAPI/Controller/API/V1/JobGroup.pm 2025-09-11 15:33:07.000000000 +0200 +++ new/openQA-5.1757696527.61d51d58/lib/OpenQA/WebAPI/Controller/API/V1/JobGroup.pm 2025-09-12 19:02:07.000000000 +0200 @@ -189,6 +189,8 @@ sub _check_keep_logs_and_results ($self, $properties, $group = undef) { my @errors; + my %errors_by_field; + my %warnings_by_field; my $prefix = $self->is_parent ? 'default_' : ''; for my $important ('', '_important') { my $log_key = "${prefix}keep${important}_logs_in_days"; @@ -197,13 +199,42 @@ my $log_value = $properties->{$log_key} // ($group ? $group->$log_key : 0); my $result_value = $properties->{$result_key} // ($group ? $group->$result_key : 0); my $job_value = $properties->{$job_key} // ($group ? $group->$job_key : 0); - push @errors, "'$log_key' must be <= '$result_key'" if $result_value != 0 && $log_value > $result_value; - push @errors, "'$result_key' must be <= '$job_key'" if $job_value != 0 && $result_value > $job_value; + if ($result_value != 0 && $log_value > $result_value) { + push @{$errors_by_field{$log_key}}, "must be <= '$result_key'"; + push @errors, "'$log_key' must be <= '$result_key'"; + } + if ($job_value != 0 && $result_value > $job_value) { + push @{$errors_by_field{$result_key}}, "must be <= '$job_key'"; + push @errors, "'$result_key' must be <= '$job_key'"; + } } - $self->render(json => {error => join(', ', @errors)}, status => 400) if @errors; + for my $field (qw(logs results jobs)) { + my $important_key = "${prefix}keep_important_${field}_in_days"; + my $regular_key = "${prefix}keep_${field}_in_days"; + my $important_value = $properties->{$important_key} // ($group ? $group->$important_key : 0); + my $regular_value = $properties->{$regular_key} // ($group ? $group->$regular_key : 0); + if ($important_value != 0 && $important_value < $regular_value) { + push @{$warnings_by_field{$important_key}}, "should be >= '$regular_key'"; + } + } + $self->{_errors} = \@errors; + $self->{_errors_by_field} = \%errors_by_field; + $self->{_warnings_by_field} = \%warnings_by_field; return @errors == 0; } +sub _render_json ($self, $id = undef) { + my $errors = $self->{_errors}; + my $status = @$errors ? 400 : 200; + my %res = ( + errors_by_field => $self->{_errors_by_field} // {}, + warnings_by_field => $self->{_warnings_by_field} // {}, + ); + $res{id} = $id if defined $id; + $res{error} = join(', ', @$errors) if @$errors; + $self->render(json => \%res, status => $status); +} + =over 4 =item create() @@ -242,13 +273,13 @@ } my $properties = $self->load_properties; - return undef unless $self->_check_keep_logs_and_results($properties); + return $self->_render_json unless $self->_check_keep_logs_and_results($properties); my $id; try { $id = $self->resultset->create($properties)->id } catch ($e) { return $self->render(json => {error => $e}, status => 400) } $self->emit_event(openqa_jobgroup_create => {id => $id}); - $self->render(json => {id => $id}); + $self->_render_json($id); } =over 4 @@ -281,13 +312,13 @@ } my $properties = $self->load_properties; - return undef unless $self->_check_keep_logs_and_results($properties, $group); + return $self->_render_json unless $self->_check_keep_logs_and_results($properties, $group); my $id; try { $id = $group->update($properties)->id } catch ($e) { return $self->render(json => {error => $e}, status => 400) } $self->emit_event(openqa_jobgroup_update => {id => $id}); - $self->render(json => {id => $id}); + $self->_render_json($id); } =over 4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1757597587.61c22a78/lib/OpenQA/WebAPI/Plugin/AMQP.pm new/openQA-5.1757696527.61d51d58/lib/OpenQA/WebAPI/Plugin/AMQP.pm --- old/openQA-5.1757597587.61c22a78/lib/OpenQA/WebAPI/Plugin/AMQP.pm 2025-09-11 15:33:07.000000000 +0200 +++ new/openQA-5.1757696527.61d51d58/lib/OpenQA/WebAPI/Plugin/AMQP.pm 2025-09-12 19:02:07.000000000 +0200 @@ -104,6 +104,7 @@ if ($event_data->{bugref} = $bugref) { $event_data->{bugurl} = OpenQA::Utils::bugurl($bugref); } + $event_data->{failedmodules} = $job->failed_modules; } my $job_settings = $job->settings_hash; for my $detail (qw(ISO HDD_1)) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1757597587.61c22a78/t/23-amqp.t new/openQA-5.1757696527.61d51d58/t/23-amqp.t --- old/openQA-5.1757597587.61c22a78/t/23-amqp.t 2025-09-11 15:33:07.000000000 +0200 +++ new/openQA-5.1757696527.61d51d58/t/23-amqp.t 2025-09-12 19:02:07.000000000 +0200 @@ -98,6 +98,7 @@ MACHINE => 'RainbowPC', TEST => 'rainbow', bugref => undef, + failedmodules => [], group_id => undef, id => $job, newbuild => undef, @@ -139,6 +140,7 @@ TEST => 'kde', bugref => 'bsc#123', bugurl => 'https://bugzilla.suse.com/show_bug.cgi?id=123', + failedmodules => ['aplay'], group_id => 1001, id => 99963, newbuild => undef, @@ -182,6 +184,7 @@ MACHINE => 'RainbowPC', TEST => 'rainbow', bugref => undef, + failedmodules => [], group_id => undef, remaining => 1, }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1757597587.61c22a78/t/api/10-jobgroups.t new/openQA-5.1757696527.61d51d58/t/api/10-jobgroups.t --- old/openQA-5.1757597587.61c22a78/t/api/10-jobgroups.t 2025-09-11 15:33:07.000000000 +0200 +++ new/openQA-5.1757696527.61d51d58/t/api/10-jobgroups.t 2025-09-12 19:02:07.000000000 +0200 @@ -529,10 +529,12 @@ $params->{keep_results_name} => 1, $params->{keep_jobs_name} => 1 ); - my @expected_errors = ( - "'$params->{keep_logs_name}' must be <= '$params->{keep_results_name}'", - "'$params->{keep_important_logs_name}' must be <= '$params->{keep_important_results_name}'", + my %expected_errors_by_field = ( + $params->{keep_logs_name} => ["must be <= '$params->{keep_results_name}'"], + $params->{keep_important_logs_name} => ["must be <= '$params->{keep_important_results_name}'"], ); + my @fields = qw(keep_logs_name keep_important_logs_name); + my @expected_errors = map { "'$params->{$_}' $expected_errors_by_field{$params->{$_}}->[0]" } @fields; my %form_invalid_1 = ( %form_invalid, $params->{keep_important_logs_name} => 5, @@ -541,13 +543,32 @@ $t->post_ok($params->{endpoint}, form => \%form_invalid_1); $t->status_is(400, "creation fails if $params->{keep_results_name} lower than $params->{keep_logs_name}"); $t->json_is('/error' => join(', ', @expected_errors), "error message on invalid $test creation (1)"); - my %form_invalid_2 = (%form_invalid, $params->{keep_logs_name} => 1, $params->{keep_results_name} => 2); + $t->json_is( + '/errors_by_field' => \%expected_errors_by_field, + "errors by field on invalid $test creation (1)" + ); + $t->json_is('/warnings_by_field' => {}, "no warnings by field on invalid $test creation (1)"); + my %form_invalid_2 = ( + %form_invalid, $params->{keep_logs_name} => 1, + $params->{keep_important_logs_name} => 0, + $params->{keep_results_name} => 2, + $params->{keep_important_results_name} => 1 + ); $t->post_ok($params->{endpoint}, form => \%form_invalid_2); $t->status_is(400, "creation fails if $params->{keep_jobs_name} lower than $params->{keep_results_name}"); $t->json_is( '/error' => "'$params->{keep_results_name}' must be <= '$params->{keep_jobs_name}'", "error message on invalid $test creation (2)" ); + $t->json_is( + '/errors_by_field' => {$params->{keep_results_name} => ["must be <= '$params->{keep_jobs_name}'"]}, + "no warnings by field on invalid $test creation (2)" + ); + $t->json_is( + '/warnings_by_field' => + {$params->{keep_important_results_name} => ["should be >= '$params->{keep_results_name}'"]}, + "no warnings by field on invalid $test creation (2)" + ); $t->post_ok($params->{endpoint}, form => {name => $params->{name}}); $t->status_is(200, "can create $test without retention parameters"); return unless my $group_id = $t->tx->res->json->{id}; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1757597587.61c22a78/t/ui/13-admin.t new/openQA-5.1757696527.61d51d58/t/ui/13-admin.t --- old/openQA-5.1757597587.61c22a78/t/ui/13-admin.t 2025-09-11 15:33:07.000000000 +0200 +++ new/openQA-5.1757696527.61d51d58/t/ui/13-admin.t 2025-09-12 19:02:07.000000000 +0200 @@ -428,17 +428,28 @@ $ele = $driver->find_element_by_id('editor-keep-important-results-in-days'); $ele->send_keys(Selenium::Remote::WDKeys->KEYS->{control}, 'a'); $ele->send_keys('500'); + $ele = $driver->find_element_by_id('editor-keep-results-in-days'); + $ele->send_keys(Selenium::Remote::WDKeys->KEYS->{control}, 'a'); + $ele->send_keys('501'); is($driver->find_element('#properties p.buttons button.btn-primary')->get_attribute('disabled'), undef, 'group properties save button is enabled'); $driver->find_element_by_id('editor-carry-over-bugrefs')->click(); $driver->find_element('#properties p.buttons button.btn-primary')->click(); wait_for_ajax(msg => 'ensure there is no race condition, even though the page is reloaded'); + my $status = $driver->find_element('.properties-status')->get_text; + my $input = $driver->find_element('#editor-keep-important-results-in-days.is-invalid.is-invalid-non-critical'); + my $badge_sel = '#editor-keep-important-results-in-days ~ .invalid-feedback .badge.text-bg-warning'; + my $badge = $driver->find_element($badge_sel)->get_text; + ok $input, 'input is highlighted to show a non-critical validation issue'; + like $status, qr/Changes applied.*warnings/i, 'remark about warnings'; + like $badge, qr/should be >= "Keep results for"/i, 'badge with warning shown'; $driver->refresh(); $driver->title_is('openQA: Job templates for Cool Group has been edited!', 'new name on title'); $driver->find_element_by_id('toggle-group-properties')->click(); is element_prop('editor-name'), 'Cool Group has been edited!', 'name edited'; is element_prop('editor-size-limit'), '1000', 'size edited'; is element_prop('editor-keep-important-results-in-days'), '500', 'keep important results in days edited'; + is element_prop('editor-keep-results-in-days'), '501', 'keep results in days edited'; is element_prop('editor-default-priority'), '50', 'default priority should be the same'; ok !element_prop('editor-carry-over-bugrefs', 'checked'), 'bug carry over disabled'; ++++++ openQA.obsinfo ++++++ --- /var/tmp/diff_new_pack.187OqO/_old 2025-09-14 18:51:18.672952112 +0200 +++ /var/tmp/diff_new_pack.187OqO/_new 2025-09-14 18:51:18.676952279 +0200 @@ -1,5 +1,5 @@ name: openQA -version: 5.1757597587.61c22a78 -mtime: 1757597587 -commit: 61c22a780705673e7e7f3851258b010055803546 +version: 5.1757696527.61d51d58 +mtime: 1757696527 +commit: 61d51d581dd3fd1923856261bbed6dbfd384114a
