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
15 changes: 15 additions & 0 deletions internal/commands/boost.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ response, so there is nothing to page through either.`,
RunE: func(cmd *cobra.Command, args []string) error {
app := appctx.FromContext(cmd.Context())
if len(args) == 0 {
// An explicitly named project cannot stand in for the item ID,
// but it is a statement of intent the hint can honor: carry it
// forward into the corrected invocation. Otherwise the generic
// missing-argument path answers (help when interactive).
explicitProject := *project
if explicitProject == "" {
explicitProject = app.Flags.Project
}
if explicitProject != "" {
hint := fmt.Sprintf("Pass the item's ID or URL: basecamp boost list <id> --project %s", explicitProject)
if eventID != "" {
hint = fmt.Sprintf("%s --event %s", hint, eventID)
}
return output.ErrUsageHint("Boosts belong to an item, so listing them needs one", hint)
}
return missingArg(cmd, "<id|url>")
}
if err := ensureAccount(cmd, app); err != nil {
Expand Down
34 changes: 34 additions & 0 deletions internal/commands/boost_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,40 @@ func TestBoostListConfiguredProjectStillNeedsAnID(t *testing.T) {
assert.Empty(t, transport.recorded())
}

// An explicitly named project cannot stand in for the item ID either, but it
// is a statement of intent: the hint carries it forward into the corrected
// invocation — through --project, its --in alias, and the root-level form
// that lands in app.Flags.Project.
func TestBoostListExplicitProjectWithoutIDCarriesProjectIntoHint(t *testing.T) {
assertHintCarriesProject := func(t *testing.T, err error, transport *recordingTransport) {
t.Helper()
requireBoostUsageError(t, err, "listing them needs one")
var e *output.Error
require.True(t, errors.As(err, &e))
assert.Contains(t, e.Hint, "--project 123")
assert.Empty(t, transport.recorded(), "a usage error must not reach the API")
}

cmd, app, transport := setupBoostListTest(t, nil)
assertHintCarriesProject(t, executeBoostCommand(cmd, app, "list", "--project", "123"), transport)

cmd, app, transport = setupBoostListTest(t, nil)
assertHintCarriesProject(t, executeBoostCommand(cmd, app, "list", "--in", "123"), transport)

cmd, app, transport = setupBoostListTest(t, nil)
app.Flags.Project = "123"
assertHintCarriesProject(t, executeBoostCommand(cmd, app, "list"), transport)

// --event is explicit intent too: when the structured hint fires, it
// rides along rather than being silently dropped from the correction.
cmd, app, transport = setupBoostListTest(t, nil)
err := executeBoostCommand(cmd, app, "list", "--project", "123", "--event", "5")
assertHintCarriesProject(t, err, transport)
var e *output.Error
require.True(t, errors.As(err, &e))
assert.Contains(t, e.Hint, "--event 5")
}

// The pagination flags existed only for the account-wide feed. With that gone
// they must not linger: an item's boosts arrive in one unpaginated response,
// and the SDK documents BoostListOptions.Page as not honoring a page number.
Expand Down
Loading