Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
22 changes: 22 additions & 0 deletions backend/apps/owasp/graphql/filters/project_health_metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""Filters for OWASP ProjectHealthMetrics."""

import strawberry
import strawberry_django
from django.db.models import Q

from apps.owasp.models.enums.project import ProjectLevel
from apps.owasp.models.project_health_metrics import ProjectHealthMetrics


@strawberry_django.filter_type(ProjectHealthMetrics, lookups=True)
class ProjectHealthMetricsFilter:
"""Filter for ProjectHealthMetrics."""

score: strawberry.auto

@strawberry_django.filter_field
# prefix is required for strawberry to work with nested filters
# Q is the return type for the filter
def level(self, value: str, prefix: str):
"""Filter by project level."""
return Q(project__level=ProjectLevel(value)) if value else Q()
8 changes: 8 additions & 0 deletions backend/apps/owasp/graphql/nodes/project_health_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import strawberry
import strawberry_django

from apps.owasp.graphql.filters.project_health_metrics import ProjectHealthMetricsFilter
from apps.owasp.models.project_health_metrics import ProjectHealthMetrics


Expand All @@ -23,6 +24,8 @@
"unanswered_issues_count",
"unassigned_issues_count",
],
filters=ProjectHealthMetricsFilter,
pagination=True,
)
class ProjectHealthMetricsNode:
"""Project health metrics node."""
Expand Down Expand Up @@ -62,6 +65,11 @@ def last_release_days_requirement(self) -> int:
"""Resolve last release age requirement in days."""
return self.last_release_days_requirement

@strawberry.field
def project_name(self) -> str:
"""Resolve project name."""
return self.project.name

@strawberry.field
def owasp_page_last_update_days(self) -> int:
"""Resolve OWASP page last update age in days."""
Expand Down
10 changes: 9 additions & 1 deletion backend/apps/owasp/graphql/queries/project_health_metrics.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""OWASP Project Health Metrics Queries."""

import strawberry
import strawberry_django

from apps.owasp.graphql.filters.project_health_metrics import ProjectHealthMetricsFilter
from apps.owasp.graphql.nodes.project_health_metrics import ProjectHealthMetricsNode
from apps.owasp.graphql.nodes.project_health_stats import ProjectHealthStatsNode
from apps.owasp.models.project_health_metrics import ProjectHealthMetrics

Expand All @@ -10,12 +13,17 @@
class ProjectHealthMetricsQuery:
"""Project health metrics queries."""

project_health_metrics: list[ProjectHealthMetricsNode] = strawberry_django.field(
filters=ProjectHealthMetricsFilter,
description="List of project health metrics.",
)

@strawberry.field
def project_health_stats(self) -> ProjectHealthStatsNode:
"""Resolve overall project health stats.

Returns:
HealthStatsNode: The overall health stats of all projects.
ProjectHealthStatsNode: The overall health stats of all projects.

"""
return ProjectHealthMetrics.get_stats()
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Test cases for the ProjectHealthMetricsFilter."""

from apps.owasp.graphql.filters.project_health_metrics import ProjectHealthMetricsFilter
from apps.owasp.models.enums.project import ProjectLevel


class TestProjectHealthMetricsFilter:
"""Test cases for ProjectHealthMetricsFilter class."""

def test_filter_has_strawberry_definition(self):
"""Check if ProjectHealthMetricsFilter has valid Strawberry definition."""
assert hasattr(ProjectHealthMetricsFilter, "__strawberry_definition__")

def test_filter_fields(self):
"""Test if the filter fields are correctly defined."""
filter_fields = {
field.name for field in ProjectHealthMetricsFilter.__strawberry_definition__.fields
}
expected_fields = {"score", "level"}
assert expected_fields.issubset(filter_fields)

def test_filtering(self):
"""Test filtering by project level and score."""
filter_instance = ProjectHealthMetricsFilter(level="flagship", score=50)
assert filter_instance.level == ProjectLevel.FLAGSHIP
assert filter_instance.score == 50
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def test_meta_configuration(self):
"open_issues_count",
"open_pull_requests_count",
"owasp_page_last_update_days",
"project_name",
"recent_releases_count",
"score",
"stars_count",
Expand Down Expand Up @@ -65,6 +66,7 @@ def _get_field_by_name(self, name):
("open_issues_count", int),
("open_pull_requests_count", int),
("owasp_page_last_update_days", int),
("project_name", str),
("stars_count", int),
("recent_releases_count", int),
("unanswered_issues_count", int),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,48 @@

from unittest.mock import patch

import pytest

from apps.owasp.graphql.nodes.project_health_metrics import ProjectHealthMetricsNode
from apps.owasp.graphql.nodes.project_health_stats import ProjectHealthStatsNode
from apps.owasp.graphql.queries.project_health_metrics import ProjectHealthMetricsQuery


class TestProjectHealthMetricsQuery:
"""Test cases for ProjectHealthMetricsQuery class."""

def test_health_stats_query_has_strawberry_definition(self):
@pytest.mark.parametrize(
"field_name",
[
"project_health_stats",
"project_health_metrics",
],
)
def test_field_query_has_strawberry_definition(self, field_name):
"""Check if ProjectHealthMetricsQuery has valid Strawberry definition."""
assert hasattr(ProjectHealthMetricsQuery, "__strawberry_definition__")

field_names = {
field.name for field in ProjectHealthMetricsQuery.__strawberry_definition__.fields
}
assert "project_health_stats" in field_names
assert field_name in field_names

def test_health_stats_field_configuration(self):
"""Test if 'project_health_stats' field is configured properly."""
health_stats_field = next(
@pytest.mark.parametrize(
("field_name", "expected_type"),
[
("project_health_metrics", ProjectHealthMetricsNode),
("project_health_stats", ProjectHealthStatsNode),
],
)
def test_field_configuration(self, field_name, expected_type):
"""Test if the field has the correct type in Strawberry definition."""
query_field = next(
field
for field in ProjectHealthMetricsQuery.__strawberry_definition__.fields
if field.name == "project_health_stats"
if field.name == field_name
)

assert health_stats_field.type is ProjectHealthStatsNode
assert query_field.type is expected_type or query_field.type.of_type is expected_type

@patch("apps.owasp.models.project_health_metrics.ProjectHealthMetrics.get_stats")
def test_resolve_health_stats(self, mock_get_stats):
Expand All @@ -46,8 +63,32 @@ def test_resolve_health_stats(self, mock_get_stats):
)
mock_get_stats.return_value = expected_stats

query = ProjectHealthMetricsQuery()
query = ProjectHealthMetricsQuery(project_health_metrics=[])
result = query.project_health_stats()
mock_get_stats.assert_called_once()

assert result == expected_stats

def test_resolve_project_health_metrics(self):
"""Test resolving project health metrics."""
metrics = [
ProjectHealthMetricsNode(
stars_count=1000,
forks_count=200,
score=85.0,
contributors_count=50,
open_issues_count=10,
open_pull_requests_count=5,
unanswered_issues_count=2,
unassigned_issues_count=1,
is_funding_requirements_compliant=True,
is_leader_requirements_compliant=True,
recent_releases_count=3,
)
]
query = ProjectHealthMetricsQuery(project_health_metrics=metrics)
result = query.project_health_metrics
assert isinstance(result, list)
assert isinstance(result[0], ProjectHealthMetricsNode)
assert len(result) == 1
assert result[0].stars_count == 1000
assert result[0].forks_count == 200