File size: 22,908 Bytes
cc036ff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
"""
Automated Bug Filing Tests

This module tests the bug filing service for all test types:
load, network, memory, mobile, desktop, visual, a11y.
"""

import os
import tempfile
from unittest.mock import patch, MagicMock

import pytest

from backend.tests.bug_discovery.bug_filing_service import BugFilingService, file_bug_from_test_failure
from backend.tests.bug_discovery.fixtures.bug_filing_fixtures import (
    test_metadata,
    test_metadata_network,
    test_metadata_memory,
    test_metadata_mobile,
    test_metadata_desktop,
    test_metadata_visual,
    test_metadata_a11y
)


class TestBugFilingLoadFailure:
    """Test bug filing for load test failures."""

    def test_bug_filing_load_failure(self, bug_filing_service, test_metadata):
        """
        Test bug filing for load test failure.

        Args:
            bug_filing_service: BugFilingService instance
            test_metadata: Sample load test metadata
        """
        result = bug_filing_service.file_bug(
            test_name="test_api_load_baseline",
            error_message="p(95) > 500ms: Actual p(95) = 650ms",
            metadata=test_metadata
        )

        # Verify issue created
        assert result["status"] == "created"
        assert result["issue_number"] == 1
        assert "github.com/test/repo/issues/1" in result["issue_url"]

    def test_bug_filing_load_labels(self, bug_filing_service, test_metadata):
        """Test that load test bugs have correct labels."""
        bug_filing_service.file_bug(
            test_name="test_api_load_baseline",
            error_message="p(95) > 500ms",
            metadata=test_metadata
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_data = last_issue[1]["json"]  # Get the json argument

        # Verify labels
        assert "bug" in issue_data["labels"]
        assert "automated" in issue_data["labels"]
        assert "test-type:load" in issue_data["labels"]
        assert "severity:high" in issue_data["labels"]
        assert "platform:web" in issue_data["labels"]

    def test_bug_filing_load_body_contains_metrics(self, bug_filing_service, test_metadata):
        """Test that load test bug body contains performance metrics."""
        bug_filing_service.file_bug(
            test_name="test_api_load_baseline",
            error_message="p(95) > 500ms",
            metadata=test_metadata
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_body = last_issue[1]["json"]["body"]

        # Verify performance metrics in body
        assert "p95_latency_ms" in issue_body
        assert "650" in issue_body
        assert "error_rate" in issue_body
        assert "Performance Metrics" in issue_body


class TestBugFilingNetworkFailure:
    """Test bug filing for network test failures."""

    def test_bug_filing_network_failure(self, bug_filing_service, test_metadata_network):
        """
        Test bug filing for network test failure.

        Args:
            bug_filing_service: BugFilingService instance
            test_metadata_network: Sample network test metadata
        """
        result = bug_filing_service.file_bug(
            test_name="test_offline_mode",
            error_message="Network error not handled: offline mode",
            metadata=test_metadata_network
        )

        # Verify issue created
        assert result["status"] == "created"
        assert result["issue_number"] == 1

    def test_bug_filing_network_labels(self, bug_filing_service, test_metadata_network):
        """Test that network test bugs have correct labels."""
        bug_filing_service.file_bug(
            test_name="test_offline_mode",
            error_message="Network error not handled",
            metadata=test_metadata_network
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_data = last_issue[1]["json"]

        # Verify labels
        assert "test-type:network" in issue_data["labels"]
        assert "severity:high" in issue_data["labels"]

    def test_bug_filing_network_body_contains_context(self, bug_filing_service, test_metadata_network):
        """Test that network test bug body includes network context."""
        bug_filing_service.file_bug(
            test_name="test_offline_mode",
            error_message="Network error not handled",
            metadata=test_metadata_network
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_body = last_issue[1]["json"]["body"]

        # Verify network context in body
        assert "offline" in issue_body.lower()
        assert "network_condition" in issue_body


class TestBugFilingMemoryLeak:
    """Test bug filing for memory leak test failures."""

    def test_bug_filing_memory_leak(self, bug_filing_service, test_metadata_memory):
        """
        Test bug filing for memory leak test failure.

        Args:
            bug_filing_service: BugFilingService instance
            test_metadata_memory: Sample memory leak test metadata
        """
        result = bug_filing_service.file_bug(
            test_name="test_memory_leak_agent_execution",
            error_message="Memory leak detected: 10MB increase",
            metadata=test_metadata_memory
        )

        # Verify issue created
        assert result["status"] == "created"
        assert result["issue_number"] == 1

    def test_bug_filing_memory_labels(self, bug_filing_service, test_metadata_memory):
        """Test that memory leak bugs have correct labels."""
        bug_filing_service.file_bug(
            test_name="test_memory_leak_agent_execution",
            error_message="Memory leak detected",
            metadata=test_metadata_memory
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_data = last_issue[1]["json"]

        # Verify labels
        assert "test-type:memory" in issue_data["labels"]
        assert "severity:high" in issue_data["labels"]

    def test_bug_filing_memory_body_contains_heap_info(self, bug_filing_service, test_metadata_memory):
        """Test that memory leak bug body includes heap snapshot comparison."""
        bug_filing_service.file_bug(
            test_name="test_memory_leak_agent_execution",
            error_message="Memory leak detected",
            metadata=test_metadata_memory
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_body = last_issue[1]["json"]["body"]

        # Verify memory info in body
        assert "10.5MB" in issue_body
        assert "memory_increase_mb" in issue_body


class TestBugFilingMobileAPI:
    """Test bug filing for mobile API test failures."""

    def test_bug_filing_mobile_api(self, bug_filing_service, test_metadata_mobile):
        """
        Test bug filing for mobile API test failure.

        Args:
            bug_filing_service: BugFilingService instance
            test_metadata_mobile: Sample mobile test metadata
        """
        result = bug_filing_service.file_bug(
            test_name="test_mobile_agent_execute",
            error_message="API returned 500",
            metadata=test_metadata_mobile
        )

        # Verify issue created
        assert result["status"] == "created"
        assert result["issue_number"] == 1

    def test_bug_filing_mobile_labels(self, bug_filing_service, test_metadata_mobile):
        """Test that mobile test bugs have correct labels."""
        bug_filing_service.file_bug(
            test_name="test_mobile_agent_execute",
            error_message="API returned 500",
            metadata=test_metadata_mobile
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_data = last_issue[1]["json"]

        # Verify labels
        assert "test-type:mobile" in issue_data["labels"]
        assert "platform:mobile" in issue_data["labels"]
        assert "severity:medium" in issue_data["labels"]

    def test_bug_filing_mobile_body_contains_endpoint(self, bug_filing_service, test_metadata_mobile):
        """Test that mobile test bug body includes endpoint info."""
        bug_filing_service.file_bug(
            test_name="test_mobile_agent_execute",
            error_message="API returned 500",
            metadata=test_metadata_mobile
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_body = last_issue[1]["json"]["body"]

        # Verify endpoint info in body
        assert "/api/v1/agents/execute" in issue_body
        assert "500" in issue_body


class TestBugFilingDesktop:
    """Test bug filing for desktop test failures."""

    def test_bug_filing_desktop(self, bug_filing_service, test_metadata_desktop):
        """
        Test bug filing for desktop test failure.

        Args:
            bug_filing_service: BugFilingService instance
            test_metadata_desktop: Sample desktop test metadata
        """
        result = bug_filing_service.file_bug(
            test_name="test_window_minimize",
            error_message="Window minimize failed",
            metadata=test_metadata_desktop
        )

        # Verify issue created
        assert result["status"] == "created"
        assert result["issue_number"] == 1

    def test_bug_filing_desktop_labels(self, bug_filing_service, test_metadata_desktop):
        """Test that desktop test bugs have correct labels."""
        bug_filing_service.file_bug(
            test_name="test_window_minimize",
            error_message="Window minimize failed",
            metadata=test_metadata_desktop
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_data = last_issue[1]["json"]

        # Verify labels
        assert "test-type:desktop" in issue_data["labels"]
        assert "platform:desktop" in issue_data["labels"]
        assert "severity:medium" in issue_data["labels"]


class TestBugFilingVisualRegression:
    """Test bug filing for visual regression test failures."""

    def test_bug_filing_visual_regression(self, bug_filing_service, test_metadata_visual):
        """
        Test bug filing for visual regression test failure.

        Args:
            bug_filing_service: BugFilingService instance
            test_metadata_visual: Sample visual regression test metadata
        """
        result = bug_filing_service.file_bug(
            test_name="test_visual_dashboard",
            error_message="Visual diff detected",
            metadata=test_metadata_visual
        )

        # Verify issue created
        assert result["status"] == "created"
        assert result["issue_number"] == 1

    def test_bug_filing_visual_labels(self, bug_filing_service, test_metadata_visual):
        """Test that visual regression bugs have correct labels."""
        bug_filing_service.file_bug(
            test_name="test_visual_dashboard",
            error_message="Visual diff detected",
            metadata=test_metadata_visual
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_data = last_issue[1]["json"]

        # Verify labels
        assert "test-type:visual" in issue_data["labels"]
        assert "severity:medium" in issue_data["labels"]

    def test_bug_filing_visual_body_contains_percy_url(self, bug_filing_service, test_metadata_visual):
        """Test that visual regression bug body includes Percy diff URL."""
        bug_filing_service.file_bug(
            test_name="test_visual_dashboard",
            error_message="Visual diff detected",
            metadata=test_metadata_visual
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_body = last_issue[1]["json"]["body"]

        # Verify Percy URL in body
        assert "percy.io" in issue_body
        assert "pixel_diff_count" in issue_body


class TestBugFilingAccessibility:
    """Test bug filing for accessibility test failures."""

    def test_bug_filing_accessibility(self, bug_filing_service, test_metadata_a11y):
        """
        Test bug filing for accessibility test failure.

        Args:
            bug_filing_service: BugFilingService instance
            test_metadata_a11y: Sample accessibility test metadata
        """
        result = bug_filing_service.file_bug(
            test_name="test_wcag_compliance_dashboard",
            error_message="WCAG violation: color-contrast",
            metadata=test_metadata_a11y
        )

        # Verify issue created
        assert result["status"] == "created"
        assert result["issue_number"] == 1

    def test_bug_filing_a11y_labels(self, bug_filing_service, test_metadata_a11y):
        """Test that accessibility test bugs have correct labels."""
        bug_filing_service.file_bug(
            test_name="test_wcag_compliance_dashboard",
            error_message="WCAG violation: color-contrast",
            metadata=test_metadata_a11y
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_data = last_issue[1]["json"]

        # Verify labels
        assert "test-type:a11y" in issue_data["labels"]
        assert "severity:high" in issue_data["labels"]

    def test_bug_filing_a11y_body_contains_violations(self, bug_filing_service, test_metadata_a11y):
        """Test that accessibility bug body includes axe-core violation details."""
        bug_filing_service.file_bug(
            test_name="test_wcag_compliance_dashboard",
            error_message="WCAG violation: color-contrast",
            metadata=test_metadata_a11y
        )

        # Get the last created issue from the mock
        last_issue = bug_filing_service.session.post.call_args
        issue_body = last_issue[1]["json"]["body"]

        # Verify violation details in body
        assert "color-contrast" in issue_body
        assert "violation_count" in issue_body
        assert "WCAG" in issue_body


class TestBugFilingIdempotency:
    """Test bug filing idempotency (no duplicate issues)."""

    def test_bug_filing_idempotency(self, bug_filing_service, test_metadata):
        """
        Test that filing the same bug twice creates only one issue.

        Args:
            bug_filing_service: BugFilingService instance
            test_metadata: Sample test metadata
        """
        # File bug first time
        result1 = bug_filing_service.file_bug(
            test_name="test_api_load_baseline",
            error_message="p(95) > 500ms",
            metadata=test_metadata
        )

        # File bug second time (same test name, error type, and metadata)
        result2 = bug_filing_service.file_bug(
            test_name="test_api_load_baseline",
            error_message="p(95) > 500ms",
            metadata=test_metadata
        )

        # Verify first bug created
        assert result1["status"] == "created"
        assert result1["issue_number"] == 1

        # Verify second bug detected as duplicate
        assert result2["status"] == "duplicate"
        assert result2["issue_number"] == 1  # Same issue as first
        assert "already filed" in result2["message"].lower()

    def test_bug_filing_different_errors_create_different_issues(self, bug_filing_service, test_metadata):
        """
        Test that different errors create different issues.

        Args:
            bug_filing_service: BugFilingService instance
            test_metadata: Sample test metadata
        """
        # File first bug
        result1 = bug_filing_service.file_bug(
            test_name="test_api_load_baseline",
            error_message="p(95) > 500ms",
            metadata=test_metadata
        )

        # File second bug with different error type
        import copy
        test_metadata2 = copy.deepcopy(test_metadata)
        test_metadata2["error_type"] = "Connection Timeout"

        result2 = bug_filing_service.file_bug(
            test_name="test_api_load_baseline",
            error_message="Connection timeout",
            metadata=test_metadata2
        )

        # Verify both bugs created
        assert result1["status"] == "created"
        assert result1["issue_number"] == 1

        assert result2["status"] == "created"
        assert result2["issue_number"] == 2

    def test_bug_filing_check_duplicate_bug_prevents_duplicates(self, bug_filing_service, test_metadata):
        """Test that _check_duplicate_bug prevents duplicate filing."""
        # Create first issue
        bug_filing_service.file_bug(
            test_name="test_api_load_baseline",
            error_message="p(95) > 500ms",
            metadata=test_metadata
        )

        # Check for duplicate manually
        title = "[Bug] Load Test Failure: Test Api Load Baseline"
        existing_issue = bug_filing_service._check_duplicate_bug(title)

        # Verify duplicate found
        assert existing_issue is not None
        assert existing_issue["number"] == 1
        assert existing_issue["state"] == "open"


class TestBugFilingConvenienceFunction:
    """Test the convenience function file_bug_from_test_failure."""

    def test_file_bug_from_test_failure_with_env_vars(self, monkeypatch):
        """Test convenience function with environment variables set."""
        # Set environment variables
        monkeypatch.setenv("GITHUB_TOKEN", "test_token_ghp_xxx")
        monkeypatch.setenv("GITHUB_REPOSITORY", "test/repo")

        # Mock BugFilingService
        with patch("backend.tests.bug_discovery.bug_filing_service.BugFilingService") as mock_service_class:
            mock_service = MagicMock()
            mock_service.file_bug.return_value = {
                "status": "created",
                "issue_number": 1,
                "issue_url": "https://github.com/test/repo/issues/1"
            }
            mock_service_class.return_value = mock_service

            # Call convenience function
            result = file_bug_from_test_failure(
                test_name="test_example",
                error_message="Test failed",
                stack_trace="Error at line 42",
                test_type="load"
            )

            # Verify service created and called
            mock_service_class.assert_called_once_with("test_token_ghp_xxx", "test/repo")
            mock_service.file_bug.assert_called_once()
            assert result["status"] == "created"

    def test_file_bug_from_test_failure_missing_token(self, monkeypatch):
        """Test convenience function raises error without GITHUB_TOKEN."""
        # Remove GITHUB_TOKEN
        monkeypatch.delenv("GITHUB_TOKEN", raising=False)

        # Should raise ValueError
        with pytest.raises(ValueError, match="GITHUB_TOKEN environment variable not set"):
            file_bug_from_test_failure(
                test_name="test_example",
                error_message="Test failed",
                stack_trace="Error at line 42",
                test_type="load"
            )

    def test_file_bug_from_test_failure_missing_repository(self, monkeypatch):
        """Test convenience function raises error without GITHUB_REPOSITORY."""
        # Set token but not repository
        monkeypatch.setenv("GITHUB_TOKEN", "test_token_ghp_xxx")
        monkeypatch.delenv("GITHUB_REPOSITORY", raising=False)

        # Should raise ValueError
        with pytest.raises(ValueError, match="GITHUB_REPOSITORY environment variable not set"):
            file_bug_from_test_failure(
                test_name="test_example",
                error_message="Test failed",
                stack_trace="Error at line 42",
                test_type="load"
            )


class TestBugFilingAttachments:
    """Test screenshot and log attachment functionality."""

    def test_attach_screenshot_with_existing_file(self, bug_filing_service, test_metadata, sample_screenshot_file):
        """Test attaching screenshot when file exists."""
        # Update metadata with real screenshot path
        test_metadata["screenshot_path"] = sample_screenshot_file

        # File bug (should attach screenshot)
        result = bug_filing_service.file_bug(
            test_name="test_with_screenshot",
            error_message="Test failed",
            metadata=test_metadata
        )

        # Verify issue created
        assert result["status"] == "created"

    def test_attach_screenshot_with_missing_file(self, bug_filing_service, test_metadata, capsys):
        """Test attaching screenshot when file doesn't exist (should warn)."""
        # Use non-existent screenshot path
        test_metadata["screenshot_path"] = "/nonexistent/screenshot.png"

        # File bug (should warn but not fail)
        result = bug_filing_service.file_bug(
            test_name="test_with_missing_screenshot",
            error_message="Test failed",
            metadata=test_metadata
        )

        # Verify issue still created
        assert result["status"] == "created"

        # Verify warning printed
        captured = capsys.readouterr()
        assert "Warning: Screenshot file not found" in captured.out

    def test_attach_logs_with_existing_file(self, bug_filing_service, test_metadata, sample_log_file):
        """Test attaching logs when file exists."""
        # Update metadata with real log path
        test_metadata["log_path"] = sample_log_file

        # File bug (should attach logs)
        result = bug_filing_service.file_bug(
            test_name="test_with_logs",
            error_message="Test failed",
            metadata=test_metadata
        )

        # Verify issue created
        assert result["status"] == "created"

    def test_attach_logs_with_missing_file(self, bug_filing_service, test_metadata, capsys):
        """Test attaching logs when file doesn't exist (should warn)."""
        # Use non-existent log path
        test_metadata["log_path"] = "/nonexistent/test.log"

        # File bug (should warn but not fail)
        result = bug_filing_service.file_bug(
            test_name="test_with_missing_logs",
            error_message="Test failed",
            metadata=test_metadata
        )

        # Verify issue still created
        assert result["status"] == "created"

        # Verify warning printed
        captured = capsys.readouterr()
        assert "Warning: Log file not found" in captured.out