diff --git a/docs/api-contract.md b/docs/api-contract.md index 7225ea1..231d64e 100644 --- a/docs/api-contract.md +++ b/docs/api-contract.md @@ -221,6 +221,7 @@ Coverage notes: rows list direct tests when a symbol is named in test names or r | method | TeamService.RemoveRepo | `func (s *TeamService) RemoveRepo(ctx context.Context, teamID int64, org, repo string) error` | RemoveRepo removes a repository from a team. | No direct tests. | | method | UserService.Follow | `func (s *UserService) Follow(ctx context.Context, username string) error` | Follow follows a user as the authenticated user. | No direct tests. | | method | UserService.GetCurrent | `func (s *UserService) GetCurrent(ctx context.Context) (*types.User, error)` | GetCurrent returns the authenticated user. | `TestUserService_Good_GetCurrent` | +| method | UserService.GetQuota | `func (s *UserService) GetQuota(ctx context.Context) (*types.QuotaInfo, error)` | GetQuota returns the authenticated user's quota information. | `TestUserService_GetQuota_Good` | | method | UserService.ListStopwatches | `func (s *UserService) ListStopwatches(ctx context.Context) ([]types.StopWatch, error)` | ListStopwatches returns all existing stopwatches for the authenticated user. | `TestUserService_ListStopwatches_Good` | | method | UserService.IterStopwatches | `func (s *UserService) IterStopwatches(ctx context.Context) iter.Seq2[types.StopWatch, error]` | IterStopwatches returns an iterator over all existing stopwatches for the authenticated user. | `TestUserService_IterStopwatches_Good` | | method | UserService.IterFollowers | `func (s *UserService) IterFollowers(ctx context.Context, username string) iter.Seq2[types.User, error]` | IterFollowers returns an iterator over all followers of a user. | No direct tests. | diff --git a/users.go b/users.go index 8aa9ac0..3f676b1 100644 --- a/users.go +++ b/users.go @@ -52,6 +52,15 @@ func (s *UserService) UpdateSettings(ctx context.Context, opts *types.UserSettin return &out, nil } +// GetQuota returns the authenticated user's quota information. +func (s *UserService) GetQuota(ctx context.Context) (*types.QuotaInfo, error) { + var out types.QuotaInfo + if err := s.client.Get(ctx, "/api/v1/user/quota", &out); err != nil { + return nil, err + } + return &out, nil +} + // ListEmails returns all email addresses for the authenticated user. func (s *UserService) ListEmails(ctx context.Context) ([]types.Email, error) { return ListAll[types.Email](ctx, s.client, "/api/v1/user/emails", nil) diff --git a/users_test.go b/users_test.go index 7b2116b..5cb55a4 100644 --- a/users_test.go +++ b/users_test.go @@ -128,6 +128,41 @@ func TestUserService_UpdateSettings_Good(t *testing.T) { } } +func TestUserService_GetQuota_Good(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + t.Errorf("expected GET, got %s", r.Method) + } + if r.URL.Path != "/api/v1/user/quota" { + t.Errorf("wrong path: %s", r.URL.Path) + } + json.NewEncoder(w).Encode(types.QuotaInfo{ + Groups: &types.QuotaGroupList{}, + Used: &types.QuotaUsed{ + Size: &types.QuotaUsedSize{ + Repos: &types.QuotaUsedSizeRepos{ + Public: 123, + Private: 456, + }, + }, + }, + }) + })) + defer srv.Close() + + f := NewForge(srv.URL, "tok") + quota, err := f.Users.GetQuota(context.Background()) + if err != nil { + t.Fatal(err) + } + if quota.Used == nil || quota.Used.Size == nil || quota.Used.Size.Repos == nil { + t.Fatalf("quota usage was not decoded: %+v", quota) + } + if quota.Used.Size.Repos.Public != 123 || quota.Used.Size.Repos.Private != 456 { + t.Errorf("unexpected repository quota usage: %+v", quota.Used.Size.Repos) + } +} + func TestUserService_ListEmails_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet {