Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package openQA for openSUSE:Factory checked 
in at 2026-03-09 16:32:48
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/openQA (Old)
 and      /work/SRC/openSUSE:Factory/.openQA.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "openQA"

Mon Mar  9 16:32:48 2026 rev:819 rq:1337659 version:5.1773056733.e071deaf

Changes:
--------
--- /work/SRC/openSUSE:Factory/openQA/openQA.changes    2026-03-06 
18:18:35.232144411 +0100
+++ /work/SRC/openSUSE:Factory/.openQA.new.8177/openQA.changes  2026-03-09 
16:33:01.982428227 +0100
@@ -1,0 +2,12 @@
+Mon Mar 09 13:08:18 UTC 2026 - [email protected]
+
+- Update to version 5.1773056733.e071deaf:
+  * feat: allow filtering by job result and state in /tests/latest
+  * style(gitlint): allow unwrappable longer URLs in git commit message body
+  * feat: unify priority management of max_job_time and throttling
+  * ci(helm): pull container images in advance
+  * ci(helm): make sure that install-chart runs after lint-chart
+  * feat: optimize size of devel:openQA:ci/base container
+  * feat: allow users to delete/anonymize their own account
+
+-------------------------------------------------------------------

Old:
----
  openQA-5.1772722702.3877b2ca.obscpio

New:
----
  openQA-5.1773056733.e071deaf.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ openQA-client-test.spec ++++++
--- /var/tmp/diff_new_pack.zfvx9V/_old  2026-03-09 16:33:04.502530980 +0100
+++ /var/tmp/diff_new_pack.zfvx9V/_new  2026-03-09 16:33:04.506531143 +0100
@@ -18,7 +18,7 @@
 
 %define         short_name openQA-client
 Name:           %{short_name}-test
-Version:        5.1772722702.3877b2ca
+Version:        5.1773056733.e071deaf
 Release:        0
 Summary:        Test package for %{short_name}
 License:        GPL-2.0-or-later

++++++ openQA-devel-test.spec ++++++
--- /var/tmp/diff_new_pack.zfvx9V/_old  2026-03-09 16:33:04.762541582 +0100
+++ /var/tmp/diff_new_pack.zfvx9V/_new  2026-03-09 16:33:04.790542723 +0100
@@ -18,7 +18,7 @@
 
 %define         short_name openQA-devel
 Name:           %{short_name}-test
-Version:        5.1772722702.3877b2ca
+Version:        5.1773056733.e071deaf
 Release:        0
 Summary:        Test package for %{short_name}
 License:        GPL-2.0-or-later

++++++ openQA-test.spec ++++++
--- /var/tmp/diff_new_pack.zfvx9V/_old  2026-03-09 16:33:05.134556750 +0100
+++ /var/tmp/diff_new_pack.zfvx9V/_new  2026-03-09 16:33:05.150557402 +0100
@@ -18,7 +18,7 @@
 
 %define         short_name openQA
 Name:           %{short_name}-test
-Version:        5.1772722702.3877b2ca
+Version:        5.1773056733.e071deaf
 Release:        0
 Summary:        Test package for openQA
 License:        GPL-2.0-or-later

++++++ openQA-worker-test.spec ++++++
--- /var/tmp/diff_new_pack.zfvx9V/_old  2026-03-09 16:33:05.426568656 +0100
+++ /var/tmp/diff_new_pack.zfvx9V/_new  2026-03-09 16:33:05.442569308 +0100
@@ -18,7 +18,7 @@
 
 %define         short_name openQA-worker
 Name:           %{short_name}-test
-Version:        5.1772722702.3877b2ca
+Version:        5.1773056733.e071deaf
 Release:        0
 Summary:        Test package for %{short_name}
 License:        GPL-2.0-or-later

++++++ openQA.spec ++++++
--- /var/tmp/diff_new_pack.zfvx9V/_old  2026-03-09 16:33:05.766582520 +0100
+++ /var/tmp/diff_new_pack.zfvx9V/_new  2026-03-09 16:33:05.806584150 +0100
@@ -99,7 +99,7 @@
 %define devel_requires %devel_no_selenium_requires chromedriver
 
 Name:           openQA
-Version:        5.1772722702.3877b2ca
+Version:        5.1773056733.e071deaf
 Release:        0
 Summary:        The openQA web-frontend, scheduler and tools
 License:        GPL-2.0-or-later

++++++ openQA-5.1772722702.3877b2ca.obscpio -> 
openQA-5.1773056733.e071deaf.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1772722702.3877b2ca/.github/workflows/check-helm-chart.yml 
new/openQA-5.1773056733.e071deaf/.github/workflows/check-helm-chart.yml
--- old/openQA-5.1772722702.3877b2ca/.github/workflows/check-helm-chart.yml     
2026-03-05 15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/.github/workflows/check-helm-chart.yml     
2026-03-09 12:45:33.000000000 +0100
@@ -26,6 +26,9 @@
         run: make test-helm-lint
   install-chart:
     runs-on: ubuntu-latest
+    needs: lint-chart
+    env:
+      REGISTRY_PATH: registry.opensuse.org/devel/openqa/containers16.0
     steps:
       - name: Checkout repository
         uses: actions/checkout@v4
@@ -46,5 +49,16 @@
       - name: Create kind cluster
         uses: helm/kind-action@v1
 
+      - name: Download images
+        run: |
+          RETRY=3 SLEEP=15 tools/retry docker pull 
$REGISTRY_PATH/openqa_webui:latest
+          RETRY=3 SLEEP=15 tools/retry docker pull 
$REGISTRY_PATH/openqa_worker:latest
+
+      - name: Load images into the Cluster
+        run: |
+          kind load docker-image --name chart-testing \
+          $REGISTRY_PATH/openqa_webui:latest \
+          $REGISTRY_PATH/openqa_worker:latest
+
       - name: Run chart-testing
-        run: make RETRY=3 test-helm-install
+        run: make test-helm-install
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1772722702.3877b2ca/.gitlint 
new/openQA-5.1773056733.e071deaf/.gitlint
--- old/openQA-5.1772722702.3877b2ca/.gitlint   2026-03-05 15:58:22.000000000 
+0100
+++ new/openQA-5.1773056733.e071deaf/.gitlint   2026-03-09 12:45:33.000000000 
+0100
@@ -1,3 +1,8 @@
 [general]
 contrib=contrib-title-conventional-commits
 ignore=body-min-length,body-is-missing
+regex-style-search=True
+
+[ignore-body-lines]
+# Accept lines stating only a long unwrappable URL
+regex=^https?:\/\/\S+$
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1772722702.3877b2ca/assets/assetpack.def 
new/openQA-5.1773056733.e071deaf/assets/assetpack.def
--- old/openQA-5.1772722702.3877b2ca/assets/assetpack.def       2026-03-05 
15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/assets/assetpack.def       2026-03-09 
12:45:33.000000000 +0100
@@ -131,6 +131,7 @@
 < javascripts/admintable.js
 < javascripts/admin_user.js
 < javascripts/admin_api_keys.js
