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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion app/lib/linear_cli/linear/comment.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ defmodule LinearCli.Linear.Comment do
attribute :body, :string, public?: true
attribute :url, :string, public?: true
attribute :user, :term, public?: true
attribute :created_at, :string, public?: true
attribute :updated_at, :string, public?: true
end

@doc "GraphQL field selection for a comment's own fields (Ruby: Comment::Base)."
Expand All @@ -37,7 +39,9 @@ defmodule LinearCli.Linear.Comment do
id: map["id"],
body: map["body"],
url: map["url"],
user: map["user"] && LinearCli.Linear.User.from_map(map["user"])
user: map["user"] && LinearCli.Linear.User.from_map(map["user"]),
created_at: map["createdAt"],
updated_at: map["updatedAt"]
)
end
end
Expand Down
12 changes: 11 additions & 1 deletion app/lib/linear_cli/linear/issue.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ defmodule LinearCli.Linear.Issue do
attribute :branch_name, :string, public?: true
attribute :description, :string, public?: true
attribute :url, :string, public?: true
attribute :priority, :float, public?: true
attribute :priority_label, :string, public?: true
attribute :priority_sort_order, :float, public?: true
attribute :created_at, :string, public?: true
attribute :updated_at, :string, public?: true
attribute :assignee, :term, public?: true
attribute :state, :term, public?: true
attribute :team, :term, public?: true
Expand All @@ -80,7 +85,7 @@ defmodule LinearCli.Linear.Issue do
attribute :inverse_relations, {:array, :term}, public?: true, default: []
end

@issue_fields "id identifier title branchName description url createdAt updatedAt"
@issue_fields "id identifier title branchName description url priority priorityLabel prioritySortOrder createdAt updatedAt"
@state_fields "id name type"

@doc "GraphQL field selection for an issue plus its assignee/team (Ruby: Issue.base_fragment)."
Expand Down Expand Up @@ -120,6 +125,11 @@ defmodule LinearCli.Linear.Issue do
branch_name: map["branchName"],
description: map["description"],
url: map["url"],
priority: map["priority"],
priority_label: map["priorityLabel"],
priority_sort_order: map["prioritySortOrder"],
created_at: map["createdAt"],
updated_at: map["updatedAt"],
assignee: map["assignee"] && LinearCli.Linear.User.from_map(map["assignee"]),
state: map["state"] && LinearCli.Linear.WorkflowState.from_map(map["state"]),
team: map["team"] && LinearCli.Linear.Team.from_map(map["team"]),
Expand Down
130 changes: 130 additions & 0 deletions app/test/linear_cli/cli/commands/issues/read_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,38 @@ defmodule LinearCli.CLI.Commands.Issues.ReadTest do
assert [label] = decoded["labels"]
assert label["name"] == "Bug"
end

test "--output json includes priority and timestamps in issue list" do
Req.Test.stub(LinearCli.Api, fn conn ->
{:ok, body, conn} = Plug.Conn.read_body(conn)
%{"query" => _} = Jason.decode!(body)

Req.Test.json(
conn,
issues_response([
issue_map(%{
"priority" => 0.0,
"priorityLabel" => "No priority",
"prioritySortOrder" => 0.0,
"createdAt" => "2024-01-15T10:30:00.000Z",
"updatedAt" => "2024-01-16T12:00:00.000Z"
})
])
)
end)

output =
capture_io(fn ->
assert :ok = LinearCli.CLI.main(["issue", "list", "--output", "json"])
end)

assert {:ok, [decoded]} = Jason.decode(output)
assert decoded["priority"] === 0.0
refute is_nil(decoded["priority"])
assert decoded["priority_label"] == "No priority"
assert decoded["created_at"] == "2024-01-15T10:30:00.000Z"
assert decoded["updated_at"] == "2024-01-16T12:00:00.000Z"
end
end

describe "issue view" do
Expand Down Expand Up @@ -865,6 +897,104 @@ defmodule LinearCli.CLI.Commands.Issues.ReadTest do
assert decoded["title"] == "Fix the thing"
end

