diff --git a/backend/apps/ai/common/utils.py b/backend/apps/ai/common/utils.py index 078fbe18a4..1d4fd279af 100644 --- a/backend/apps/ai/common/utils.py +++ b/backend/apps/ai/common/utils.py @@ -40,8 +40,6 @@ def create_chunks_and_embeddings( ValueError: If context is None or invalid """ - from apps.ai.models.chunk import Chunk - try: last_request_time = datetime.now(UTC) - timedelta( seconds=DEFAULT_LAST_REQUEST_OFFSET_SECONDS @@ -94,8 +92,6 @@ def regenerate_chunks_for_context(context: Context): context (Context): The specific context instance to be updated. """ - from apps.ai.models.chunk import Chunk - context.chunks.all().delete() new_chunk_texts = Chunk.split_text(context.content) diff --git a/backend/apps/ai/models/chunk.py b/backend/apps/ai/models/chunk.py index ad6f6c60cf..e47979db3f 100644 --- a/backend/apps/ai/models/chunk.py +++ b/backend/apps/ai/models/chunk.py @@ -13,6 +13,8 @@ class Chunk(TimestampedModel): """AI Chunk model for storing text chunks with embeddings.""" class Meta: + """Model options.""" + db_table = "ai_chunks" verbose_name = "Chunk" unique_together = ("context", "text") diff --git a/backend/apps/ai/models/context.py b/backend/apps/ai/models/context.py index f94d6810f0..60a7a5f4e1 100644 --- a/backend/apps/ai/models/context.py +++ b/backend/apps/ai/models/context.py @@ -22,6 +22,8 @@ class Context(TimestampedModel): source = models.CharField(max_length=100, blank=True, default="") class Meta: + """Model options.""" + db_table = "ai_contexts" verbose_name = "Context" unique_together = ("entity_type", "entity_id", "source") diff --git a/backend/apps/api/models/api_key.py b/backend/apps/api/models/api_key.py index e2ce3e3ae0..505d3c6cc8 100644 --- a/backend/apps/api/models/api_key.py +++ b/backend/apps/api/models/api_key.py @@ -17,6 +17,8 @@ class ApiKey(models.Model): """API key model.""" class Meta: + """Model options.""" + db_table = "api_keys" verbose_name_plural = "API keys" ordering = ["-created_at"] diff --git a/backend/apps/common/management/commands/algolia_update_replicas.py b/backend/apps/common/management/commands/algolia_update_replicas.py index 47e04c2c13..e238e6716b 100644 --- a/backend/apps/common/management/commands/algolia_update_replicas.py +++ b/backend/apps/common/management/commands/algolia_update_replicas.py @@ -10,6 +10,6 @@ class Command(BaseCommand): def handle(self, *_args, **_options) -> None: """Update replicas for Algolia indices.""" - print("\n Starting replica configuration...") + self.stdout.write("\n Starting replica configuration...\n") ProjectIndex.configure_replicas() - print("\n Replica have been Successfully created.") + self.stdout.write(self.style.SUCCESS("\n Replicas have been successfully created.\n")) diff --git a/backend/apps/common/management/commands/algolia_update_synonyms.py b/backend/apps/common/management/commands/algolia_update_synonyms.py index 00c8485020..5ea898360e 100644 --- a/backend/apps/common/management/commands/algolia_update_synonyms.py +++ b/backend/apps/common/management/commands/algolia_update_synonyms.py @@ -11,8 +11,8 @@ class Command(BaseCommand): def handle(self, *_args, **_options) -> None: """Update synonyms for Algolia indices.""" - print("\nThe following models synonyms were reindexed:") + self.stdout.write("\nThe following models synonyms were reindexed:") for index in (IssueIndex, ProjectIndex): count = index.update_synonyms() if count: - print(f"{7 * ' '} * {index.index_name.capitalize()} --> {count}") + self.stdout.write(f"{7 * ' '} * {index.index_name.capitalize()} --> {count}") diff --git a/backend/apps/common/management/commands/dump_data.py b/backend/apps/common/management/commands/dump_data.py index 8c9631338c..145c4a8ed0 100644 --- a/backend/apps/common/management/commands/dump_data.py +++ b/backend/apps/common/management/commands/dump_data.py @@ -86,7 +86,7 @@ def handle(self, *args, **options): dump_cmd += [f"--table={table}" for table in tables] dump_cmd += ["-f", str(output_path)] - run(dump_cmd, check=True, env=env) + run(dump_cmd, check=True, env=env) # noqa: S603 self.stdout.write(self.style.SUCCESS(f"Created dump: {output_path}")) except CalledProcessError as e: message = f"Command failed: {e.cmd}" diff --git a/backend/apps/common/management/commands/purge_data.py b/backend/apps/common/management/commands/purge_data.py index 2bdacbf2a4..c18eb6e613 100644 --- a/backend/apps/common/management/commands/purge_data.py +++ b/backend/apps/common/management/commands/purge_data.py @@ -29,4 +29,4 @@ def handle(self, *_args, **options) -> None: sql.Identifier(model._meta.db_table) ) ) - print(f"Purged {nest_app}.{model.__name__}") + self.stdout.write(f"Purged {nest_app}.{model.__name__}") diff --git a/backend/apps/core/models/prompt.py b/backend/apps/core/models/prompt.py index 61ea21ae05..3ea7a5637e 100644 --- a/backend/apps/core/models/prompt.py +++ b/backend/apps/core/models/prompt.py @@ -15,6 +15,8 @@ class Prompt(TimestampedModel): """Prompt model.""" class Meta: + """Model options.""" + db_table = "nest_prompts" verbose_name_plural = "Prompts" diff --git a/backend/apps/github/management/commands/github_add_related_repositories.py b/backend/apps/github/management/commands/github_add_related_repositories.py index 887be0ab8f..558d2fb08a 100644 --- a/backend/apps/github/management/commands/github_add_related_repositories.py +++ b/backend/apps/github/management/commands/github_add_related_repositories.py @@ -43,7 +43,7 @@ def handle(self, *args, **options) -> None: projects = [] for idx, project in enumerate(active_projects[offset:]): prefix = f"{idx + offset + 1} of {active_projects_count}" - print(f"{prefix:<10} {project.owasp_url}") + self.stdout.write(f"{prefix:<10} {project.owasp_url}\n") repository_urls = project.related_urls.copy() for repository_url in repository_urls: diff --git a/backend/apps/github/management/commands/github_enrich_issues.py b/backend/apps/github/management/commands/github_enrich_issues.py index 1ac5c5e113..76b2ed4a0e 100644 --- a/backend/apps/github/management/commands/github_enrich_issues.py +++ b/backend/apps/github/management/commands/github_enrich_issues.py @@ -55,7 +55,7 @@ def handle(self, *args, **options) -> None: update_fields += ["summary"] if (update_summary := options["update_summary"]) else [] for idx, issue in enumerate(open_issues[offset:]): prefix = f"{idx + offset + 1} of {open_issues_count - offset}" - print(f"{prefix:<10} {issue.title}") + self.stdout.write(f"{prefix:<10} {issue.title}\n") if update_hint: issue.generate_hint(open_ai=open_ai) diff --git a/backend/apps/github/management/commands/github_update_owasp_organization.py b/backend/apps/github/management/commands/github_update_owasp_organization.py index feb0a83023..e21d26a206 100644 --- a/backend/apps/github/management/commands/github_update_owasp_organization.py +++ b/backend/apps/github/management/commands/github_update_owasp_organization.py @@ -73,7 +73,7 @@ def handle(self, *_args, **options) -> None: prefix = f"{idx + offset + 1} of {gh_repositories_count}" entity_key = gh_repository.name.lower() repository_url = f"https://github.com/OWASP/{entity_key}" - print(f"{prefix:<12} {repository_url}") + self.stdout.write(f"{prefix:<12} {repository_url}\n") try: owasp_organization, owasp_repository = sync_repository( @@ -117,7 +117,7 @@ def handle(self, *_args, **options) -> None: local_owasp_repositories_count == remote_owasp_repositories_count ) result = "==" if has_same_repositories_count else "!=" - print( + self.stdout.write( "\n" f"OWASP GitHub repositories count {result} synced repositories count: " f"{remote_owasp_repositories_count} {result} {local_owasp_repositories_count}" diff --git a/backend/apps/github/management/commands/github_update_related_organizations.py b/backend/apps/github/management/commands/github_update_related_organizations.py index 6eda5d53c1..77b5686d0a 100644 --- a/backend/apps/github/management/commands/github_update_related_organizations.py +++ b/backend/apps/github/management/commands/github_update_related_organizations.py @@ -53,7 +53,7 @@ def handle(self, *_args, **options) -> None: organizations_count = organizations.count() for idx, organization in enumerate(organizations): prefix = f"{idx + 1} of {organizations_count}" - print(f"{prefix:<10} {organization.url}\n") + self.stdout.write(f"{prefix:<10} {organization.url}\n") if organization.related_projects.count() != 1: logger.error( @@ -76,7 +76,7 @@ def handle(self, *_args, **options) -> None: repository_url = ( f"https://github.com/{organization.login}/{gh_repository.name.lower()}" ) - print(f"{prefix:<12} {repository_url}") + self.stdout.write(f"{prefix:<12} {repository_url}\n") try: _, repository = sync_repository(gh_repository) diff --git a/backend/apps/github/management/commands/github_update_users.py b/backend/apps/github/management/commands/github_update_users.py index 42a6e51e4d..7b81bcc930 100644 --- a/backend/apps/github/management/commands/github_update_users.py +++ b/backend/apps/github/management/commands/github_update_users.py @@ -48,7 +48,7 @@ def handle(self, *args, **options): users = [] for idx, user in enumerate(active_users[offset:]): prefix = f"{idx + offset + 1} of {active_users_count - offset}" - print(f"{prefix:<10} {user.title}") + self.stdout.write(f"{prefix:<10} {user.title}\n") user.contributions_count = user_contributions.get(user.id, 0) users.append(user) diff --git a/backend/apps/github/models/comment.py b/backend/apps/github/models/comment.py index 3543dfd085..23ee0a4048 100644 --- a/backend/apps/github/models/comment.py +++ b/backend/apps/github/models/comment.py @@ -12,6 +12,8 @@ class Comment(BulkSaveModel, TimestampedModel): """Represents a comment on a GitHub Issue.""" class Meta: + """Model options.""" + db_table = "github_comments" verbose_name = "Comment" verbose_name_plural = "Comments" diff --git a/backend/apps/github/models/commit.py b/backend/apps/github/models/commit.py index b5525e56c4..4ebf7712cc 100644 --- a/backend/apps/github/models/commit.py +++ b/backend/apps/github/models/commit.py @@ -12,6 +12,8 @@ class Commit(BulkSaveModel, NodeModel, TimestampedModel): """Commit model.""" class Meta: + """Model options.""" + db_table = "github_commits" indexes = [ models.Index(fields=["-created_at"], name="commit_created_at_desc_idx"), diff --git a/backend/apps/github/models/common.py b/backend/apps/github/models/common.py index 795fc0b249..e7b69fa610 100644 --- a/backend/apps/github/models/common.py +++ b/backend/apps/github/models/common.py @@ -8,6 +8,8 @@ class GenericUserModel(models.Model): """Generic user model.""" class Meta: + """Model options.""" + abstract = True name = models.CharField(verbose_name="Name", max_length=200, blank=True, default="") @@ -77,6 +79,8 @@ class NodeModel(models.Model): """Node model.""" class Meta: + """Model options.""" + abstract = True node_id = models.CharField(verbose_name="Node ID", unique=True) diff --git a/backend/apps/github/models/generic_issue_model.py b/backend/apps/github/models/generic_issue_model.py index f3dfae837d..f44226d125 100644 --- a/backend/apps/github/models/generic_issue_model.py +++ b/backend/apps/github/models/generic_issue_model.py @@ -13,9 +13,13 @@ class GenericIssueModel(BulkSaveModel, IssueIndexMixin, NodeModel, TimestampedMo objects = models.Manager() class Meta: + """Model options.""" + abstract = True class State(models.TextChoices): + """Issue state choices.""" + OPEN = "open", "Open" CLOSED = "closed", "Closed" diff --git a/backend/apps/github/models/issue.py b/backend/apps/github/models/issue.py index 9ec35e0e57..bbb0ebe49c 100644 --- a/backend/apps/github/models/issue.py +++ b/backend/apps/github/models/issue.py @@ -23,6 +23,8 @@ class Issue(GenericIssueModel): open_issues = OpenIssueManager() class Meta: + """Model options.""" + db_table = "github_issues" indexes = [ models.Index(fields=["-created_at"]), diff --git a/backend/apps/github/models/label.py b/backend/apps/github/models/label.py index b25c1f93a2..43ea7036aa 100644 --- a/backend/apps/github/models/label.py +++ b/backend/apps/github/models/label.py @@ -10,6 +10,8 @@ class Label(BulkSaveModel, NodeModel, TimestampedModel): """Label model.""" class Meta: + """Model options.""" + db_table = "github_labels" verbose_name_plural = "Labels" diff --git a/backend/apps/github/models/milestone.py b/backend/apps/github/models/milestone.py index 8d88552e33..1f1b49ea5b 100644 --- a/backend/apps/github/models/milestone.py +++ b/backend/apps/github/models/milestone.py @@ -15,6 +15,8 @@ class Milestone(GenericIssueModel): closed_milestones = ClosedMilestoneManager() class Meta: + """Model options.""" + db_table = "github_milestones" verbose_name_plural = "Milestones" ordering = ["-updated_at", "-state"] diff --git a/backend/apps/github/models/organization.py b/backend/apps/github/models/organization.py index a062cf6061..d11ab0834c 100644 --- a/backend/apps/github/models/organization.py +++ b/backend/apps/github/models/organization.py @@ -30,6 +30,8 @@ class Organization( related_organizations = RelatedOrganizationsManager() class Meta: + """Model options.""" + db_table = "github_organizations" verbose_name_plural = "Organizations" diff --git a/backend/apps/github/models/pull_request.py b/backend/apps/github/models/pull_request.py index e3677880d9..866ec51443 100644 --- a/backend/apps/github/models/pull_request.py +++ b/backend/apps/github/models/pull_request.py @@ -15,6 +15,8 @@ class PullRequest(GenericIssueModel): open_pull_requests = OpenPullRequestManager() class Meta: + """Model options.""" + db_table = "github_pull_requests" indexes = [ models.Index(fields=["-created_at"]), diff --git a/backend/apps/github/models/release.py b/backend/apps/github/models/release.py index 33d110bc6d..e0cf8a63a9 100644 --- a/backend/apps/github/models/release.py +++ b/backend/apps/github/models/release.py @@ -11,6 +11,8 @@ class Release(BulkSaveModel, NodeModel, ReleaseIndexMixin, TimestampedModel): """Release model.""" class Meta: + """Model options.""" + db_table = "github_releases" indexes = [ models.Index(fields=["-created_at"], name="release_created_at_desc_idx"), diff --git a/backend/apps/github/models/repository.py b/backend/apps/github/models/repository.py index 8648d49666..72929a8d5b 100644 --- a/backend/apps/github/models/repository.py +++ b/backend/apps/github/models/repository.py @@ -25,6 +25,8 @@ class Repository(NodeModel, RepositoryIndexMixin, TimestampedModel): """Repository model.""" class Meta: + """Model options.""" + constraints = [ models.UniqueConstraint(fields=("key", "owner"), name="unique_key_owner"), ] diff --git a/backend/apps/github/models/repository_contributor.py b/backend/apps/github/models/repository_contributor.py index fd920a8ff0..6d4475a4ef 100644 --- a/backend/apps/github/models/repository_contributor.py +++ b/backend/apps/github/models/repository_contributor.py @@ -21,6 +21,8 @@ class RepositoryContributor(BulkSaveModel, TimestampedModel): objects = RepositoryContributorManager() class Meta: + """Model options.""" + db_table = "github_repository_contributors" indexes = [ models.Index( diff --git a/backend/apps/github/models/user.py b/backend/apps/github/models/user.py index 72d07acce2..ec99779d55 100644 --- a/backend/apps/github/models/user.py +++ b/backend/apps/github/models/user.py @@ -29,6 +29,8 @@ class User(NodeModel, GenericUserModel, TimestampedModel, UserIndexMixin): """User model.""" class Meta: + """Model options.""" + db_table = "github_users" indexes = [ models.Index(fields=["-created_at"], name="github_user_created_at_desc"), @@ -106,7 +108,7 @@ def _get_led_entities(self, entity_model): QuerySet: Entities where this user is an active, reviewed leader. """ - from apps.owasp.models.entity_member import EntityMember + from apps.owasp.models.entity_member import EntityMember # noqa: PLC0415 leader_memberships = EntityMember.objects.filter( member=self, @@ -128,7 +130,7 @@ def chapters(self) -> QuerySet[Chapter]: QuerySet[Chapter]: Chapters where this user is an active, reviewed leader. """ - from apps.owasp.models.chapter import Chapter + from apps.owasp.models.chapter import Chapter # noqa: PLC0415 return self._get_led_entities(Chapter) @@ -140,7 +142,7 @@ def projects(self) -> QuerySet[Project]: QuerySet[Project]: Projects where this user is an active, reviewed leader. """ - from apps.owasp.models.project import Project + from apps.owasp.models.project import Project # noqa: PLC0415 return self._get_led_entities(Project) diff --git a/backend/apps/mentorship/apps.py b/backend/apps/mentorship/apps.py index 18f16a0dd4..1034258ae0 100644 --- a/backend/apps/mentorship/apps.py +++ b/backend/apps/mentorship/apps.py @@ -11,4 +11,4 @@ class MentorshipConfig(AppConfig): def ready(self): """Ready.""" - import apps.mentorship.signals # noqa: F401 + import apps.mentorship.signals # noqa: F401, PLC0415 diff --git a/backend/apps/mentorship/models/common/experience_level.py b/backend/apps/mentorship/models/common/experience_level.py index cf9c281b17..60be1cba46 100644 --- a/backend/apps/mentorship/models/common/experience_level.py +++ b/backend/apps/mentorship/models/common/experience_level.py @@ -7,6 +7,8 @@ class ExperienceLevel(models.Model): """Matching attributes model.""" class Meta: + """Model options.""" + abstract = True class ExperienceLevelChoices(models.TextChoices): diff --git a/backend/apps/mentorship/models/common/matching_attributes.py b/backend/apps/mentorship/models/common/matching_attributes.py index e35def8a7c..edf5a0e0e7 100644 --- a/backend/apps/mentorship/models/common/matching_attributes.py +++ b/backend/apps/mentorship/models/common/matching_attributes.py @@ -7,6 +7,8 @@ class MatchingAttributes(models.Model): """Matching attributes model.""" class Meta: + """Model options.""" + abstract = True domains = models.JSONField( diff --git a/backend/apps/mentorship/models/common/start_end_range.py b/backend/apps/mentorship/models/common/start_end_range.py index 93c3e00704..226d33dfcd 100644 --- a/backend/apps/mentorship/models/common/start_end_range.py +++ b/backend/apps/mentorship/models/common/start_end_range.py @@ -7,6 +7,8 @@ class StartEndRange(models.Model): """Start/end range model.""" class Meta: + """Model options.""" + abstract = True ended_at = models.DateTimeField(verbose_name="End date and time") diff --git a/backend/apps/mentorship/models/issue_user_interest.py b/backend/apps/mentorship/models/issue_user_interest.py index 7970c2d466..583aee0b8d 100644 --- a/backend/apps/mentorship/models/issue_user_interest.py +++ b/backend/apps/mentorship/models/issue_user_interest.py @@ -7,6 +7,8 @@ class IssueUserInterest(models.Model): """Represents users interested in a specific issue within a module.""" class Meta: + """Model options.""" + db_table = "mentorship_issue_user_interests" verbose_name = "Issue User Interest" verbose_name_plural = "Issue User Interests" diff --git a/backend/apps/mentorship/models/mentee.py b/backend/apps/mentorship/models/mentee.py index 5a9bc6bd0f..7ca813fa40 100644 --- a/backend/apps/mentorship/models/mentee.py +++ b/backend/apps/mentorship/models/mentee.py @@ -12,6 +12,8 @@ class Mentee(ExperienceLevel, MatchingAttributes, TimestampedModel): """Mentee model.""" class Meta: + """Model options.""" + db_table = "mentorship_mentees" verbose_name_plural = "Mentees" diff --git a/backend/apps/mentorship/models/mentee_module.py b/backend/apps/mentorship/models/mentee_module.py index 7e5e848111..15e2df5026 100644 --- a/backend/apps/mentorship/models/mentee_module.py +++ b/backend/apps/mentorship/models/mentee_module.py @@ -12,6 +12,8 @@ class MenteeModule(StartEndRange, TimestampedModel): """Mentee module enrollment.""" class Meta: + """Model options.""" + db_table = "mentorship_mentee_modules" verbose_name_plural = "Mentee modules" unique_together = ("mentee", "module") diff --git a/backend/apps/mentorship/models/mentee_program.py b/backend/apps/mentorship/models/mentee_program.py index 04d07579b2..689bb0aa94 100644 --- a/backend/apps/mentorship/models/mentee_program.py +++ b/backend/apps/mentorship/models/mentee_program.py @@ -12,6 +12,8 @@ class MenteeProgram(ExperienceLevel, StartEndRange, TimestampedModel): """Mentee program enrollment.""" class Meta: + """Model options.""" + db_table = "mentorship_mentee_programs" verbose_name_plural = "Mentee programs" unique_together = ("mentee", "program") diff --git a/backend/apps/mentorship/models/mentor.py b/backend/apps/mentorship/models/mentor.py index 5b6e7707d0..e81beebbd7 100644 --- a/backend/apps/mentorship/models/mentor.py +++ b/backend/apps/mentorship/models/mentor.py @@ -12,6 +12,8 @@ class Mentor(ExperienceLevel, MatchingAttributes, TimestampedModel): """Mentor model.""" class Meta: + """Model options.""" + db_table = "mentorship_mentors" verbose_name_plural = "Mentors" diff --git a/backend/apps/mentorship/models/mentor_module.py b/backend/apps/mentorship/models/mentor_module.py index 0f58ea2ad5..20785894d5 100644 --- a/backend/apps/mentorship/models/mentor_module.py +++ b/backend/apps/mentorship/models/mentor_module.py @@ -12,6 +12,8 @@ class MentorModule(ExperienceLevel, MatchingAttributes, TimestampedModel): """Mentor module model.""" class Meta: + """Model options.""" + db_table = "mentorship_mentor_modules" unique_together = ("mentor", "module") verbose_name = "Mentor module" diff --git a/backend/apps/mentorship/models/module.py b/backend/apps/mentorship/models/module.py index 27fae9a8d6..109bfb9d70 100644 --- a/backend/apps/mentorship/models/module.py +++ b/backend/apps/mentorship/models/module.py @@ -21,6 +21,8 @@ class Module(ExperienceLevel, MatchingAttributes, StartEndRange, TimestampedMode published_modules = PublishedModuleManager() class Meta: + """Model options.""" + db_table = "mentorship_modules" verbose_name_plural = "Modules" constraints = [ diff --git a/backend/apps/mentorship/models/program.py b/backend/apps/mentorship/models/program.py index 1ec11f4add..447000c32d 100644 --- a/backend/apps/mentorship/models/program.py +++ b/backend/apps/mentorship/models/program.py @@ -19,10 +19,14 @@ class Program(MatchingAttributes, ProgramIndexMixin, StartEndRange, TimestampedM """Program model representing an overarching mentorship initiative.""" class Meta: + """Model options.""" + db_table = "mentorship_programs" verbose_name_plural = "Programs" class ProgramStatus(models.TextChoices): + """Program lifecycle status choices.""" + DRAFT = "draft", "Draft" PUBLISHED = "published", "Published" COMPLETED = "completed", "Completed" diff --git a/backend/apps/mentorship/models/task.py b/backend/apps/mentorship/models/task.py index 27a46293cb..0441bcfcf2 100644 --- a/backend/apps/mentorship/models/task.py +++ b/backend/apps/mentorship/models/task.py @@ -11,6 +11,8 @@ class Task(TimestampedModel): """Connects a Module, a GitHub Issue, and a Mentee to track work.""" class Meta: + """Model options.""" + db_table = "mentorship_tasks" verbose_name_plural = "Tasks" unique_together = ("issue", "assignee") diff --git a/backend/apps/mentorship/models/task_level.py b/backend/apps/mentorship/models/task_level.py index b678550b1f..a92fb846e3 100644 --- a/backend/apps/mentorship/models/task_level.py +++ b/backend/apps/mentorship/models/task_level.py @@ -9,6 +9,8 @@ class TaskLevel(models.Model): """Task level model representing difficulty and prerequisites for mentorship tasks.""" class Meta: + """Model options.""" + db_table = "mentorship_task_levels" verbose_name_plural = "Task Levels" ordering = ["name"] diff --git a/backend/apps/nest/models/badge.py b/backend/apps/nest/models/badge.py index 112b3d1575..03524f1038 100644 --- a/backend/apps/nest/models/badge.py +++ b/backend/apps/nest/models/badge.py @@ -11,6 +11,8 @@ class Badge(BulkSaveModel, TimestampedModel): """Represents a user badge for roles or achievements.""" class BadgeCssClass(models.TextChoices): + """Badge icon CSS class choices.""" + AWARD = "award", "Award" BUG_SLASH = "bug_slash", "Bug Slash" CERTIFICATE = "certificate", "Certificate" @@ -19,6 +21,8 @@ class BadgeCssClass(models.TextChoices): STAR = "star", "Star" class Meta: + """Model options.""" + db_table = "nest_badges" ordering = ["weight", "name"] verbose_name_plural = "Badges" diff --git a/backend/apps/nest/models/user.py b/backend/apps/nest/models/user.py index 6923c80307..9b39415c69 100644 --- a/backend/apps/nest/models/user.py +++ b/backend/apps/nest/models/user.py @@ -18,6 +18,8 @@ class User(AbstractUser): """Nest user model.""" class Meta: + """Model options.""" + db_table = "nest_users" verbose_name_plural = "Users" ordering = ["username"] diff --git a/backend/apps/nest/models/user_badge.py b/backend/apps/nest/models/user_badge.py index d034cba97f..3d24b10742 100644 --- a/backend/apps/nest/models/user_badge.py +++ b/backend/apps/nest/models/user_badge.py @@ -11,6 +11,8 @@ class UserBadge(BulkSaveModel, TimestampedModel): """Represents a user badge relation.""" class Meta: + """Model options.""" + db_table = "nest_user_badges" unique_together = ( "badge", diff --git a/backend/apps/owasp/management/commands/owasp_aggregate_projects.py b/backend/apps/owasp/management/commands/owasp_aggregate_projects.py index 2c0b9c6702..485d15bc9b 100644 --- a/backend/apps/owasp/management/commands/owasp_aggregate_projects.py +++ b/backend/apps/owasp/management/commands/owasp_aggregate_projects.py @@ -26,7 +26,7 @@ def handle(self, *_args, **options) -> None: projects = [] for idx, project in enumerate(active_projects[offset:]): prefix = f"{idx + offset + 1} of {active_projects_count}" - print(f"{prefix:<10} {project.owasp_url}") + self.stdout.write(f"{prefix:<10} {project.owasp_url}\n") # Deactivate project with archived repositories. if project.owasp_repository.is_archived: diff --git a/backend/apps/owasp/management/commands/owasp_create_member_snapshot.py b/backend/apps/owasp/management/commands/owasp_create_member_snapshot.py index c7f8687b4d..307750df7d 100644 --- a/backend/apps/owasp/management/commands/owasp_create_member_snapshot.py +++ b/backend/apps/owasp/management/commands/owasp_create_member_snapshot.py @@ -2,7 +2,7 @@ import logging from collections import defaultdict -from datetime import UTC, datetime +from datetime import UTC, datetime, timedelta from django.contrib.contenttypes.models import ContentType from django.core.management.base import BaseCommand @@ -87,8 +87,6 @@ def generate_heatmap_data(self, commits, pull_requests, issues, start_at, end_at dict: Mapping of date strings (YYYY-MM-DD) to contribution counts. """ - from datetime import timedelta - # Initialize all dates in range with 0 heatmap_data: dict[str, int] = {} current_date = start_at.date() @@ -250,8 +248,6 @@ def generate_communication_heatmap_data(self, messages, start_at, end_at) -> dic dict: Mapping of date strings (YYYY-MM-DD) to message counts (all dates initialized). """ - from datetime import timedelta - # Initialize all dates in range with 0 heatmap_data: dict[str, int] = {} current_date = start_at.date() diff --git a/backend/apps/owasp/management/commands/owasp_enrich_chapters.py b/backend/apps/owasp/management/commands/owasp_enrich_chapters.py index d67723c11d..2233653670 100644 --- a/backend/apps/owasp/management/commands/owasp_enrich_chapters.py +++ b/backend/apps/owasp/management/commands/owasp_enrich_chapters.py @@ -33,7 +33,7 @@ def handle(self, *args, **options) -> None: for idx, chapter in enumerate(active_chapters[offset:]): prefix = f"{idx + offset + 1} of {active_chapters_count}" - print(f"{prefix:<10} {chapter.owasp_url}") + self.stdout.write(f"{prefix:<10} {chapter.owasp_url}\n") # Summary. if not chapter.summary and (prompt := Prompt.get_owasp_chapter_summary()): diff --git a/backend/apps/owasp/management/commands/owasp_enrich_committees.py b/backend/apps/owasp/management/commands/owasp_enrich_committees.py index 5829b1e0a3..cf634a7220 100644 --- a/backend/apps/owasp/management/commands/owasp_enrich_committees.py +++ b/backend/apps/owasp/management/commands/owasp_enrich_committees.py @@ -49,7 +49,7 @@ def handle(self, *args, **options) -> None: offset = options["offset"] for idx, committee in enumerate(active_committees[offset:]): prefix = f"{idx + offset + 1} of {active_committees_count - offset}" - print(f"{prefix:<10} {committee.owasp_url}") + self.stdout.write(f"{prefix:<10} {committee.owasp_url}\n") # Generate summary if update_summary and (prompt := Prompt.get_owasp_committee_summary()): diff --git a/backend/apps/owasp/management/commands/owasp_enrich_events.py b/backend/apps/owasp/management/commands/owasp_enrich_events.py index 72f90fe08c..3c3a8093f3 100644 --- a/backend/apps/owasp/management/commands/owasp_enrich_events.py +++ b/backend/apps/owasp/management/commands/owasp_enrich_events.py @@ -29,9 +29,10 @@ def handle(self, *args, **options) -> None: all_events = [] offset = options["offset"] + total_count = events.count() for idx, event in enumerate(events[offset:]): - prefix = f"{idx + offset + 1} of {events.count()}" - print(f"{prefix:<10} {event.url}") + prefix = f"{idx + offset + 1} of {total_count}" + self.stdout.write(f"{prefix:<10} {event.url}\n") # Summary. if not event.summary and (prompt := Prompt.get_owasp_event_summary()): event.generate_summary(prompt) diff --git a/backend/apps/owasp/management/commands/owasp_enrich_projects.py b/backend/apps/owasp/management/commands/owasp_enrich_projects.py index e2b3d4e01e..932691789d 100644 --- a/backend/apps/owasp/management/commands/owasp_enrich_projects.py +++ b/backend/apps/owasp/management/commands/owasp_enrich_projects.py @@ -47,7 +47,7 @@ def handle(self, *args, **options) -> None: offset = options["offset"] for idx, project in enumerate(active_projects[offset:]): prefix = f"{idx + offset + 1} of {active_projects_count - offset}" - print(f"{prefix:<10} {project.owasp_url}") + self.stdout.write(f"{prefix:<10} {project.owasp_url}\n") # Generate summary if update_summary and (prompt := Prompt.get_owasp_project_summary()): diff --git a/backend/apps/owasp/management/commands/owasp_scrape_chapters.py b/backend/apps/owasp/management/commands/owasp_scrape_chapters.py index 6af1050eb5..f278c9576b 100644 --- a/backend/apps/owasp/management/commands/owasp_scrape_chapters.py +++ b/backend/apps/owasp/management/commands/owasp_scrape_chapters.py @@ -32,7 +32,7 @@ def handle(self, *args, **options) -> None: chapters = [] for idx, chapter in enumerate(active_chapters[offset:]): prefix = f"{idx + offset + 1} of {active_chapters_count}" - print(f"{prefix:<10} {chapter.owasp_url}") + self.stdout.write(f"{prefix:<10} {chapter.owasp_url}\n") scraper = OwaspScraper(chapter.owasp_url) if scraper.page_tree is None: diff --git a/backend/apps/owasp/management/commands/owasp_scrape_committees.py b/backend/apps/owasp/management/commands/owasp_scrape_committees.py index 09d5bcfead..fb4690276f 100644 --- a/backend/apps/owasp/management/commands/owasp_scrape_committees.py +++ b/backend/apps/owasp/management/commands/owasp_scrape_committees.py @@ -32,7 +32,7 @@ def handle(self, *args, **options) -> None: committees = [] for idx, committee in enumerate(active_committees[offset:]): prefix = f"{idx + offset + 1} of {active_committees_count}" - print(f"{prefix:<10} {committee.owasp_url}") + self.stdout.write(f"{prefix:<10} {committee.owasp_url}\n") scraper = OwaspScraper(committee.owasp_url) if scraper.page_tree is None: diff --git a/backend/apps/owasp/management/commands/owasp_scrape_projects.py b/backend/apps/owasp/management/commands/owasp_scrape_projects.py index c66e115685..f39c501a7f 100644 --- a/backend/apps/owasp/management/commands/owasp_scrape_projects.py +++ b/backend/apps/owasp/management/commands/owasp_scrape_projects.py @@ -44,7 +44,7 @@ def handle(self, *args, **options) -> None: projects = [] for idx, project in enumerate(active_projects[offset:]): prefix = f"{idx + offset + 1} of {active_projects_count}" - print(f"{prefix:<10} {project.owasp_url}") + self.stdout.write(f"{prefix:<10} {project.owasp_url}\n") scraper = OwaspScraper(project.owasp_url) if scraper.page_tree is None: diff --git a/backend/apps/owasp/management/commands/owasp_update_project_health_requirements.py b/backend/apps/owasp/management/commands/owasp_update_project_health_requirements.py index 1ab1759fd4..ae84cfaee2 100644 --- a/backend/apps/owasp/management/commands/owasp_update_project_health_requirements.py +++ b/backend/apps/owasp/management/commands/owasp_update_project_health_requirements.py @@ -126,7 +126,7 @@ def handle(self, *args, **options) -> None: defaults=self.get_level_requirements(level_code), ) - print( + self.stdout.write( f"{'Created' if created else 'Updated'} health requirements for " f"{level_name} projects" ) diff --git a/backend/apps/owasp/models/board_of_directors.py b/backend/apps/owasp/models/board_of_directors.py index 58d5f4b9c2..caba28fb1f 100644 --- a/backend/apps/owasp/models/board_of_directors.py +++ b/backend/apps/owasp/models/board_of_directors.py @@ -17,6 +17,8 @@ class BoardOfDirectors(models.Model): """Model representing OWASP Board of Directors.""" class Meta: + """Model options.""" + db_table = "owasp_board_of_directors" verbose_name_plural = "Board of Directors" diff --git a/backend/apps/owasp/models/chapter.py b/backend/apps/owasp/models/chapter.py index cd23aca55b..5c6ec259d2 100644 --- a/backend/apps/owasp/models/chapter.py +++ b/backend/apps/owasp/models/chapter.py @@ -28,6 +28,8 @@ class Chapter( active_chapters = ActiveChapterManager() class Meta: + """Model options.""" + db_table = "owasp_chapters" indexes = [ models.Index(fields=["-created_at"], name="chapter_created_at_desc_idx"), diff --git a/backend/apps/owasp/models/committee.py b/backend/apps/owasp/models/committee.py index 90e2ff741e..fb1ef8e649 100644 --- a/backend/apps/owasp/models/committee.py +++ b/backend/apps/owasp/models/committee.py @@ -23,6 +23,8 @@ class Committee( active_committees = ActiveCommitteeManager() class Meta: + """Model options.""" + db_table = "owasp_committees" verbose_name_plural = "Committees" diff --git a/backend/apps/owasp/models/common.py b/backend/apps/owasp/models/common.py index 53896b8051..bbca0c2a23 100644 --- a/backend/apps/owasp/models/common.py +++ b/backend/apps/owasp/models/common.py @@ -30,6 +30,8 @@ class RepositoryBasedEntityModel(models.Model): """Repository based entity model.""" class Meta: + """Model options.""" + abstract = True name = models.CharField(verbose_name="Name", max_length=100) diff --git a/backend/apps/owasp/models/entity_channel.py b/backend/apps/owasp/models/entity_channel.py index 165181e91e..ff3e387a07 100644 --- a/backend/apps/owasp/models/entity_channel.py +++ b/backend/apps/owasp/models/entity_channel.py @@ -9,9 +9,13 @@ class EntityChannel(models.Model): """Model representing a link between an entity and a channel.""" class Platform(models.TextChoices): + """Channel platform choices.""" + SLACK = "slack", "Slack" class Meta: + """Model options.""" + db_table = "owasp_entity_channels" unique_together = ( "channel_id", diff --git a/backend/apps/owasp/models/entity_member.py b/backend/apps/owasp/models/entity_member.py index 151fc4ac00..72afcde7c7 100644 --- a/backend/apps/owasp/models/entity_member.py +++ b/backend/apps/owasp/models/entity_member.py @@ -11,11 +11,15 @@ class EntityMember(models.Model): """EntityMember model.""" class Role(models.TextChoices): + """Entity member role choices.""" + CANDIDATE = "candidate", "Candidate" LEADER = "leader", "Leader" MEMBER = "member", "Member" class Meta: + """Model options.""" + db_table = "owasp_entity_members" unique_together = ( "entity_type", diff --git a/backend/apps/owasp/models/event.py b/backend/apps/owasp/models/event.py index bc67f49117..0767ca67b7 100644 --- a/backend/apps/owasp/models/event.py +++ b/backend/apps/owasp/models/event.py @@ -29,6 +29,8 @@ class Event(BulkSaveModel, TimestampedModel): """Event model.""" class Meta: + """Model options.""" + db_table = "owasp_events" indexes = [ models.Index(fields=["-start_date"], name="event_start_date_desc_idx"), diff --git a/backend/apps/owasp/models/member_profile.py b/backend/apps/owasp/models/member_profile.py index ba52ed5a1d..a782bc467a 100644 --- a/backend/apps/owasp/models/member_profile.py +++ b/backend/apps/owasp/models/member_profile.py @@ -16,6 +16,8 @@ class MemberProfile(TimestampedModel): """ class Meta: + """Model options.""" + db_table = "owasp_member_profiles" verbose_name_plural = "Member Profiles" diff --git a/backend/apps/owasp/models/member_snapshot.py b/backend/apps/owasp/models/member_snapshot.py index eca95f775d..1d9fdd463f 100644 --- a/backend/apps/owasp/models/member_snapshot.py +++ b/backend/apps/owasp/models/member_snapshot.py @@ -15,6 +15,8 @@ class MemberSnapshot(TimestampedModel): """ class Meta: + """Model options.""" + db_table = "owasp_member_snapshots" verbose_name_plural = "Member Snapshots" unique_together = ("github_user", "start_at", "end_at") diff --git a/backend/apps/owasp/models/post.py b/backend/apps/owasp/models/post.py index cbc9aa78df..d7840ca877 100644 --- a/backend/apps/owasp/models/post.py +++ b/backend/apps/owasp/models/post.py @@ -13,6 +13,8 @@ class Post(BulkSaveModel, TimestampedModel): """Post model.""" class Meta: + """Model options.""" + db_table = "owasp_posts" indexes = [ models.Index(fields=["-published_at"], name="post_published_at_desc_idx"), diff --git a/backend/apps/owasp/models/project.py b/backend/apps/owasp/models/project.py index b6b7759e0c..1e3d6cc310 100644 --- a/backend/apps/owasp/models/project.py +++ b/backend/apps/owasp/models/project.py @@ -45,6 +45,8 @@ class Project( active_projects = ActiveProjectManager() class Meta: + """Model options.""" + db_table = "owasp_projects" indexes = [ models.Index(fields=["-created_at"], name="project_created_at_desc_idx"), diff --git a/backend/apps/owasp/models/project_health_metrics.py b/backend/apps/owasp/models/project_health_metrics.py index 8e055cf04a..6d90ed784f 100644 --- a/backend/apps/owasp/models/project_health_metrics.py +++ b/backend/apps/owasp/models/project_health_metrics.py @@ -19,6 +19,8 @@ class ProjectHealthMetrics(BulkSaveModel, TimestampedModel): """Project health metrics model.""" class Meta: + """Model options.""" + db_table = "owasp_project_health_metrics" verbose_name_plural = "Project Health Metrics" diff --git a/backend/apps/owasp/models/project_health_requirements.py b/backend/apps/owasp/models/project_health_requirements.py index 0feaa7b1bd..2fcbb65cd6 100644 --- a/backend/apps/owasp/models/project_health_requirements.py +++ b/backend/apps/owasp/models/project_health_requirements.py @@ -10,6 +10,8 @@ class ProjectHealthRequirements(TimestampedModel): """Project health requirements model.""" class Meta: + """Model options.""" + db_table = "owasp_project_health_requirements" verbose_name_plural = "Project Health Requirements" ordering = ["level"] diff --git a/backend/apps/owasp/models/snapshot.py b/backend/apps/owasp/models/snapshot.py index 960130a73d..8c2f6a4add 100644 --- a/backend/apps/owasp/models/snapshot.py +++ b/backend/apps/owasp/models/snapshot.py @@ -8,6 +8,8 @@ class Snapshot(models.Model): """Model representing a snapshot of data processing.""" class Meta: + """Model options.""" + db_table = "owasp_snapshots" verbose_name_plural = "Snapshots" @@ -17,6 +19,8 @@ class Meta: ] class Status(models.TextChoices): + """Snapshot processing status choices.""" + PENDING = "pending", "Pending" PROCESSING = "processing", "Processing" COMPLETED = "completed", "Completed" diff --git a/backend/apps/owasp/models/sponsor.py b/backend/apps/owasp/models/sponsor.py index f69383473b..d7d0e38158 100644 --- a/backend/apps/owasp/models/sponsor.py +++ b/backend/apps/owasp/models/sponsor.py @@ -15,10 +15,14 @@ class Sponsor(BulkSaveModel, TimestampedModel): objects = models.Manager() class Meta: + """Model options.""" + db_table = "owasp_sponsors" verbose_name_plural = "Sponsors" class SponsorType(models.TextChoices): + """Sponsor type choices.""" + DIAMOND = "Diamond" PLATINUM = "Platinum" GOLD = "Gold" @@ -27,6 +31,8 @@ class SponsorType(models.TextChoices): NOT_SPONSOR = "Not a Sponsor" class MemberType(models.TextChoices): + """Member type choices.""" + PLATINUM = "Platinum" GOLD = "Gold" SILVER = "Silver" diff --git a/backend/apps/slack/apps.py b/backend/apps/slack/apps.py index 422b72ba25..fe50e62e03 100644 --- a/backend/apps/slack/apps.py +++ b/backend/apps/slack/apps.py @@ -28,7 +28,7 @@ class SlackConfig(AppConfig): def ready(self): """Configure Slack events when the app is ready.""" super().ready() - from apps.slack.events import configure_slack_events + from apps.slack.events import configure_slack_events # noqa: PLC0415 configure_slack_events() @@ -66,7 +66,7 @@ def log_events( next (function): The next middleware function in the chain. """ - from apps.slack.models.event import Event + from apps.slack.models.event import Event # noqa: PLC0415 try: Event.create(context, payload) diff --git a/backend/apps/slack/management/commands/slack_sync_messages.py b/backend/apps/slack/management/commands/slack_sync_messages.py index 81960cdcae..52f4853ce4 100644 --- a/backend/apps/slack/management/commands/slack_sync_messages.py +++ b/backend/apps/slack/management/commands/slack_sync_messages.py @@ -453,7 +453,7 @@ def _fetch_messages( Message.bulk_save(messages.copy()) if include_replies and messages: - print("Fetching message replies...") + self.stdout.write("Fetching message replies...") for message in messages: if not message.has_replies: continue @@ -546,7 +546,7 @@ def _fetch_replies( ) if replies_count := len(replies): - print( + self.stdout.write( f"Saving {replies_count} repl{pluralize(replies_count, 'y,ies')} for {message.url}" ) Message.bulk_save(replies) diff --git a/backend/apps/slack/models/conversation.py b/backend/apps/slack/models/conversation.py index 9735786c24..a1fd303ab4 100644 --- a/backend/apps/slack/models/conversation.py +++ b/backend/apps/slack/models/conversation.py @@ -16,6 +16,8 @@ class Conversation(TimestampedModel): """Slack Conversation model.""" class Meta: + """Model options.""" + db_table = "slack_conversations" verbose_name_plural = "Conversations" diff --git a/backend/apps/slack/models/event.py b/backend/apps/slack/models/event.py index ce7942ddac..b3ebfb96e4 100644 --- a/backend/apps/slack/models/event.py +++ b/backend/apps/slack/models/event.py @@ -12,6 +12,8 @@ class Event(TimestampedModel): """Event model.""" class Meta: + """Model options.""" + db_table = "slack_events" verbose_name_plural = "Events" diff --git a/backend/apps/slack/models/member.py b/backend/apps/slack/models/member.py index 7f2e36f11d..25a4174664 100644 --- a/backend/apps/slack/models/member.py +++ b/backend/apps/slack/models/member.py @@ -11,6 +11,8 @@ class Member(TimestampedModel): """Slack Member model.""" class Meta: + """Model options.""" + db_table = "slack_members" verbose_name_plural = "Members" diff --git a/backend/apps/slack/models/message.py b/backend/apps/slack/models/message.py index dd06a7e991..61332099fe 100644 --- a/backend/apps/slack/models/message.py +++ b/backend/apps/slack/models/message.py @@ -16,6 +16,8 @@ class Message(TimestampedModel): """Slack Message model.""" class Meta: + """Model options.""" + db_table = "slack_messages" verbose_name_plural = "Messages" unique_together = ("conversation", "slack_message_id") diff --git a/backend/apps/slack/models/workspace.py b/backend/apps/slack/models/workspace.py index c94e5d51a5..ff602b0a0a 100644 --- a/backend/apps/slack/models/workspace.py +++ b/backend/apps/slack/models/workspace.py @@ -12,6 +12,8 @@ class Workspace(TimestampedModel): """Slack Workspace model.""" class Meta: + """Model options.""" + db_table = "slack_workspaces" verbose_name_plural = "Workspaces" diff --git a/backend/apps/slack/utils.py b/backend/apps/slack/utils.py index 606a0ec07b..274cc7bbd2 100644 --- a/backend/apps/slack/utils.py +++ b/backend/apps/slack/utils.py @@ -63,7 +63,7 @@ def get_gsoc_projects(year: int) -> list: list: A list of GSoC projects with their attributes. """ - from apps.owasp.index.search.project import get_projects + from apps.owasp.index.search.project import get_projects # noqa: PLC0415 return get_projects( attributes=["idx_name", "idx_url"], @@ -150,7 +150,7 @@ def get_sponsors_data(limit: int = 10) -> QuerySet | None: QuerySet or None: A queryset of sponsors, or None if an error occurs. """ - from apps.owasp.models.sponsor import Sponsor + from apps.owasp.models.sponsor import Sponsor # noqa: PLC0415 try: return Sponsor.objects.all()[:limit] @@ -170,7 +170,7 @@ def get_posts_data(limit: int = 5) -> QuerySet | None: QuerySet or None: A queryset of recent posts, or None if an error occurs. """ - from apps.owasp.models.post import Post + from apps.owasp.models.post import Post # noqa: PLC0415 try: return Post.recent_posts()[:limit] diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 20b4de9f0f..ac874b9464 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -72,7 +72,6 @@ lint.ignore = [ "D407", # https://docs.astral.sh/ruff/rules/missing-dashed-underline-after-section/ "DJ012", # https://docs.astral.sh/ruff/rules/django-unordered-body-content-in-model/ "FIX002", # https://docs.astral.sh/ruff/rules/line-contains-todo/ - "PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/ "PLR0912", # https://docs.astral.sh/ruff/rules/too-many-branches/ "PLR0913", # https://docs.astral.sh/ruff/rules/too-many-arguments/ "PLR0915", # https://docs.astral.sh/ruff/rules/too-many-statements/ @@ -83,44 +82,41 @@ lint.per-file-ignores."**/__init__.py" = [ "D104", # https://docs.astral.sh/ruff/rules/undocumented-public-package/ "F401", # https://docs.astral.sh/ruff/rules/unused-import/ ] -lint.per-file-ignores."**/management/commands/*.py" = [ +lint.per-file-ignores."apps/**/management/commands/*.py" = [ "D101", # https://docs.astral.sh/ruff/rules/undocumented-public-class/ "D102", # https://docs.astral.sh/ruff/rules/undocumented-public-method/ "SLF001", # https://docs.astral.sh/ruff/rules/private-member-access/ - "T201", # https://docs.astral.sh/ruff/rules/print/ ] -lint.per-file-ignores."**/management/commands/dump_data.py" = [ - "S603", # https://docs.astral.sh/ruff/rules/subprocess-without-shell-equals-true/, - "S607", # https://docs.astral.sh/ruff/rules/start-process-with-partial-path/, -] -lint.per-file-ignores."**/migrations/*.py" = [ +lint.per-file-ignores."apps/**/migrations/*.py" = [ "D100", # https://docs.astral.sh/ruff/rules/undocumented-public-module/ "D101", # https://docs.astral.sh/ruff/rules/undocumented-public-class/ "D104", # https://docs.astral.sh/ruff/rules/undocumented-public-package/ "E501", # https://docs.astral.sh/ruff/rules/line-too-long/ "RUF012", # https://docs.astral.sh/ruff/rules/mutable-class-default/ ] -lint.per-file-ignores."**/models/*.py" = [ - "D106", # https://docs.astral.sh/ruff/rules/undocumented-public-nested-class/ +lint.per-file-ignores."apps/api/rest/**/*.py" = [ + "ARG001", # https://docs.astral.sh/ruff/rules/unused-function-argument/ + "B008", # https://docs.astral.sh/ruff/rules/function-call-in-default-argument/ ] -lint.per-file-ignores."**/settings/*.py" = [ +lint.per-file-ignores."apps/github/models/mixins/*.py" = [ "PLC0415" ] +lint.per-file-ignores."apps/slack/commands/*.py" = [ "PLC0415" ] +lint.per-file-ignores."apps/slack/common/handlers/*.py" = [ "PLC0415" ] +lint.per-file-ignores."apps/slack/events/**/*.py" = [ "PLC0415" ] +lint.per-file-ignores."settings/*.py" = [ "RUF012", # https://docs.astral.sh/ruff/rules/mutable-class-default/ ] -lint.per-file-ignores."**/tests/**/*.py" = [ +lint.per-file-ignores."tests/**/*.py" = [ "D100", # https://docs.astral.sh/ruff/rules/undocumented-public-module/ "D101", # https://docs.astral.sh/ruff/rules/undocumented-public-class/ "D102", # https://docs.astral.sh/ruff/rules/undocumented-public-method/ "D103", # https://docs.astral.sh/ruff/rules/undocumented-public-function/ + "D106", # https://docs.astral.sh/ruff/rules/undocumented-public-nested-class/ "D107", # https://docs.astral.sh/ruff/rules/undocumented-public-init/ "PLR2004", # https://docs.astral.sh/ruff/rules/magic-value-comparison/ "RUF012", # https://docs.astral.sh/ruff/rules/mutable-class-default/ "S101", # https://docs.astral.sh/ruff/rules/assert/ "SLF001", # https://docs.astral.sh/ruff/rules/private-member-access/ ] -lint.per-file-ignores."apps/api/rest/**/*.py" = [ - "ARG001", # https://docs.astral.sh/ruff/rules/unused-function-argument/ - "B008", # https://docs.astral.sh/ruff/rules/function-call-in-default-argument/ -] [tool.pytest] ini_options.DJANGO_CONFIGURATION = "Test" diff --git a/backend/tests/apps/ai/management/commands/ai_update_chapter_chunks_test.py b/backend/tests/apps/ai/management/commands/ai_update_chapter_chunks_test.py index 7df18a89bd..f846e33501 100644 --- a/backend/tests/apps/ai/management/commands/ai_update_chapter_chunks_test.py +++ b/backend/tests/apps/ai/management/commands/ai_update_chapter_chunks_test.py @@ -4,6 +4,7 @@ from django.core.management.base import BaseCommand from apps.ai.management.commands.ai_update_chapter_chunks import Command +from apps.owasp.models.chapter import Chapter @pytest.fixture @@ -24,8 +25,6 @@ def test_command_inheritance(self, command): assert isinstance(command, BaseCommand) def test_model_class_property(self, command): - from apps.owasp.models.chapter import Chapter - assert command.model_class == Chapter def test_entity_name_property(self, command): diff --git a/backend/tests/apps/ai/management/commands/ai_update_chapter_context_test.py b/backend/tests/apps/ai/management/commands/ai_update_chapter_context_test.py index 792e2f2f02..5a8d637c5b 100644 --- a/backend/tests/apps/ai/management/commands/ai_update_chapter_context_test.py +++ b/backend/tests/apps/ai/management/commands/ai_update_chapter_context_test.py @@ -4,7 +4,9 @@ import pytest +from apps.ai.common.base.context_command import BaseContextCommand from apps.ai.management.commands.ai_update_chapter_context import Command +from apps.owasp.models.chapter import Chapter @pytest.fixture @@ -23,8 +25,6 @@ def mock_chapter(): class TestAiCreateChapterContextCommand: def test_command_inheritance(self, command): """Test that the command inherits from BaseContextCommand.""" - from apps.ai.common.base.context_command import BaseContextCommand - assert isinstance(command, BaseContextCommand) def test_command_help_text(self, command): @@ -33,8 +33,6 @@ def test_command_help_text(self, command): def test_model_class_property(self, command): """Test the model_class property returns Chapter.""" - from apps.owasp.models.chapter import Chapter - assert command.model_class == Chapter def test_entity_name_property(self, command): diff --git a/backend/tests/apps/ai/management/commands/ai_update_committee_chunks_test.py b/backend/tests/apps/ai/management/commands/ai_update_committee_chunks_test.py index e889fe5b11..2244b6c5d1 100644 --- a/backend/tests/apps/ai/management/commands/ai_update_committee_chunks_test.py +++ b/backend/tests/apps/ai/management/commands/ai_update_committee_chunks_test.py @@ -6,6 +6,7 @@ from django.core.management.base import BaseCommand from apps.ai.management.commands.ai_update_committee_chunks import Command +from apps.owasp.models.committee import Committee @pytest.fixture @@ -35,8 +36,6 @@ def test_command_inheritance(self, command): def test_model_class_method(self, command): """Test the model_class method returns Committee.""" - from apps.owasp.models.committee import Committee - assert command.model_class == Committee def test_entity_name_method(self, command): diff --git a/backend/tests/apps/ai/management/commands/ai_update_committee_context_test.py b/backend/tests/apps/ai/management/commands/ai_update_committee_context_test.py index 1010011473..b43625fcc5 100644 --- a/backend/tests/apps/ai/management/commands/ai_update_committee_context_test.py +++ b/backend/tests/apps/ai/management/commands/ai_update_committee_context_test.py @@ -4,7 +4,9 @@ import pytest +from apps.ai.common.base.context_command import BaseContextCommand from apps.ai.management.commands.ai_update_committee_context import Command +from apps.owasp.models.committee import Committee @pytest.fixture @@ -30,8 +32,6 @@ class TestAiCreateCommitteeContextCommand: def test_command_inheritance(self, command): """Test that the command inherits from BaseContextCommand.""" - from apps.ai.common.base.context_command import BaseContextCommand - assert isinstance(command, BaseContextCommand) def test_command_help_text(self, command): @@ -40,8 +40,6 @@ def test_command_help_text(self, command): def test_model_class_method(self, command): """Test the model_class method returns Committee.""" - from apps.owasp.models.committee import Committee - assert command.model_class == Committee def test_entity_name_method(self, command): diff --git a/backend/tests/apps/ai/management/commands/ai_update_event_chunks_test.py b/backend/tests/apps/ai/management/commands/ai_update_event_chunks_test.py index 298b50b185..b72307f09d 100644 --- a/backend/tests/apps/ai/management/commands/ai_update_event_chunks_test.py +++ b/backend/tests/apps/ai/management/commands/ai_update_event_chunks_test.py @@ -6,6 +6,7 @@ from django.core.management.base import BaseCommand from apps.ai.management.commands.ai_update_event_chunks import Command +from apps.owasp.models.event import Event @pytest.fixture @@ -32,8 +33,6 @@ def test_command_inheritance(self, command): def test_model_class_property(self, command): """Test the model_class property returns Event.""" - from apps.owasp.models.event import Event - assert command.model_class == Event def test_entity_name_property(self, command): diff --git a/backend/tests/apps/ai/management/commands/ai_update_event_context_test.py b/backend/tests/apps/ai/management/commands/ai_update_event_context_test.py index 8f4d2d12ce..6159cbffb8 100644 --- a/backend/tests/apps/ai/management/commands/ai_update_event_context_test.py +++ b/backend/tests/apps/ai/management/commands/ai_update_event_context_test.py @@ -4,7 +4,9 @@ import pytest +from apps.ai.common.base.context_command import BaseContextCommand from apps.ai.management.commands.ai_update_event_context import Command +from apps.owasp.models.event import Event @pytest.fixture @@ -23,8 +25,6 @@ def mock_event(): class TestAiCreateEventContextCommand: def test_command_inheritance(self, command): """Test that the command inherits from BaseContextCommand.""" - from apps.ai.common.base.context_command import BaseContextCommand - assert isinstance(command, BaseContextCommand) def test_command_help_text(self, command): @@ -33,8 +33,6 @@ def test_command_help_text(self, command): def test_model_class_property(self, command): """Test the model_class property returns Event.""" - from apps.owasp.models.event import Event - assert command.model_class == Event def test_entity_name_property(self, command): diff --git a/backend/tests/apps/ai/management/commands/ai_update_project_chunks_test.py b/backend/tests/apps/ai/management/commands/ai_update_project_chunks_test.py index 85f889ddaf..ac731a9ace 100644 --- a/backend/tests/apps/ai/management/commands/ai_update_project_chunks_test.py +++ b/backend/tests/apps/ai/management/commands/ai_update_project_chunks_test.py @@ -4,6 +4,7 @@ from django.core.management.base import BaseCommand from apps.ai.management.commands.ai_update_project_chunks import Command +from apps.owasp.models.project import Project @pytest.fixture @@ -24,8 +25,6 @@ def test_command_inheritance(self, command): assert isinstance(command, BaseCommand) def test_model_class_property(self, command): - from apps.owasp.models.project import Project - assert command.model_class == Project def test_entity_name_property(self, command): diff --git a/backend/tests/apps/ai/management/commands/ai_update_project_context_test.py b/backend/tests/apps/ai/management/commands/ai_update_project_context_test.py index 82df257634..c7958c150b 100644 --- a/backend/tests/apps/ai/management/commands/ai_update_project_context_test.py +++ b/backend/tests/apps/ai/management/commands/ai_update_project_context_test.py @@ -2,7 +2,9 @@ import pytest +from apps.ai.common.base.context_command import BaseContextCommand from apps.ai.management.commands.ai_update_project_context import Command +from apps.owasp.models.project import Project @pytest.fixture @@ -21,8 +23,6 @@ def mock_project(): class TestAiCreateProjectContextCommand: def test_command_inheritance(self, command): """Test that the command inherits from BaseContextCommand.""" - from apps.ai.common.base.context_command import BaseContextCommand - assert isinstance(command, BaseContextCommand) def test_command_help_text(self, command): @@ -31,8 +31,6 @@ def test_command_help_text(self, command): def test_model_class_property(self, command): """Test the model_class property returns Project.""" - from apps.owasp.models.project import Project - assert command.model_class == Project def test_entity_name_property(self, command): diff --git a/backend/tests/apps/ai/management/commands/ai_update_slack_message_chunks_test.py b/backend/tests/apps/ai/management/commands/ai_update_slack_message_chunks_test.py index 22c9db9772..b220b544cb 100644 --- a/backend/tests/apps/ai/management/commands/ai_update_slack_message_chunks_test.py +++ b/backend/tests/apps/ai/management/commands/ai_update_slack_message_chunks_test.py @@ -4,6 +4,7 @@ from django.core.management.base import BaseCommand from apps.ai.management.commands.ai_update_slack_message_chunks import Command +from apps.slack.models.message import Message @pytest.fixture @@ -24,8 +25,6 @@ def test_command_inheritance(self, command): assert isinstance(command, BaseCommand) def test_model_class_property(self, command): - from apps.slack.models.message import Message - assert command.model_class == Message def test_entity_name_property(self, command): diff --git a/backend/tests/apps/ai/management/commands/ai_update_slack_message_context_test.py b/backend/tests/apps/ai/management/commands/ai_update_slack_message_context_test.py index b0af18f568..ae85fddae3 100644 --- a/backend/tests/apps/ai/management/commands/ai_update_slack_message_context_test.py +++ b/backend/tests/apps/ai/management/commands/ai_update_slack_message_context_test.py @@ -2,7 +2,9 @@ import pytest +from apps.ai.common.base.context_command import BaseContextCommand from apps.ai.management.commands.ai_update_slack_message_context import Command +from apps.slack.models.message import Message @pytest.fixture @@ -21,14 +23,10 @@ def mock_message(): class TestAiCreateSlackMessageContextCommand: def test_command_inheritance(self, command): """Test that the command inherits from BaseContextCommand.""" - from apps.ai.common.base.context_command import BaseContextCommand - assert isinstance(command, BaseContextCommand) def test_model_class_property(self, command): """Test the model_class property returns Message.""" - from apps.slack.models.message import Message - assert command.model_class == Message def test_entity_name_property(self, command): diff --git a/backend/tests/apps/ai/models/chunk_test.py b/backend/tests/apps/ai/models/chunk_test.py index 192fac23a4..b02f861b73 100644 --- a/backend/tests/apps/ai/models/chunk_test.py +++ b/backend/tests/apps/ai/models/chunk_test.py @@ -159,6 +159,4 @@ def test_meta_class_attributes(self): def test_context_relationship(self): context_field = Chunk._meta.get_field("context") - from apps.ai.models.context import Context - assert context_field.related_model == Context diff --git a/backend/tests/apps/ai/models/context_test.py b/backend/tests/apps/ai/models/context_test.py index d7bae59044..ebde57a97b 100644 --- a/backend/tests/apps/ai/models/context_test.py +++ b/backend/tests/apps/ai/models/context_test.py @@ -4,6 +4,7 @@ import pytest from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ValidationError from apps.ai.models.context import Context from apps.common.models import TimestampedModel @@ -107,8 +108,6 @@ def test_context_validation(self, mock_full_clean): @patch("apps.ai.models.context.Context.full_clean") def test_context_validation_source_too_long(self, mock_full_clean): - from django.core.exceptions import ValidationError - mock_full_clean.side_effect = ValidationError("Source too long") context = Context() @@ -299,9 +298,7 @@ def test_str_method_fallback_to_str(self): @patch("apps.ai.models.context.Context.__init__") def test_update_data_new_context_with_save(self, mock_init, mock_get): """Test update_data creating a new context with save=True.""" - from apps.ai.models.context import Context as ContextModel - - mock_get.side_effect = ContextModel.DoesNotExist + mock_get.side_effect = Context.DoesNotExist mock_init.return_value = None # __init__ should return None content = "New test content" @@ -317,7 +314,7 @@ def test_update_data_new_context_with_save(self, mock_init, mock_get): mock_get_for_model.return_value = mock_content_type # Mock the context instance and its save method - with patch.object(ContextModel, "save") as mock_save: + with patch.object(Context, "save") as mock_save: result = Context.update_data( content, mock_content_object, source=source, save=True ) @@ -340,9 +337,7 @@ def test_update_data_new_context_with_save(self, mock_init, mock_get): @patch("apps.ai.models.context.Context.__init__") def test_update_data_new_context_without_save(self, mock_init, mock_get): """Test update_data creating a new context with save=False.""" - from apps.ai.models.context import Context as ContextModel - - mock_get.side_effect = ContextModel.DoesNotExist + mock_get.side_effect = Context.DoesNotExist mock_init.return_value = None # __init__ should return None content = "New test content" @@ -358,7 +353,7 @@ def test_update_data_new_context_without_save(self, mock_init, mock_get): mock_get_for_model.return_value = mock_content_type # Mock the context instance and its save method - with patch.object(ContextModel, "save") as mock_save: + with patch.object(Context, "save") as mock_save: result = Context.update_data( content, mock_content_object, source=source, save=False ) diff --git a/backend/tests/apps/api/rest/v0/snapshot_test.py b/backend/tests/apps/api/rest/v0/snapshot_test.py index 0a69c31d7a..f90fcdfc8d 100644 --- a/backend/tests/apps/api/rest/v0/snapshot_test.py +++ b/backend/tests/apps/api/rest/v0/snapshot_test.py @@ -7,6 +7,8 @@ from apps.api.rest.v0.snapshot import ( SnapshotDetail, + SnapshotIssue, + SnapshotRelease, get_snapshot, list_snapshot_chapters, list_snapshot_issues, @@ -242,8 +244,6 @@ def test_list_snapshot_releases_success(self, mock_filter): assert response[0].tag_name == self.release.tag_name def test_snapshot_issue_resolver_no_organization(self): - from apps.api.rest.v0.snapshot import SnapshotIssue - issue = MagicMock(spec=Issue) issue.repository = MagicMock() issue.repository.organization = None @@ -253,8 +253,6 @@ def test_snapshot_issue_resolver_no_organization(self): assert SnapshotIssue.resolve_repository_name(issue) == "test-repo" def test_snapshot_release_resolver_no_organization(self): - from apps.api.rest.v0.snapshot import SnapshotRelease - release = MagicMock(spec=Release) release.repository = MagicMock() release.repository.organization = None diff --git a/backend/tests/apps/api/rest/v0/structured_search_test.py b/backend/tests/apps/api/rest/v0/structured_search_test.py index dda65d726a..326093c317 100644 --- a/backend/tests/apps/api/rest/v0/structured_search_test.py +++ b/backend/tests/apps/api/rest/v0/structured_search_test.py @@ -1,8 +1,8 @@ """Tests for structured search utility.""" -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch -from apps.api.rest.v0.structured_search import apply_structured_search +from apps.api.rest.v0.structured_search import QueryParserError, apply_structured_search FIELD_SCHEMA = { "name": {"type": "string", "lookup": "icontains"}, @@ -141,10 +141,6 @@ def test_multiple_conditions(): def test_query_parser_error_returns_original_queryset(): """Test that QueryParserError returns original queryset.""" - from unittest.mock import patch - - from apps.api.rest.v0.structured_search import QueryParserError - qs = make_queryset() with patch("apps.api.rest.v0.structured_search.QueryParser") as mock_parser_class: mock_parser_class.side_effect = QueryParserError("Test error") @@ -156,8 +152,6 @@ def test_query_parser_error_returns_original_queryset(): def test_condition_field_not_in_schema_is_skipped(): """Test that condition with field not in field_schema is skipped.""" - from unittest.mock import patch - qs = make_queryset() with patch("apps.api.rest.v0.structured_search.QueryParser") as mock_parser_class: mock_parser = MagicMock() @@ -170,8 +164,6 @@ def test_condition_field_not_in_schema_is_skipped(): def test_boolean_field_uses_no_lookup_suffix(): """Test boolean field uses empty lookup suffix.""" - from unittest.mock import patch - schema_with_boolean = { "active": {"type": "boolean", "field": "is_active"}, } @@ -189,8 +181,6 @@ def test_boolean_field_uses_no_lookup_suffix(): def test_number_field_with_none_value_is_skipped(): """Test that number field with None value is skipped.""" - from unittest.mock import patch - qs = make_queryset() with patch("apps.api.rest.v0.structured_search.QueryParser") as mock_parser_class: mock_parser = MagicMock() diff --git a/backend/tests/apps/common/management/commands/algolia_update_replicas_test.py b/backend/tests/apps/common/management/commands/algolia_update_replicas_test.py index 2f9fb3eee4..50abc33435 100644 --- a/backend/tests/apps/common/management/commands/algolia_update_replicas_test.py +++ b/backend/tests/apps/common/management/commands/algolia_update_replicas_test.py @@ -26,7 +26,7 @@ def test_successful_replica_configuration(self): expected_output = ( "\n Starting replica configuration...\n" - "\n Replica have been Successfully created.\n" + "\n Replicas have been successfully created.\n" ) assert fake_out.getvalue() == expected_output self.mock_replica_update.assert_called_once() diff --git a/backend/tests/apps/common/management/commands/purge_data_test.py b/backend/tests/apps/common/management/commands/purge_data_test.py index e423fc0e88..047022d887 100644 --- a/backend/tests/apps/common/management/commands/purge_data_test.py +++ b/backend/tests/apps/common/management/commands/purge_data_test.py @@ -22,8 +22,7 @@ class TestPurgeDataCommand: @patch("apps.common.management.commands.purge_data.apps.get_app_config") @patch("apps.common.management.commands.purge_data.connection.cursor") - @patch("builtins.print") - def test_handle(self, mock_print, mock_cursor, mock_get_app_config, nest_apps, mock_models): + def test_handle(self, mock_cursor, mock_get_app_config, nest_apps, mock_models): mock_cursor.return_value.__enter__.return_value = MagicMock() cursor_instance = mock_cursor.return_value.__enter__.return_value @@ -44,6 +43,7 @@ def get_app_config_side_effect(app_name): mock_get_app_config.side_effect = get_app_config_side_effect command = Command() + command.stdout = MagicMock() command.handle() for app_name in nest_apps: @@ -53,4 +53,4 @@ def get_app_config_side_effect(app_name): sql.Identifier(table_name) ) cursor_instance.execute.assert_any_call(expected_query) - mock_print.assert_any_call(f"Purged {app_name}.{model_name}") + command.stdout.write.assert_any_call(f"Purged {app_name}.{model_name}") diff --git a/backend/tests/apps/github/management/commands/github_add_related_repositories_test.py b/backend/tests/apps/github/management/commands/github_add_related_repositories_test.py index cc8300caf1..2055aeb4ce 100644 --- a/backend/tests/apps/github/management/commands/github_add_related_repositories_test.py +++ b/backend/tests/apps/github/management/commands/github_add_related_repositories_test.py @@ -75,8 +75,8 @@ def test_handle( with ( mock.patch.object(Project, "active_projects", mock_active_projects), mock.patch.object(Project, "bulk_save") as mock_project_bulk_save, - mock.patch("builtins.print") as mock_print, ): + command.stdout = mock.MagicMock() command.handle(offset=offset) mock_get_github_client.assert_called_once() @@ -91,7 +91,7 @@ def test_handle( mock_project_bulk_save.assert_called_once() - assert mock_print.call_count > 0 + assert command.stdout.write.call_count > 0 @mock.patch("apps.github.management.commands.github_add_related_repositories.get_github_client") diff --git a/backend/tests/apps/github/management/commands/github_get_installation_id_test.py b/backend/tests/apps/github/management/commands/github_get_installation_id_test.py index ab447106ff..36524d8897 100644 --- a/backend/tests/apps/github/management/commands/github_get_installation_id_test.py +++ b/backend/tests/apps/github/management/commands/github_get_installation_id_test.py @@ -7,6 +7,8 @@ import pytest from django.test import SimpleTestCase +from apps.github.management.commands.github_get_installation_id import Command + class TestGitHubGetInstallationId(SimpleTestCase): """Test the GitHub get installation ID management command.""" @@ -23,8 +25,6 @@ def setUp(self): @mock.patch("apps.github.management.commands.github_get_installation_id.Auth.AppAuth") def test_get_installation_id_success(self, mock_app_auth, mock_github_integration): """Test successful retrieval of installation ID.""" - from apps.github.management.commands.github_get_installation_id import Command - # Mock the installation mock_installation = mock.MagicMock() mock_installation.id = 12345 @@ -63,8 +63,6 @@ def test_get_installation_id_success(self, mock_app_auth, mock_github_integratio @mock.patch("apps.github.management.commands.github_get_installation_id.Auth.AppAuth") def test_get_installation_id_no_installations(self, mock_app_auth, mock_github_integration): """Test when no installations are found.""" - from apps.github.management.commands.github_get_installation_id import Command - mock_gi_instance = mock.MagicMock() mock_gi_instance.get_installations.return_value = [] mock_github_integration.return_value = mock_gi_instance @@ -85,16 +83,12 @@ def test_get_installation_id_no_installations(self, mock_app_auth, mock_github_i def test_get_installation_id_no_app_id(self): """Test when no app ID is provided.""" - from apps.github.management.commands.github_get_installation_id import Command - command = Command() with pytest.raises(SystemExit): command.handle() def test_get_installation_id_private_key_file_not_found(self): """Test when private key file is not found.""" - from apps.github.management.commands.github_get_installation_id import Command - command = Command() with ( mock.patch("pathlib.Path.exists", return_value=False), @@ -104,8 +98,6 @@ def test_get_installation_id_private_key_file_not_found(self): def test_get_installation_id_empty_private_key_file(self): """Test when private key file is empty.""" - from apps.github.management.commands.github_get_installation_id import Command - command = Command() with ( mock.patch("pathlib.Path.open", mock.mock_open(read_data="")), @@ -117,8 +109,6 @@ def test_get_installation_id_empty_private_key_file(self): @mock.patch("apps.github.management.commands.github_get_installation_id.Path") def test_get_installation_id_with_custom_private_key_file(self, mock_path): """Test with custom private key file path.""" - from apps.github.management.commands.github_get_installation_id import Command - custom_key_path = "/custom/path/key.pem" mock_path.return_value.exists.return_value = True mock_path.return_value.open = mock.mock_open(read_data=self.test_private_key) @@ -138,8 +128,6 @@ def test_get_installation_id_with_custom_private_key_file(self, mock_path): @mock.patch("apps.github.management.commands.github_get_installation_id.GithubIntegration") def test_get_installation_id_github_exception(self, mock_github_integration, mock_app_auth): """Test when a generic exception occurs during GitHub API interaction.""" - from apps.github.management.commands.github_get_installation_id import Command - mock_app_auth.return_value = mock.MagicMock() mock_github_integration.side_effect = Exception("API Error") @@ -160,8 +148,6 @@ def test_get_installation_id_multiple_installations( self, mock_app_auth, mock_github_integration ): """Test successful retrieval of multiple installation IDs.""" - from apps.github.management.commands.github_get_installation_id import Command - mock_installation_1 = mock.MagicMock() mock_installation_1.id = 12345 mock_installation_1.account.type = "Organization" @@ -199,8 +185,6 @@ def test_get_installation_id_multiple_installations( def test_get_installation_id_permission_error(self): """Test when a permission error occurs reading the private key file.""" - from apps.github.management.commands.github_get_installation_id import Command - command = Command() with ( mock.patch("pathlib.Path.open", side_effect=PermissionError), @@ -211,8 +195,6 @@ def test_get_installation_id_permission_error(self): def test_get_installation_id_file_not_found_error(self): """Test when a file not found error occurs reading the private key file.""" - from apps.github.management.commands.github_get_installation_id import Command - command = Command() with ( mock.patch("pathlib.Path.open", side_effect=FileNotFoundError), @@ -223,8 +205,6 @@ def test_get_installation_id_file_not_found_error(self): def test_add_arguments(self): """Test that the command's arguments are correctly added.""" - from apps.github.management.commands.github_get_installation_id import Command - mock_parser = mock.MagicMock() command = Command() command.add_arguments(mock_parser) @@ -246,8 +226,6 @@ def test_get_installation_id_with_environment_app_id( self, mock_app_auth, mock_github_integration ): """Test using app ID from environment variable.""" - from apps.github.management.commands.github_get_installation_id import Command - mock_gi_instance = mock.MagicMock() mock_gi_instance.get_installations.return_value = [] mock_github_integration.return_value = mock_gi_instance diff --git a/backend/tests/apps/github/management/commands/github_update_owasp_organization_test.py b/backend/tests/apps/github/management/commands/github_update_owasp_organization_test.py index 12d49c9900..fadcae0ab8 100644 --- a/backend/tests/apps/github/management/commands/github_update_owasp_organization_test.py +++ b/backend/tests/apps/github/management/commands/github_update_owasp_organization_test.py @@ -135,7 +135,6 @@ def __getitem__(self, index): mock.patch.object(Chapter, "objects") as mock_chapter_objects, mock.patch.object(Committee, "objects") as mock_committee_objects, mock.patch.object(Repository, "objects") as mock_repository_objects, - mock.patch("builtins.print") as mock_print, ): mock_project_update.return_value = mock_repository mock_chapter_update.return_value = mock_repository @@ -146,6 +145,7 @@ def __getitem__(self, index): mock_committee_objects.all.return_value = [] mock_repository_objects.filter.return_value.count.return_value = 1 + command.stdout = mock.MagicMock() command.handle(repository=repository_name, offset=offset) mock_get_github_client.assert_called_once() @@ -166,7 +166,7 @@ def __getitem__(self, index): elif repository_name.startswith("www-committee-"): assert mock_committee_update.call_count == expected_calls["committee"] else: - assert mock_print.call_count > 0 + assert command.stdout.write.call_count > 0 mock_project_bulk_save.assert_called_once() mock_chapter_bulk_save.assert_called_once() @@ -231,18 +231,18 @@ def __getitem__(self, index): mock.patch.object(Committee, "update_data"), mock.patch.object(Project, "objects") as mock_project_objects, mock.patch.object(Repository, "objects") as mock_repository_objects, - mock.patch("builtins.print") as mock_print, ): mock_project = mock.Mock() mock_project.owasp_repository = mock.Mock() mock_project_objects.all.return_value = [mock_project] mock_repository_objects.filter.return_value.count.return_value = 2 + command.stdout = mock.MagicMock() command.handle(repository=None, offset=0) assert mock_sync_repository.call_count == 3 mock_logger.exception.assert_called_once_with( "Error syncing repository %s", "https://github.com/OWASP/www-chapter-error" ) - mock_print.assert_any_call( + command.stdout.write.assert_any_call( "\nOWASP GitHub repositories count != synced repositories count: 3 != 2" ) mock_project_objects.all.assert_called_once() diff --git a/backend/tests/apps/github/management/commands/github_update_users_test.py b/backend/tests/apps/github/management/commands/github_update_users_test.py index 2f4f7e6080..f4defc3e6a 100644 --- a/backend/tests/apps/github/management/commands/github_update_users_test.py +++ b/backend/tests/apps/github/management/commands/github_update_users_test.py @@ -53,9 +53,9 @@ def test_handle_with_default_offset(self, mock_repository_contributor, mock_user ] mock_repository_contributor.objects = mock_rc_objects - with patch("builtins.print") as mock_print: - command = Command() - command.handle(offset=0) + command = Command() + command.stdout = MagicMock() + command.handle(offset=0) mock_user.objects.order_by.assert_called_once_with("-created_at") mock_users_queryset.count.assert_called_once() @@ -64,10 +64,10 @@ def test_handle_with_default_offset(self, mock_repository_contributor, mock_user mock_rc_objects.exclude.return_value.values.assert_called_once_with("user_id") mock_rc_objects.exclude.return_value.values.return_value.annotate.assert_called_once() - assert mock_print.call_count == 3 - mock_print.assert_any_call("1 of 3 User 1") - mock_print.assert_any_call("2 of 3 User 2") - mock_print.assert_any_call("3 of 3 User 3") + assert command.stdout.write.call_count == 3 + command.stdout.write.assert_any_call("1 of 3 User 1\n") + command.stdout.write.assert_any_call("2 of 3 User 2\n") + command.stdout.write.assert_any_call("3 of 3 User 3\n") assert mock_user1.contributions_count == 10 assert mock_user2.contributions_count == 20 @@ -97,17 +97,17 @@ def test_handle_with_custom_offset(self, mock_repository_contributor, mock_user) ] mock_repository_contributor.objects = mock_rc_queryset - with patch("builtins.print") as mock_print: - command = Command() - command.handle(offset=1) + command = Command() + command.stdout = MagicMock() + command.handle(offset=1) mock_user.objects.order_by.assert_called_once_with("-created_at") mock_users_queryset.count.assert_called_once() mock_users_queryset.__getitem__.assert_called_once_with(slice(1, None)) - assert mock_print.call_count == 2 - mock_print.assert_any_call("2 of 2 User 2") - mock_print.assert_any_call("3 of 2 User 3") + assert command.stdout.write.call_count == 2 + command.stdout.write.assert_any_call("2 of 2 User 2\n") + command.stdout.write.assert_any_call("3 of 2 User 3\n") assert mock_user1.contributions_count == 20 assert mock_user2.contributions_count == 30 @@ -135,13 +135,13 @@ def test_handle_with_users_having_no_contributions( mock_rc_queryset.exclude.return_value.values.return_value.annotate.return_value = [] mock_repository_contributor.objects = mock_rc_queryset - with patch("builtins.print") as mock_print: - command = Command() - command.handle(offset=0) + command = Command() + command.stdout = MagicMock() + command.handle(offset=0) - assert mock_print.call_count == 2 - mock_print.assert_any_call("1 of 2 User 1") - mock_print.assert_any_call("2 of 2 User 2") + assert command.stdout.write.call_count == 2 + command.stdout.write.assert_any_call("1 of 2 User 1\n") + command.stdout.write.assert_any_call("2 of 2 User 2\n") assert mock_user1.contributions_count == 0 assert mock_user2.contributions_count == 0 @@ -168,11 +168,11 @@ def test_handle_with_single_user(self, mock_repository_contributor, mock_user): ] mock_repository_contributor.objects = mock_rc_queryset - with patch("builtins.print") as mock_print: - command = Command() - command.handle(offset=0) + command = Command() + command.stdout = MagicMock() + command.handle(offset=0) - mock_print.assert_called_once_with("1 of 1 User 1") + command.stdout.write.assert_called_once_with("1 of 1 User 1\n") assert mock_user1.contributions_count == 15 @@ -194,11 +194,11 @@ def test_handle_with_empty_user_list(self, mock_repository_contributor, mock_use mock_rc_queryset.exclude.return_value.values.return_value.annotate.return_value = [] mock_repository_contributor.objects = mock_rc_queryset - with patch("builtins.print") as mock_print: - command = Command() - command.handle(offset=0) + command = Command() + command.stdout = MagicMock() + command.handle(offset=0) - mock_print.assert_not_called() + command.stdout.write.assert_not_called() assert mock_user.bulk_save.call_count == 1 assert mock_user.bulk_save.call_args_list[-1][0][0] == [] @@ -224,13 +224,13 @@ def test_handle_with_exact_batch_size(self, mock_repository_contributor, mock_us ] mock_repository_contributor.objects = mock_rc_queryset - with patch("builtins.print") as mock_print: - command = Command() - command.handle(offset=0) + command = Command() + command.stdout = MagicMock() + command.handle(offset=0) - assert mock_print.call_count == 2 - mock_print.assert_any_call("1 of 2 User 1") - mock_print.assert_any_call("2 of 2 User 2") + assert command.stdout.write.call_count == 2 + command.stdout.write.assert_any_call("1 of 2 User 1\n") + command.stdout.write.assert_any_call("2 of 2 User 2\n") assert mock_user1.contributions_count == 10 assert mock_user2.contributions_count == 20 diff --git a/backend/tests/apps/github/models/release_test.py b/backend/tests/apps/github/models/release_test.py index 554638e698..d0b195977e 100644 --- a/backend/tests/apps/github/models/release_test.py +++ b/backend/tests/apps/github/models/release_test.py @@ -1,4 +1,4 @@ -from datetime import UTC +from datetime import UTC, datetime from unittest.mock import Mock from apps.github.models.release import Release @@ -73,8 +73,6 @@ def test_str_representation(self, mocker): def test_summary_property(self): """Tests the summary property returns the correct format.""" - from datetime import datetime - release = Release(tag_name="v1.0", published_at=datetime(2023, 1, 1, tzinfo=UTC)) assert release.summary == "v1.0 on Jan 01, 2023" diff --git a/backend/tests/apps/owasp/admin/mixins_test.py b/backend/tests/apps/owasp/admin/mixins_test.py index d9efdf6c1d..8619cc00e5 100644 --- a/backend/tests/apps/owasp/admin/mixins_test.py +++ b/backend/tests/apps/owasp/admin/mixins_test.py @@ -8,6 +8,7 @@ GenericEntityAdminMixin, StandardOwaspAdminMixin, ) +from apps.owasp.admin.widgets import ChannelIdWidget from apps.owasp.models.project import Project @@ -153,8 +154,6 @@ class TestEntityChannelInline: def test_formfield_for_dbfield_channel_id(self, mocker): """Test that channel_id field gets ChannelIdWidget.""" - from apps.owasp.admin.widgets import ChannelIdWidget - inline = EntityChannelInline(Project, mocker.Mock()) mock_db_field = mocker.Mock() mock_db_field.name = "channel_id" diff --git a/backend/tests/apps/owasp/api/internal/nodes/chapter_test.py b/backend/tests/apps/owasp/api/internal/nodes/chapter_test.py index c82606073c..e64e5bb7c5 100644 --- a/backend/tests/apps/owasp/api/internal/nodes/chapter_test.py +++ b/backend/tests/apps/owasp/api/internal/nodes/chapter_test.py @@ -63,8 +63,6 @@ def test_resolve_contribution_stats(self): def test_contribution_stats_transforms_snake_case_to_camel_case(self): """Test that contribution_stats resolver transforms snake_case keys to camelCase.""" - from unittest.mock import Mock - mock_chapter = Mock() mock_chapter.contribution_stats = { "commits": 75, @@ -144,8 +142,6 @@ def test_suggested_location_resolver(self): def test_suggested_location_resolver_none(self): """Test suggested_location resolver when None.""" - from unittest.mock import Mock - mock_chapter = Mock() mock_chapter.idx_suggested_location = None diff --git a/backend/tests/apps/owasp/api/internal/nodes/project_test.py b/backend/tests/apps/owasp/api/internal/nodes/project_test.py index 4c05be4f3f..c9f5939d3e 100644 --- a/backend/tests/apps/owasp/api/internal/nodes/project_test.py +++ b/backend/tests/apps/owasp/api/internal/nodes/project_test.py @@ -1,6 +1,6 @@ """Test cases for ProjectNode.""" -from unittest.mock import MagicMock +from unittest.mock import MagicMock, Mock from apps.github.api.internal.nodes.issue import IssueNode from apps.github.api.internal.nodes.milestone import MilestoneNode @@ -116,8 +116,6 @@ def test_resolve_contribution_data(self): def test_contribution_stats_transforms_snake_case_to_camel_case(self): """Test that contribution_stats resolver transforms snake_case keys to camelCase.""" - from unittest.mock import Mock - mock_project = Mock() mock_project.contribution_stats = { "commits": 100, diff --git a/backend/tests/apps/owasp/api/internal/queries/board_of_directors_test.py b/backend/tests/apps/owasp/api/internal/queries/board_of_directors_test.py index 6bfb638a9a..e1c6ebad88 100644 --- a/backend/tests/apps/owasp/api/internal/queries/board_of_directors_test.py +++ b/backend/tests/apps/owasp/api/internal/queries/board_of_directors_test.py @@ -3,6 +3,7 @@ from unittest.mock import Mock, patch from apps.owasp.api.internal.queries.board_of_directors import BoardOfDirectorsQuery +from apps.owasp.models.board_of_directors import BoardOfDirectors class TestBoardOfDirectorsQuery: @@ -23,8 +24,6 @@ def test_board_of_directors_found(self): assert result == mock_board def test_board_of_directors_not_found(self): - from apps.owasp.models.board_of_directors import BoardOfDirectors - query = BoardOfDirectorsQuery() with patch( diff --git a/backend/tests/apps/owasp/api/internal/queries/member_snapshot_test.py b/backend/tests/apps/owasp/api/internal/queries/member_snapshot_test.py index ad87ae500b..b96a6a1253 100644 --- a/backend/tests/apps/owasp/api/internal/queries/member_snapshot_test.py +++ b/backend/tests/apps/owasp/api/internal/queries/member_snapshot_test.py @@ -2,13 +2,12 @@ from unittest.mock import Mock, patch +from apps.github.models.user import User from apps.owasp.api.internal.queries.member_snapshot import MemberSnapshotQuery class TestMemberSnapshotQuery: def test_member_snapshot_user_not_found(self): - from apps.github.models.user import User - query = MemberSnapshotQuery() with patch("apps.owasp.api.internal.queries.member_snapshot.User.objects.get") as mock_get: @@ -103,8 +102,6 @@ def test_member_snapshots_by_user(self): assert result == mock_snapshots def test_member_snapshots_user_not_found_returns_empty(self): - from apps.github.models.user import User - query = MemberSnapshotQuery() with patch("apps.owasp.api.internal.queries.member_snapshot.User.objects.get") as mock_get: diff --git a/backend/tests/apps/owasp/management/commands/owasp_aggregate_projects_test.py b/backend/tests/apps/owasp/management/commands/owasp_aggregate_projects_test.py index 74cf49a5f7..c3474d0aa0 100644 --- a/backend/tests/apps/owasp/management/commands/owasp_aggregate_projects_test.py +++ b/backend/tests/apps/owasp/management/commands/owasp_aggregate_projects_test.py @@ -75,17 +75,15 @@ def test_handle(self, mock_bulk_save, command, mock_project, offset, projects): ) mock_active_projects.order_by.return_value = mock_active_projects - with ( - mock.patch.object(Project, "active_projects", mock_active_projects), - mock.patch("builtins.print") as mock_print, - ): + with mock.patch.object(Project, "active_projects", mock_active_projects): + command.stdout = mock.MagicMock() command.handle(offset=offset) assert mock_bulk_save.called - assert mock_print.call_count == projects - offset + assert command.stdout.write.call_count == projects - offset - for call in mock_print.call_args_list: - args, _ = call + for call in command.stdout.write.call_args_list: + args = call[0] assert "https://owasp.org/www-project-test" in args[0] @mock.patch.dict("os.environ", {"GITHUB_TOKEN": "test-token"}) @@ -126,10 +124,8 @@ def test_handle_deactivates_archived_project(self, mock_bulk_save, command, mock ) mock_active_projects.order_by.return_value = mock_active_projects - with ( - mock.patch.object(Project, "active_projects", mock_active_projects), - mock.patch("builtins.print"), - ): + with mock.patch.object(Project, "active_projects", mock_active_projects): + command.stdout = mock.MagicMock() command.handle(offset=0) assert not mock_project.is_active @@ -169,9 +165,7 @@ def test_handle_no_release_or_license(self, mock_bulk_save, command, mock_projec ) mock_active_projects.order_by.return_value = mock_active_projects - with ( - mock.patch.object(Project, "active_projects", mock_active_projects), - mock.patch("builtins.print"), - ): + with mock.patch.object(Project, "active_projects", mock_active_projects): + command.stdout = mock.MagicMock() command.handle(offset=0) assert mock_bulk_save.called diff --git a/backend/tests/apps/owasp/management/commands/owasp_enrich_chapters_test.py b/backend/tests/apps/owasp/management/commands/owasp_enrich_chapters_test.py index 8a1c13d373..7ccdd1447e 100644 --- a/backend/tests/apps/owasp/management/commands/owasp_enrich_chapters_test.py +++ b/backend/tests/apps/owasp/management/commands/owasp_enrich_chapters_test.py @@ -69,19 +69,19 @@ def test_handle( mock.patch.object( Prompt, "get_owasp_chapter_summary", mock_prompt.get_owasp_chapter_summary ), - mock.patch("builtins.print") as mock_print, mock.patch("time.sleep", return_value=None), ): + command.stdout = mock.MagicMock() command.handle(offset=offset) mock_active_chapters.count.assert_called_once() assert mock_bulk_save.called - assert mock_print.call_count == len(mock_chapters_list) - offset + assert command.stdout.write.call_count == len(mock_chapters_list) - offset - for call in mock_print.call_args_list: - args, _ = call + for call in command.stdout.write.call_args_list: + args = call[0] assert "https://owasp.org/www-chapter-test" in args[0] for chapter in mock_chapters_list: diff --git a/backend/tests/apps/owasp/management/commands/owasp_enrich_committees_test.py b/backend/tests/apps/owasp/management/commands/owasp_enrich_committees_test.py index 4641072b0e..72ce0a60b9 100644 --- a/backend/tests/apps/owasp/management/commands/owasp_enrich_committees_test.py +++ b/backend/tests/apps/owasp/management/commands/owasp_enrich_committees_test.py @@ -83,8 +83,8 @@ def test_handle( mock.patch.object( Prompt, "get_owasp_committee_summary", mock_prompt.get_owasp_committee_summary ), - mock.patch("builtins.print") as mock_print, ): + command.stdout = mock.MagicMock() command.handle( offset=offset, force_update_summary=force_update_summary, @@ -98,10 +98,10 @@ def test_handle( assert mock_bulk_save.called - assert mock_print.call_count == committees - offset + assert command.stdout.write.call_count == committees - offset - for call in mock_print.call_args_list: - args, _ = call + for call in command.stdout.write.call_args_list: + args = call[0] assert "https://owasp.org/www-committee-test" in args[0] if update_summary: diff --git a/backend/tests/apps/owasp/management/commands/owasp_enrich_projects_test.py b/backend/tests/apps/owasp/management/commands/owasp_enrich_projects_test.py index f2f1b7e970..122945becf 100644 --- a/backend/tests/apps/owasp/management/commands/owasp_enrich_projects_test.py +++ b/backend/tests/apps/owasp/management/commands/owasp_enrich_projects_test.py @@ -81,8 +81,8 @@ def test_handle( mock.patch.object( Prompt, "get_owasp_project_summary", mock_prompt.get_owasp_project_summary ), - mock.patch("builtins.print") as mock_print, ): + command.stdout = mock.MagicMock() command.handle( offset=offset, force_update_summary=force_update_summary, @@ -96,10 +96,10 @@ def test_handle( assert mock_bulk_save.called - assert mock_print.call_count == projects - offset + assert command.stdout.write.call_count == projects - offset - for call in mock_print.call_args_list: - args, _ = call + for call in command.stdout.write.call_args_list: + args = call[0] assert "https://owasp.org/www-project-test" in args[0] if update_summary: diff --git a/backend/tests/apps/owasp/management/commands/owasp_generate_community_snapshot_video_test.py b/backend/tests/apps/owasp/management/commands/owasp_generate_community_snapshot_video_test.py index d2617bed7a..4aae143f15 100644 --- a/backend/tests/apps/owasp/management/commands/owasp_generate_community_snapshot_video_test.py +++ b/backend/tests/apps/owasp/management/commands/owasp_generate_community_snapshot_video_test.py @@ -113,8 +113,6 @@ def test_handle_with_valid_snapshots( mock_slide_builder.assert_called_once() mock_generator.assert_called_once() assert mock_generator_instance.append_slide.call_count == 4 - from pathlib import Path - mock_generator_instance.generate_video.assert_called_once() args = mock_generator_instance.generate_video.call_args assert isinstance(args[0][0], Path) diff --git a/backend/tests/apps/owasp/management/commands/owasp_scrape_chapters_test.py b/backend/tests/apps/owasp/management/commands/owasp_scrape_chapters_test.py index fe5f1ab963..744f25e31c 100644 --- a/backend/tests/apps/owasp/management/commands/owasp_scrape_chapters_test.py +++ b/backend/tests/apps/owasp/management/commands/owasp_scrape_chapters_test.py @@ -62,7 +62,6 @@ def test_handle(self, mock_bulk_save, command, mock_chapter, offset, chapters): with ( mock.patch.object(Chapter, "active_chapters", mock_active_chapters), - mock.patch("builtins.print") as mock_print, mock.patch("time.sleep", return_value=None), mock.patch( "apps.owasp.management.commands.owasp_scrape_chapters.OwaspScraper", @@ -73,16 +72,17 @@ def test_handle(self, mock_bulk_save, command, mock_chapter, offset, chapters): side_effect=normalize_url, ), ): + command.stdout = mock.MagicMock() command.handle(offset=offset) mock_active_chapters.count.assert_called_once() assert mock_bulk_save.called - assert mock_print.call_count == (chapters - offset) + assert command.stdout.write.call_count == (chapters - offset) - for call in mock_print.call_args_list: - args, _ = call + for call in command.stdout.write.call_args_list: + args = call[0] assert "https://owasp.org/www-chapter-test" in args[0] for chapter in mock_chapters_list: diff --git a/backend/tests/apps/owasp/management/commands/owasp_scrape_committees_test.py b/backend/tests/apps/owasp/management/commands/owasp_scrape_committees_test.py index 78a52832a8..cb810e3a24 100644 --- a/backend/tests/apps/owasp/management/commands/owasp_scrape_committees_test.py +++ b/backend/tests/apps/owasp/management/commands/owasp_scrape_committees_test.py @@ -62,7 +62,6 @@ def test_handle(self, mock_bulk_save, command, mock_committee, offset, committee with ( mock.patch.object(Committee, "active_committees", mock_active_committees), - mock.patch("builtins.print") as mock_print, mock.patch("time.sleep", return_value=None), mock.patch( "apps.owasp.management.commands.owasp_scrape_committees.OwaspScraper", @@ -73,16 +72,17 @@ def test_handle(self, mock_bulk_save, command, mock_committee, offset, committee side_effect=normalize_url, ), ): + command.stdout = mock.MagicMock() command.handle(offset=offset) mock_active_committees.count.assert_called_once() assert mock_bulk_save.called - assert mock_print.call_count == (committees - offset) + assert command.stdout.write.call_count == (committees - offset) - for call in mock_print.call_args_list: - args, _ = call + for call in command.stdout.write.call_args_list: + args = call[0] assert "https://owasp.org/www-committee-test" in args[0] for committee in mock_committees_list: diff --git a/backend/tests/apps/owasp/management/commands/owasp_scrape_projects_test.py b/backend/tests/apps/owasp/management/commands/owasp_scrape_projects_test.py index fb312413d2..0cccb3edca 100644 --- a/backend/tests/apps/owasp/management/commands/owasp_scrape_projects_test.py +++ b/backend/tests/apps/owasp/management/commands/owasp_scrape_projects_test.py @@ -44,13 +44,13 @@ def test_audience(self, mock_github, mock_bulk_save, command, mock_project): with ( mock.patch.object(Project, "active_projects", mock_active_projects), - mock.patch("builtins.print"), mock.patch("time.sleep"), mock.patch( "apps.owasp.management.commands.owasp_scrape_projects.OwaspScraper", return_value=mock_scraper, ), ): + command.stdout = mock.MagicMock() command.handle(offset=0) mock_bulk_save.assert_called_once() @@ -100,17 +100,17 @@ def test_urls(self, mock_github, mock_bulk_save, command, mock_project, offset, with ( mock.patch.object(Project, "active_projects", mock_active_projects), - mock.patch("builtins.print") as mock_print, mock.patch("time.sleep", return_value=None), mock.patch( "apps.owasp.management.commands.owasp_scrape_projects.OwaspScraper", return_value=mock_scraper, ), ): + command.stdout = mock.MagicMock() command.handle(offset=offset) assert mock_bulk_save.called - assert mock_print.call_count == (project_count - offset) + assert command.stdout.write.call_count == (project_count - offset) last_project = mock_bulk_save.call_args[0][0][-1] assert last_project.invalid_urls == ["https://invalid.com/repo3"] diff --git a/backend/tests/apps/owasp/models/common_test.py b/backend/tests/apps/owasp/models/common_test.py index a60f30efa1..caa949644d 100644 --- a/backend/tests/apps/owasp/models/common_test.py +++ b/backend/tests/apps/owasp/models/common_test.py @@ -1,4 +1,5 @@ from unittest.mock import MagicMock, Mock, patch +from urllib.parse import urlparse as original_urlparse import pytest @@ -543,8 +544,6 @@ def test_get_metadata_yaml_scanner_error(self): def test_get_urls_with_domain_value_error(self): """Test get_urls handles ValueError during domain filtering.""" - from urllib.parse import urlparse as original_urlparse - model = EntityModel() repository = Repository() repository.name = "www-project-example" diff --git a/backend/tests/apps/slack/commands/ai_test.py b/backend/tests/apps/slack/commands/ai_test.py index e7e5af1b0f..c121e3ed0a 100644 --- a/backend/tests/apps/slack/commands/ai_test.py +++ b/backend/tests/apps/slack/commands/ai_test.py @@ -5,6 +5,7 @@ import pytest from apps.slack.commands.ai import Ai +from apps.slack.commands.command import CommandBase class TestAiCommand: @@ -156,8 +157,6 @@ def test_render_blocks_returns_none(self, mock_get_blocks): def test_ai_command_inheritance(self): """Test that Ai command inherits from CommandBase.""" - from apps.slack.commands.command import CommandBase - ai_command = Ai() assert isinstance(ai_command, CommandBase) diff --git a/backend/tests/apps/slack/events/message_posted_test.py b/backend/tests/apps/slack/events/message_posted_test.py index 2f431416f9..6cbb399cd2 100644 --- a/backend/tests/apps/slack/events/message_posted_test.py +++ b/backend/tests/apps/slack/events/message_posted_test.py @@ -108,7 +108,7 @@ def test_handle_event_conversation_not_found(self, message_handler): client.auth_test.return_value = {"user_id": "U987654"} with patch("apps.slack.events.message_posted.Conversation") as mock_conversation: - from apps.slack.models.conversation import Conversation + from apps.slack.models.conversation import Conversation # noqa: PLC0415 mock_conversation.DoesNotExist = Conversation.DoesNotExist mock_conversation.objects.get.side_effect = Conversation.DoesNotExist @@ -140,7 +140,7 @@ def test_handle_event_assistant_disabled(self, message_handler, conversation_moc client.auth_test.return_value = {"user_id": "U987654"} with patch("apps.slack.events.message_posted.Conversation") as mock_conversation: - from apps.slack.models.conversation import Conversation + from apps.slack.models.conversation import Conversation # noqa: PLC0415 mock_conversation.DoesNotExist = Conversation.DoesNotExist mock_conversation.objects.get.side_effect = Conversation.DoesNotExist @@ -263,7 +263,7 @@ def test_handle_event_member_not_found(self, message_handler, conversation_mock) ): mock_conversation.objects.get.return_value = conversation_mock - from apps.slack.models.member import Member + from apps.slack.models.member import Member # noqa: PLC0415 mock_member.DoesNotExist = Member.DoesNotExist mock_member.objects.get.side_effect = Member.DoesNotExist @@ -296,7 +296,7 @@ def test_handle_event_empty_text(self, message_handler): client.auth_test.return_value = {"user_id": "U987654"} with patch("apps.slack.events.message_posted.Conversation") as mock_conversation: - from apps.slack.models.conversation import Conversation + from apps.slack.models.conversation import Conversation # noqa: PLC0415 mock_conversation.DoesNotExist = Conversation.DoesNotExist mock_conversation.objects.get.side_effect = Conversation.DoesNotExist diff --git a/backend/tests/apps/slack/views_test.py b/backend/tests/apps/slack/views_test.py index d88bf62385..e83a33c122 100644 --- a/backend/tests/apps/slack/views_test.py +++ b/backend/tests/apps/slack/views_test.py @@ -11,7 +11,7 @@ def test_slack_request_handler_works(self, mocker): mock_handler_instance = mock_slack_request_handler.return_value mock_handler_instance.handle.return_value = "mock_http_response" - from apps.slack.views import slack_request_handler + from apps.slack.views import slack_request_handler # noqa: PLC0415 request = RequestFactory().post("/slack/events/") response = slack_request_handler(request)