mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-22 06:18:06 +00:00
Enable ado render testing (#1144)
This commit is contained in:
@ -4,7 +4,7 @@
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import logging
|
||||
from typing import Iterator, List, Optional, Union
|
||||
from typing import Iterator, List, Optional, Tuple, Union
|
||||
|
||||
from azure.devops.connection import Connection
|
||||
from azure.devops.credentials import BasicAuthentication
|
||||
@ -60,12 +60,19 @@ class ADO:
|
||||
filename: str,
|
||||
config: ADOTemplate,
|
||||
report: Report,
|
||||
*,
|
||||
renderer: Optional[Render] = None,
|
||||
):
|
||||
self.config = config
|
||||
if renderer:
|
||||
self.renderer = renderer
|
||||
else:
|
||||
self.renderer = Render(container, filename, report)
|
||||
self.project = self.render(self.config.project)
|
||||
|
||||
def connect(self) -> None:
|
||||
auth_token = get_secret_string_value(self.config.auth_token)
|
||||
self.client = get_ado_client(self.config.base_url, auth_token)
|
||||
self.project = self.render(self.config.project)
|
||||
|
||||
def render(self, template: str) -> str:
|
||||
return self.renderer.render(template)
|
||||
@ -169,9 +176,8 @@ class ADO:
|
||||
if document:
|
||||
self.client.update_work_item(document, item.id, project=self.project)
|
||||
|
||||
def create_new(self) -> None:
|
||||
def render_new(self) -> Tuple[str, List[JsonPatchOperation]]:
|
||||
task_type = self.render(self.config.type)
|
||||
|
||||
document = []
|
||||
if "System.Tags" not in self.config.ado_fields:
|
||||
document.append(
|
||||
@ -187,6 +193,10 @@ class ADO:
|
||||
document.append(
|
||||
JsonPatchOperation(op="Add", path="/fields/%s" % field, value=value)
|
||||
)
|
||||
return (task_type, document)
|
||||
|
||||
def create_new(self) -> None:
|
||||
task_type, document = self.render_new()
|
||||
|
||||
entry = self.client.create_work_item(
|
||||
document=document, project=self.project, type=task_type
|
||||
|
@ -37,13 +37,26 @@ def fail_task(report: Report, error: Exception) -> None:
|
||||
|
||||
|
||||
class Render:
|
||||
def __init__(self, container: Container, filename: str, report: Report):
|
||||
def __init__(
|
||||
self,
|
||||
container: Container,
|
||||
filename: str,
|
||||
report: Report,
|
||||
*,
|
||||
task: Optional[Task] = None,
|
||||
job: Optional[Job] = None,
|
||||
target_url: Optional[str] = None,
|
||||
input_url: Optional[str] = None,
|
||||
report_url: Optional[str] = None,
|
||||
):
|
||||
self.report = report
|
||||
self.container = container
|
||||
self.filename = filename
|
||||
if not task:
|
||||
task = Task.get(report.job_id, report.task_id)
|
||||
if not task:
|
||||
raise ValueError(f"invalid task {report.task_id}")
|
||||
if not job:
|
||||
job = Job.get(report.job_id)
|
||||
if not job:
|
||||
raise ValueError(f"invalid job {report.job_id}")
|
||||
@ -52,15 +65,21 @@ class Render:
|
||||
self.job_config = job.config
|
||||
self.env = SandboxedEnvironment()
|
||||
|
||||
self.target_url: Optional[str] = None
|
||||
self.target_url = target_url
|
||||
if not self.target_url:
|
||||
setup_container = get_setup_container(task.config)
|
||||
if setup_container:
|
||||
self.target_url = auth_download_url(
|
||||
setup_container, self.report.executable.replace("setup/", "", 1)
|
||||
)
|
||||
|
||||
if report_url:
|
||||
self.report_url = report_url
|
||||
else:
|
||||
self.report_url = auth_download_url(container, filename)
|
||||
self.input_url: Optional[str] = None
|
||||
|
||||
self.input_url = input_url
|
||||
if not self.input_url:
|
||||
if self.report.input_blob:
|
||||
self.input_url = auth_download_url(
|
||||
self.report.input_blob.container, self.report.input_blob.name
|
||||
|
32
src/api-service/tests/data/ado-config.json
Normal file
32
src/api-service/tests/data/ado-config.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"base_url": "https://dev.azure.com/contoso",
|
||||
"auth_token": "DO NOT PUT YOUR PAT HERE",
|
||||
"type": "Bug",
|
||||
"project": "Contoso",
|
||||
"unique_fields": [
|
||||
"Microsoft.VSTS.Build.FoundIn"
|
||||
],
|
||||
"comment": "my comment",
|
||||
"ado_fields": {
|
||||
"System.AssignedTo": "example@contoso.com",
|
||||
"System.Tags": "OneFuzz; OneFuzz-Pipeline; example@example.com",
|
||||
"System.AreaPath": "MY\\AREA Path\\Here",
|
||||
"System.IterationPath": "MY\\ITERATION",
|
||||
"Microsoft.VSTS.Scheduling.StoryPoints": "1",
|
||||
"Microsoft.VSTS.Build.FoundIn": "{{ report.input_sha256 }}",
|
||||
"System.Title": "[OneFuzz] - {{ report.crash_site }}",
|
||||
"Microsoft.VSTS.TCM.ReproSteps": "This is the call stack. You may wish to confirm: <ul> {% for item in report.call_stack %} <li> {{ item }} </li> {% endfor %} </ul>"
|
||||
},
|
||||
"on_duplicate": {
|
||||
"comment": "DEDUPED!",
|
||||
"set_state": {
|
||||
"Resolved": "Active"
|
||||
},
|
||||
"ado_fields": {
|
||||
"System.IterationPath": "MY\\ITERATION"
|
||||
},
|
||||
"increment": [
|
||||
"Microsoft.VSTS.Scheduling.StoryPoints"
|
||||
]
|
||||
}
|
||||
}
|
42
src/api-service/tests/data/ado-rendered.json
Normal file
42
src/api-service/tests/data/ado-rendered.json
Normal file
@ -0,0 +1,42 @@
|
||||
[
|
||||
{
|
||||
"op": "Add",
|
||||
"path": "/fields/System.AssignedTo",
|
||||
"value": "example@contoso.com"
|
||||
},
|
||||
{
|
||||
"op": "Add",
|
||||
"path": "/fields/System.Tags",
|
||||
"value": "OneFuzz; OneFuzz-Pipeline; example@example.com;Onefuzz"
|
||||
},
|
||||
{
|
||||
"op": "Add",
|
||||
"path": "/fields/System.AreaPath",
|
||||
"value": "MY\\AREA Path\\Here"
|
||||
},
|
||||
{
|
||||
"op": "Add",
|
||||
"path": "/fields/System.IterationPath",
|
||||
"value": "MY\\ITERATION"
|
||||
},
|
||||
{
|
||||
"op": "Add",
|
||||
"path": "/fields/Microsoft.VSTS.Scheduling.StoryPoints",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"op": "Add",
|
||||
"path": "/fields/Microsoft.VSTS.Build.FoundIn",
|
||||
"value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"op": "Add",
|
||||
"path": "/fields/System.Title",
|
||||
"value": "[OneFuzz] - callfunc"
|
||||
},
|
||||
{
|
||||
"op": "Add",
|
||||
"path": "/fields/Microsoft.VSTS.TCM.ReproSteps",
|
||||
"value": "This is the call stack. You may wish to confirm: <ul> <li> test.exe.exe+0x2 </li> <li> test.exe.exe+0x9 </li> <li> test.exe.exe+0x3 </li> <li> test.exe.exe+0xd </li> <li> test.exe.exe+0x8 </li> <li> test.exe.exe+0x8 </li> <li> test.exe.exe+0x2 </li> </ul>"
|
||||
}
|
||||
]
|
23
src/api-service/tests/data/crash-report-with-html.json
Normal file
23
src/api-service/tests/data/crash-report-with-html.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"call_stack": [
|
||||
"test.exe.exe+0x2",
|
||||
"test.exe.exe+0x9",
|
||||
"test.exe.exe+0x3",
|
||||
"test.exe.exe+0xd",
|
||||
"test.exe.exe+0x8",
|
||||
"test.exe.exe+0x8",
|
||||
"test.exe.exe+0x2"
|
||||
],
|
||||
"call_stack_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"crash_site": "callfunc",
|
||||
"crash_type": "customtype",
|
||||
"executable": "test2.exe",
|
||||
"input_blob": {
|
||||
"account": "storageaccount1",
|
||||
"container": "storagecontainer1",
|
||||
"name": "%23A_B.exe.exe%2B0x0_a_b_c_0x5b1e7_a_515c01e.tar"
|
||||
},
|
||||
"input_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"job_id": "581e0ba6-9757-47e5-a077-dba21e604961",
|
||||
"task_id": "4a949007-b59f-4a47-8418-8a2c94b4aef1"
|
||||
}
|
86
src/api-service/tests/test_ado_render.py
Executable file
86
src/api-service/tests/test_ado_render.py
Executable file
@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
|
||||
import json
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from onefuzztypes.enums import OS, TaskType
|
||||
from onefuzztypes.models import ADOTemplate, JobConfig, Report, TaskConfig, TaskDetails
|
||||
from onefuzztypes.primitives import Container
|
||||
|
||||
from __app__.onefuzzlib.jobs import Job
|
||||
from __app__.onefuzzlib.notifications.ado import ADO
|
||||
from __app__.onefuzzlib.notifications.common import Render
|
||||
from __app__.onefuzzlib.tasks.main import Task
|
||||
|
||||
|
||||
class TestReportParse(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.env_patch = patch.dict(
|
||||
"os.environ", {"ONEFUZZ_INSTANCE_NAME": "contoso-test"}
|
||||
)
|
||||
self.env_patch.start()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.env_patch.stop()
|
||||
|
||||
def test_sample(self) -> None:
|
||||
expected_path = Path(__file__).parent / "data" / "ado-rendered.json"
|
||||
with open(expected_path, "r") as handle:
|
||||
expected_document = json.load(handle)
|
||||
|
||||
report_path = Path(__file__).parent / "data" / "crash-report-with-html.json"
|
||||
with open(report_path, "r") as handle:
|
||||
report_raw = json.load(handle)
|
||||
|
||||
ado_path = Path(__file__).parent / "data" / "ado-config.json"
|
||||
with open(ado_path, "r") as handle:
|
||||
ado_raw = json.load(handle)
|
||||
|
||||
report = Report.parse_obj(report_raw)
|
||||
config = ADOTemplate.parse_obj(ado_raw)
|
||||
|
||||
container = Container("containername")
|
||||
filename = "test.json"
|
||||
|
||||
job = Job(
|
||||
config=JobConfig(project="project", name="name", build="build", duration=1)
|
||||
)
|
||||
task = Task(
|
||||
config=TaskConfig(
|
||||
job_id=job.job_id,
|
||||
tags={},
|
||||
containers=[],
|
||||
task=TaskDetails(type=TaskType.libfuzzer_fuzz, duration=1),
|
||||
),
|
||||
job_id=job.job_id,
|
||||
os=OS.linux,
|
||||
)
|
||||
|
||||
renderer = Render(
|
||||
container,
|
||||
filename,
|
||||
report,
|
||||
task=task,
|
||||
job=job,
|
||||
target_url="https://contoso.com/1",
|
||||
input_url="https://contoso.com/2",
|
||||
report_url="https://contoso.com/3",
|
||||
)
|
||||
|
||||
ado = ADO(container, filename, config, report, renderer=renderer)
|
||||
work_item_type, document = ado.render_new()
|
||||
self.assertEqual(work_item_type, "Bug")
|
||||
|
||||
as_obj = [x.as_dict() for x in document]
|
||||
|
||||
self.assertEqual(as_obj, expected_document)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
Reference in New Issue
Block a user