Commit graph

5670 commits

Author SHA1 Message Date
Babak K. Shandiz
c87d262d4b
fix(discussion create): display spinner
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-05-07 09:58:42 +01:00
Babak K. Shandiz
25778ce08a
docs(discussion create): add preview suffix
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-05-07 09:36:21 +01:00
Babak K. Shandiz
0c0d316b9a
test(discussion create): consolidate into TestCreateRun table test
- Merge TestCreateRun_nonInteractive, TestCreateRun_tty, and related tests
  into a single TestCreateRun table with 11 cases
- Add partial-flag cases for missing title, body, and category
- Add tty blank body returns error case
- Add tty does not prompt when all flags provided case

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-07 09:08:51 +01:00
Babak K. Shandiz
47cabb26ae
fix(discussion create): remove success message from stderr
Only print the discussion URL to stdout. No additional output on stderr.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-07 09:08:51 +01:00
Babak K. Shandiz
bdceb21248
fix(discussion create): allow --repo override
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-05-07 08:58:53 +01:00
Babak K. Shandiz
3850cacb55
fix(discussion create): improve validation and output behavior
- Move non-interactive flag validation to arg parsing stage
- Add blank title/body/category validation at flag parsing
- Keep interactive blank-title/body checks after prompts
- Always print URL to stdout; success message to stderr in TTY mode
- Consolidate test cases and add isTTY field

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-07 08:46:07 +01:00
Max Beizer
a1fd235755
fix: label atomicity, validation order, blankAllowed, help text
- Resolve labels before creating discussion so a bad label name
  doesn't leave an orphaned discussion (atomicity fix)
- Validate --title/--category/--body non-interactively before calling
  ListCategories to avoid an unnecessary network round-trip
- Set blankAllowed=false so the markdown editor rejects empty bodies
- Clarify help text: --body is required when not running interactively
- Update tests to match new behavior; rename label-not-found test to
  make the atomicity guarantee explicit

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 17:29:12 -05:00
Max Beizer
e471f3f8f1
feat: add gh discussion create command
Implements the 'gh discussion create' CLI command, wiring up the
already-merged Create client method. Supports:
- --title/-t, --body/-b, --category/-c, --label/-l flags
- Interactive prompting when TTY and required flags are missing
- Non-TTY mode requiring all flags
- TTY and non-TTY output formats

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 14:52:07 -05:00
Babak K. Shandiz
5d917f1af2
test(discussion/client): add label pagination and early-break tests
- Add "paginates labels across multiple pages" case (two pages, one label each)
- Add "stops paginating labels when all found" case (early break verified via reg.Verify)
- Update "creates discussion with labels" to two labels with variable assertions
- Update "label not found" to verify all missing labels reported at once

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-05 12:46:53 +01:00
Babak K. Shandiz
618dbf33f0
fix(discussion/client): improve label resolution error handling
- Break early from pagination when all wanted labels are found
- Collect all missing labels and report them in a single error message
- Guard missing-label check with len(found) != len(wanted)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-05 12:46:33 +01:00
Max Beizer
fd06ad7556
discussion create: add label support via two-step GraphQL
- Add Labels []string field to CreateDiscussionInput
- Implement resolveLabels helper: paginated RepositoryLabels query,
  case-insensitive match, error if any label not found
- Implement addLabelsToDiscussion helper: calls addLabelsToLabelable
  mutation after createDiscussion
- Wire label logic into Create: resolve labels, apply them, populate
  d.Labels from resolved values
- Add three TestCreate cases: success with labels, label not found,
  mutation failure

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-01 14:17:38 -05:00
Babak K. Shandiz
d6b46f75d4
test(discussion/client): verify Create mutation variables and error paths
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-01 09:12:39 +01:00
Babak K. Shandiz
d78703efaa
fix(discussion/client): polish Create implementation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-01 09:12:15 +01:00
Max Beizer
2e5623180a
feat(discussion/client): implement Create mutation with tests
Implement the createDiscussion GraphQL mutation in the discussion client.

- Add getRepositoryMeta helper to resolve repo node ID and check
  discussions-enabled flag before mutating
