Skip to content

Commit

Permalink
Add test case issue reporting (#207)
Browse files Browse the repository at this point in the history
* Add test case reported issues

* Add put and delete endpoints

* Return template_id with test results

* Add test case name to test case issues

* Allow filtering issues by case name

* Add some reported issues to seed script

* Merge migrations

* Add some reported issues to seed script

* typo

Co-authored-by: Nadzeya H <[email protected]>

* Capitalize error message

Co-authored-by: Nadzeya H <[email protected]>

---------

Co-authored-by: Nadzeya H <[email protected]>
  • Loading branch information
omar-selo and nadzyah authored Aug 30, 2024
1 parent cfb4caf commit 01bd669
Show file tree
Hide file tree
Showing 10 changed files with 382 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Add TestCaseIssue table
Revision ID: ba6550a03bc8
Revises: 2745d4e5bc72
Create Date: 2024-08-30 13:03:39.864116+00:00
"""
import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "ba6550a03bc8"
down_revision = "2745d4e5bc72"
branch_labels = None
depends_on = None


def upgrade() -> None:
op.create_table(
"test_case_issue",
sa.Column("template_id", sa.String(), nullable=False),
sa.Column("case_name", sa.String(), nullable=False),
sa.Column("url", sa.String(), nullable=False),
sa.Column("description", sa.String(), nullable=False),
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("test_case_issue_pkey")),
)
op.create_index(
op.f("test_case_issue_case_name_ix"),
"test_case_issue",
["case_name"],
unique=False,
)
op.create_index(
op.f("test_case_issue_template_id_ix"),
"test_case_issue",
["template_id"],
unique=False,
)


def downgrade() -> None:
op.drop_index(op.f("test_case_issue_template_id_ix"), table_name="test_case_issue")
op.drop_index(op.f("test_case_issue_case_name_ix"), table_name="test_case_issue")
op.drop_table("test_case_issue")
27 changes: 27 additions & 0 deletions backend/scripts/seed_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@

import requests
from fastapi.testclient import TestClient
from pydantic import HttpUrl
from sqlalchemy import select
from sqlalchemy.orm import Session

from test_observer.controllers.test_cases.models import ReportedIssueRequest
from test_observer.controllers.test_executions.models import (
C3TestResult,
C3TestResultStatus,
Expand All @@ -25,6 +27,7 @@
START_TEST_EXECUTION_URL = f"{BASE_URL}/test-executions/start-test"
END_TEST_EXECUTION_URL = f"{BASE_URL}/test-executions/end-test"
RERUN_TEST_EXECUTION_URL = f"{BASE_URL}/test-executions/reruns"
TEST_CASE_ISSUE_URL = f"{BASE_URL}/test-cases/reported-issues"

START_TEST_EXECUTION_REQUESTS = [
StartTestExecutionRequest(
Expand Down Expand Up @@ -225,6 +228,7 @@
),
C3TestResult(
name="bluetooth4/beacon_eddystone_url_hci0",
template_id="bluetooth4/beacon_eddystone_url_interface",
status=C3TestResultStatus.FAIL,
category="Bluetooth tests",
comment="",
Expand Down Expand Up @@ -283,6 +287,24 @@
),
]

TEST_CASE_ISSUE_REQUESTS = [
ReportedIssueRequest(
template_id=END_TEST_EXECUTION_REQUESTS[0].test_results[2].template_id, # type: ignore
url=HttpUrl("http://bug1.link"),
description="known issue 1",
),
ReportedIssueRequest(
case_name=END_TEST_EXECUTION_REQUESTS[0].test_results[0].name,
url=HttpUrl("http://bug2.link"),
description="known issue 2",
),
ReportedIssueRequest(
case_name=END_TEST_EXECUTION_REQUESTS[0].test_results[1].name,
url=HttpUrl("http://bug3.link"),
description="known issue 3",
),
]


def seed_data(client: TestClient | requests.Session, session: Session | None = None):
session = session or SessionLocal()
Expand All @@ -307,6 +329,11 @@ def seed_data(client: TestClient | requests.Session, session: Session | None = N
END_TEST_EXECUTION_URL, json=end_request.model_dump(mode="json")
).raise_for_status()

for case_issue_request in TEST_CASE_ISSUE_REQUESTS:
client.post(
TEST_CASE_ISSUE_URL, json=case_issue_request.model_dump(mode="json")
).raise_for_status()

_rerun_some_test_executions(client, test_executions)

_add_bugurl_and_duedate(session)
Expand Down
3 changes: 2 additions & 1 deletion backend/test_observer/controllers/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from test_observer.data_access.setup import get_db

from . import test_executions
from . import test_cases, test_executions
from .application import version
from .artefacts import artefacts
from .reports import reports
Expand All @@ -33,6 +33,7 @@
router.include_router(test_executions.router, prefix="/v1/test-executions")
router.include_router(artefacts.router, prefix="/v1/artefacts")
router.include_router(reports.router, prefix="/v1/reports")
router.include_router(test_cases.router, prefix="/v1/test-cases")


@router.get("/")
Expand Down
6 changes: 6 additions & 0 deletions backend/test_observer/controllers/test_cases/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from fastapi import APIRouter

from . import reported_issues

router = APIRouter(tags=["test-cases"])
router.include_router(reported_issues.router)
26 changes: 26 additions & 0 deletions backend/test_observer/controllers/test_cases/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from datetime import datetime

from pydantic import BaseModel, HttpUrl, model_validator


class ReportedIssueRequest(BaseModel):
template_id: str = ""
case_name: str = ""
description: str
url: HttpUrl

@model_validator(mode="after")
def check_a_or_b(self):
if not self.case_name and not self.template_id:
raise ValueError("Either case_name or template_id is required")
return self


class ReportedIssueResponse(BaseModel):
id: int
template_id: str = ""
case_name: str = ""
description: str
url: HttpUrl
created_at: datetime
updated_at: datetime
58 changes: 58 additions & 0 deletions backend/test_observer/controllers/test_cases/reported_issues.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from fastapi import APIRouter, Depends
from sqlalchemy import select
from sqlalchemy.orm import Session

from test_observer.data_access.models import TestCaseIssue
from test_observer.data_access.setup import get_db

from .models import ReportedIssueRequest, ReportedIssueResponse

router = APIRouter()


endpoint = "/reported-issues"


@router.get(endpoint, response_model=list[ReportedIssueResponse])
def get_reported_issues(
template_id: str | None = None,
case_name: str | None = None,
db: Session = Depends(get_db),
):
stmt = select(TestCaseIssue)
if template_id:
stmt = stmt.where(TestCaseIssue.template_id == template_id)
if case_name:
stmt = stmt.where(TestCaseIssue.case_name == case_name)
return db.execute(stmt).scalars()


@router.post(endpoint, response_model=ReportedIssueResponse)
def create_reported_issue(request: ReportedIssueRequest, db: Session = Depends(get_db)):
issue = TestCaseIssue(
template_id=request.template_id,
url=request.url,
description=request.description,
case_name=request.case_name,
)
db.add(issue)
db.commit()

return issue


@router.put(endpoint + "/{issue_id}", response_model=ReportedIssueResponse)
def update_reported_issue(
issue_id: int, request: ReportedIssueRequest, db: Session = Depends(get_db)
):
issue = db.get(TestCaseIssue, issue_id)
for field in request.model_fields:
setattr(issue, field, getattr(request, field))
db.commit()
return issue


@router.delete(endpoint + "/{issue_id}")
def delete_reported_issue(issue_id: int, db: Session = Depends(get_db)):
db.delete(db.get(TestCaseIssue, issue_id))
db.commit()
3 changes: 2 additions & 1 deletion backend/test_observer/controllers/test_executions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
# Omar Selo <[email protected]>


from datetime import datetime
from enum import Enum
from typing import Annotated

from datetime import datetime
from pydantic import (
AliasPath,
BaseModel,
Expand Down Expand Up @@ -143,6 +143,7 @@ class TestResultDTO(BaseModel):
id: int
name: str = Field(validation_alias=AliasPath("test_case", "name"))
category: str = Field(validation_alias=AliasPath("test_case", "category"))
template_id: str = Field(validation_alias=AliasPath("test_case", "template_id"))
status: TestResultStatus
comment: str
io_log: str
Expand Down
30 changes: 30 additions & 0 deletions backend/test_observer/data_access/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,3 +447,33 @@ class TestEvent(Base):
ForeignKey("test_execution.id", ondelete="CASCADE")
)
test_execution: Mapped["TestExecution"] = relationship(back_populates="test_events")

def __repr__(self) -> str:
return data_model_repr(
self,
"event_name",
"timestamp",
"detail",
"test_execution_id",
)


class TestCaseIssue(Base):
"""
A table to store issues reported on certain tests
"""

__tablename__ = "test_case_issue"

template_id: Mapped[str] = mapped_column(index=True)
case_name: Mapped[str] = mapped_column(index=True)
url: Mapped[str]
description: Mapped[str]

def __repr__(self) -> str:
return data_model_repr(
self,
"template_id",
"url",
"description",
)
Loading

0 comments on commit 01bd669

Please sign in to comment.