@@ -145,3 +145,165 @@ def test_delete_ad_hoc_command_in_active_state(ad_hoc_command_factory, delete, a
145145 adhoc = ad_hoc_command_factory (initial_state = status )
146146 url = reverse ('api:ad_hoc_command_detail' , kwargs = {'pk' : adhoc .pk })
147147 delete (url , None , admin , expect = 403 )
148+
149+
150+ @pytest .fixture
151+ def job_with_heavy_fields (job_factory ):
152+ job = job_factory ()
153+ job .extra_vars = '{"some_var": "some_value"}'
154+ job .artifacts = {"some_artifact" : "some_value" }
155+ job .save ()
156+ return job
157+
158+
159+ def _job_result (response , job_id ):
160+ for row in response .data ['results' ]:
161+ if row ['id' ] == job_id :
162+ return row
163+ raise AssertionError ('job {} not found in {}' .format (job_id , [r ['id' ] for r in response .data ['results' ]]))
164+
165+
166+ @pytest .mark .django_db
167+ def test_unified_jobs_list_excludes_heavy_fields_by_default (get , admin , job_with_heavy_fields ):
168+ response = get (reverse ('api:unified_job_list' ) + '?id={}' .format (job_with_heavy_fields .id ), admin , expect = 200 )
169+ row = _job_result (response , job_with_heavy_fields .id )
170+ assert 'artifacts' not in row
171+ assert 'extra_vars' not in row
172+
173+
174+ @pytest .mark .django_db
175+ def test_unified_jobs_list_include_artifacts (get , admin , job_with_heavy_fields ):
176+ response = get (
177+ reverse ('api:unified_job_list' ) + '?id={}&include=artifacts' .format (job_with_heavy_fields .id ),
178+ admin ,
179+ expect = 200 ,
180+ )
181+ row = _job_result (response , job_with_heavy_fields .id )
182+ assert 'artifacts' in row
183+ assert 'extra_vars' not in row
184+
185+
186+ @pytest .mark .django_db
187+ def test_unified_jobs_list_include_extra_vars (get , admin , job_with_heavy_fields ):
188+ response = get (
189+ reverse ('api:unified_job_list' ) + '?id={}&include=extra_vars' .format (job_with_heavy_fields .id ),
190+ admin ,
191+ expect = 200 ,
192+ )
193+ row = _job_result (response , job_with_heavy_fields .id )
194+ assert 'extra_vars' in row
195+ assert 'artifacts' not in row
196+
197+
198+ @pytest .mark .django_db
199+ def test_unified_jobs_list_include_both (get , admin , job_with_heavy_fields ):
200+ response = get (
201+ reverse ('api:unified_job_list' ) + '?id={}&include=artifacts,extra_vars' .format (job_with_heavy_fields .id ),
202+ admin ,
203+ expect = 200 ,
204+ )
205+ row = _job_result (response , job_with_heavy_fields .id )
206+ assert 'artifacts' in row
207+ assert 'extra_vars' in row
208+
209+
210+ @pytest .mark .django_db
211+ def test_unified_jobs_list_include_tolerates_whitespace (get , admin , job_with_heavy_fields ):
212+ response = get (
213+ reverse ('api:unified_job_list' ) + '?id={}&include=%20artifacts%20,%20extra_vars%20' .format (job_with_heavy_fields .id ),
214+ admin ,
215+ expect = 200 ,
216+ )
217+ row = _job_result (response , job_with_heavy_fields .id )
218+ assert 'artifacts' in row
219+ assert 'extra_vars' in row
220+
221+
222+ @pytest .mark .django_db
223+ def test_unified_jobs_list_include_ignores_unknown (get , admin , job_with_heavy_fields ):
224+ response = get (
225+ reverse ('api:unified_job_list' ) + '?id={}&include=does_not_exist' .format (job_with_heavy_fields .id ),
226+ admin ,
227+ expect = 200 ,
228+ )
229+ row = _job_result (response , job_with_heavy_fields .id )
230+ assert 'artifacts' not in row
231+ assert 'extra_vars' not in row
232+
233+
234+ @pytest .mark .django_db
235+ def test_unified_jobs_list_include_does_not_honor_always_stripped (get , admin , job_with_heavy_fields ):
236+ # Always-stripped fields like event_processing_finished, job_args, result_traceback
237+ # must remain stripped regardless of the ?include= param — they cannot be re-included.
238+ response = get (
239+ reverse ('api:unified_job_list' ) + '?id={}&include=job_args,result_traceback,event_processing_finished' .format (job_with_heavy_fields .id ),
240+ admin ,
241+ expect = 200 ,
242+ )
243+ row = _job_result (response , job_with_heavy_fields .id )
244+ assert 'event_processing_finished' not in row
245+ assert 'job_args' not in row
246+ assert 'result_traceback' not in row
247+
248+
249+ @pytest .mark .django_db
250+ def test_jobs_list_excludes_heavy_fields_by_default (get , admin , job_with_heavy_fields ):
251+ response = get (reverse ('api:job_list' ) + '?id={}' .format (job_with_heavy_fields .id ), admin , expect = 200 )
252+ row = _job_result (response , job_with_heavy_fields .id )
253+ assert 'artifacts' not in row
254+ assert 'extra_vars' not in row
255+
256+
257+ @pytest .mark .django_db
258+ def test_jobs_list_include_extra_vars (get , admin , job_with_heavy_fields ):
259+ response = get (
260+ reverse ('api:job_list' ) + '?id={}&include=extra_vars' .format (job_with_heavy_fields .id ),
261+ admin ,
262+ expect = 200 ,
263+ )
264+ row = _job_result (response , job_with_heavy_fields .id )
265+ assert 'extra_vars' in row
266+ assert 'artifacts' not in row
267+
268+
269+ def _deferred_field_names (qs ):
270+ # queryset.query.deferred_loading is (names, defer_flag). When defer_flag is
271+ # True the names are deferred; when False (an .only() was applied) the
272+ # deferred set is every concrete field not in names.
273+ names , defer = qs .query .deferred_loading
274+ if defer :
275+ return set (names )
276+ all_fields = {f .name for f in qs .model ._meta .concrete_fields }
277+ return all_fields - set (names )
278+
279+
280+ def _job_list_queryset (user , querystring = '' ):
281+ from rest_framework .request import Request
282+ from rest_framework .test import APIRequestFactory
283+
284+ from awx .api .views import JobList
285+
286+ view = JobList ()
287+ view .request = Request (APIRequestFactory ().get ('/api/v2/jobs/' + querystring ))
288+ view .request .user = user
289+ return view .get_queryset ()
290+
291+
292+ @pytest .mark .django_db
293+ def test_jobs_list_queryset_defers_heavy_columns_by_default (admin ):
294+ deferred = _deferred_field_names (_job_list_queryset (admin ))
295+ assert {'extra_vars' , 'artifacts' } <= deferred
296+
297+
298+ @pytest .mark .django_db
299+ def test_jobs_list_queryset_defers_only_unrequested_heavy_columns (admin ):
300+ deferred = _deferred_field_names (_job_list_queryset (admin , '?include=extra_vars' ))
301+ assert 'artifacts' in deferred
302+ assert 'extra_vars' not in deferred
303+
304+
305+ @pytest .mark .django_db
306+ def test_jobs_list_queryset_defers_nothing_when_all_included (admin ):
307+ deferred = _deferred_field_names (_job_list_queryset (admin , '?include=extra_vars,artifacts' ))
308+ assert 'extra_vars' not in deferred
309+ assert 'artifacts' not in deferred
0 commit comments