- Skip repo lookup when CreateDiscussionInput.RepositoryID is provided
- Reuse discussionListNode mapping for consistent field coverage
- Table-driven tests: field mapping, pre-resolved repo ID, discussions
  disabled, repo not found, mutation error

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-29 12:36:03 -05:00
Babak K. Shandiz
52f219a5ac
test(discussion view): consolidate view run tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-29 13:05:21 +01:00
Babak K. Shandiz
54ddede12f
test(discussion/view): consolidate NewCmdView flag parsing tests
Replace scattered standalone test functions with a single table-driven
TestNewCmdView covering all arg/flag parsing: number, hash, URL, web,
comments, replies, limit, after, order, mutual exclusivity, and invalid
inputs. Uses shlex.Split for arg parsing and asserts opts fields
individually per case.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 11:03:55 +01:00
Babak K. Shandiz
cf40f9293d
chore(discussion/client): polish and cleanup tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 10:41:54 +01:00
Babak K. Shandiz
e263abfb2c
test(discussion/client): improve GetCommentReplies test coverage
Inline all JSON response helpers (getCommentRepliesResp, wrapRepliesBlock,
bareCommentNode, replyNode) to avoid cross-test coupling. Fix JSON response
shape to place "node" as sibling of "repository" under "data", matching the
real GraphQL query structure. Populate "maps all fields" with non-zero
values and use a single assert.Equal for the full Discussion struct.

Match real API error responses: discussions disabled returns NOT_FOUND on
discussion, node not found returns NOT_FOUND with null node, wrong-type
node returns empty object. Add missing test cases for repo not found and
first page newest reversal.

Move shared wantComments/wantTotal/wantCursor/wantNext/wantDirection fields
into assertDisc callbacks for both TestGetWithComments and
TestGetCommentReplies, giving each case full ownership of its assertions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 10:38:19 +01:00
Babak K. Shandiz
19369d0444
test(discussion/client): improve GetWithComments test coverage
Inline all JSON response helpers (getWithCommentsResp, wrapCommentsBlock,
commentNode) to avoid cross-test coupling. Add missing test cases for
empty comments, first page newest reversal, multiple replies on a single
comment, and repo not found. Populate the "maps comments with replies"
case with non-zero field values and use a single assert.Equal for the
full Discussion struct. Match real API error responses for discussions
disabled and repo not found cases. Rename wantDisc to assertDisc across
all client test functions and require it for non-error cases.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 10:38:19 +01:00
Babak K. Shandiz
573f3f0a63
test(discussion/test): use nil next page to match API behaviour
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-28 10:38:19 +01:00
Babak K. Shandiz
45bd958f75
test(discussion/client): improve GetByNumber test coverage
Inline mock JSON responses, use non-default values for all fields to
verify mapping, add repo-not-found test case, and match real API
behaviour for discussions-disabled response (null discussion with
NOT_FOUND error).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 10:38:15 +01:00
Max Beizer
11130fd6be
test(discussion view): add tests for view command client methods and --replies mode
Add table-driven tests for:

Client (client_impl_test.go):
- TestGetByNumber: field mapping, discussions disabled
- TestGetWithComments: field mapping, forward/backward pagination,
  reply reversal in newest mode, discussions disabled
- TestGetCommentReplies: field mapping, forward/backward pagination,
  reply reversal, discussions disabled, nil node, wrong node type

Command (view_test.go):
- TestNewCmdView_repliesFlags: mutual exclusivity with --comments/--web,
  --order/--limit/--after require --comments or --replies, pagination
  flags work with --replies
- TestViewRun_replies: TTY/non-TTY/JSON output, pagination hints,
  routing assertion (GetCommentReplies called, not GetByNumber or
  GetWithComments)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-27 15:42:36 -05:00
Babak K. Shandiz
59701725d0
fix(discussion view): guard against empty comment slice in replies mode
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-27 18:46:14 +01:00
Babak K. Shandiz
56db9ee6e9
feat(gh discussion view): add --replies flag for paginated reply viewing
Add --replies <COMMENT-ID> flag to view paginated replies on a specific
discussion comment. Mutually exclusive with --comments and --web. Works
with --limit, --after, and --order for pagination control. Supports TTY,
raw, and JSON output modes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-27 10:58:06 +01:00
Babak K. Shandiz
b090b4d2fb
feat(discussion/client): add GetCommentReplies with paginated reply fetching
Extract discussionReplyNode and mapReplyFromNode as reusable types for
reply nodes. Add GetCommentReplies to the DiscussionClient interface,
implemented using a combined node(id:) and repository.discussion query
since the Discussion type does not expose a comment(id:) field.

