fix: Eliminate all 111 test warnings by fixing root causes
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
- Replace deprecated datetime.utcnow() with datetime.now(timezone.utc) across all domain models, services, infrastructure, and test files - Add missing timezone imports to all affected files - Fix pytest.ini configuration format from [tool:pytest] to [pytest] - Remove warning suppressions to expose actual issues - Ensure proper pytest marker registration for smoke tests Results: - 305 passed, 2 skipped, 0 warnings (down from 111 warnings) - All functionality preserved with modern datetime API usage - Improved code quality by addressing root causes vs suppression 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ Contains core business entities and value objects for issue management.
|
|||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from .exceptions import IssueStateError
|
from .exceptions import IssueStateError
|
||||||
@@ -88,7 +88,7 @@ class Issue:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.state = IssueState.CLOSED
|
self.state = IssueState.CLOSED
|
||||||
self.closed_at = datetime.utcnow()
|
self.closed_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
def reopen(self) -> None:
|
def reopen(self) -> None:
|
||||||
"""Reopen the issue - domain business rule."""
|
"""Reopen the issue - domain business rule."""
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ Contains business logic for issue-related operations.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Dict, Any, List
|
from typing import Dict, Any, List
|
||||||
|
from datetime import datetime, timezone
|
||||||
from .models import Issue, IssueState, LabelCategories
|
from .models import Issue, IssueState, LabelCategories
|
||||||
from .exceptions import IssueValidationError
|
from .exceptions import IssueValidationError
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ class IssueStatusService:
|
|||||||
def calculate_issue_age_days(self, issue: Issue) -> int:
|
def calculate_issue_age_days(self, issue: Issue) -> int:
|
||||||
"""Calculate issue age in days."""
|
"""Calculate issue age in days."""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
return (datetime.utcnow() - issue.created_at).days
|
return (datetime.now(timezone.utc) - issue.created_at).days
|
||||||
|
|
||||||
def is_stale_issue(self, issue: Issue, stale_threshold_days: int = 30) -> bool:
|
def is_stale_issue(self, issue: Issue, stale_threshold_days: int = 30) -> bool:
|
||||||
"""Determine if issue is considered stale based on business rules."""
|
"""Determine if issue is considered stale based on business rules."""
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Contains core business entities and value objects for project management.
|
|||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List, Optional, Dict, Any
|
from typing import List, Optional, Dict, Any
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from .exceptions import MilestoneError
|
from .exceptions import MilestoneError
|
||||||
@@ -47,7 +47,7 @@ class Milestone:
|
|||||||
"""Check if milestone is overdue."""
|
"""Check if milestone is overdue."""
|
||||||
if not self.due_date or self.state == "closed":
|
if not self.due_date or self.state == "closed":
|
||||||
return False
|
return False
|
||||||
return datetime.utcnow() > self.due_date
|
return datetime.now(timezone.utc) > self.due_date
|
||||||
|
|
||||||
def is_completed(self) -> bool:
|
def is_completed(self) -> bool:
|
||||||
"""Check if milestone is completed."""
|
"""Check if milestone is completed."""
|
||||||
@@ -128,7 +128,7 @@ class Project:
|
|||||||
return # Already archived
|
return # Already archived
|
||||||
|
|
||||||
self.state = ProjectState.ARCHIVED
|
self.state = ProjectState.ARCHIVED
|
||||||
self.archived_at = datetime.utcnow()
|
self.archived_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
def activate(self) -> None:
|
def activate(self) -> None:
|
||||||
"""Activate the project."""
|
"""Activate the project."""
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Contains business logic for project-related operations.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Dict, Any, List
|
from typing import Dict, Any, List
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from .models import Project, Milestone, ProjectState
|
from .models import Project, Milestone, ProjectState
|
||||||
from .exceptions import ProjectValidationError
|
from .exceptions import ProjectValidationError
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ class ProjectManagementService:
|
|||||||
def calculate_project_velocity(self, project: Project, days_back: int = 30) -> float:
|
def calculate_project_velocity(self, project: Project, days_back: int = 30) -> float:
|
||||||
"""Calculate project velocity based on recent milestone completions."""
|
"""Calculate project velocity based on recent milestone completions."""
|
||||||
completed_milestones = project.get_completed_milestones()
|
completed_milestones = project.get_completed_milestones()
|
||||||
cutoff_date = datetime.utcnow() - timedelta(days=days_back)
|
cutoff_date = datetime.now(timezone.utc) - timedelta(days=days_back)
|
||||||
|
|
||||||
# Count milestones completed in the specified period
|
# Count milestones completed in the specified period
|
||||||
# Note: This would need milestone completion dates in a real implementation
|
# Note: This would need milestone completion dates in a real implementation
|
||||||
@@ -132,7 +132,7 @@ class ProjectManagementService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Business rule: Due date cannot be in the past
|
# Business rule: Due date cannot be in the past
|
||||||
if due_date and due_date < datetime.utcnow():
|
if due_date and due_date < datetime.now(timezone.utc):
|
||||||
raise ProjectValidationError(
|
raise ProjectValidationError(
|
||||||
"Milestone due date cannot be in the past",
|
"Milestone due date cannot be in the past",
|
||||||
field="due_date",
|
field="due_date",
|
||||||
@@ -148,7 +148,7 @@ class ProjectManagementService:
|
|||||||
|
|
||||||
# Higher priority for milestones with due dates
|
# Higher priority for milestones with due dates
|
||||||
if milestone.due_date:
|
if milestone.due_date:
|
||||||
days_until_due = (milestone.due_date - datetime.utcnow()).days
|
days_until_due = (milestone.due_date - datetime.now(timezone.utc)).days
|
||||||
if days_until_due <= 7:
|
if days_until_due <= 7:
|
||||||
priority_score += 50 # Very urgent
|
priority_score += 50 # Very urgent
|
||||||
elif days_until_due <= 30:
|
elif days_until_due <= 30:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ with context-aware logging capabilities.
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, Any, Optional
|
from typing import Dict, Any, Optional
|
||||||
|
|
||||||
from .context import get_current_log_context
|
from .context import get_current_log_context
|
||||||
@@ -54,7 +54,7 @@ class BaseFormatter(logging.Formatter):
|
|||||||
|
|
||||||
def _add_standard_fields(self, record: logging.LogRecord) -> None:
|
def _add_standard_fields(self, record: logging.LogRecord) -> None:
|
||||||
"""Add standard fields to log record."""
|
"""Add standard fields to log record."""
|
||||||
record.timestamp = datetime.utcnow().isoformat() + 'Z'
|
record.timestamp = datetime.now(timezone.utc).isoformat() + 'Z'
|
||||||
record.logger_name = record.name
|
record.logger_name = record.name
|
||||||
record.level_name = record.levelname
|
record.level_name = record.levelname
|
||||||
record.thread_name = record.threadName
|
record.thread_name = record.threadName
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import uuid
|
|||||||
from infrastructure.logging import get_logger
|
from infrastructure.logging import get_logger
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
from infrastructure.repositories.interfaces import WorkspaceRepository
|
from infrastructure.repositories.interfaces import WorkspaceRepository
|
||||||
from infrastructure.exceptions import (
|
from infrastructure.exceptions import (
|
||||||
@@ -78,7 +78,7 @@ class FilesystemWorkspaceRepository(WorkspaceRepository):
|
|||||||
# Create workspace metadata file
|
# Create workspace metadata file
|
||||||
metadata = {
|
metadata = {
|
||||||
"id": workspace_id,
|
"id": workspace_id,
|
||||||
"created_at": datetime.utcnow().isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"type": "markitect_workspace"
|
"type": "markitect_workspace"
|
||||||
}
|
}
|
||||||
@@ -348,7 +348,7 @@ class FilesystemWorkspaceRepository(WorkspaceRepository):
|
|||||||
logger.info(f"Starting cleanup of workspaces older than {days_threshold} days")
|
logger.info(f"Starting cleanup of workspaces older than {days_threshold} days")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cutoff_date = datetime.utcnow() - timedelta(days=days_threshold)
|
cutoff_date = datetime.now(timezone.utc) - timedelta(days=days_threshold)
|
||||||
deleted_count = 0
|
deleted_count = 0
|
||||||
|
|
||||||
if not self.base_path.exists():
|
if not self.base_path.exists():
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
from infrastructure.logging import get_logger
|
from infrastructure.logging import get_logger
|
||||||
from typing import List, Optional, Dict, Any
|
from typing import List, Optional, Dict, Any
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
@@ -563,8 +563,8 @@ class GiteaProjectRepository(ProjectRepository):
|
|||||||
def _map_api_project_to_domain(self, api_data: Dict[str, Any]) -> Project:
|
def _map_api_project_to_domain(self, api_data: Dict[str, Any]) -> Project:
|
||||||
"""Map Gitea API project data to domain Project object."""
|
"""Map Gitea API project data to domain Project object."""
|
||||||
# For now, create a basic project since Gitea projects API might be limited
|
# For now, create a basic project since Gitea projects API might be limited
|
||||||
created_at = datetime.fromisoformat(api_data.get("created_at", datetime.utcnow().isoformat()).replace("Z", "+00:00"))
|
created_at = datetime.fromisoformat(api_data.get("created_at", datetime.now(timezone.utc).isoformat()).replace("Z", "+00:00"))
|
||||||
updated_at = datetime.fromisoformat(api_data.get("updated_at", datetime.utcnow().isoformat()).replace("Z", "+00:00"))
|
updated_at = datetime.fromisoformat(api_data.get("updated_at", datetime.now(timezone.utc).isoformat()).replace("Z", "+00:00"))
|
||||||
|
|
||||||
return Project(
|
return Project(
|
||||||
id=str(api_data.get("id", 0)),
|
id=str(api_data.get("id", 0)),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import json
|
|||||||
import uuid
|
import uuid
|
||||||
from infrastructure.logging import get_logger
|
from infrastructure.logging import get_logger
|
||||||
from typing import List, Optional, Dict, Any
|
from typing import List, Optional, Dict, Any
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ class SqliteDocumentRepository(DocumentRepository):
|
|||||||
# Store document
|
# Store document
|
||||||
ast_json = json.dumps(ast)
|
ast_json = json.dumps(ast)
|
||||||
file_size = len(content)
|
file_size = len(content)
|
||||||
now = datetime.utcnow().isoformat()
|
now = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
conn.execute("""
|
conn.execute("""
|
||||||
INSERT INTO documents (id, filename, content, ast_json, content_hash, file_size, created_at, updated_at)
|
INSERT INTO documents (id, filename, content, ast_json, content_hash, file_size, created_at, updated_at)
|
||||||
@@ -337,7 +337,7 @@ class SqliteDocumentRepository(DocumentRepository):
|
|||||||
|
|
||||||
# Add updated timestamp
|
# Add updated timestamp
|
||||||
updates.append("updated_at = ?")
|
updates.append("updated_at = ?")
|
||||||
params.append(datetime.utcnow().isoformat())
|
params.append(datetime.now(timezone.utc).isoformat())
|
||||||
|
|
||||||
# Add document_id for WHERE clause
|
# Add document_id for WHERE clause
|
||||||
params.append(document_id)
|
params.append(document_id)
|
||||||
@@ -541,7 +541,7 @@ class SqliteCacheRepository(CacheRepository):
|
|||||||
expires_at = None
|
expires_at = None
|
||||||
if ttl:
|
if ttl:
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
expires_at = (datetime.utcnow() + timedelta(seconds=ttl)).isoformat()
|
expires_at = (datetime.now(timezone.utc) + timedelta(seconds=ttl)).isoformat()
|
||||||
|
|
||||||
# Serialize value
|
# Serialize value
|
||||||
value_json = json.dumps(value)
|
value_json = json.dumps(value)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Tests pure business logic with no external dependencies.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
from domain.issues.models import Issue, Label, IssueState, LabelCategories
|
from domain.issues.models import Issue, Label, IssueState, LabelCategories
|
||||||
from domain.issues.exceptions import IssueStateError
|
from domain.issues.exceptions import IssueStateError
|
||||||
@@ -71,8 +71,8 @@ class TestIssue:
|
|||||||
|
|
||||||
def test_issue_creation_with_valid_data(self):
|
def test_issue_creation_with_valid_data(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
created_at = datetime.utcnow()
|
created_at = datetime.now(timezone.utc)
|
||||||
updated_at = datetime.utcnow()
|
updated_at = datetime.now(timezone.utc)
|
||||||
labels = [Label("bug"), Label("priority:high")]
|
labels = [Label("bug"), Label("priority:high")]
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -107,8 +107,8 @@ class TestIssue:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=labels,
|
labels=labels,
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -128,8 +128,8 @@ class TestIssue:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[],
|
labels=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -147,9 +147,9 @@ class TestIssue:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.CLOSED,
|
state=IssueState.CLOSED,
|
||||||
labels=[],
|
labels=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow(),
|
updated_at=datetime.now(timezone.utc),
|
||||||
closed_at=datetime.utcnow()
|
closed_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act & Assert
|
# Act & Assert
|
||||||
@@ -167,9 +167,9 @@ class TestIssue:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.CLOSED,
|
state=IssueState.CLOSED,
|
||||||
labels=[],
|
labels=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow(),
|
updated_at=datetime.now(timezone.utc),
|
||||||
closed_at=datetime.utcnow()
|
closed_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -186,8 +186,8 @@ class TestIssue:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[],
|
labels=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act & Assert
|
# Act & Assert
|
||||||
@@ -203,8 +203,8 @@ class TestIssue:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[Label("bug")],
|
labels=[Label("bug")],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
new_label = Label("priority:high")
|
new_label = Label("priority:high")
|
||||||
|
|
||||||
@@ -223,8 +223,8 @@ class TestIssue:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[label],
|
labels=[label],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -240,8 +240,8 @@ class TestIssue:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[Label("bug"), Label("priority:high")],
|
labels=[Label("bug"), Label("priority:high")],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -258,8 +258,8 @@ class TestIssue:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[Label("bug"), Label("priority:high")],
|
labels=[Label("bug"), Label("priority:high")],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act & Assert
|
# Act & Assert
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Tests business logic in issue services with no external dependencies.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
from domain.issues.models import Issue, Label, IssueState
|
from domain.issues.models import Issue, Label, IssueState
|
||||||
from domain.issues.services import IssueStatusService, IssueValidationService
|
from domain.issues.services import IssueStatusService, IssueValidationService
|
||||||
@@ -26,8 +26,8 @@ class TestIssueStatusService:
|
|||||||
title="Closed Issue",
|
title="Closed Issue",
|
||||||
state=IssueState.CLOSED,
|
state=IssueState.CLOSED,
|
||||||
labels=[],
|
labels=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
project_info = {"kanban_columns": ["Todo", "In Progress", "Review", "Done"]}
|
project_info = {"kanban_columns": ["Todo", "In Progress", "Review", "Done"]}
|
||||||
|
|
||||||
@@ -50,8 +50,8 @@ class TestIssueStatusService:
|
|||||||
title="Test Issue",
|
title="Test Issue",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[Label(status_label)],
|
labels=[Label(status_label)],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
project_info = {"kanban_columns": ["Todo", "In Progress", "Review", "Blocked", "Ready", "Done"]}
|
project_info = {"kanban_columns": ["Todo", "In Progress", "Review", "Blocked", "Ready", "Done"]}
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ class TestIssueStatusService:
|
|||||||
title="New Issue",
|
title="New Issue",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[Label("bug")], # No status label
|
labels=[Label("bug")], # No status label
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
project_info = {"kanban_columns": ["Todo", "In Progress", "Done"]}
|
project_info = {"kanban_columns": ["Todo", "In Progress", "Done"]}
|
||||||
|
|
||||||
@@ -92,8 +92,8 @@ class TestIssueStatusService:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[Label(priority_label)],
|
labels=[Label(priority_label)],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -110,8 +110,8 @@ class TestIssueStatusService:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[Label("bug")], # No priority label
|
labels=[Label("bug")], # No priority label
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -128,8 +128,8 @@ class TestIssueStatusService:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[Label("status:in-progress")],
|
labels=[Label("status:in-progress")],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -143,14 +143,14 @@ class TestIssueStatusService:
|
|||||||
|
|
||||||
def test_extract_state_info_for_closed_issue(self, service):
|
def test_extract_state_info_for_closed_issue(self, service):
|
||||||
# Arrange
|
# Arrange
|
||||||
closed_at = datetime.utcnow()
|
closed_at = datetime.now(timezone.utc)
|
||||||
issue = Issue(
|
issue = Issue(
|
||||||
number=1,
|
number=1,
|
||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.CLOSED,
|
state=IssueState.CLOSED,
|
||||||
labels=[],
|
labels=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow(),
|
updated_at=datetime.now(timezone.utc),
|
||||||
closed_at=closed_at
|
closed_at=closed_at
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -164,14 +164,14 @@ class TestIssueStatusService:
|
|||||||
|
|
||||||
def test_calculate_issue_age_days(self, service):
|
def test_calculate_issue_age_days(self, service):
|
||||||
# Arrange
|
# Arrange
|
||||||
created_at = datetime.utcnow() - timedelta(days=5)
|
created_at = datetime.now(timezone.utc) - timedelta(days=5)
|
||||||
issue = Issue(
|
issue = Issue(
|
||||||
number=1,
|
number=1,
|
||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[],
|
labels=[],
|
||||||
created_at=created_at,
|
created_at=created_at,
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -182,14 +182,14 @@ class TestIssueStatusService:
|
|||||||
|
|
||||||
def test_is_stale_issue_with_old_open_issue(self, service):
|
def test_is_stale_issue_with_old_open_issue(self, service):
|
||||||
# Arrange
|
# Arrange
|
||||||
created_at = datetime.utcnow() - timedelta(days=45)
|
created_at = datetime.now(timezone.utc) - timedelta(days=45)
|
||||||
issue = Issue(
|
issue = Issue(
|
||||||
number=1,
|
number=1,
|
||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[],
|
labels=[],
|
||||||
created_at=created_at,
|
created_at=created_at,
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -200,14 +200,14 @@ class TestIssueStatusService:
|
|||||||
|
|
||||||
def test_is_stale_issue_with_recent_open_issue(self, service):
|
def test_is_stale_issue_with_recent_open_issue(self, service):
|
||||||
# Arrange
|
# Arrange
|
||||||
created_at = datetime.utcnow() - timedelta(days=15)
|
created_at = datetime.now(timezone.utc) - timedelta(days=15)
|
||||||
issue = Issue(
|
issue = Issue(
|
||||||
number=1,
|
number=1,
|
||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[],
|
labels=[],
|
||||||
created_at=created_at,
|
created_at=created_at,
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -218,15 +218,15 @@ class TestIssueStatusService:
|
|||||||
|
|
||||||
def test_is_stale_issue_with_closed_issue_never_stale(self, service):
|
def test_is_stale_issue_with_closed_issue_never_stale(self, service):
|
||||||
# Arrange
|
# Arrange
|
||||||
created_at = datetime.utcnow() - timedelta(days=100)
|
created_at = datetime.now(timezone.utc) - timedelta(days=100)
|
||||||
issue = Issue(
|
issue = Issue(
|
||||||
number=1,
|
number=1,
|
||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.CLOSED,
|
state=IssueState.CLOSED,
|
||||||
labels=[],
|
labels=[],
|
||||||
created_at=created_at,
|
created_at=created_at,
|
||||||
updated_at=datetime.utcnow(),
|
updated_at=datetime.now(timezone.utc),
|
||||||
closed_at=datetime.utcnow()
|
closed_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -322,8 +322,8 @@ class TestIssueValidationService:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[Label("bug")],
|
labels=[Label("bug")],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
new_label = "enhancement"
|
new_label = "enhancement"
|
||||||
|
|
||||||
@@ -337,8 +337,8 @@ class TestIssueValidationService:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[Label("bug")],
|
labels=[Label("bug")],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
new_label = "bug"
|
new_label = "bug"
|
||||||
|
|
||||||
@@ -355,8 +355,8 @@ class TestIssueValidationService:
|
|||||||
title="Test",
|
title="Test",
|
||||||
state=IssueState.OPEN,
|
state=IssueState.OPEN,
|
||||||
labels=[Label("priority:high")],
|
labels=[Label("priority:high")],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
new_label = "priority:low"
|
new_label = "priority:low"
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Tests pure business logic with no external dependencies.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
from domain.projects.models import Project, Milestone, ProjectState
|
from domain.projects.models import Project, Milestone, ProjectState
|
||||||
from domain.projects.exceptions import MilestoneError
|
from domain.projects.exceptions import MilestoneError
|
||||||
@@ -16,7 +16,7 @@ class TestMilestone:
|
|||||||
|
|
||||||
def test_milestone_creation(self):
|
def test_milestone_creation(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
due_date = datetime.utcnow() + timedelta(days=30)
|
due_date = datetime.now(timezone.utc) + timedelta(days=30)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
milestone = Milestone(
|
milestone = Milestone(
|
||||||
@@ -91,7 +91,7 @@ class TestMilestone:
|
|||||||
|
|
||||||
def test_is_overdue_with_past_due_date(self):
|
def test_is_overdue_with_past_due_date(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
past_date = datetime.utcnow() - timedelta(days=1)
|
past_date = datetime.now(timezone.utc) - timedelta(days=1)
|
||||||
milestone = Milestone(
|
milestone = Milestone(
|
||||||
id=1,
|
id=1,
|
||||||
title="Test",
|
title="Test",
|
||||||
@@ -107,7 +107,7 @@ class TestMilestone:
|
|||||||
|
|
||||||
def test_is_overdue_with_future_due_date(self):
|
def test_is_overdue_with_future_due_date(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
future_date = datetime.utcnow() + timedelta(days=1)
|
future_date = datetime.now(timezone.utc) + timedelta(days=1)
|
||||||
milestone = Milestone(
|
milestone = Milestone(
|
||||||
id=1,
|
id=1,
|
||||||
title="Test",
|
title="Test",
|
||||||
@@ -138,7 +138,7 @@ class TestMilestone:
|
|||||||
|
|
||||||
def test_is_overdue_with_closed_milestone(self):
|
def test_is_overdue_with_closed_milestone(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
past_date = datetime.utcnow() - timedelta(days=1)
|
past_date = datetime.now(timezone.utc) - timedelta(days=1)
|
||||||
milestone = Milestone(
|
milestone = Milestone(
|
||||||
id=1,
|
id=1,
|
||||||
title="Test",
|
title="Test",
|
||||||
@@ -298,8 +298,8 @@ class TestProject:
|
|||||||
|
|
||||||
def test_project_creation(self):
|
def test_project_creation(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
created_at = datetime.utcnow()
|
created_at = datetime.now(timezone.utc)
|
||||||
updated_at = datetime.utcnow()
|
updated_at = datetime.now(timezone.utc)
|
||||||
milestones = [
|
milestones = [
|
||||||
Milestone(1, "M1", None, None, "open", 2, 1),
|
Milestone(1, "M1", None, None, "open", 2, 1),
|
||||||
Milestone(2, "M2", None, None, "closed", 0, 3)
|
Milestone(2, "M2", None, None, "closed", 0, 3)
|
||||||
@@ -336,8 +336,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=milestones,
|
milestones=milestones,
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -360,8 +360,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=milestones,
|
milestones=milestones,
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -372,8 +372,8 @@ class TestProject:
|
|||||||
|
|
||||||
def test_get_overdue_milestones(self):
|
def test_get_overdue_milestones(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
past_date = datetime.utcnow() - timedelta(days=1)
|
past_date = datetime.now(timezone.utc) - timedelta(days=1)
|
||||||
future_date = datetime.utcnow() + timedelta(days=1)
|
future_date = datetime.now(timezone.utc) + timedelta(days=1)
|
||||||
milestones = [
|
milestones = [
|
||||||
Milestone(1, "M1", None, past_date, "open", 2, 1), # Overdue
|
Milestone(1, "M1", None, past_date, "open", 2, 1), # Overdue
|
||||||
Milestone(2, "M2", None, future_date, "open", 1, 0), # Not overdue
|
Milestone(2, "M2", None, future_date, "open", 1, 0), # Not overdue
|
||||||
@@ -385,8 +385,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=milestones,
|
milestones=milestones,
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -408,8 +408,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=milestones,
|
milestones=milestones,
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -426,8 +426,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=[],
|
milestones=[],
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -448,8 +448,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=milestones,
|
milestones=milestones,
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act & Assert
|
# Act & Assert
|
||||||
@@ -465,8 +465,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=[],
|
milestones=[],
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -484,9 +484,9 @@ class TestProject:
|
|||||||
state=ProjectState.ARCHIVED,
|
state=ProjectState.ARCHIVED,
|
||||||
milestones=[],
|
milestones=[],
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow(),
|
updated_at=datetime.now(timezone.utc),
|
||||||
archived_at=datetime.utcnow()
|
archived_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -504,8 +504,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=[],
|
milestones=[],
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
milestone = Milestone(1, "New Milestone", None, None, "open", 0, 0)
|
milestone = Milestone(1, "New Milestone", None, None, "open", 0, 0)
|
||||||
|
|
||||||
@@ -526,8 +526,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=[milestone1],
|
milestones=[milestone1],
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act & Assert
|
# Act & Assert
|
||||||
@@ -543,8 +543,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=[milestone],
|
milestones=[milestone],
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -561,8 +561,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=[],
|
milestones=[],
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act & Assert
|
# Act & Assert
|
||||||
@@ -578,8 +578,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=[milestone],
|
milestones=[milestone],
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
@@ -596,8 +596,8 @@ class TestProject:
|
|||||||
state=ProjectState.ACTIVE,
|
state=ProjectState.ACTIVE,
|
||||||
milestones=[],
|
milestones=[],
|
||||||
kanban_columns=[],
|
kanban_columns=[],
|
||||||
created_at=datetime.utcnow(),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.utcnow()
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
|
|||||||
Reference in New Issue
Block a user