Skip to content

Commit 89952d7

Browse files
committed
improved rex UI tests reliability
1 parent e4f0dc2 commit 89952d7

1 file changed

Lines changed: 95 additions & 59 deletions

File tree

tests/foreman/ui/test_remoteexecution.py

Lines changed: 95 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from wait_for import wait_for
2222

2323
from robottelo.config import settings
24-
from robottelo.constants import ANY_CONTEXT
24+
from robottelo.constants import ANY_CONTEXT, DEFAULT_LOC
2525
from robottelo.utils.datafactory import (
2626
gen_string,
2727
valid_hostgroups_list_short,
@@ -98,7 +98,6 @@ def test_positive_hostgroups_full_nested_names(
9898

9999
@pytest.mark.rhel_ver_list([settings.content_host.default_rhel_version])
100100
def test_positive_run_default_job_template(
101-
session,
102101
target_sat,
103102
rex_contenthost,
104103
module_org,
@@ -129,7 +128,15 @@ def test_positive_run_default_job_template(
129128

130129
with target_sat.ui_session() as session:
131130
session.organization.select(module_org.name)
132-
assert session.host.search(hostname)[0]['Name'] == hostname
131+
session.location.select(loc_name=DEFAULT_LOC)
132+
# Search with retry to handle potential context/indexing delays
133+
result = wait_for(
134+
lambda: session.all_hosts.search(f"name = {hostname}"),
135+
timeout=30,
136+
delay=2,
137+
fail_condition=[],
138+
).out
139+
assert result[0]['Name'] == hostname
133140
command = 'ls'
134141
session.jobinvocation.run(
135142
{
@@ -153,7 +160,7 @@ def test_positive_run_default_job_template(
153160

154161

155162
@pytest.mark.rhel_ver_list([settings.content_host.default_rhel_version])
156-
def test_rex_through_host_details(session, target_sat, rex_contenthost, module_org):
163+
def test_rex_through_host_details(target_sat, rex_contenthost, module_org):
157164
"""Run remote execution using the new host details page
158165
159166
:id: ee625595-4995-43b2-9e6d-633c9b33ff93
@@ -185,7 +192,17 @@ def test_rex_through_host_details(session, target_sat, rex_contenthost, module_o
185192
)
186193
task_status = target_sat.api.ForemanTask(id=task_result[0].id).poll()
187194
assert task_status['result'] == 'success'
188-
recent_jobs = session.host_new.get_details(hostname, "overview.recent_jobs")['overview']
195+
196+
# Wait for recent jobs table to load with job data
197+
recent_jobs = wait_for(
198+
lambda: session.host_new.get_details(hostname, "overview.recent_jobs")['overview'],
199+
timeout=30,
200+
delay=2,
201+
fail_condition=lambda result: (
202+
not result.get('recent_jobs', {}).get('finished', {}).get('table')
203+
),
204+
).out
205+
189206
assert recent_jobs['recent_jobs']['finished']['table'][0]['column0'] == "Run ls"
190207
assert recent_jobs['recent_jobs']['finished']['table'][0]['column2'] == "succeeded"
191208

@@ -195,7 +212,7 @@ def test_rex_through_host_details(session, target_sat, rex_contenthost, module_o
195212
'ui_user', [{'admin': True}, {'admin': False}], indirect=True, ids=['adminuser', 'nonadminuser']
196213
)
197214
def test_positive_run_custom_job_template(
198-
session, module_org, default_location, target_sat, ui_user, rex_contenthost
215+
module_org, default_location, target_sat, ui_user, rex_contenthost
199216
):
200217
"""Run a job template on a host
201218
@@ -226,7 +243,15 @@ def test_positive_run_custom_job_template(
226243
job_template_name = gen_string('alpha')
227244
with target_sat.ui_session() as session:
228245
session.organization.select(module_org.name)
229-
assert session.host.search(hostname)[0]['Name'] == hostname
246+
session.location.select(default_location.name)
247+
# Search with retry to handle potential context/indexing delays
248+
result = wait_for(
249+
lambda: session.all_hosts.search(f"name = {hostname}"),
250+
timeout=30,
251+
delay=2,
252+
fail_condition=[],
253+
).out
254+
assert result[0]['Name'] == hostname
230255
session.jobtemplate.create(
231256
{
232257
'template.name': job_template_name,
@@ -255,9 +280,7 @@ def test_positive_run_custom_job_template(
255280

256281
@pytest.mark.upgrade
257282
@pytest.mark.rhel_ver_list([settings.content_host.default_rhel_version])
258-
def test_positive_run_job_template_multiple_hosts(
259-
session, module_org, target_sat, rex_contenthosts
260-
):
283+
def test_positive_run_job_template_multiple_hosts(module_org, target_sat, rex_contenthosts):
261284
"""Run a job template against multiple hosts
262285
263286
:id: c4439ec0-bb80-47f6-bc31-fa7193bfbeeb
@@ -268,7 +291,7 @@ def test_positive_run_job_template_multiple_hosts(
268291
269292
1. Set remote_execution_connect_by_ip on hosts to true
270293
2. Navigate to the hosts page and select at least two hosts
271-
3. Click the "Select Action"
294+
3. Click the "Schedule a job"
272295
4. Select the job and appropriate template
273296
5. Run the job
274297
@@ -282,9 +305,15 @@ def test_positive_run_job_template_multiple_hosts(
282305
with target_sat.ui_session() as session:
283306
session.organization.select(module_org.name)
284307
for host in host_names:
285-
assert session.host.search(host)[0]['Name'] == host
286-
session.host.reset_search()
287-
job_status = session.host.schedule_remote_job(
308+
# Search with retry to handle potential context/indexing delays
309+
result = wait_for(
310+
lambda h=host: session.all_hosts.search(f"name = {h}"),
311+
timeout=30,
312+
delay=2,
313+
fail_condition=[],
314+
).out
315+
assert result[0]['Name'] == host
316+
job_status = session.host_new.schedule_remote_job(
288317
host_names,
289318
{
290319
'category_and_template.job_category': 'Commands',
@@ -298,7 +327,7 @@ def test_positive_run_job_template_multiple_hosts(
298327

299328

300329
@pytest.mark.rhel_ver_list([settings.content_host.default_rhel_version])
301-
def test_positive_run_scheduled_job_template_by_ip(session, module_org, rex_contenthost):
330+
def test_positive_run_scheduled_job_template_by_ip(module_org, target_sat, rex_contenthost):
302331
"""Schedule a job to be ran against a host by ip
303332
304333
:id: 4387bed9-969d-45fb-80c2-b0905bb7f1bd
@@ -323,13 +352,20 @@ def test_positive_run_scheduled_job_template_by_ip(session, module_org, rex_cont
323352
"""
324353
job_time = 6 * 60
325354
hostname = rex_contenthost.hostname
326-
with session:
355+
with target_sat.ui_session() as session:
327356
session.organization.select(module_org.name)
328-
session.location.select('Default Location')
329-
assert session.host.search(hostname)[0]['Name'] == hostname
357+
session.location.select(loc_name=DEFAULT_LOC)
358+
# Search with retry to handle potential context/indexing delays
359+
result = wait_for(
360+
lambda: session.all_hosts.search(f"name = {hostname}"),
361+
timeout=30,
362+
delay=2,
363+
fail_condition=[],
364+
).out
365+
assert result[0]['Name'] == hostname
330366
plan_time = session.browser.get_client_datetime() + datetime.timedelta(seconds=job_time)
331367
command_to_run = 'sleep 10'
332-
job_status = session.host.schedule_remote_job(
368+
job_status = session.host_new.schedule_remote_job(
333369
[hostname],
334370
{
335371
'category_and_template.job_category': 'Commands',
@@ -341,52 +377,52 @@ def test_positive_run_scheduled_job_template_by_ip(session, module_org, rex_cont
341377
},
342378
wait_for_results=False,
343379
)
344-
# Note that to create this host scheduled job we spent some time from that plan_time, as it
345-
# was calculated before creating the job
380+
# Track state transitions through polling
346381
job_left_time = (plan_time - session.browser.get_client_datetime()).total_seconds()
347-
# assert that we have time left to wait, otherwise we have to use more job time,
348-
# the job_time must be significantly greater than job creation time.
349-
assert job_left_time > 0
382+
assert job_left_time > 0, "Job scheduled time already passed during creation"
383+
384+
# Initial state: should be waiting to start
350385
assert job_status['hosts'][0]['Name'] == hostname
351386
assert job_status['hosts'][0]['Status'] in ('Awaiting start', 'N/A')
352-
# sleep 3/4 of the left time
353-
time.sleep(job_left_time * 3 / 4)
354-
job_status = session.jobinvocation.read(f'Run {command_to_run}', hostname, 'hosts')
355-
assert job_status['hosts'][0]['Name'] == hostname
356-
assert job_status['hosts'][0]['Status'] in (
357-
'Awaiting start',
358-
'N/A',
359-
'Succeeded',
360-
)
361-
# recalculate the job left time to be more accurate
362-
job_left_time = (plan_time - session.browser.get_client_datetime()).total_seconds()
363-
# the last read time should not take more than 1/4 of the last left time
364-
assert job_left_time > 0
365-
wait_for(
366-
lambda: (
367-
session.jobinvocation.read(f'Run {command_to_run}', hostname, 'hosts')['hosts'][0][
368-
'Status'
369-
]
370-
== 'Pending'
371-
),
372-
timeout=(job_left_time + 30),
373-
delay=1,
374-
)
375-
# wait the job to change status to "Succeeded"
376-
wait_for(
377-
lambda: (
378-
session.jobinvocation.read(f'Run {command_to_run}', hostname, 'hosts')['hosts'][0][
379-
'Status'
380-
]
381-
== 'Succeeded'
382-
),
383-
timeout=30,
384-
delay=1,
385-
)
387+
388+
# Poll for state transitions and record them
389+
observed_states = [job_status['hosts'][0]['Status']]
390+
poll_timeout = job_left_time + 60 # Extra buffer for job execution
391+
poll_start = time.time()
392+
393+
while time.time() - poll_start < poll_timeout:
394+
job_status = session.jobinvocation.read(f'Run {command_to_run}', hostname, 'hosts')
395+
current_status = job_status['hosts'][0]['Status']
396+
397+
# Record state changes
398+
if current_status != observed_states[-1]:
399+
observed_states.append(current_status)
400+
401+
# Exit when job completes
402+
if current_status in ('Succeeded', 'Failed', 'Cancelled'):
403+
break
404+
405+
time.sleep(2)
406+
407+
# Read final complete status
386408
job_status = session.jobinvocation.read(f'Run {command_to_run}', hostname)
409+
final_status = job_status['hosts'][0]['Status']
410+
if final_status != observed_states[-1]:
411+
observed_states.append(final_status)
412+
413+
# Verify state progression
414+
# Expected: Awaiting start/N/A -> Running/Pending -> Succeeded
415+
assert 'Succeeded' in observed_states, f"Job never succeeded. States: {observed_states}"
416+
417+
has_pre_run_state = any(s in ('Awaiting start', 'N/A') for s in observed_states[:-1])
418+
has_running_state = any(s in ('Pending', 'Running') for s in observed_states)
419+
assert has_pre_run_state, f"Missing pre-run state. States: {observed_states}"
420+
assert has_running_state or len(observed_states) == 2, (
421+
f"Job should go through running state or transition directly. States: {observed_states}"
422+
)
423+
387424
assert job_status['overall_status']['is_success']
388425
assert job_status['hosts'][0]['Name'] == hostname
389-
assert job_status['hosts'][0]['Status'] == 'Succeeded'
390426

391427

392428
@pytest.mark.rhel_ver_list([settings.content_host.default_rhel_version])

0 commit comments

Comments
 (0)