Add ExportReply() for leaf reply nodes (no nested replies) and include
cursor/next pagination fields in the comment Export() replies object.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-27 10:57:45 +01:00
Babak K. Shandiz
5946d1a298
chore(discussion/client): apply formatting
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-27 09:13:36 +01:00
Babak K. Shandiz
524a503e86
refactor(discussion/client): use strongly-typed query for fetching comments
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-27 09:09:21 +01:00
Babak K. Shandiz
72a6c98d37
refactor(discussion view): simplify mode check
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-27 08:46:56 +01:00
Babak K. Shandiz
49a846aa1a
docs(discussion view): use non-default behaviour in example
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-27 08:45:50 +01:00
Max Beizer
9f3a31ddb3
fix(discussion view): use GraphQL variables for cursor, fix --json comments
Fix two issues in the discussion view command:

1. GraphQL injection via cursor interpolation: The --after cursor value
   was interpolated directly into the raw GraphQL query string using
   fmt.Sprintf, which is unsafe since cursor values come from user input.
   Now uses GraphQL variables ($cursor: String) instead, matching the
   pattern used by issue list, pr list, and other commands.

2. Incomplete --json comments output: Running `gh discussion view N
   --json comments` silently returned only totalCount with no comment
   nodes, because the data fetch was gated solely on the --comments flag.
   Now checks if the JSON exporter requests the comments field and
   fetches full comment data accordingly, matching how issue view and
   pr view drive data loading from exporter fields.

Also fixes example text that said "newest" but showed --order oldest.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 12:19:04 -05:00
Babak K. Shandiz
d2e081bce1
feat(discussion view): add cursor-based pagination to comments
Add --limit and --after flags for paginating through discussion comments.
Cursor output is shown in TTY (hint message), raw (next: field), and JSON.

Change GetWithComments to accept 'after' cursor and 'newest' bool instead
of order string. Implement forward/backward cursor-based pagination in
GraphQL queries depending on comment order.

Change Replies from []DiscussionComment to DiscussionCommentList with
Direction field. Display direction-aware messages (newer/older) for both
comments and replies.

Move DiscussionFields and reactionGroupList from shared to view package.
Delete shared/display.go.

Add 7 new pagination tests and update existing test fixtures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 23:50:04 +01:00
Babak K. Shandiz
75b71505c8
refactor(discussion view): use slices package for label sorting
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 22:19:59 +01:00
Babak K. Shandiz
6bd96abd6a
fix(gh discussion view): wrap arg parse error as flag error
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 22:02:44 +01:00
Babak K. Shandiz
f7a79683c0
test(discussion/shared): add test for ParseDiscussionArg
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-24 21:56:23 +01:00
Babak K. Shandiz
ca8d878d80
docs(discussion/shared): explain why we accept http scheme
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-24 21:56:19 +01:00
Babak K. Shandiz
7603a0c8ce
fix(discussion/shared): quote arg in errors
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-24 21:56:11 +01:00
Babak K. Shandiz
57b3e9091c
fix(discussion/shared): anchor discussion url regexp
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-24 21:55:54 +01:00
Babak K. Shandiz
cd3b23bf36
docs(discussion view): add preview remark
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-24 21:37:58 +01:00
Max Beizer
dea54ab3ab
fix: gofmt alignment in GetWithComments response struct
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 21:24:31 +01:00
Max Beizer
ca84d4c6a3
Add discussion view --comments with threaded display
Implement the --comments flag for discussion view, showing threaded
comments with replies.

Features:
- --comments flag fetches and displays discussion comments
- --order flag (oldest/newest) controls comment ordering
- Answer badge (✓ Answer) on marked answer comments
- Threaded replies with indentation
- Truncation messages when more replies exist than fetched
- TTY: markdown-rendered comments with author/timestamp/reactions
- Non-TTY: stable tab-delimited format for scripting
- JSON: populated comment nodes via ExportData

Implementation:
- GetWithComments uses raw GraphQL to dynamically switch between
  first/last based on ordering. Fetches 30 comments with 4 replies
  each. Explicitly reverses for newest-first ordering.