+< javascripts/delete_account.js
 < javascripts/admin_needle.js
 < javascripts/admin_worker.js
 < javascripts/admin_groups.js
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1772722702.3877b2ca/assets/javascripts/delete_account.js 
new/openQA-5.1773056733.e071deaf/assets/javascripts/delete_account.js
--- old/openQA-5.1772722702.3877b2ca/assets/javascripts/delete_account.js       
1970-01-01 01:00:00.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/assets/javascripts/delete_account.js       
2026-03-09 12:45:33.000000000 +0100
@@ -0,0 +1,21 @@
+function setup_delete_account() {
+  const input = document.getElementById('confirm-delete');
+  const btn = document.getElementById('confirm-delete-btn');
+  if (!input || !btn) return;
+
+  input.addEventListener('input', function () {
+    btn.disabled = input.value !== input.dataset.expected;
+  });
+
+  btn.addEventListener('click', function () {
+    const deleteUrl = btn.dataset.deleteUrl;
+    const redirectUrl = btn.dataset.redirectUrl;
+    fetchWithCSRF(deleteUrl, {method: 'DELETE'}).then(function (response) {
+      if (response.ok) {
+        window.location.href = redirectUrl;
+      } else {
+        alert('Failed to delete account. Please try again.');
+      }
+    });
+  });
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1772722702.3877b2ca/container/devel:openQA:ci/base/Dockerfile 
new/openQA-5.1773056733.e071deaf/container/devel:openQA:ci/base/Dockerfile
--- old/openQA-5.1772722702.3877b2ca/container/devel:openQA:ci/base/Dockerfile  
2026-03-05 15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/container/devel:openQA:ci/base/Dockerfile  
2026-03-09 12:45:33.000000000 +0100
@@ -6,18 +6,15 @@
 #!NoSquash
 FROM opensuse/leap:16.0
 
-# only dependencies for CircleCI to be able to load/save the package cache
+# dependencies for CircleCI to be able to load/save the package cache
 # and fix permissions for the unprivileged user we use
-RUN zypper -n in tar gzip sudo
-
-# these are autoinst dependencies
-RUN zypper install -y gcc-c++ cmake ninja pkgconfig\(opencv4\) pkg-config 
perl\(Module::CPANfile\) pkgconfig\(fftw3\) pkgconfig\(libpng\) 
pkgconfig\(sndfile\) pkgconfig\(theoraenc\) tesseract-ocr
-
-# openQA dependencies
-RUN zypper install -y perl-CSS-Sass ruby-devel npm python3-base 
python3-requests git-core rsync curl 'postgresql-devel>=14' 
'postgresql-server>=14' qemu qemu-tools tar xorg-x11-fonts sudo make
-
-# openQA chromedriver for Selenium tests
-RUN zypper install -y chromedriver
+# + autoinst dependencies
+# + openQA dependencies
+# + openQA chromedriver for Selenium tests
+RUN zypper -n in tar gzip sudo \
+    gcc-c++ cmake ninja pkgconfig\(opencv4\) pkg-config 
perl\(Module::CPANfile\) pkgconfig\(fftw3\) pkgconfig\(libpng\) 
pkgconfig\(sndfile\) pkgconfig\(theoraenc\) tesseract-ocr \
+    perl-CSS-Sass ruby-devel npm python3-base python3-requests git-core rsync 
curl 'postgresql-devel>=14' 'postgresql-server>=14' qemu qemu-tools tar 
xorg-x11-fonts sudo make \
+    chromedriver && zypper clean -a
 
 ENV LANG=en_US.UTF-8
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1772722702.3877b2ca/dbicdh/PostgreSQL/deploy/103/001-auto-__VERSION.sql
 
new/openQA-5.1773056733.e071deaf/dbicdh/PostgreSQL/deploy/103/001-auto-__VERSION.sql
--- 
old/openQA-5.1772722702.3877b2ca/dbicdh/PostgreSQL/deploy/103/001-auto-__VERSION.sql
        1970-01-01 01:00:00.000000000 +0100
+++ 
new/openQA-5.1773056733.e071deaf/dbicdh/PostgreSQL/deploy/103/001-auto-__VERSION.sql
        2026-03-09 12:45:33.000000000 +0100
@@ -0,0 +1,18 @@
+--
+-- Created by SQL::Translator::Producer::PostgreSQL
+-- Created on Tue Jul 22 11:14:48 2025
+--
+;
+--
+-- Table: dbix_class_deploymenthandler_versions
+--
+CREATE TABLE dbix_class_deploymenthandler_versions (
+  id serial NOT NULL,
+  version character varying(50) NOT NULL,
+  ddl text,
+  upgrade_sql text,
+  PRIMARY KEY (id),
+  CONSTRAINT dbix_class_deploymenthandler_versions_version UNIQUE (version)
+);
+
+;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1772722702.3877b2ca/dbicdh/PostgreSQL/deploy/103/001-auto.sql 
new/openQA-5.1773056733.e071deaf/dbicdh/PostgreSQL/deploy/103/001-auto.sql
--- old/openQA-5.1772722702.3877b2ca/dbicdh/PostgreSQL/deploy/103/001-auto.sql  
1970-01-01 01:00:00.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/dbicdh/PostgreSQL/deploy/103/001-auto.sql  
2026-03-09 12:45:33.000000000 +0100
@@ -0,0 +1,800 @@
+--
+-- Created by SQL::Translator::Producer::PostgreSQL
+-- Created on Tue Jul 22 11:14:48 2025
+--
+;
+--
+-- Table: bugs
+--
+CREATE TABLE bugs (
+  id bigserial NOT NULL,
+  bugid text NOT NULL,
+  title text,
+  priority text,
+  assigned boolean,
+  assignee text,
+  open boolean,
+  status text,
+  resolution text,
+  existing boolean DEFAULT '1' NOT NULL,
+  refreshed boolean DEFAULT '0' NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT bugs_bugid UNIQUE (bugid)
+);
+
+;
+--
+-- Table: gru_tasks
+--
+CREATE TABLE gru_tasks (
+  id bigserial NOT NULL,
+  taskname text NOT NULL,
+  args text NOT NULL,
+  run_at timestamp NOT NULL,
+  priority integer NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id)
+);
+CREATE INDEX gru_tasks_run_at_reversed on gru_tasks (run_at DESC);
+
+;
+--
+-- Table: job_group_parents
+--
+CREATE TABLE job_group_parents (
+  id bigserial NOT NULL,
+  name text NOT NULL,
+  size_limit_gb integer,
+  exclusively_kept_asset_size bigint,
+  default_keep_logs_in_days integer,
+  default_keep_important_logs_in_days integer,
+  default_keep_results_in_days integer,
+  default_keep_important_results_in_days integer,
+  default_keep_jobs_in_days integer,
+  default_keep_important_jobs_in_days integer,
+  default_priority integer,
+  sort_order integer,
+  description text,
+  build_version_sort integer DEFAULT 1 NOT NULL,
+  carry_over_bugrefs boolean,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT job_group_parents_name UNIQUE (name)
+);
+
+;
+--
+-- Table: job_modules
+--
+CREATE TABLE job_modules (
+  id bigserial NOT NULL,
+  job_id bigint NOT NULL,
+  name text NOT NULL,
+  script text NOT NULL,
+  category text NOT NULL,
+  milestone integer DEFAULT 0 NOT NULL,
+  important integer DEFAULT 0 NOT NULL,
+  fatal integer DEFAULT 0 NOT NULL,
+  always_rollback integer DEFAULT 0 NOT NULL,
+  result character varying DEFAULT 'none' NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT job_modules_job_id_name_category_script UNIQUE (job_id, name, 
category, script)
+);
+CREATE INDEX job_modules_idx_job_id on job_modules (job_id);
+CREATE INDEX idx_job_modules_result on job_modules (result);
+
+;
+--
+-- Table: job_settings
+--
+CREATE TABLE job_settings (
+  id bigserial NOT NULL,
+  key text NOT NULL,
+  value text NOT NULL,
+  job_id bigint NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id)
+);
+CREATE INDEX job_settings_idx_job_id on job_settings (job_id);
+CREATE INDEX idx_value_settings on job_settings (key, value);
+CREATE INDEX idx_job_id_value_settings on job_settings (job_id, key, value);
+
+;
+--
+-- Table: job_template_settings
+--
+CREATE TABLE job_template_settings (
+  id bigserial NOT NULL,
+  job_template_id bigint NOT NULL,
+  key text NOT NULL,
+  value text NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT job_template_settings_job_template_id_key UNIQUE 
(job_template_id, key)
+);
+CREATE INDEX job_template_settings_idx_job_template_id on 
job_template_settings (job_template_id);
+
+;
+--
+-- Table: machine_settings
+--
+CREATE TABLE machine_settings (
+  id bigserial NOT NULL,
+  machine_id bigint NOT NULL,
+  key text NOT NULL,
+  value text NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT machine_settings_machine_id_key UNIQUE (machine_id, key)
+);
+CREATE INDEX machine_settings_idx_machine_id on machine_settings (machine_id);
+
+;
+--
+-- Table: machines
+--
+CREATE TABLE machines (
+  id bigserial NOT NULL,
+  name text NOT NULL,
+  backend text NOT NULL,
+  description text,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT machines_name UNIQUE (name)
+);
+
+;
+--
+-- Table: needle_dirs
+--
+CREATE TABLE needle_dirs (
+  id bigserial NOT NULL,
+  path text NOT NULL,
+  name text NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT needle_dirs_path UNIQUE (path)
+);
+
+;
+--
+-- Table: product_settings
+--
+CREATE TABLE product_settings (
+  id bigserial NOT NULL,
+  product_id bigint NOT NULL,
+  key text NOT NULL,
+  value text NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT product_settings_product_id_key UNIQUE (product_id, key)
+);
+CREATE INDEX product_settings_idx_product_id on product_settings (product_id);
+
+;
+--
+-- Table: products
+--
+CREATE TABLE products (
+  id bigserial NOT NULL,
+  name text NOT NULL,
+  distri text NOT NULL,
+  version text DEFAULT '' NOT NULL,
+  arch text NOT NULL,
+  flavor text NOT NULL,
+  description text,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT products_distri_version_arch_flavor UNIQUE (distri, version, 
arch, flavor)
+);
+
+;
+--
+-- Table: screenshots
+--
+CREATE TABLE screenshots (
+  id bigserial NOT NULL,
+  filename text NOT NULL,
+  t_created timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT screenshots_filename UNIQUE (filename)
+);
+
+;
+--
+-- Table: secrets
+--
+CREATE TABLE secrets (
+  id bigserial NOT NULL,
+  secret text NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT secrets_secret UNIQUE (secret)
+);
+
+;
+--
+-- Table: test_suite_settings
+--
+CREATE TABLE test_suite_settings (
+  id bigserial NOT NULL,
+  test_suite_id bigint NOT NULL,
+  key text NOT NULL,
+  value text NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT test_suite_settings_test_suite_id_key UNIQUE (test_suite_id, key)
+);
+CREATE INDEX test_suite_settings_idx_test_suite_id on test_suite_settings 
(test_suite_id);
+
+;
+--
+-- Table: test_suites
+--
+CREATE TABLE test_suites (
+  id bigserial NOT NULL,
+  name text NOT NULL,
+  description text,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT test_suites_name UNIQUE (name)
+);
+
+;
+--
+-- Table: users
+--
+CREATE TABLE users (
+  id bigserial NOT NULL,
+  username text NOT NULL,
+  provider text DEFAULT '' NOT NULL,
+  email text,
+  fullname text,
+  nickname text,
+  is_operator integer DEFAULT 0 NOT NULL,
+  is_admin integer DEFAULT 0 NOT NULL,
+  feature_version integer DEFAULT 1 NOT NULL,
+  deleted_at timestamp,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT users_username_provider UNIQUE (username, provider)
+);
+
+;
+--
+-- Table: worker_properties
+--
+CREATE TABLE worker_properties (
+  id bigserial NOT NULL,
+  key text NOT NULL,
+  value text NOT NULL,
+  worker_id bigint NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id)
+);
+CREATE INDEX worker_properties_idx_worker_id on worker_properties (worker_id);
+
+;
+--
+-- Table: api_keys
+--
+CREATE TABLE api_keys (
+  id bigserial NOT NULL,
+  key text NOT NULL,
+  secret text NOT NULL,
+  user_id bigint NOT NULL,
+  t_expiration timestamp,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT api_keys_key UNIQUE (key)
+);
+CREATE INDEX api_keys_idx_user_id on api_keys (user_id);
+
+;
+--
+-- Table: audit_events
+--
+CREATE TABLE audit_events (
+  id bigserial NOT NULL,
+  user_id bigint,
+  connection_id text,
+  event text NOT NULL,
+  event_data text,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id)
+);
+CREATE INDEX audit_events_idx_user_id on audit_events (user_id);
+
+;
+--
+-- Table: comments
+--
+CREATE TABLE comments (
+  id bigserial NOT NULL,
+  job_id bigint,
+  group_id bigint,
+  parent_group_id bigint,
+  text text NOT NULL,
+  user_id bigint NOT NULL,
+  flags integer DEFAULT 0,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id)
+);
+CREATE INDEX comments_idx_group_id on comments (group_id);
+CREATE INDEX comments_idx_job_id on comments (job_id);
+CREATE INDEX comments_idx_parent_group_id on comments (parent_group_id);
+CREATE INDEX comments_idx_user_id on comments (user_id);
+
+;
+--
+-- Table: job_groups
+--
+CREATE TABLE job_groups (
+  id bigserial NOT NULL,
+  name text NOT NULL,
+  parent_id bigint,
+  size_limit_gb integer,
+  exclusively_kept_asset_size bigint,
+  keep_logs_in_days integer,
+  keep_important_logs_in_days integer,
+  keep_results_in_days integer,
+  keep_important_results_in_days integer,
+  keep_jobs_in_days integer,
+  keep_important_jobs_in_days integer,
+  default_priority integer,
+  sort_order integer,
+  description text,
+  template text,
+  build_version_sort integer DEFAULT 1 NOT NULL,
+  carry_over_bugrefs boolean,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT job_groups_name_parent_id UNIQUE (name, parent_id)
+);
+CREATE INDEX job_groups_idx_parent_id on job_groups (parent_id);
+
+;
+--
+-- Table: workers
+--
+CREATE TABLE workers (
+  id bigserial NOT NULL,
+  host text NOT NULL,
+  instance integer NOT NULL,
+  job_id bigint,
+  t_seen timestamp,
+  upload_progress jsonb,
+  error text,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT workers_host_instance UNIQUE (host, instance),
+  CONSTRAINT workers_job_id UNIQUE (job_id)
+);
+CREATE INDEX workers_idx_job_id on workers (job_id);
+
+;
+--
+-- Table: needles
+--
+CREATE TABLE needles (
+  id bigserial NOT NULL,
+  dir_id bigint NOT NULL,
+  filename text NOT NULL,
+  last_seen_time timestamp,
+  last_seen_module_id bigint,
+  last_matched_time timestamp,
+  last_matched_module_id bigint,
+  last_updated timestamp,
+  file_present boolean DEFAULT '1' NOT NULL,
+  tags text[],
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT needles_dir_id_filename UNIQUE (dir_id, filename)
+);
+CREATE INDEX needles_idx_dir_id on needles (dir_id);
+CREATE INDEX needles_idx_last_matched_module_id on needles 
(last_matched_module_id);
+CREATE INDEX needles_idx_last_seen_module_id on needles (last_seen_module_id);
+
+;
+--
+-- Table: scheduled_products
+--
+CREATE TABLE scheduled_products (
+  id bigserial NOT NULL,
+  distri text DEFAULT '' NOT NULL,
+  version text DEFAULT '' NOT NULL,
+  flavor text DEFAULT '' NOT NULL,
+  arch text DEFAULT '' NOT NULL,
+  build text DEFAULT '' NOT NULL,
+  iso text DEFAULT '' NOT NULL,
+  status text DEFAULT 'added' NOT NULL,
+  settings jsonb NOT NULL,
+  results jsonb,
+  user_id bigint,
+  gru_task_id bigint,
+  minion_job_id bigint,
+  webhook_id text,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id)
+);
+CREATE INDEX scheduled_products_idx_gru_task_id on scheduled_products 
(gru_task_id);
+CREATE INDEX scheduled_products_idx_user_id on scheduled_products (user_id);
+CREATE INDEX scheduled_products_idx_webhook_id on scheduled_products 
(webhook_id);
+
+;
+--
+-- Table: job_templates
+--
+CREATE TABLE job_templates (
+  id bigserial NOT NULL,
+  product_id bigint NOT NULL,
+  machine_id bigint NOT NULL,
+  test_suite_id bigint NOT NULL,
+  name text DEFAULT '' NOT NULL,
+  description text DEFAULT '' NOT NULL,
+  prio integer,
+  group_id bigint NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT scenario UNIQUE (product_id, machine_id, name, test_suite_id)
+);
+CREATE INDEX job_templates_idx_group_id on job_templates (group_id);
+CREATE INDEX job_templates_idx_machine_id on job_templates (machine_id);
+CREATE INDEX job_templates_idx_product_id on job_templates (product_id);
+CREATE INDEX job_templates_idx_test_suite_id on job_templates (test_suite_id);
+
+;
+--
+-- Table: jobs
+--
+CREATE TABLE jobs (
+  id bigserial NOT NULL,
+  result_dir text,
+  archived boolean DEFAULT '0' NOT NULL,
+  state character varying DEFAULT 'scheduled' NOT NULL,
+  priority integer DEFAULT 50 NOT NULL,
+  result character varying DEFAULT 'none' NOT NULL,
+  reason character varying,
+  clone_id bigint,
+  blocked_by_id bigint,
+  TEST text NOT NULL,
+  DISTRI text DEFAULT '' NOT NULL,
+  VERSION text DEFAULT '' NOT NULL,
+  FLAVOR text DEFAULT '' NOT NULL,
+  ARCH text DEFAULT '' NOT NULL,
+  BUILD text DEFAULT '' NOT NULL,
+  MACHINE text,
+  group_id bigint,
+  assigned_worker_id bigint,
+  t_started timestamp,
+  t_finished timestamp,
+  logs_present boolean DEFAULT '1' NOT NULL,
+  passed_module_count integer DEFAULT 0 NOT NULL,
+  failed_module_count integer DEFAULT 0 NOT NULL,
+  softfailed_module_count integer DEFAULT 0 NOT NULL,
+  skipped_module_count integer DEFAULT 0 NOT NULL,
+  externally_skipped_module_count integer DEFAULT 0 NOT NULL,
+  scheduled_product_id bigint,
+  result_size bigint,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id)
+);
+CREATE INDEX jobs_idx_assigned_worker_id on jobs (assigned_worker_id);
+CREATE INDEX jobs_idx_blocked_by_id on jobs (blocked_by_id);
+CREATE INDEX jobs_idx_clone_id on jobs (clone_id);
+CREATE INDEX jobs_idx_group_id on jobs (group_id);
+CREATE INDEX jobs_idx_scheduled_product_id on jobs (scheduled_product_id);
+CREATE INDEX idx_jobs_state on jobs (state);
+CREATE INDEX idx_jobs_result on jobs (result);
+CREATE INDEX idx_jobs_build_group on jobs (BUILD, group_id);
+CREATE INDEX idx_jobs_scenario on jobs (VERSION, DISTRI, FLAVOR, TEST, 
MACHINE, ARCH);
+
+;
+--
+-- Table: assets
+--
+CREATE TABLE assets (
+  id bigserial NOT NULL,
+  type text NOT NULL,
+  name text NOT NULL,
+  size bigint,
+  checksum text,
+  last_use_job_id bigint,
+  fixed boolean DEFAULT '0' NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (id),
+  CONSTRAINT assets_type_name UNIQUE (type, name)
+);
+CREATE INDEX assets_idx_last_use_job_id on assets (last_use_job_id);
+
+;
+--
+-- Table: developer_sessions
+--
+CREATE TABLE developer_sessions (
+  job_id bigint NOT NULL,
+  user_id bigint NOT NULL,
+  ws_connection_count integer DEFAULT 0 NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  PRIMARY KEY (job_id)
+);
+CREATE INDEX developer_sessions_idx_user_id on developer_sessions (user_id);
+
+;
+--
+-- Table: gru_dependencies
+--
+CREATE TABLE gru_dependencies (
+  job_id bigint NOT NULL,
+  gru_task_id bigint NOT NULL,
+  PRIMARY KEY (job_id, gru_task_id)
+);
+CREATE INDEX gru_dependencies_idx_gru_task_id on gru_dependencies 
(gru_task_id);
+CREATE INDEX gru_dependencies_idx_job_id on gru_dependencies (job_id);
+
+;
+--
+-- Table: job_dependencies
+--
+CREATE TABLE job_dependencies (
+  child_job_id bigint NOT NULL,
+  parent_job_id bigint NOT NULL,
+  dependency integer NOT NULL,
+  PRIMARY KEY (child_job_id, parent_job_id, dependency)
+);
+CREATE INDEX job_dependencies_idx_child_job_id on job_dependencies 
(child_job_id);
+CREATE INDEX job_dependencies_idx_parent_job_id on job_dependencies 
(parent_job_id);
+CREATE INDEX idx_job_dependencies_dependency on job_dependencies (dependency);
+
+;
+--
+-- Table: job_locks
+--
+CREATE TABLE job_locks (
+  name text NOT NULL,
+  owner bigint NOT NULL,
+  locked_by text,
+  count integer DEFAULT 1 NOT NULL,
+  PRIMARY KEY (name, owner)
+);
+CREATE INDEX job_locks_idx_owner on job_locks (owner);
+
+;
+--
+-- Table: job_networks
+--
+CREATE TABLE job_networks (
+  name text NOT NULL,
+  job_id bigint NOT NULL,
+  vlan integer NOT NULL,
+  PRIMARY KEY (name, job_id)
+);
+CREATE INDEX job_networks_idx_job_id on job_networks (job_id);
+
+;
+--
+-- Table: jobs_assets
+--
+CREATE TABLE jobs_assets (
+  job_id bigint NOT NULL,
+  asset_id bigint NOT NULL,
+  created_by boolean DEFAULT '0' NOT NULL,
+  t_created timestamp NOT NULL,
+  t_updated timestamp NOT NULL,
+  CONSTRAINT jobs_assets_job_id_asset_id UNIQUE (job_id, asset_id)
+);
+CREATE INDEX jobs_assets_idx_asset_id on jobs_assets (asset_id);
+CREATE INDEX jobs_assets_idx_job_id on jobs_assets (job_id);
+
+;
+--
+-- Table: screenshot_links
+--
+CREATE TABLE screenshot_links (
+  screenshot_id bigint NOT NULL,
+  job_id bigint NOT NULL
+);
+CREATE INDEX screenshot_links_idx_job_id on screenshot_links (job_id);
+CREATE INDEX screenshot_links_idx_screenshot_id on screenshot_links 
(screenshot_id);
+
+;
+--
+-- Foreign Key Definitions
+--
+
+;
+ALTER TABLE job_modules ADD CONSTRAINT job_modules_fk_job_id FOREIGN KEY 
(job_id)
+  REFERENCES jobs (id) ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE job_settings ADD CONSTRAINT job_settings_fk_job_id FOREIGN KEY 
(job_id)
+  REFERENCES jobs (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE job_template_settings ADD CONSTRAINT 
job_template_settings_fk_job_template_id FOREIGN KEY (job_template_id)
+  REFERENCES job_templates (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE machine_settings ADD CONSTRAINT machine_settings_fk_machine_id 
FOREIGN KEY (machine_id)
+  REFERENCES machines (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE product_settings ADD CONSTRAINT product_settings_fk_product_id 
FOREIGN KEY (product_id)
+  REFERENCES products (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE test_suite_settings ADD CONSTRAINT 
test_suite_settings_fk_test_suite_id FOREIGN KEY (test_suite_id)
+  REFERENCES test_suites (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE worker_properties ADD CONSTRAINT worker_properties_fk_worker_id 
FOREIGN KEY (worker_id)
+  REFERENCES workers (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE api_keys ADD CONSTRAINT api_keys_fk_user_id FOREIGN KEY (user_id)
+  REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE audit_events ADD CONSTRAINT audit_events_fk_user_id FOREIGN KEY 
(user_id)
+  REFERENCES users (id) DEFERRABLE;
+
+;
+ALTER TABLE comments ADD CONSTRAINT comments_fk_group_id FOREIGN KEY (group_id)
+  REFERENCES job_groups (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE comments ADD CONSTRAINT comments_fk_job_id FOREIGN KEY (job_id)
+  REFERENCES jobs (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE comments ADD CONSTRAINT comments_fk_parent_group_id FOREIGN KEY 
(parent_group_id)
+  REFERENCES job_group_parents (id) ON DELETE CASCADE ON UPDATE CASCADE 
DEFERRABLE;
+
+;
+ALTER TABLE comments ADD CONSTRAINT comments_fk_user_id FOREIGN KEY (user_id)
+  REFERENCES users (id) DEFERRABLE;
+
+;
+ALTER TABLE job_groups ADD CONSTRAINT job_groups_fk_parent_id FOREIGN KEY 
(parent_id)
+  REFERENCES job_group_parents (id) ON DELETE SET NULL ON UPDATE CASCADE 
DEFERRABLE;
+
+;
+ALTER TABLE workers ADD CONSTRAINT workers_fk_job_id FOREIGN KEY (job_id)
+  REFERENCES jobs (id) ON DELETE SET NULL DEFERRABLE;
+
+;
+ALTER TABLE needles ADD CONSTRAINT needles_fk_dir_id FOREIGN KEY (dir_id)
+  REFERENCES needle_dirs (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE needles ADD CONSTRAINT needles_fk_last_matched_module_id FOREIGN 
KEY (last_matched_module_id)
+  REFERENCES job_modules (id) ON DELETE SET NULL DEFERRABLE;
+
+;
+ALTER TABLE needles ADD CONSTRAINT needles_fk_last_seen_module_id FOREIGN KEY 
(last_seen_module_id)
+  REFERENCES job_modules (id) ON DELETE SET NULL DEFERRABLE;
+
+;
+ALTER TABLE scheduled_products ADD CONSTRAINT 
scheduled_products_fk_gru_task_id FOREIGN KEY (gru_task_id)
+  REFERENCES gru_tasks (id) ON DELETE SET NULL DEFERRABLE;
+
+;
+ALTER TABLE scheduled_products ADD CONSTRAINT scheduled_products_fk_user_id 
FOREIGN KEY (user_id)
+  REFERENCES users (id) ON DELETE SET NULL DEFERRABLE;
+
+;
+ALTER TABLE job_templates ADD CONSTRAINT job_templates_fk_group_id FOREIGN KEY 
(group_id)
+  REFERENCES job_groups (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE job_templates ADD CONSTRAINT job_templates_fk_machine_id FOREIGN 
KEY (machine_id)
+  REFERENCES machines (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE job_templates ADD CONSTRAINT job_templates_fk_product_id FOREIGN 
KEY (product_id)
+  REFERENCES products (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE job_templates ADD CONSTRAINT job_templates_fk_test_suite_id 
FOREIGN KEY (test_suite_id)
+  REFERENCES test_suites (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE jobs ADD CONSTRAINT jobs_fk_assigned_worker_id FOREIGN KEY 
(assigned_worker_id)
+  REFERENCES workers (id) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE jobs ADD CONSTRAINT jobs_fk_blocked_by_id FOREIGN KEY 
(blocked_by_id)
+  REFERENCES jobs (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE jobs ADD CONSTRAINT jobs_fk_clone_id FOREIGN KEY (clone_id)
+  REFERENCES jobs (id) ON DELETE SET NULL DEFERRABLE;
+
+;
+ALTER TABLE jobs ADD CONSTRAINT jobs_fk_group_id FOREIGN KEY (group_id)
+  REFERENCES job_groups (id) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE jobs ADD CONSTRAINT jobs_fk_scheduled_product_id FOREIGN KEY 
(scheduled_product_id)
+  REFERENCES scheduled_products (id) ON DELETE SET NULL ON UPDATE CASCADE 
DEFERRABLE;
+
+;
+ALTER TABLE assets ADD CONSTRAINT assets_fk_last_use_job_id FOREIGN KEY 
(last_use_job_id)
+  REFERENCES jobs (id) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE developer_sessions ADD CONSTRAINT developer_sessions_fk_job_id 
FOREIGN KEY (job_id)
+  REFERENCES jobs (id) ON DELETE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE developer_sessions ADD CONSTRAINT developer_sessions_fk_user_id 
FOREIGN KEY (user_id)
+  REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE gru_dependencies ADD CONSTRAINT gru_dependencies_fk_gru_task_id 
FOREIGN KEY (gru_task_id)
+  REFERENCES gru_tasks (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE gru_dependencies ADD CONSTRAINT gru_dependencies_fk_job_id FOREIGN 
KEY (job_id)
+  REFERENCES jobs (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE job_dependencies ADD CONSTRAINT job_dependencies_fk_child_job_id 
FOREIGN KEY (child_job_id)
+  REFERENCES jobs (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE job_dependencies ADD CONSTRAINT job_dependencies_fk_parent_job_id 
FOREIGN KEY (parent_job_id)
+  REFERENCES jobs (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE job_locks ADD CONSTRAINT job_locks_fk_owner FOREIGN KEY (owner)
+  REFERENCES jobs (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE job_networks ADD CONSTRAINT job_networks_fk_job_id FOREIGN KEY 
(job_id)
+  REFERENCES jobs (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE jobs_assets ADD CONSTRAINT jobs_assets_fk_asset_id FOREIGN KEY 
(asset_id)
+  REFERENCES assets (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE jobs_assets ADD CONSTRAINT jobs_assets_fk_job_id FOREIGN KEY 
(job_id)
+  REFERENCES jobs (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE screenshot_links ADD CONSTRAINT screenshot_links_fk_job_id FOREIGN 
KEY (job_id)
+  REFERENCES jobs (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+;
+ALTER TABLE screenshot_links ADD CONSTRAINT screenshot_links_fk_screenshot_id 
FOREIGN KEY (screenshot_id)
+  REFERENCES screenshots (id) ON UPDATE CASCADE DEFERRABLE;
+
+;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1772722702.3877b2ca/dbicdh/PostgreSQL/upgrade/102-103/001-auto.sql 
new/openQA-5.1773056733.e071deaf/dbicdh/PostgreSQL/upgrade/102-103/001-auto.sql
--- 
old/openQA-5.1772722702.3877b2ca/dbicdh/PostgreSQL/upgrade/102-103/001-auto.sql 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/openQA-5.1773056733.e071deaf/dbicdh/PostgreSQL/upgrade/102-103/001-auto.sql 
    2026-03-09 12:45:33.000000000 +0100
@@ -0,0 +1,7 @@
+--
+-- Upgrade from 102 to 103
+--
+-- Add deleted_at column to users table for GDPR compliance (user data 
deletion)
+--
+ALTER TABLE users ADD COLUMN deleted_at timestamp;
+CREATE INDEX users_idx_deleted_at ON users (deleted_at);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1772722702.3877b2ca/etc/openqa/openqa.ini 
new/openQA-5.1773056733.e071deaf/etc/openqa/openqa.ini
--- old/openQA-5.1772722702.3877b2ca/etc/openqa/openqa.ini      2026-03-05 
15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/etc/openqa/openqa.ini      2026-03-09 
12:45:33.000000000 +0100
@@ -361,11 +361,6 @@
 #wait_for_grutask_retries = 6
 ## Maximum size of MCP results in bytes (to prevent performance issues)
 # mcp_max_result_size = 500000
-## To encourage job scenarios with shorter runtime: If MAX_JOB_TIME is greater
-## than DEFAULT_MAX_JOB_TIME, MAX_JOB_TIME divided by this number is added to
-## the job priority. Can be 0 to disable and negative to prioritize long 
running
-## job scenarios.
-#max_job_time_prio_scale = 100
 ## Minimum storage duration for scheduled products
 ## Scheduled products are kept as long as the jobs they have created exist. So
 ## their retention is controlled by the retention of their jobs and you 
normally
@@ -376,8 +371,13 @@
 ## Throttling: set a comma-separated list of test parameters that trigger 
throttling
 ## of scheduled openQA jobs by priority. Example: PARAM1:SCALE1[:THRESH1][,...]
 ## Set a scale that a parameter value, minus an optional threshold, is 
multiplied by 
-## and added to each job prio, as: '$base_prio + $scale# * ($param# - 
$thresh#)'
-#prio_throttling_parameters =
+## and added to each job prio, as: '$base_prio + $scale# * ($param# - 
$thresh#)'.
+## MAX_JOB_TIME also is configured here: to encourage tests with shorter 
runtime,
+## when MAX_JOB_TIME > DEFAULT_MAX_JOB_TIME, a configured SCALE > 0 is 
applied; otherwise
+## SCALE = 0 (or empty) to disable throttling or < 0 to prioritize long 
running jobs.
+## To configure a custom set of scaling parameters, add also the below default 
+## rule, e.g. "prio_throttling_parameters = MAX_JOB_TIME:0.007,FOO:3:2000"
+#prio_throttling_parameters = MAX_JOB_TIME:0.007
 
 [archiving]
 ## Moves logs of jobs which are preserved during the cleanup because they are
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1772722702.3877b2ca/lib/OpenQA/Schema/Result/Users.pm 
new/openQA-5.1773056733.e071deaf/lib/OpenQA/Schema/Result/Users.pm
--- old/openQA-5.1772722702.3877b2ca/lib/OpenQA/Schema/Result/Users.pm  
2026-03-05 15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/lib/OpenQA/Schema/Result/Users.pm  
2026-03-09 12:45:33.000000000 +0100
@@ -4,10 +4,11 @@
 package OpenQA::Schema::Result::Users;
 
 
-use Mojo::Base 'DBIx::Class::Core';
+use Mojo::Base 'DBIx::Class::Core', -signatures;
 
 use URI::Escape 'uri_escape';
 use Digest::MD5 'md5_hex';
+use DateTime;
 
 __PACKAGE__->table('users');
 __PACKAGE__->load_components(qw(InflateColumn::DateTime Timestamps));
@@ -51,6 +52,10 @@
         data_type => 'integer',
         default_value => 1,
     },
+    deleted_at => {
+        data_type => 'timestamp',
+        is_nullable => 1,
+    },
 );
 __PACKAGE__->add_timestamps;
 __PACKAGE__->set_primary_key('id');
@@ -58,6 +63,8 @@
 __PACKAGE__->has_many(
     developer_sessions => 'OpenQA::Schema::Result::DeveloperSessions',
     'user_id', {cascade_delete => 1});
+__PACKAGE__->has_many(comments => 'OpenQA::Schema::Result::Comments', 
'user_id');
+__PACKAGE__->has_many(audit_events => 'OpenQA::Schema::Result::AuditEvents', 
'user_id');
 __PACKAGE__->add_unique_constraint([qw(username provider)]);
 
 sub name {
@@ -84,5 +91,32 @@
     }
 }
 
+sub is_deleted ($self) { defined $self->deleted_at }
+
+sub anonymize ($self) {
+    return if $self->is_deleted;
+    my $user_id = $self->id;
+    my $schema = $self->result_source->schema;
+    $schema->txn_do(
+        sub {
+            $self->api_keys->delete;
+            $_->update({event_data => _anonymize_event_data($_->event_data, 
$self->username)})
+              for $self->audit_events->all;
+            $self->update(
+                {
+                    username => "deleted-user-$user_id",
+                    email => undef,
+                    fullname => undef,
+                    nickname => undef,
+                    deleted_at => DateTime->now,
+                });
+        });
+}
+
+sub _anonymize_event_data ($event_data, $username) {
+    return $event_data unless defined $event_data && defined $username;
+    my $placeholder = 'deleted-user';
+    $event_data =~ s/\Q$username\E/$placeholder/gr;
+}
 
 1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1772722702.3877b2ca/lib/OpenQA/Schema/ResultSet/Jobs.pm 
new/openQA-5.1773056733.e071deaf/lib/OpenQA/Schema/ResultSet/Jobs.pm
--- old/openQA-5.1772722702.3877b2ca/lib/OpenQA/Schema/ResultSet/Jobs.pm        
2026-03-05 15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/lib/OpenQA/Schema/ResultSet/Jobs.pm        
2026-03-09 12:45:33.000000000 +0100
@@ -170,41 +170,50 @@
     return $job;
 }
 
-sub _apply_prio_throttling ($settings, $new_job_args) {
-    my $debug_msg;
-    my $max_job_time = looks_like_number $settings->{MAX_JOB_TIME} ? 
$settings->{MAX_JOB_TIME} : 0;
-    my $timeout_scale = looks_like_number $settings->{TIMEOUT_SCALE} ? 
$settings->{TIMEOUT_SCALE} : 0;
-    $max_job_time *= $timeout_scale if $max_job_time and $timeout_scale > 1;
-    if ($max_job_time and $max_job_time > DEFAULT_MAX_JOB_TIME) {
-        if (my $scale = 
OpenQA::App->singleton->config->{misc_limits}->{max_job_time_prio_scale}) {
-            my $malus = int($max_job_time / $scale);
-            $debug_msg = sprintf 'Adding priority malus to newly created job 
(old: %d, malus: %s)',
-              $new_job_args->{priority}, $malus;
-            $new_job_args->{priority} += $malus;
-        }
+sub _update_priority ($value, $throt_config, $job_args) {
+    my $scale = $throt_config->{scale} // 0;
+    my $reference = $throt_config->{reference} // 0;
+    my $prio = int(($value - $reference) * $scale);
+    $job_args->{priority} += $prio;
+    return ": $prio, scale: $scale" . ($reference ? ", reference: $reference;" 
: ';');
+}
+
+sub _apply_max_job_time_prio ($factor, $time, $throt_config, $job_args) {
+    my $info = '';
+    $factor = (looks_like_number $factor && $factor > 0) ? $factor : 1;
+    $time = (looks_like_number $time && $time > 0) ? $time : 
DEFAULT_MAX_JOB_TIME;
+    $time = (int($time * $factor));
+    if ($time > DEFAULT_MAX_JOB_TIME && defined $throt_config) {
+        $info = 'MAX_JOB_TIME' . _update_priority($time, $throt_config, 
$job_args);
     }
+    return $info;
+}
 
+sub _apply_prio_throttling ($settings, $new_job_args) {
+    my $debug_msg;
+    my $base_prio = $new_job_args->{priority} // 0;
     if (my $throttling
         = OpenQA::App->singleton && 
OpenQA::App->singleton->config->{misc_limits}->{prio_throttling_data})
     {
-        my $throttling_info;
+        my $throttling_info = _apply_max_job_time_prio(
+            $settings->{TIMEOUT_SCALE},
+            $settings->{MAX_JOB_TIME},
+            $throttling->{MAX_JOB_TIME},
+            $new_job_args
+        );
         for my $resource (keys %$throttling) {
-            next unless defined $settings->{$resource};
-            my $scale = $throttling->{$resource}->{scale};
-            my $reference = $throttling->{$resource}->{reference};
-            my $prio = int(($settings->{$resource} - $reference) * $scale);
-            $throttling_info .= "$resource, scale: $scale" . ($reference ? ", 
reference: $reference;" : ';');
-            $new_job_args->{priority} += $prio;
+            next if (!defined $settings->{$resource} || $resource eq 
'MAX_JOB_TIME');
+            $throttling_info
+              .= $resource . _update_priority($settings->{$resource}, 
$throttling->{$resource}, $new_job_args);
         }
         $debug_msg .= sprintf
-          '. Adjusting job priority by %s based on resource requirement(s): 
%s',
-          $new_job_args->{priority}, $throttling_info
+          '- Adjusting job priority from %d to %d based on resource 
requirement(s): %s',
+          $base_prio, $new_job_args->{priority}, $throttling_info
           if $throttling_info;
     }
     return $debug_msg;
 }
 
-
 sub _handle_dependency_settings ($self, $settings, $new_job_args) {
     my $job_settings = $self->result_source->schema->resultset('JobSettings');
     # handle dependencies
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1772722702.3877b2ca/lib/OpenQA/Schema.pm 
new/openQA-5.1773056733.e071deaf/lib/OpenQA/Schema.pm
--- old/openQA-5.1772722702.3877b2ca/lib/OpenQA/Schema.pm       2026-03-05 
15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/lib/OpenQA/Schema.pm       2026-03-09 
12:45:33.000000000 +0100
@@ -22,7 +22,7 @@
 
 # after bumping the version please look at the instructions in the 
docs/Contributing.asciidoc file
 # on what scripts should be run and how
-our $VERSION = $ENV{OPENQA_SCHEMA_VERSION_OVERRIDE} // 102;
+our $VERSION = $ENV{OPENQA_SCHEMA_VERSION_OVERRIDE} // 103;
 
 __PACKAGE__->load_namespaces;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1772722702.3877b2ca/lib/OpenQA/Setup.pm 
new/openQA-5.1773056733.e071deaf/lib/OpenQA/Setup.pm
--- old/openQA-5.1772722702.3877b2ca/lib/OpenQA/Setup.pm        2026-03-05 
15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/lib/OpenQA/Setup.pm        2026-03-09 
12:45:33.000000000 +0100
@@ -257,9 +257,8 @@
             wait_for_grutask_retries => 6,    # exponential, ~4 minutes
             worker_limit_retry_delay => ONE_HOUR / 4,
             mcp_max_result_size => 500000,
-            max_job_time_prio_scale => 100,
             scheduled_product_min_storage_duration => 34,
-            prio_throttling_parameters => '',
+            prio_throttling_parameters => 'MAX_JOB_TIME:0.007',
             prio_throttling_data => undef,
         },
         archiving => {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1772722702.3877b2ca/lib/OpenQA/WebAPI/Controller/API/V1/User.pm 
new/openQA-5.1773056733.e071deaf/lib/OpenQA/WebAPI/Controller/API/V1/User.pm
--- 
old/openQA-5.1772722702.3877b2ca/lib/OpenQA/WebAPI/Controller/API/V1/User.pm    
    2026-03-05 15:58:22.000000000 +0100
+++ 
new/openQA-5.1773056733.e071deaf/lib/OpenQA/WebAPI/Controller/API/V1/User.pm    
    2026-03-09 12:45:33.000000000 +0100
@@ -35,9 +35,30 @@
 sub delete ($self) {
     my $user = $self->schema->resultset('Users')->find($self->param('id'));
     return $self->render(json => {error => 'Not found'}, status => 404) unless 
$user;
-    my $result = $user->delete();
-    $self->emit_event('openqa_user_deleted', {username => $user->username});
-    $self->render(json => {result => $result});
+    my $username = $user->username;
+    $user->anonymize;
+    $self->emit_event('openqa_user_deleted', {username => $username});
+    $self->render(json => {result => 1});
+}
+
+=over 4
+
+=item delete_self()
+
+Delete the current user's own account (anonymize all data).
+
+=back
+
+=cut
+
+sub delete_self ($self) {
+    my $user = $self->current_user;
+    return $self->render(json => {error => 'Not found'}, status => 404) unless 
$user;
+    return $self->render(json => {error => 'User already deleted'}, status => 
400) if $user->is_deleted;
+    my $username = $user->username;
+    $user->anonymize;
+    $self->emit_event('openqa_user_deleted', {username => $username});
+    $self->render(json => {result => 1});
 }
 
 =over 4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1772722702.3877b2ca/lib/OpenQA/WebAPI/Controller/Test.pm 
new/openQA-5.1773056733.e071deaf/lib/OpenQA/WebAPI/Controller/Test.pm
--- old/openQA-5.1772722702.3877b2ca/lib/OpenQA/WebAPI/Controller/Test.pm       
2026-03-05 15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/lib/OpenQA/WebAPI/Controller/Test.pm       
2026-03-09 12:45:33.000000000 +0100
@@ -994,7 +994,7 @@
 
 sub _get_latest_job ($self) {
     my %search_args = (limit => 1);
-    for my $arg (OpenQA::Schema::Result::Jobs::SCENARIO_WITH_MACHINE_KEYS) {
+    for my $arg (OpenQA::Schema::Result::Jobs::SCENARIO_WITH_MACHINE_KEYS, 
keys %{META_MAPPING()}) {
         my $key = lc $arg;
         next unless defined $self->param($key);
         $search_args{$key} = $self->param($key);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1772722702.3877b2ca/lib/OpenQA/WebAPI.pm 
new/openQA-5.1773056733.e071deaf/lib/OpenQA/WebAPI.pm
--- old/openQA-5.1772722702.3877b2ca/lib/OpenQA/WebAPI.pm       2026-03-05 
15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/lib/OpenQA/WebAPI.pm       2026-03-09 
12:45:33.000000000 +0100
@@ -473,6 +473,7 @@
     
$api_ru->get('/users/me/api_keys')->name('apiv1_list_user_api_keys')->to('user#list_api_keys');
     
$api_ru->post('/users/me/api_keys')->name('apiv1_create_user_api_key')->to('user#create_api_key');
     
$api_ru->delete('/users/me/api_keys/<key>')->name('apiv1_delete_user_api_key')->to('user#delete_api_key');
+    
$api_ru->delete('/users/me')->name('apiv1_delete_current_user')->to('user#delete_self');
 
     # api/v1/search
     
$api_public_r->get('/experimental/search')->name('apiv1_search_query')->to('search#query');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1772722702.3877b2ca/t/10-overview_badge.t 
new/openQA-5.1773056733.e071deaf/t/10-overview_badge.t
--- old/openQA-5.1772722702.3877b2ca/t/10-overview_badge.t      2026-03-05 
15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/t/10-overview_badge.t      2026-03-09 
12:45:33.000000000 +0100
@@ -14,6 +14,7 @@
 my $test_case = OpenQA::Test::Case->new;
 $test_case->init_data(fixtures_glob => '01-jobs.pl 05-job_modules.pl');
 my $t = Test::Mojo->new('OpenQA::WebAPI');
+my $jobs = $t->app->schema->resultset('Jobs');
 
 
$t->get_ok('/tests/overview/badge')->status_is(200)->content_type_is('image/svg+xml')->content_like(qr/running/);
 
$t->get_ok('/tests/overview/badge?result=passed')->status_is(200)->content_like(qr/passed/);
@@ -31,10 +32,14 @@
       ->header_is('Cache-Control' => 'max-age=0, 
no-cache')->element_exists('svg', 'valid svg badge');
     
$t->get_ok('/tests/9992711111/badge')->status_is(404)->content_type_is('image/svg+xml')->element_exists('svg')
       ->content_like(qr/404/, 'valid 404 svg badge');
-    
$t->get_ok('/tests/latest/badge?test=kde&machine=32bit')->status_is(200)->content_type_is('image/svg+xml')
-      ->element_exists('svg', 'valid latest svg badge');
-    $t->app->schema->resultset('Jobs')->find(99928)
-      ->update({state => SCHEDULED, result => NONE, blocked_by_id => 99927});
+    
$t->get_ok('/tests/latest/badge?test=kde&machine=64bit')->status_is(200)->content_type_is('image/svg+xml')
+      ->element_exists('svg', 'valid latest svg 
badge')->content_like(qr/running/);
+    
$t->get_ok('/tests/latest/badge?test=kde&machine=64bit&result=passed')->status_is(404);
+    $jobs->find(99963)->update({result => FAILED, state => DONE});
+    $jobs->find(99962)->update({result => PASSED, state => DONE});
+    
$t->get_ok('/tests/latest/badge?test=kde&machine=64bit&result=passed')->status_is(200)->content_like(qr/passed/);
+    
$t->get_ok('/tests/latest/badge?test=kde&machine=64bit')->status_is(200)->content_like(qr/failed/);
+    $jobs->find(99928)->update({state => SCHEDULED, result => NONE, 
blocked_by_id => 99927});
     
$t->get_ok('/tests/99928/badge')->status_is(200)->content_type_is('image/svg+xml')->element_exists('svg')
       ->content_like(qr/blocked/, 'valid blocked svg badge');
 };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1772722702.3877b2ca/t/api/04-jobs.t 
new/openQA-5.1773056733.e071deaf/t/api/04-jobs.t
--- old/openQA-5.1772722702.3877b2ca/t/api/04-jobs.t    2026-03-05 
15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/t/api/04-jobs.t    2026-03-09 
12:45:33.000000000 +0100
@@ -17,7 +17,7 @@
 use OpenQA::Test::TimeLimit '30';
 use OpenQA::Test::Utils 'mock_io_loop';
 use OpenQA::App;
-use OpenQA::Constants 'WORKER_COMMAND_CANCEL';
+use OpenQA::Constants qw(WORKER_COMMAND_CANCEL DEFAULT_MAX_JOB_TIME);
 use OpenQA::Events;
 use OpenQA::File;
 use OpenQA::Parser 'parser';
@@ -1029,43 +1029,84 @@
 
 subtest 'priority correctly assigned when posting job' => sub {
     my $default_prio = 50;
-
     # post new job and check default priority
     $t->post_ok('/api/v1/jobs', form => \%jobs_post_params)->status_is(200);
     $t->get_ok('/api/v1/jobs/' . $t->tx->res->json->{id})->status_is(200);
     $t->json_is('/job/group', 'opensuse', 'group assigned (1)');
     $t->json_is('/job/priority', $default_prio, 'global default priority 
assigned');
 
-    subtest 'priority malus due to high MAX_JOB_TIME' => sub {
-        my %new_job_args = (priority => $default_prio);
+    subtest 'priority scaling due to high MAX_JOB_TIME-1' => sub {
         my $max_time = 7300;
+        my $add = 51;
         local $jobs_post_params{MAX_JOB_TIME} = $max_time;
         $t->post_ok('/api/v1/jobs', form => 
\%jobs_post_params)->status_is(200);
         $t->get_ok('/api/v1/jobs/' . $t->tx->res->json->{id})->status_is(200);
-        $t->json_is('/job/priority', $default_prio + $max_time / 100, 
'increased prio value');
+        $t->json_is('/job/priority', $default_prio + $add, 'post /api: 
increased prio value');
 
-        local $jobs_post_params{TIMEOUT_SCALE} = 2;
+        my %new_job_args = (priority => $default_prio);
+        $add = 0;
+        local $jobs_post_params{MAX_JOB_TIME} = DEFAULT_MAX_JOB_TIME;
+        $t->app->config->{misc_limits}->{prio_throttling_parameters} = 
'MAX_JOB_TIME:1000';
+        my $config = OpenQA::Setup::read_config($t->app);
+        $config->{misc_limits}->{prio_throttling_data} = 
OpenQA::Setup::_load_prio_throttling($t->app, $config);
         
OpenQA::Schema::ResultSet::Jobs::_apply_prio_throttling(\%jobs_post_params, 
\%new_job_args);
-        is $new_job_args{priority}, $default_prio + $max_time * 2 / 100, 
'increased prio value with TIMEOUT_SCALE';
+        is $new_job_args{priority}, $default_prio + $add, 'no change to prio 
for max_job_time = DEFAULT';
+
+        %new_job_args = (priority => $default_prio);
+        $add = 118;
+        local $jobs_post_params{MAX_JOB_TIME} = DEFAULT_MAX_JOB_TIME;
+        local $jobs_post_params{TIMEOUT_SCALE} = 3;
+        $t->app->config->{misc_limits}->{prio_throttling_parameters} = 
'MAX_JOB_TIME:0.0055,YYY:0.01:100000';
+        $config = OpenQA::Setup::read_config($t->app);
+        $config->{misc_limits}->{prio_throttling_data} = 
OpenQA::Setup::_load_prio_throttling($t->app, $config);
+        
OpenQA::Schema::ResultSet::Jobs::_apply_prio_throttling(\%jobs_post_params, 
\%new_job_args);
+        is $new_job_args{priority}, $default_prio + $add, 'increased prio for 
value max_job_time * TIMEOUT_SCALE';
         delete $jobs_post_params{TIMEOUT_SCALE};
 
-        my $limits = OpenQA::App->singleton->config->{misc_limits};
         %new_job_args = (priority => $default_prio);
-        $limits->{max_job_time_prio_scale} = 10;
+        $add = 0;
+        local $jobs_post_params{MAX_JOB_TIME} = 1800;
+        $config = OpenQA::Setup::read_config($t->app);
+        $config->{misc_limits}->{prio_throttling_parameters} = 
'MAX_JOB_TIME:0.005';
+        $config->{misc_limits}->{prio_throttling_data} = 
OpenQA::Setup::_load_prio_throttling($t->app, $config);
+        
OpenQA::Schema::ResultSet::Jobs::_apply_prio_throttling(\%jobs_post_params, 
\%new_job_args);
+        is $new_job_args{priority}, $default_prio + $add, 'no change to prio 
for max_job_time < DEFAULT';
+
+        %new_job_args = (priority => $default_prio);
+        $add = 0;
+        local $jobs_post_params{MAX_JOB_TIME} = 11000;
+        $config = OpenQA::Setup::read_config($t->app);
+        $config->{misc_limits}->{prio_throttling_parameters} = 
'FAKE_MAX_JOB_TIME:10:100000';
+        $config->{misc_limits}->{prio_throttling_data} = 
OpenQA::Setup::_load_prio_throttling($t->app, $config);
         
OpenQA::Schema::ResultSet::Jobs::_apply_prio_throttling(\%jobs_post_params, 
\%new_job_args);
-        is $new_job_args{priority}, $default_prio + $max_time / 10, 'custom 
scale value: increased prio value';
+        is $new_job_args{priority}, $default_prio + $add,
+          'prio value remained unchanged if MAX_JOB_TIME is not configured';
 
         %new_job_args = (priority => $default_prio);
-        $limits->{max_job_time_prio_scale} = 0;
+        $add = 0;
+        local $jobs_post_params{MAX_JOB_TIME} = 11000;
+        $config = OpenQA::Setup::read_config($t->app);
+        $config->{misc_limits}->{prio_throttling_parameters} = 
'MAX_JOB_TIME::100000';
+        $config->{misc_limits}->{prio_throttling_data} = 
OpenQA::Setup::_load_prio_throttling($t->app, $config);
         
OpenQA::Schema::ResultSet::Jobs::_apply_prio_throttling(\%jobs_post_params, 
\%new_job_args);
-        is $new_job_args{priority}, $default_prio, 'feature disabled: prio 
value unchanged';
+        is $new_job_args{priority}, $default_prio + $add,
+          'prio value unchanged being scale factor missing in configuration';
+
+        %new_job_args = (priority => $default_prio);
+        $add = 51 + 20;
+        local $jobs_post_params{MAX_JOB_TIME} = DEFAULT_MAX_JOB_TIME + 100;
+        local $jobs_post_params{QEMURAM} = 4096;
+        $config = OpenQA::Setup::read_config($t->app);
+        $config->{misc_limits}->{prio_throttling_parameters} = 
'QEMURAM:0.01:2048, MAX_JOB_TIME:0.007';
+        $config->{misc_limits}->{prio_throttling_data} = 
OpenQA::Setup::_load_prio_throttling($t->app, $config);
+        
OpenQA::Schema::ResultSet::Jobs::_apply_prio_throttling(\%jobs_post_params, 
\%new_job_args);
+        is $new_job_args{priority}, $default_prio + $add, 'increased prio due 
to high MAX_JOB_TIME and QEMURAM';
     };
 
     subtest 'priority scaled up due to QEMURAM demand' => sub {
         my %new_job_args = (priority => $default_prio);
         my $add = 20;
         local $jobs_post_params{QEMURAM} = 4096;
-
         my $config = OpenQA::Setup::read_config($t->app);
         $config->{misc_limits}->{prio_throttling_parameters} = 'XXX :0.2, 
QEMURAM:0.01:2048';
         $config->{misc_limits}->{prio_throttling_data} = 
OpenQA::Setup::_load_prio_throttling($t->app, $config);
@@ -1077,7 +1118,6 @@
         my %new_job_args = (priority => $default_prio);
         my $add = -10;
         local $jobs_post_params{QEMURAM} = 1024;
-
         my $config = OpenQA::Setup::read_config($t->app);
         $config->{misc_limits}->{prio_throttling_parameters} = 'XXX :0.2, 
QEMURAM:0.01:2048';
         $config->{misc_limits}->{prio_throttling_data} = 
OpenQA::Setup::_load_prio_throttling($t->app, $config);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1772722702.3877b2ca/t/api/15-users.t 
new/openQA-5.1773056733.e071deaf/t/api/15-users.t
--- old/openQA-5.1772722702.3877b2ca/t/api/15-users.t   2026-03-05 
15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/t/api/15-users.t   2026-03-09 
12:45:33.000000000 +0100
@@ -13,7 +13,7 @@
 use OpenQA::Client;
 use OpenQA::Test::Case;
 
-OpenQA::Test::Case->new->init_data(fixtures_glob => '03-users.pl');
+OpenQA::Test::Case->new->init_data(fixtures_glob => '0{1-jobs,3-users}.pl');
 
 my $t = Test::Mojo->new('OpenQA::WebAPI');
 my $app = $t->app;
@@ -80,4 +80,44 @@
     $t->delete_ok("/api/v1/users/me/api_keys/NONEXISTENT")->status_is(404, 
'delete nonexistent key');
 };
 
+subtest 'test delete_self' => sub {
+    my $user = $app->schema->resultset('Users')->find(99902);
+    ok $user, 'user exists before deletion';
+    ok $user->api_keys->create({t_expiration => DateTime->now->add(years => 
1)});
+    ok $user->comments->create({text => 'test comment'}), 'comment created';
+    ok $user->developer_sessions->create({job_id => 80000}), 'developer 
session created';
+    $t->delete_ok('/api/v1/users/me')->status_is(200, 'user can delete own 
account');
+    $user->discard_changes;
+    ok $user->is_deleted, 'user is marked as deleted';
+    is $user->username, 'deleted-user-99902', 'username anonymized';
+    is $user->email, undef, 'email cleared';
+    is $user->fullname, undef, 'fullname cleared';
+    is $user->nickname, undef, 'nickname cleared';
+    my $keys_after = $app->schema->resultset('ApiKeys')->search({user_id => 
99902})->count;
+    is $keys_after, 0, 'API keys deleted';
+    is $user->comments->count, 1, 'comment preserved';
+    is $user->developer_sessions->count, 1, 'developer session preserved';
+    my $event = OpenQA::Test::Case::find_most_recent_event($app->schema, 
'user_deleted');
+    is $event->{username}, 'https://openid.camelot.uk/lancelot', 'delete event 
has original username';
+};
+
+subtest 'anonymize audit event data' => sub {
+    my $user = $app->schema->resultset('Users')->find(99903);
+    ok $user, 'user 99903 (percival) exists';
+    my $username = $user->username;
+    my $event_data = "User $username performed an action";
+    $user->audit_events->create(
+        {
+            event => 'job_created',
+            event_data => $event_data,
+        });
+    my $audit_event = $user->audit_events->find({event => 'job_created'});
+    is $audit_event->event_data, $event_data, 'audit event created with 
username in event_data';
+    $user->anonymize;
+    $user->discard_changes;
+    $audit_event->discard_changes;
+    is $audit_event->event_data, "User deleted-user performed an action", 
'event_data anonymized';
+    is $audit_event->user_id, 99903, 'audit event association preserved';
+};
+
 done_testing();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1772722702.3877b2ca/t/config.t 
new/openQA-5.1773056733.e071deaf/t/config.t
--- old/openQA-5.1772722702.3877b2ca/t/config.t 2026-03-05 15:58:22.000000000 
+0100
+++ new/openQA-5.1773056733.e071deaf/t/config.t 2026-03-09 12:45:33.000000000 
+0100
@@ -193,9 +193,8 @@
             wait_for_grutask_retries => 6,
             worker_limit_retry_delay => ONE_HOUR / 4,
             mcp_max_result_size => 500000,
-            max_job_time_prio_scale => 100,
             scheduled_product_min_storage_duration => 34,
-            prio_throttling_parameters => '',
+            prio_throttling_parameters => 'MAX_JOB_TIME:0.007',
         },
         archiving => {
             archive_preserved_important_jobs => 0,
@@ -218,7 +217,7 @@
     $test_config->{_openid_secret} = $config->{_openid_secret};
     $test_config->{logging}->{level} = 'debug';
     $test_config->{global}->{service_port_delta} = 2;
-    $test_config->{misc_limits}->{prio_throttling_data} = undef;
+    $test_config->{misc_limits}->{prio_throttling_data} = {MAX_JOB_TIME => 
{scale => '0.007', reference => 0}};
     is ref delete $config->{global}->{auto_clone_regex}, 'Regexp', 
'auto_clone_regex parsed as regex';
     ok delete $config->{'test_preset example'}, 'default values for example 
tests assigned';
     is_deeply $config, $test_config, '"test" configuration';
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-5.1772722702.3877b2ca/t/ui/18-tests-details.t 
new/openQA-5.1773056733.e071deaf/t/ui/18-tests-details.t
--- old/openQA-5.1772722702.3877b2ca/t/ui/18-tests-details.t    2026-03-05 
15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/t/ui/18-tests-details.t    2026-03-09 
12:45:33.000000000 +0100
@@ -643,6 +643,16 @@
     like $build_href, qr/groupid=1001/, 'href to test overview';
     like $build_href, qr/version=13.1/, 'href to test overview';
     like $build_href, qr/build=0091/, 'href to test overview';
+    $jobs->find(99963)->update({result => FAILED, state => DONE});
+    $jobs->find(99962)->update({result => PASSED, state => DONE});
+    $t->get_ok('/tests/latest?test=kde&machine=64bit')->status_is(200);
+    is $t->tx->res->dom->at('#info_box .card-header a')->{href}, 
'/tests/99963', 'returns latest job';
+    
$t->get_ok('/tests/latest?test=kde&machine=64bit&result=failed')->status_is(200);
+    is $t->tx->res->dom->at('#info_box .card-header a')->{href}, 
'/tests/99963', 'returns latest failed job';
+    
$t->get_ok('/tests/latest?test=kde&machine=64bit&result=passed')->status_is(200);
+    is $t->tx->res->dom->at('#info_box .card-header a')->{href}, 
'/tests/99962',
+      'returns latest passed job (skipping newer failed one)';
+    
$t->get_ok('/tests/latest?test=kde&machine=64bit&result=parallel_failed')->status_is(404);
     $t->get_ok('/tests/latest?test=foobar')->status_is(404);
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-5.1772722702.3877b2ca/templates/webapi/api_key/index.html.ep 
new/openQA-5.1773056733.e071deaf/templates/webapi/api_key/index.html.ep
--- old/openQA-5.1772722702.3877b2ca/templates/webapi/api_key/index.html.ep     
2026-03-05 15:58:22.000000000 +0100
+++ new/openQA-5.1773056733.e071deaf/templates/webapi/api_key/index.html.ep     
2026-03-09 12:45:33.000000000 +0100
@@ -1,5 +1,8 @@
 % layout 'bootstrap';
 % title 'API Keys';
+% content_for 'ready_function' => begin
+    setup_delete_account();
+% end
 <div>
     <h2><%= title %></h2>
     %= include 'layouts/info'
@@ -39,4 +42,50 @@
             % }
         </tbody>
     </table>
+
+    <div class="card mt-4 border-danger">
+        <div class="card-header bg-danger text-white">Delete My Account</div>
+        <div class="card-body">
+            <p>Deleting your account will:</p>
+            <ul>
+                <li>Remove all your API keys</li>
+                <li>Anonymize your comments (your username will be 
removed)</li>
+                <li>Anonymize audit log entries (your username will be 
replaced)</li>
+                <li>Replace your username, email, and profile information with 
placeholder values</li>
+            </ul>
+            <div class="alert alert-warning">
+                <strong>This action cannot be undone.</strong>
+            </div>
+            <button type="button" class="btn btn-danger" 
data-bs-toggle="modal" data-bs-target="#deleteAccountModal">
+                Delete My Account
+            </button>
+        </div>
+    </div>
+
+    <div class="modal fade" id="deleteAccountModal" tabindex="-1">
+        <div class="modal-dialog">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">Confirm Account Deletion</h5>
+                    <button type="button" class="btn-close" 
data-bs-dismiss="modal"></button>
+                </div>
+                <div class="modal-body">
+                    <p>Are you sure you want to delete your account? This 
action cannot be undone.</p>
+                    <div class="mb-3">
+                        <label class="form-label" for="confirm-delete">Type 
your username to confirm:</label>
+                        % my $user = (ref($current_user) // '') eq 
'OpenQA::Schema::Result::Users' ? $current_user : ($current_user->{user} // '');
+                        <input type="text" class="form-control" 
id="confirm-delete" data-expected="<%= $user && $user->can('name') ? 
$user->name : '' %>">
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary" 
data-bs-dismiss="modal">Cancel</button>
+                    <button type="button" class="btn btn-danger" 
id="confirm-delete-btn" disabled
+                        data-delete-url="<%= 
url_for('apiv1_delete_current_user') %>"
+                        data-redirect-url="<%= url_for('index') %>">
+                        Delete My Account
+                    </button>
+                </div>
+            </div>
+        </div>
+    </div>
 </div>

++++++ openQA.obsinfo ++++++
--- /var/tmp/diff_new_pack.zfvx9V/_old  2026-03-09 16:33:29.895566336 +0100
+++ /var/tmp/diff_new_pack.zfvx9V/_new  2026-03-09 16:33:29.903566662 +0100
@@ -1,5 +1,5 @@
 name: openQA
-version: 5.1772722702.3877b2ca
-mtime: 1772722702
-commit: 3877b2caf194913eca23a443821a36f48498b8f5
+version: 5.1773056733.e071deaf
+mtime: 1773056733
+commit: e071deaf70856b3cb629e753cbb7f864afbfb37d
 

Reply via email to