feat: add buzz archived command to list archived goals#226
feat: add buzz archived command to list archived goals#226
buzz archived command to list archived goals#226Conversation
Co-authored-by: narthur <4655422+narthur@users.noreply.github.com>
buzz archived command to list archived goals
🚀 Pre-release Build ReadyTest builds are ready! Install directly using the # Install the pre-release
bin install https://github.com/PinePeakDigital/buzz/releases/tag/pr-226-latest buzz-pr-226# Run the pre-release
buzz-pr-226# Uninstall the pre-release
bin remove buzz-pr-226Direct Download LinksOr download binaries directly from the pre-release page:
|
There was a problem hiding this comment.
Pull request overview
Adds a new CLI subcommand to surface Beeminder archived goals, backed by a new API fetch helper and updated docs/tests.
Changes:
- Added
FetchArchivedGoals()to call Beeminder’s/goals/archived.jsonendpoint. - Added
buzz archivedcommand handler and refactored shared table printing intoprintGoalsTable. - Added mock-server tests for the archived goals fetch and documented the new command in the README.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| main.go | Wires new archived subcommand, adds handler, and extracts shared goals table printing. |
| beeminder.go | Introduces FetchArchivedGoals() API call mirroring FetchGoals(). |
| beeminder_test.go | Adds mock-server coverage for FetchArchivedGoals() success/empty/error paths. |
| README.md | Documents buzz archived usage and example output. |
| // FetchArchivedGoals fetches the user's archived goals from Beeminder API | ||
| func FetchArchivedGoals(config *Config) ([]Goal, error) { | ||
| baseURL := getBaseURL(config) | ||
| url := fmt.Sprintf("%s/api/v1/users/%s/goals/archived.json?auth_token=%s", | ||
| baseURL, config.Username, config.AuthToken) | ||
|
|
||
| LogRequest(config, "GET", url) | ||
| resp, err := http.Get(url) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to fetch archived goals: %w", err) | ||
| } | ||
| defer resp.Body.Close() | ||
| LogResponse(config, resp.StatusCode, url) | ||
|
|
||
| if resp.StatusCode != http.StatusOK { | ||
| return nil, fmt.Errorf("API returned status %d", resp.StatusCode) | ||
| } | ||
|
|
||
| var goals []Goal | ||
| if err := json.NewDecoder(resp.Body).Decode(&goals); err != nil { | ||
| return nil, fmt.Errorf("failed to decode archived goals: %w", err) | ||
| } | ||
|
|
||
| return goals, nil | ||
| } |
There was a problem hiding this comment.
FetchArchivedGoals duplicates FetchGoals almost line-for-line (URL construction, logging, http.Get, status check, JSON decode). This increases the chance the two drift (e.g., future improvements to error handling/timeouts only applied to one). Consider extracting a shared helper for GET+decode (parameterized by endpoint path) and have both FetchGoals and FetchArchivedGoals call it.
beeminder.go
Outdated
| baseURL := getBaseURL(config) | ||
| url := fmt.Sprintf("%s/api/v1/users/%s/goals/archived.json?auth_token=%s", | ||
| baseURL, config.Username, config.AuthToken) | ||
|
|
There was a problem hiding this comment.
FetchArchivedGoals builds the request URL via fmt.Sprintf and interpolates username/auth_token without URL-escaping. If either contains characters like spaces, '+' or '&', the request can break or change query semantics. Prefer constructing the URL with net/url (PathEscape for the username path segment and url.Values for the auth_token query) similar to how goal slugs are escaped elsewhere in this file.
| func TestFetchArchivedGoalsEmptyList(t *testing.T) { | ||
| mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| w.Header().Set("Content-Type", "application/json") | ||
| w.Write([]byte("[]")) | ||
| })) | ||
| defer mockServer.Close() |
There was a problem hiding this comment.
TestFetchArchivedGoalsEmptyList doesn't verify the request method/path/query (unlike the main mock-server test). That means it could still pass even if FetchArchivedGoals hits the wrong endpoint or omits auth_token. Add the same assertions (GET, /api/v1/users/testuser/goals/archived.json, auth_token=testtoken) to make this test actually validate the behavior it’s meant to cover.
|
|
||
| // TestFetchArchivedGoalsAPIError tests FetchArchivedGoals when the API returns an error | ||
| func TestFetchArchivedGoalsAPIError(t *testing.T) { | ||
| mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
There was a problem hiding this comment.
TestFetchArchivedGoalsAPIError doesn't assert that the failing request was made to the archived-goals endpoint (method/path/auth_token). As written, it only checks that some error occurs, so a regression in URL construction could slip by. Mirror the request assertions used in TestFetchArchivedGoalsWithMockServer before returning 500.
| mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| // Verify it's a GET request | |
| if r.Method != http.MethodGet { | |
| t.Errorf("Expected GET request, got %s", r.Method) | |
| } | |
| // Verify the URL path targets the archived goals endpoint | |
| if !strings.Contains(r.URL.Path, "/users/testuser/archived_goals.json") { | |
| t.Errorf("Unexpected URL path for archived goals: %s", r.URL.Path) | |
| } | |
| // Verify auth_token is passed as a query parameter | |
| if r.URL.Query().Get("auth_token") != "testtoken" { | |
| t.Errorf("Expected auth_token query parameter to be %q, got %q", "testtoken", r.URL.Query().Get("auth_token")) | |
| } |
main.go
Outdated
| // handleArchivedCommand outputs a summary list of all archived goals with slug, title, rate, and stakes | ||
| func handleArchivedCommand() { |
There was a problem hiding this comment.
The doc comment for handleArchivedCommand says it outputs slug, title, rate, and stakes, but the shared table includes a Units column as well. Update the comment to match the actual output columns (or adjust the output to match the comment).
|
@copilot apply changes based on the comments in this thread |
Co-authored-by: narthur <4655422+narthur@users.noreply.github.com>
Done in 1de7684. Applied all five review changes:
|
FetchArchivedGoals()function inbeeminder.gofetchGoalsList()helper —FetchGoalsandFetchArchivedGoalsboth delegate to it; usesurl.PathEscapefor username andurl.Valuesfor auth_tokenFetchArchivedGoals()inbeeminder_test.go— all with full request assertions (method, path, auth_token)handleArchivedCommand()inmain.goprintGoalsTable()helper to remove duplicationcase "archived":in CLI switch statement inmain.gohandleArchivedCommandto include Units columnprintHelp()and "Available commands" error message inmain.goREADME.mdwith documentation for the new commandOriginal prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.