test "--output json includes priority=0 as a real value and timestamps" do
Req.Test.stub(LinearCli.Api, fn conn ->
{:ok, body, conn} = Plug.Conn.read_body(conn)
%{"query" => _} = Jason.decode!(body)

Req.Test.json(conn, %{
"data" => %{
"issue" =>
issue_map(%{
"priority" => 0.0,
"priorityLabel" => "No priority",
"prioritySortOrder" => 0.0,
"createdAt" => "2024-01-15T10:30:00.000Z",
"updatedAt" => "2024-01-16T12:00:00.000Z"
})
}
})
end)

output =
capture_io(fn ->
assert :ok = LinearCli.CLI.main(["issue", "view", "CRY-1", "--output", "json"])
end)

decoded = Jason.decode!(output)
assert decoded["priority"] === 0.0
refute is_nil(decoded["priority"])
assert decoded["priority_label"] == "No priority"
assert decoded["priority_sort_order"] === 0.0
assert decoded["created_at"] == "2024-01-15T10:30:00.000Z"
assert decoded["updated_at"] == "2024-01-16T12:00:00.000Z"
end

test "--output json includes non-zero priority and timestamps" do
Req.Test.stub(LinearCli.Api, fn conn ->
{:ok, body, conn} = Plug.Conn.read_body(conn)
%{"query" => _} = Jason.decode!(body)

Req.Test.json(conn, %{
"data" => %{
"issue" =>
issue_map(%{
"priority" => 2.0,
"priorityLabel" => "High",
"prioritySortOrder" => 100.5,
"createdAt" => "2024-03-01T09:00:00.000Z",
"updatedAt" => "2024-03-02T11:00:00.000Z"
})
}
})
end)

output =
capture_io(fn ->
assert :ok = LinearCli.CLI.main(["issue", "view", "CRY-1", "--output", "json"])
end)

decoded = Jason.decode!(output)
assert decoded["priority"] == 2.0
assert decoded["priority_label"] == "High"
assert decoded["priority_sort_order"] == 100.5
assert decoded["created_at"] == "2024-03-01T09:00:00.000Z"
assert decoded["updated_at"] == "2024-03-02T11:00:00.000Z"
end

test "--output json includes timestamps on nested comments" do
comment_map = %{
"id" => "c1",
"body" => "First comment",
"url" => "https://linear.app/team/issue/CRY-1#comment-c1",
"user" => nil,
"createdAt" => "2024-01-20T08:00:00.000Z",
"updatedAt" => "2024-01-20T08:30:00.000Z"
}

Req.Test.stub(LinearCli.Api, fn conn ->
{:ok, body, conn} = Plug.Conn.read_body(conn)
%{"query" => _} = Jason.decode!(body)

Req.Test.json(conn, %{
"data" => %{
"issue" => issue_map(%{"comments" => %{"nodes" => [comment_map]}})
}
})
end)

output =
capture_io(fn ->
assert :ok = LinearCli.CLI.main(["issue", "view", "CRY-1", "--output", "json"])
end)

decoded = Jason.decode!(output)
assert [comment] = decoded["comments"]
assert comment["id"] == "c1"
assert comment["created_at"] == "2024-01-20T08:00:00.000Z"
assert comment["updated_at"] == "2024-01-20T08:30:00.000Z"
end

