# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import json from unittest.mock import AsyncMock, patch import pytest from mirage import MountMode, Workspace from mirage.resource.linear import LinearConfig, LinearResource MOCK_TEAMS = [{ "id": "TEAM1", "key": "ENG", "name": "Engineering", "description": "Core team", "timezone": "UTC", "updatedAt": "2026-04-05T00:00:00Z", "states": { "nodes": [{ "id": "STATE1", "name": "Todo", "type": "unstarted", }] }, }] MOCK_ISSUES = [{ "id": "ISSUE1", "identifier": "ENG-1", "title": "Fix login", "url": "https://linear.app/issue/ENG-1", "project": None, "state": { "id": "STATE1", "name": "Todo", }, }] MOCK_ISSUE_DETAIL = { "id": "ISSUE1", "identifier": "ENG-1", "title": "Fix login", "description": "Login page broken", "priority": 2, "url": "https://linear.app/issue/ENG-1", "createdAt": "2026-04-05T00:00:00Z", "updatedAt": "2026-04-05T00:00:00Z", "team": { "id": "TEAM1", "key": "ENG", "name": "Engineering", }, "state": { "id": "STATE1", "name": "Todo", }, "project": {}, "cycle": {}, "assignee": {}, "creator": { "id": "USER1", "name": "Alice", "email": "alice@example.com", }, "labels": { "nodes": [] }, } MOCK_COMMENTS = [{ "id": "C1", "body": "looks good", "createdAt": "2026-04-05T00:00:00Z", "updatedAt": "2026-04-05T00:00:00Z", "url": "https://linear.app/comment", "user": { "id": "USER1", "name": "Alice", "displayName": "Alice", "email": "alice@example.com", }, }] def _make_workspace(): config = LinearConfig(api_key="lin_api_test") resource = LinearResource(config=config) return Workspace({"/linear": resource}, mode=MountMode.READ) def _patches(): return ( patch("mirage.core.linear.readdir.list_teams", new_callable=AsyncMock, return_value=MOCK_TEAMS), patch("mirage.core.linear.readdir.list_team_issues", new_callable=AsyncMock, return_value=MOCK_ISSUES), patch("mirage.core.linear.readdir.list_team_projects", new_callable=AsyncMock, return_value=[]), patch("mirage.core.linear.readdir.list_team_members", new_callable=AsyncMock, return_value=[]), patch("mirage.core.linear.read.list_teams", new_callable=AsyncMock, return_value=MOCK_TEAMS), patch("mirage.core.linear.read.get_issue", new_callable=AsyncMock, return_value=MOCK_ISSUE_DETAIL), patch("mirage.core.linear.read.list_issue_comments", new_callable=AsyncMock, return_value=MOCK_COMMENTS), patch("mirage.core.linear.read.list_team_issues", new_callable=AsyncMock, return_value=MOCK_ISSUES), patch("mirage.core.linear.read.list_team_projects", new_callable=AsyncMock, return_value=[]), ) @pytest.fixture def ws(): return _make_workspace() def _all_patches(): patches = _patches() from contextlib import ExitStack stack = ExitStack() for p in patches: stack.enter_context(p) return stack @pytest.mark.asyncio async def test_ls_teams(ws): with _all_patches(): r = await ws.execute("ls /linear/teams/") assert "ENG__Engineering__TEAM1" in (await r.stdout_str()) assert r.exit_code == 0 @pytest.mark.asyncio async def test_ls_issues(ws): with _all_patches(): await ws.execute("ls /linear/teams/") r = await ws.execute("ls /linear/teams/ENG__Engineering__TEAM1/issues/" ) assert "ENG-1__ISSUE1" in (await r.stdout_str()) assert r.exit_code == 0 @pytest.mark.asyncio async def test_cat_issue(ws): with _all_patches(): await ws.execute("ls /linear/teams/") r = await ws.execute("cat /linear/teams/ENG__Engineering__TEAM1" "/issues/ENG-1__ISSUE1/issue.json") payload = json.loads(await r.stdout_str()) assert payload["issue_key"] == "ENG-1" assert payload["title"] == "Fix login" assert r.exit_code == 0 @pytest.mark.asyncio async def test_grep_issue(ws): with _all_patches(): await ws.execute("ls /linear/teams/") r = await ws.execute("grep login /linear/teams/ENG__Engineering__TEAM1" "/issues/ENG-1__ISSUE1/issue.json") assert "login" in (await r.stdout_str()).lower() assert r.exit_code == 0 @pytest.mark.asyncio async def test_head_issue(ws): with _all_patches(): await ws.execute("ls /linear/teams/") r = await ws.execute("head -n 3 /linear/teams/ENG__Engineering__TEAM1" "/issues/ENG-1__ISSUE1/issue.json") assert r.exit_code == 0 assert len((await r.stdout_str()).strip().splitlines()) <= 3 @pytest.mark.asyncio async def test_stat_issue(ws): with _all_patches(): await ws.execute("ls /linear/teams/") r = await ws.execute("stat /linear/teams/ENG__Engineering__TEAM1" "/issues/ENG-1__ISSUE1/issue.json") assert r.exit_code == 0