Skip to main content
Glama
test_models.py21.1 kB
"""Tests for Pydantic models.""" import pytest from src.models import ( BranchRestriction, BranchSummary, CommentSummary, CommitDetail, CommitStatus, CommitSummary, DeploymentSummary, DirectoryEntry, EnvironmentSummary, GroupPermission, PipelineDetail, PipelineStep, PipelineSummary, ProjectDetail, ProjectSummary, PullRequestDetail, PullRequestSummary, RepositoryDetail, RepositorySummary, TagSummary, UserPermission, WebhookSummary, truncate_timestamp, ) class TestTruncateTimestamp: def test_truncates_to_minutes(self): assert truncate_timestamp("2025-01-15T14:30:45.123456Z") == "2025-01-15T14:30" def test_handles_none(self): assert truncate_timestamp(None) is None def test_handles_empty_string(self): assert truncate_timestamp("") == "" def test_handles_short_string(self): assert truncate_timestamp("2025-01-15") == "2025-01-15" class TestRepositorySummary: def test_from_api_extracts_fields(self): raw = { "name": "my-repo", "full_name": "workspace/my-repo", "description": "A test repo", "is_private": True, "project": {"key": "PROJ"}, } model = RepositorySummary.from_api(raw) assert model.name == "my-repo" assert model.full_name == "workspace/my-repo" assert model.description == "A test repo" assert model.private is True assert model.project == "PROJ" def test_truncates_description(self): raw = { "name": "repo", "full_name": "ws/repo", "description": "x" * 200, "is_private": True, } model = RepositorySummary.from_api(raw) assert len(model.description) == 100 def test_handles_missing_project(self): raw = { "name": "repo", "full_name": "ws/repo", "project": None, "is_private": True, } model = RepositorySummary.from_api(raw) assert model.project is None def test_handles_none_description(self): raw = { "name": "repo", "full_name": "ws/repo", "description": None, "is_private": False, } model = RepositorySummary.from_api(raw) assert model.description == "" class TestRepositoryDetail: def test_from_api_extracts_all_fields(self): raw = { "name": "my-repo", "full_name": "ws/my-repo", "description": "Description", "is_private": True, "created_on": "2025-01-01T00:00:00Z", "updated_on": "2025-01-02T00:00:00Z", "mainbranch": {"name": "main"}, "project": {"key": "DS"}, } clone_urls = {"https": "https://example.com", "ssh": "git@example.com"} model = RepositoryDetail.from_api(raw, clone_urls) assert model.name == "my-repo" assert model.mainbranch == "main" assert model.clone_urls == clone_urls assert model.project == "DS" def test_truncates_timestamps(self): raw = { "name": "repo", "full_name": "ws/repo", "is_private": True, "created_on": "2025-01-01T14:30:45.123456Z", "updated_on": "2025-01-02T09:15:32.789012Z", } model = RepositoryDetail.from_api(raw, {}) assert model.created == "2025-01-01T14:30" assert model.updated == "2025-01-02T09:15" class TestPullRequestSummary: def test_from_api_extracts_fields(self): raw = { "id": 123, "title": "My PR", "state": "OPEN", "author": {"display_name": "John"}, "source": {"branch": {"name": "feature"}}, "destination": {"branch": {"name": "main"}}, } model = PullRequestSummary.from_api(raw, url="https://example.com/pr/123") assert model.id == 123 assert model.title == "My PR" assert model.author == "John" assert model.source_branch == "feature" assert model.destination_branch == "main" assert model.url == "https://example.com/pr/123" def test_handles_missing_author(self): raw = { "id": 1, "title": "PR", "state": "OPEN", "author": None, "source": {"branch": {"name": "f"}}, "destination": {"branch": {"name": "m"}}, } model = PullRequestSummary.from_api(raw) assert model.author is None class TestPullRequestDetail: def test_from_api_extracts_all_fields(self): raw = { "id": 456, "title": "Big PR", "description": "Long description", "state": "MERGED", "author": {"display_name": "Jane"}, "source": {"branch": {"name": "feature-x"}}, "destination": {"branch": {"name": "develop"}}, "created_on": "2025-01-01T00:00:00Z", "updated_on": "2025-01-02T00:00:00Z", "comment_count": 5, "task_count": 2, } model = PullRequestDetail.from_api(raw, url="https://pr.url") assert model.id == 456 assert model.description == "Long description" assert model.comment_count == 5 assert model.task_count == 2 def test_truncates_timestamps(self): raw = { "id": 1, "title": "PR", "state": "OPEN", "created_on": "2025-01-01T14:30:45.123456Z", "updated_on": "2025-01-02T09:15:32.789012Z", } model = PullRequestDetail.from_api(raw) assert model.created == "2025-01-01T14:30" assert model.updated == "2025-01-02T09:15" class TestCommitSummary: def test_truncates_hash_to_12(self): raw = { "hash": "abc123def456789012345678901234567890", "message": "Commit message", "author": {"raw": "John <john@example.com>"}, "date": "2025-01-01T00:00:00Z", } model = CommitSummary.from_api(raw) assert len(model.hash) == 12 assert model.hash == "abc123def456" def test_extracts_first_line_of_message(self): raw = { "hash": "abc123", "message": "First line\nSecond line\nThird line", "author": {"raw": "Author"}, } model = CommitSummary.from_api(raw) assert model.message == "First line" def test_handles_none_message(self): raw = {"hash": "abc123", "message": None, "author": {}} model = CommitSummary.from_api(raw) assert model.message == "" def test_truncates_date(self): raw = { "hash": "abc123", "message": "msg", "date": "2025-01-01T14:30:45.123456Z", } model = CommitSummary.from_api(raw) assert model.date == "2025-01-01T14:30" class TestCommitDetail: def test_from_api_extracts_fields(self): raw = { "hash": "abc123def456", "message": "Full message here", "author": { "raw": "John <john@example.com>", "user": {"display_name": "John Doe"}, }, "date": "2025-01-01T00:00:00Z", "parents": [{"hash": "parent123456"}], } model = CommitDetail.from_api(raw) assert model.hash == "abc123def456" assert model.author_raw == "John <john@example.com>" assert model.author_user == "John Doe" assert model.parents == ["parent123456"] def test_truncates_date(self): raw = { "hash": "abc123", "date": "2025-01-01T14:30:45.123456Z", } model = CommitDetail.from_api(raw) assert model.date == "2025-01-01T14:30" class TestBranchSummary: def test_from_api_extracts_fields(self): raw = { "name": "feature-branch", "target": { "hash": "abc123def456789", "message": "Latest commit\nMore details", "date": "2025-01-01T00:00:00Z", }, } model = BranchSummary.from_api(raw) assert model.name == "feature-branch" assert model.commit == "abc123def456" assert model.message == "Latest commit" def test_truncates_date(self): raw = { "name": "branch", "target": { "hash": "abc123", "date": "2025-01-01T14:30:45.123456Z", }, } model = BranchSummary.from_api(raw) assert model.date == "2025-01-01T14:30" class TestPipelineSummary: def test_from_api_extracts_fields(self): raw = { "uuid": "{pipe-uuid}", "build_number": 42, "state": {"name": "COMPLETED", "result": {"name": "SUCCESSFUL"}}, "target": {"ref_name": "main"}, "created_on": "2025-01-01T00:00:00Z", } model = PipelineSummary.from_api(raw) assert model.uuid == "{pipe-uuid}" assert model.build_number == 42 assert model.state == "COMPLETED" assert model.result == "SUCCESSFUL" assert model.branch == "main" def test_handles_no_result(self): raw = { "uuid": "{uuid}", "state": {"name": "IN_PROGRESS"}, "target": {"ref_name": "dev"}, } model = PipelineSummary.from_api(raw) assert model.state == "IN_PROGRESS" assert model.result is None def test_truncates_created(self): raw = { "uuid": "{uuid}", "state": {"name": "COMPLETED"}, "created_on": "2025-01-01T14:30:45.123456Z", } model = PipelineSummary.from_api(raw) assert model.created == "2025-01-01T14:30" class TestPipelineDetail: def test_from_api_extracts_all_fields(self): raw = { "uuid": "{uuid}", "build_number": 100, "state": {"name": "COMPLETED", "result": {"name": "FAILED"}}, "target": {"ref_name": "main"}, "created_on": "2025-01-01T00:00:00Z", "completed_on": "2025-01-01T01:00:00Z", "duration_in_seconds": 3600, } model = PipelineDetail.from_api(raw) assert model.duration_s == 3600 assert model.result == "FAILED" def test_truncates_timestamps(self): raw = { "uuid": "{uuid}", "state": {"name": "COMPLETED"}, "created_on": "2025-01-01T14:30:45.123456Z", "completed_on": "2025-01-01T15:45:32.789012Z", } model = PipelineDetail.from_api(raw) assert model.created == "2025-01-01T14:30" assert model.completed == "2025-01-01T15:45" class TestPipelineStep: def test_from_api_extracts_fields(self): raw = { "uuid": "{step-uuid}", "name": "Build", "state": {"name": "COMPLETED", "result": {"name": "SUCCESSFUL"}}, } model = PipelineStep.from_api(raw) assert model.uuid == "{step-uuid}" assert model.name == "Build" assert model.state == "COMPLETED" assert model.result == "SUCCESSFUL" class TestTagSummary: def test_from_api_extracts_fields(self): raw = { "name": "v1.0.0", "target": {"hash": "abc123def456789"}, "message": "Release tag", "tagger": {"raw": "John <john@example.com>"}, "date": "2025-01-01T00:00:00Z", } model = TagSummary.from_api(raw) assert model.name == "v1.0.0" assert model.target == "abc123def456" assert model.message == "Release tag" assert model.tagger == "John <john@example.com>" def test_handles_none_target(self): raw = {"name": "tag", "target": None, "tagger": None} model = TagSummary.from_api(raw) assert model.target == "" assert model.tagger is None def test_truncates_date(self): raw = { "name": "v1.0.0", "target": {"hash": "abc123"}, "date": "2025-01-01T14:30:45.123456Z", } model = TagSummary.from_api(raw) assert model.date == "2025-01-01T14:30" def test_lightweight_tag_excludes_none(self): """Lightweight tags (no annotation) exclude empty fields from output.""" raw = { "name": "v1.0.0", "target": {"hash": "abc123def456789"}, "message": None, "tagger": None, "date": None, } model = TagSummary.from_api(raw) dumped = model.model_dump() assert dumped == {"name": "v1.0.0", "target": "abc123def456"} assert "message" not in dumped assert "tagger" not in dumped assert "date" not in dumped def test_annotated_tag_includes_all(self): """Annotated tags include message, tagger, and date.""" raw = { "name": "v1.0.0", "target": {"hash": "abc123def456789"}, "message": "Release", "tagger": {"raw": "John <john@example.com>"}, "date": "2025-01-01T14:30:00Z", } model = TagSummary.from_api(raw) dumped = model.model_dump() assert dumped["message"] == "Release" assert dumped["tagger"] == "John <john@example.com>" assert dumped["date"] == "2025-01-01T14:30" class TestProjectSummary: def test_from_api_extracts_fields(self): raw = { "key": "PROJ", "name": "My Project", "description": "Project description", "is_private": True, "created_on": "2025-01-01T00:00:00Z", } model = ProjectSummary.from_api(raw) assert model.key == "PROJ" assert model.name == "My Project" assert model.description == "Project description" assert model.private is True def test_truncates_description(self): raw = { "key": "P", "name": "P", "description": "x" * 200, "is_private": True, } model = ProjectSummary.from_api(raw) assert len(model.description) == 100 def test_truncates_created(self): raw = { "key": "P", "name": "P", "is_private": True, "created_on": "2025-01-01T14:30:45.123456Z", } model = ProjectSummary.from_api(raw) assert model.created == "2025-01-01T14:30" class TestProjectDetail: def test_truncates_timestamps(self): raw = { "key": "P", "name": "P", "is_private": True, "created_on": "2025-01-01T14:30:45.123456Z", "updated_on": "2025-01-02T09:15:32.789012Z", } model = ProjectDetail.from_api(raw) assert model.created == "2025-01-01T14:30" assert model.updated == "2025-01-02T09:15" class TestWebhookSummary: def test_from_api_extracts_fields(self): raw = { "uuid": "{webhook-uuid}", "url": "https://example.com/webhook", "description": "My webhook", "events": ["repo:push", "pullrequest:created"], "active": True, "created_at": "2025-01-01T00:00:00Z", } model = WebhookSummary.from_api(raw) assert model.uuid == "{webhook-uuid}" assert model.url == "https://example.com/webhook" assert model.events == ["repo:push", "pullrequest:created"] def test_truncates_created(self): raw = { "uuid": "{uuid}", "url": "https://example.com", "created_at": "2025-01-01T14:30:45.123456Z", } model = WebhookSummary.from_api(raw) assert model.created == "2025-01-01T14:30" class TestEnvironmentSummary: def test_from_api_extracts_fields(self): raw = { "uuid": "{env-uuid}", "name": "production", "environment_type": {"name": "Production"}, "rank": 1, } model = EnvironmentSummary.from_api(raw) assert model.uuid == "{env-uuid}" assert model.name == "production" assert model.environment_type == "Production" assert model.rank == 1 class TestDeploymentSummary: def test_from_api_extracts_fields(self): raw = { "uuid": "{deploy-uuid}", "state": { "name": "COMPLETED", "started_on": "2025-01-01T00:00:00Z", "completed_on": "2025-01-01T01:00:00Z", }, "commit": {"hash": "abc123def456789"}, "release": {"pipeline": {"uuid": "{pipe-uuid}"}}, } model = DeploymentSummary.from_api(raw) assert model.uuid == "{deploy-uuid}" assert model.state == "COMPLETED" assert model.commit == "abc123def456" assert model.pipeline_uuid == "{pipe-uuid}" def test_truncates_timestamps(self): raw = { "uuid": "{uuid}", "state": { "name": "COMPLETED", "started_on": "2025-01-01T14:30:45.123456Z", "completed_on": "2025-01-01T15:45:32.789012Z", }, } model = DeploymentSummary.from_api(raw) assert model.started == "2025-01-01T14:30" assert model.completed == "2025-01-01T15:45" class TestCommentSummary: def test_from_api_extracts_fields(self): raw = { "id": 123, "user": {"display_name": "Commenter"}, "content": {"raw": "Great PR!"}, "created_on": "2025-01-01T00:00:00Z", "inline": {"path": "src/main.py", "to": 42}, } model = CommentSummary.from_api(raw) assert model.id == 123 assert model.author == "Commenter" assert model.content == "Great PR!" assert model.inline == {"path": "src/main.py", "to": 42} def test_truncates_timestamps(self): raw = { "id": 1, "created_on": "2025-01-01T14:30:45.123456Z", "updated_on": "2025-01-02T09:15:32.789012Z", } model = CommentSummary.from_api(raw) assert model.created == "2025-01-01T14:30" assert model.updated == "2025-01-02T09:15" class TestCommitStatus: def test_from_api_extracts_fields(self): raw = { "key": "ci-build", "name": "CI Build", "state": "SUCCESSFUL", "description": "All tests passed", "url": "https://ci.example.com/build/123", "created_on": "2025-01-01T00:00:00Z", } model = CommitStatus.from_api(raw) assert model.key == "ci-build" assert model.state == "SUCCESSFUL" assert model.url == "https://ci.example.com/build/123" def test_truncates_timestamps(self): raw = { "key": "ci", "state": "SUCCESSFUL", "created_on": "2025-01-01T14:30:45.123456Z", "updated_on": "2025-01-02T09:15:32.789012Z", } model = CommitStatus.from_api(raw) assert model.created == "2025-01-01T14:30" assert model.updated == "2025-01-02T09:15" class TestBranchRestriction: def test_from_api_extracts_fields(self): raw = { "id": 1, "kind": "require_approvals_to_merge", "pattern": "main", "branch_match_kind": "glob", "value": 2, "users": [{"display_name": "Admin"}], "groups": [{"name": "Developers"}], } model = BranchRestriction.from_api(raw) assert model.id == 1 assert model.kind == "require_approvals_to_merge" assert model.pattern == "main" assert model.value == 2 assert model.users == ["Admin"] assert model.groups == ["Developers"] class TestUserPermission: def test_from_api_extracts_fields(self): raw = { "user": {"display_name": "John", "account_id": "123abc"}, "permission": "write", } model = UserPermission.from_api(raw) assert model.user == "John" assert model.account_id == "123abc" assert model.permission == "write" class TestGroupPermission: def test_from_api_extracts_fields(self): raw = { "group": {"name": "Developers", "slug": "developers"}, "permission": "admin", } model = GroupPermission.from_api(raw) assert model.group == "Developers" assert model.slug == "developers" assert model.permission == "admin" class TestDirectoryEntry: def test_from_api_extracts_fields(self): raw = {"path": "src/main.py", "type": "commit_file", "size": 1024} model = DirectoryEntry.from_api(raw) assert model.path == "src/main.py" assert model.type == "commit_file" assert model.size == 1024

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/JaviMaligno/mcp-server-bitbucket'

If you have feedback or need assistance with the MCP directory API, please join our Discord server