gogcli/internal/cmd/exit_codes_test.go
salmonumbrella 3371e3f3ad
feat(cli): agent ergonomics + gmail watch exclude labels (#201)
* feat(cli): improve agent ergonomics

* fix(cli): address code review findings

- Fix nil pointer dereference in confirmDestructive when flags is nil
- Deduplicate dry-run logic by delegating to dryRunExit
- Remove deprecated net.Error.Temporary() call (dead since Go 1.18)
- Add unit tests for resolveTasklistID and resolveCalendarID

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve PR #201 conflicts and follow-ups (#201) (thanks @salmonumbrella)

* fix: resolve rebase fallout for PR #201 landing (#201) (thanks @salmonumbrella)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 03:09:49 +01:00

102 lines
3.1 KiB
Go

package cmd
import (
"context"
"errors"
"testing"
"github.com/99designs/keyring"
ggoogleapi "google.golang.org/api/googleapi"
"github.com/steipete/gogcli/internal/config"
gogapi "github.com/steipete/gogcli/internal/googleapi"
)
func TestStableExitCode_PreservesExistingExitError(t *testing.T) {
in := &ExitError{Code: 3, Err: errors.New("no results")}
out := stableExitCode(in)
if !errors.Is(out, in) {
t.Fatalf("expected same error instance")
}
if got := ExitCode(out); got != 3 {
t.Fatalf("expected exit code 3, got %d", got)
}
}
func TestStableExitCode_AuthRequired(t *testing.T) {
in := &gogapi.AuthRequiredError{Service: "gmail", Email: "a@b.com", Cause: keyring.ErrKeyNotFound}
out := stableExitCode(in)
if got := ExitCode(out); got != exitCodeAuthRequired {
t.Fatalf("expected exit code %d, got %d", exitCodeAuthRequired, got)
}
}
func TestStableExitCode_CredentialsMissing(t *testing.T) {
in := &config.CredentialsMissingError{Path: "/tmp/credentials.json", Cause: errors.New("missing")}
out := stableExitCode(in)
if got := ExitCode(out); got != exitCodeConfig {
t.Fatalf("expected exit code %d, got %d", exitCodeConfig, got)
}
}
func TestStableExitCode_GoogleAPINotFound(t *testing.T) {
in := &ggoogleapi.Error{Code: 404, Message: "not found"}
out := stableExitCode(in)
if got := ExitCode(out); got != exitCodeNotFound {
t.Fatalf("expected exit code %d, got %d", exitCodeNotFound, got)
}
}
func TestStableExitCode_GoogleAPIRateLimited(t *testing.T) {
in := &ggoogleapi.Error{Code: 429, Message: "too many requests"}
out := stableExitCode(in)
if got := ExitCode(out); got != exitCodeRateLimited {
t.Fatalf("expected exit code %d, got %d", exitCodeRateLimited, got)
}
}
func TestStableExitCode_GoogleAPIQuotaExceeded(t *testing.T) {
in := &ggoogleapi.Error{
Code: 403,
Message: "quota exceeded",
Errors: []ggoogleapi.ErrorItem{{Reason: "quotaExceeded"}},
}
out := stableExitCode(in)
if got := ExitCode(out); got != exitCodeRateLimited {
t.Fatalf("expected exit code %d, got %d", exitCodeRateLimited, got)
}
}
func TestStableExitCode_GoogleAPIRetryable(t *testing.T) {
in := &ggoogleapi.Error{Code: 503, Message: "backend error"}
out := stableExitCode(in)
if got := ExitCode(out); got != exitCodeRetryable {
t.Fatalf("expected exit code %d, got %d", exitCodeRetryable, got)
}
}
func TestStableExitCode_Cancelled(t *testing.T) {
out := stableExitCode(context.Canceled)
if got := ExitCode(out); got != exitCodeCancelled {
t.Fatalf("expected exit code %d, got %d", exitCodeCancelled, got)
}
}
func TestStableExitCode_DeadlineExceeded(t *testing.T) {
out := stableExitCode(context.DeadlineExceeded)
if got := ExitCode(out); got != exitCodeRetryable {
t.Fatalf("expected exit code %d, got %d", exitCodeRetryable, got)
}
}
func TestStableExitCode_GenericErrorUnchanged(t *testing.T) {
in := errors.New("boom")
out := stableExitCode(in)
if !errors.Is(out, in) {
t.Fatalf("expected stableExitCode to return original error for generic errors")
}
if got := ExitCode(out); got != 1 {
t.Fatalf("expected exit code 1, got %d", got)
}
}