- --order without --comments returns a flag error
- Reuses existing shared.ReactionGroupList for reaction display

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 21:24:30 +01:00
Max Beizer
d6e63f63d3
fix(discussion view): align with post-review list changes
- Rewrite GetByNumber to use strongly-typed GraphQL query instead of
  removed raw string interpolation (discussionFields/discussionNode)
- Fix State string references to use Closed bool throughout view.go
- Fix DiscussionAuthor → DiscussionActor type rename in tests
- Add ReactionGroups field to Discussion domain type and ExportData
- Add computed "state" field to ExportData for JSON output
- Add shared.DiscussionFields for view command's --json flag
- Regenerate client mock

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 21:24:30 +01:00
Max Beizer
d9e9751823
Add discussion view command
Implement `gh discussion view` for viewing a single discussion with:

- Number or URL argument via shared.ParseDiscussionArg
- TTY output: title, metadata (state, category, author, age, comment
  count), labels, markdown-rendered body, reactions
- Context-aware author attribution: "Asked by" for answerable
  categories (Q&A), "Started by" for others
- Non-TTY output: key-value pairs matching `gh issue view` format
- JSON output via Exporter (Discussion.ExportData)
- --web flag to open in browser
- Pager support for TTY output

Also adds:
- GetByNumber client method with not-found detection
- shared.ParseDiscussionArg for number/URL/#number parsing
- shared.ReactionGroupList for emoji reaction display

Comment threading (--comments) is deferred to the next PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 21:24:25 +01:00
Babak K. Shandiz
2b794ed992
refactor(discussion/client): remove redundant "first" variable init
The fetch loop already assigns "first" on each iteration, so the
initial assignment in the variables map is dead code. Remove it from
both List and Search.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 14:21:24 +01:00
Babak K. Shandiz
81117364ba
refactor(discussion/client): convert tests to httpStubs pattern
Replace responses/checkVarsFns with httpStubs func per test case in
TestList, TestSearch, and TestListCategories, matching the pattern used
in agent-task/capi tests. Expand inline JSON to heredoc, remove flower
box comments, and add "empty list" and "exact fit" test cases.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 14:19:19 +01:00
Max Beizer
45de7db4d5
style: run gofmt on client_impl_test.go
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-22 16:49:22 -05:00
Max Beizer
5db230b317
test(discussion): assert GQL variables, add Bot actor and limit>100 cases
- Replace false-positive filter tests with GraphQLQuery responders that
  assert on actual GQL variables (first, after, states, answered, orderBy,
  categoryId in List; query string qualifiers in Search)
- Add Bot actor test case (Bot.ID maps to DiscussionActor.ID, Name is empty)
- Add limit>100 test cases for both List and Search to verify the
  per-iteration first variable is set correctly (100 on page 1, remainder
  on page 2)
- Fix limit>100 bug in client_impl.go: move variables["first"] assignment
  inside the loop so each iteration caps at min(remaining, 100)
- Remove intStr helper; use strconv.Itoa directly

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-22 16:36:07 -05:00
Max Beizer
aa080ad28a
refactor(discussion): convert client tests to table-driven
Addresses babakks' review on PR #13252:
- Convert 18 individual test functions to 3 table-driven functions
- Replace ptr helper with new(value) syntax throughout
- Assert all Discussion struct fields in success cases
- Add after-cursor test cases for pagination
- Fold pagination tests into table structure

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-22 15:04:59 -05:00
Max Beizer
35e8cc93cf
test(discussion): add httpmock unit tests for DiscussionClient
Add comprehensive httpmock-based unit tests for the client package covering:
- List: success path, discussions disabled, limit/filter validation, pagination
- Search: success path, filter validation, pagination
- ListCategories: success path, discussions disabled

Tests use httpmock.Registry with defer Verify(t) to ensure all stubs
are exercised, following the established testing pattern in this repo.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-21 09:12:31 -05:00
Babak K. Shandiz
2a4a982ae4
fix(discussion list): include search keywords in web mode
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-14 15:07:32 +01:00
Babak K. Shandiz
f62875fb63
fix(discussion list): quote author qualifier in web mode
Signed-off-by: Babak K. Shandiz <babakks@github.com>
2026-04-14 15:05:22 +01:00