Skip to content

Commit 142c990

Browse files
authored
test(todos): guard todos show --json surfaces SDK fields (Refs #449) (#564)
Add TestTodosShowJSONSurfacesSDKFields, a regression guard for #449: the fields that report (due_on, starts_on, comments_count, boosts_count, completion_subscribers, description_attachments) must all surface in the --json output of `todos show`. The implementation already landed on main via the SDK model — SDK #375 added completion_subscribers/comments_count, SDK #400 added description_attachments, absorbed by CLI #552/#554's SDK bumps; due_on, starts_on and boosts_count already existed with omitempty. `todos show` emits the SDK Todo object directly, so the fields surface today. This is coverage, not a fix (hence Refs, not Fixes). The test serves a fully-populated todo and asserts both the presence and the values of all six fields, keeping the omitempty date/count fields exercised with nonzero values so a future SDK bump can't silently drop them.
1 parent e7501c5 commit 142c990

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

internal/commands/todos_test.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2922,3 +2922,121 @@ func TestTodosListAggregateSortGuard(t *testing.T) {
29222922
errA := executeTodosCommand(NewTodosCmd(), appA, "list", "--in", "123", "--assignee", "Alice", "--sort", "title")
29232923
require.NoError(t, errA)
29242924
}
2925+
2926+
// populatedTodoTransport serves a single fully-populated todo from the GET
2927+
// endpoint so that todos show exercises every field #449 reported as missing.
2928+
// The description carries no bc-attachment markup, so no download path fires;
2929+
// description_attachments rides along as SDK metadata independent of the
2930+
// rich-text body.
2931+
type populatedTodoTransport struct{}
2932+
2933+
func (populatedTodoTransport) RoundTrip(req *http.Request) (*http.Response, error) {
2934+
// Fail closed: this transport only knows how to answer the todo GET. Any
2935+
// other route or method means `todos show` took an unexpected path, which
2936+
// should fail the test rather than silently pass on the canned body.
2937+
if req.Method != http.MethodGet || !strings.Contains(req.URL.Path, "/todos/999") {
2938+
return nil, fmt.Errorf("unexpected request: %s %s", req.Method, req.URL.Path)
2939+
}
2940+
2941+
header := make(http.Header)
2942+
header.Set("Content-Type", "application/json")
2943+
2944+
body := `{
2945+
"id": 999,
2946+
"status": "active",
2947+
"title": "Populated todo",
2948+
"type": "Todo",
2949+
"content": "Populated todo",
2950+
"description": "<div>Plain description, no attachments</div>",
2951+
"completed": false,
2952+
"position": 1,
2953+
"starts_on": "2026-08-01",
2954+
"due_on": "2026-08-15",
2955+
"comments_count": 4,
2956+
"boosts_count": 3,
2957+
"completion_subscribers": [
2958+
{"id": 5551, "name": "Reviewer One"},
2959+
{"id": 5552, "name": "Reviewer Two"}
2960+
],
2961+
"description_attachments": [
2962+
{
2963+
"id": 8801,
2964+
"sgid": "sgid-abc",
2965+
"filename": "spec.pdf",
2966+
"content_type": "application/pdf",
2967+
"byte_size": 2048,
2968+
"download_url": "https://example.com/spec.pdf",
2969+
"previewable": false,
2970+
"preview_url": "",
2971+
"thumbnail_url": ""
2972+
}
2973+
]
2974+
}`
2975+
2976+
return &http.Response{
2977+
StatusCode: 200,
2978+
Body: io.NopCloser(strings.NewReader(body)),
2979+
Header: header,
2980+
}, nil
2981+
}
2982+
2983+
// TestTodosShowJSONSurfacesSDKFields is a regression guard for #449: the
2984+
// fields that report (due_on, starts_on, comments_count, boosts_count,
2985+
// completion_subscribers, description_attachments) must all surface in the
2986+
// --json output of todos show. The fix landed on main via the SDK model
2987+
// (SDK #375/#400) absorbed by CLI #552/#554; this test keeps the omitempty
2988+
// date/count fields exercised with nonzero values so a future SDK bump can't
2989+
// silently drop them.
2990+
func TestTodosShowJSONSurfacesSDKFields(t *testing.T) {
2991+
app, buf := setupGroupTodoApp(t, populatedTodoTransport{})
2992+
2993+
err := executeTodosCommand(NewTodosCmd(), app, "show", "999")
2994+
require.NoError(t, err)
2995+
2996+
var resp struct {
2997+
Data map[string]json.RawMessage `json:"data"`
2998+
}
2999+
require.NoError(t, json.Unmarshal(buf.Bytes(), &resp))
3000+
3001+
// Every reported field must be present as a key in the emitted JSON.
3002+
for _, key := range []string{
3003+
"due_on", "starts_on", "comments_count", "boosts_count",
3004+
"completion_subscribers", "description_attachments",
3005+
} {
3006+
require.Contains(t, resp.Data, key, "todos show --json must surface %q (#449)", key)
3007+
}
3008+
3009+
var dueOn, startsOn string
3010+
require.NoError(t, json.Unmarshal(resp.Data["due_on"], &dueOn))
3011+
require.NoError(t, json.Unmarshal(resp.Data["starts_on"], &startsOn))
3012+
assert.Equal(t, "2026-08-15", dueOn)
3013+
assert.Equal(t, "2026-08-01", startsOn)
3014+
3015+
var commentsCount, boostsCount int
3016+
require.NoError(t, json.Unmarshal(resp.Data["comments_count"], &commentsCount))
3017+
require.NoError(t, json.Unmarshal(resp.Data["boosts_count"], &boostsCount))
3018+
assert.Equal(t, 4, commentsCount)
3019+
assert.Equal(t, 3, boostsCount)
3020+
3021+
var subscribers []struct {
3022+
ID int64 `json:"id"`
3023+
Name string `json:"name"`
3024+
}
3025+
require.NoError(t, json.Unmarshal(resp.Data["completion_subscribers"], &subscribers))
3026+
require.Len(t, subscribers, 2)
3027+
assert.Equal(t, int64(5551), subscribers[0].ID)
3028+
assert.Equal(t, "Reviewer One", subscribers[0].Name)
3029+
assert.Equal(t, int64(5552), subscribers[1].ID)
3030+
assert.Equal(t, "Reviewer Two", subscribers[1].Name)
3031+
3032+
var attachments []struct {
3033+
ID int64 `json:"id"`
3034+
Filename string `json:"filename"`
3035+
DownloadURL string `json:"download_url"`
3036+
}
3037+
require.NoError(t, json.Unmarshal(resp.Data["description_attachments"], &attachments))
3038+
require.Len(t, attachments, 1)
3039+
assert.Equal(t, int64(8801), attachments[0].ID)
3040+
assert.Equal(t, "spec.pdf", attachments[0].Filename)
3041+
assert.Equal(t, "https://example.com/spec.pdf", attachments[0].DownloadURL)
3042+
}

0 commit comments

Comments
 (0)