test "lc i v ISSUE_ID alias routes to issue view" do
Req.Test.stub(LinearCli.Api, fn conn ->
{:ok, body, conn} = Plug.Conn.read_body(conn)
Expand Down
86 changes: 86 additions & 0 deletions app/test/linear_cli/cli/display_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,90 @@ defmodule LinearCli.CLI.DisplayTest do
assert output =~ "EXT-6"
refute output =~ "["
end

test "JSON output preserves priority=0 as a real numeric value" do
issue = %Issue{
id: "issue-7",
identifier: "EXT-7",
title: "No priority issue",
description: nil,
priority: 0.0,
priority_label: "No priority",
priority_sort_order: 0.0,
comments: [],
labels: []
}

output = capture_io(fn -> Display.show(issue, %{output: "json"}) end)

decoded = Jason.decode!(output)
assert decoded["priority"] === 0.0
refute is_nil(decoded["priority"])
assert decoded["priority_label"] == "No priority"
assert decoded["priority_sort_order"] === 0.0
end

test "JSON output uses snake_case field names for priority and timestamps" do
issue = %Issue{
id: "issue-8",
identifier: "EXT-8",
title: "Field name stability",
description: nil,
priority: 1.0,
priority_label: "Urgent",
priority_sort_order: 42.0,
created_at: "2024-01-15T10:30:00.000Z",
updated_at: "2024-01-16T12:00:00.000Z",
comments: [],
labels: []
}

output = capture_io(fn -> Display.show(issue, %{output: "json"}) end)

decoded = Jason.decode!(output)
assert Map.has_key?(decoded, "priority")
assert Map.has_key?(decoded, "priority_label")
assert Map.has_key?(decoded, "priority_sort_order")
assert Map.has_key?(decoded, "created_at")
assert Map.has_key?(decoded, "updated_at")
refute Map.has_key?(decoded, "priorityLabel")
refute Map.has_key?(decoded, "prioritySortOrder")
refute Map.has_key?(decoded, "createdAt")
refute Map.has_key?(decoded, "updatedAt")
assert decoded["priority"] == 1.0
assert decoded["priority_label"] == "Urgent"
assert decoded["created_at"] == "2024-01-15T10:30:00.000Z"
assert decoded["updated_at"] == "2024-01-16T12:00:00.000Z"
end

test "JSON output includes timestamps on nested comment structs" do
alias LinearCli.Linear.Comment

issue = %Issue{
id: "issue-9",
identifier: "EXT-9",
title: "With comments",
description: nil,
priority: nil,
comments: [
%Comment{
id: "c1",
body: "A comment",
url: "https://linear.app/x#comment-c1",
user: nil,
created_at: "2024-01-20T08:00:00.000Z",
updated_at: "2024-01-20T08:30:00.000Z"
}
],
labels: []
}

output = capture_io(fn -> Display.show(issue, %{output: "json"}) end)

decoded = Jason.decode!(output)
assert [comment] = decoded["comments"]
assert comment["id"] == "c1"
assert comment["created_at"] == "2024-01-20T08:00:00.000Z"
assert comment["updated_at"] == "2024-01-20T08:30:00.000Z"
end
end
39 changes: 39 additions & 0 deletions app/test/linear_cli/linear/comment_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,44 @@ defmodule LinearCli.Linear.CommentTest do

assert {:error, %Ash.Error.Unknown{}} = Linear.add_comment("nope", "Looks good")
end

test "extracts createdAt and updatedAt from the API response" do
Req.Test.stub(LinearCli.Api, fn conn ->
{:ok, body, conn} = Plug.Conn.read_body(conn)
%{"variables" => %{"issueId" => _, "body" => _}} = Jason.decode!(body)

Req.Test.json(conn, %{
"data" => %{
"commentCreate" => %{
"comment" => %{
"id" => "c1",
"body" => "Hello",
"url" => "https://linear.app/team/issue/CRY-1#comment-c1",
"user" => nil,
"createdAt" => "2024-03-10T14:00:00.000Z",
"updatedAt" => "2024-03-10T14:05:00.000Z"
}
}
}
})
end)

assert {:ok, comment} = Linear.add_comment("CRY-1", "Hello")
assert comment.created_at == "2024-03-10T14:00:00.000Z"
assert comment.updated_at == "2024-03-10T14:05:00.000Z"
end

test "tolerates missing timestamp fields (nil stays nil)" do
comment =
LinearCli.Linear.Comment.from_map(%{
"id" => "c2",
"body" => "Old comment",
"url" => nil,
"user" => nil
})

assert is_nil(comment.created_at)
assert is_nil(comment.updated_at)
end
end
end
Loading