goplaces/internal/cli/cli_test.go
Peter Steinberger fb660d880f
Some checks failed
ci / test (push) Has been cancelled
fix: document json arrays + regressions (#2) (thanks @salmonumbrella)
2026-01-10 18:36:12 +01:00

777 lines
20 KiB
Go

package cli
import (
"bytes"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/steipete/goplaces"
)
const placesSearchPath = "/places:searchText"
func TestRunSearchJSON(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != placesSearchPath {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
_, _ = w.Write([]byte(`{"places": [{"id": "abc"}]}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"search",
"coffee",
"--api-key", "test-key",
"--base-url", server.URL,
"--json",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d (stdout=%s stderr=%s)", exitCode, stdout.String(), stderr.String())
}
if stderr.Len() != 0 {
t.Fatalf("unexpected stderr: %s", stderr.String())
}
results := decodeJSONArray(t, stdout.String())
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d (stdout=%s)", len(results), stdout.String())
}
if results[0]["place_id"] != "abc" {
t.Fatalf("unexpected result payload: %#v", results[0])
}
}
func TestRunSearchHuman(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`{"places": [{"id": "abc", "displayName": {"text": "Cafe"}}]}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"search",
"coffee",
"--api-key", "test-key",
"--base-url", server.URL,
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d (stdout=%s stderr=%s)", exitCode, stdout.String(), stderr.String())
}
if !strings.Contains(stdout.String(), "Cafe") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
}
func TestRunSearchWithFilters(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var payload map[string]any
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
t.Fatalf("decode request: %v", err)
}
if payload["includedType"] != "cafe" {
t.Fatalf("unexpected includedType: %#v", payload["includedType"])
}
if payload["languageCode"] != "en" {
t.Fatalf("unexpected languageCode: %#v", payload["languageCode"])
}
if payload["regionCode"] != "US" {
t.Fatalf("unexpected regionCode: %#v", payload["regionCode"])
}
_, _ = w.Write([]byte(`{"places": [{"id": "abc"}]}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"search",
"coffee",
"--api-key", "test-key",
"--base-url", server.URL,
"--json",
"--keyword", "best",
"--type", "cafe",
"--open-now=true",
"--min-rating", "4.2",
"--price-level", "1",
"--lat", "40.0",
"--lng=-70.0",
"--radius-m", "500",
"--language", "en",
"--region", "US",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d (stdout=%s stderr=%s)", exitCode, stdout.String(), stderr.String())
}
if stderr.Len() != 0 {
t.Fatalf("unexpected stderr: %s", stderr.String())
}
results := decodeJSONArray(t, stdout.String())
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d (stdout=%s)", len(results), stdout.String())
}
if results[0]["place_id"] != "abc" {
t.Fatalf("unexpected result payload: %#v", results[0])
}
}
func TestRunAutocompleteJSON(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/places:autocomplete" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
_, _ = w.Write([]byte(`{"suggestions": [{"placePrediction": {"placeId": "abc"}}]}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"autocomplete",
"coffee",
"--api-key", "test-key",
"--base-url", server.URL,
"--json",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d (stdout=%s stderr=%s)", exitCode, stdout.String(), stderr.String())
}
if stderr.Len() != 0 {
t.Fatalf("unexpected stderr: %s", stderr.String())
}
suggestions := decodeJSONArray(t, stdout.String())
if len(suggestions) != 1 {
t.Fatalf("expected 1 suggestion, got %d (stdout=%s)", len(suggestions), stdout.String())
}
if suggestions[0]["place_id"] != "abc" {
t.Fatalf("unexpected suggestion payload: %#v", suggestions[0])
}
}
func TestRunAutocompleteHuman(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`{"suggestions": [{"placePrediction": {"placeId": "abc", "text": {"text": "Cafe"}}}]}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"autocomplete",
"coffee",
"--api-key", "test-key",
"--base-url", server.URL,
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "Cafe") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
if stderr.Len() != 0 {
t.Fatalf("unexpected stderr: %s", stderr.String())
}
}
func TestRunNearbyJSON(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/places:searchNearby" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
_, _ = w.Write([]byte(`{"places": [{"id": "abc"}]}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"nearby",
"--lat", "1",
"--lng", "2",
"--radius-m", "3",
"--api-key", "test-key",
"--base-url", server.URL,
"--json",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if stderr.Len() != 0 {
t.Fatalf("unexpected stderr: %s", stderr.String())
}
results := decodeJSONArray(t, stdout.String())
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d (stdout=%s)", len(results), stdout.String())
}
if results[0]["place_id"] != "abc" {
t.Fatalf("unexpected result payload: %#v", results[0])
}
}
func TestRunNearbyHuman(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`{"places": [{"id": "abc", "displayName": {"text": "Cafe"}}]}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"nearby",
"--lat", "1",
"--lng", "2",
"--radius-m", "3",
"--api-key", "test-key",
"--base-url", server.URL,
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "Cafe") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
if stderr.Len() != 0 {
t.Fatalf("unexpected stderr: %s", stderr.String())
}
}
func TestRunRouteJSON(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/directions/v2:computeRoutes":
_, _ = w.Write([]byte("{\"routes\":[{\"polyline\":{\"encodedPolyline\":\"_p~iF~ps|U_ulLnnqC_mqNvxq`@\"}}]}"))
case placesSearchPath:
_, _ = w.Write([]byte(`{"places":[{"id":"abc","displayName":{"text":"Cafe"}}]}`))
default:
t.Fatalf("unexpected path: %s", r.URL.Path)
}
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"route",
"coffee",
"--from", "A",
"--to", "B",
"--api-key", "test-key",
"--base-url", server.URL,
"--routes-base-url", server.URL,
"--json",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "\"waypoints\"") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
}
func TestRunRouteHuman(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/directions/v2:computeRoutes":
_, _ = w.Write([]byte("{\"routes\":[{\"polyline\":{\"encodedPolyline\":\"_p~iF~ps|U_ulLnnqC_mqNvxq`@\"}}]}"))
case placesSearchPath:
_, _ = w.Write([]byte(`{"places":[{"id":"abc","displayName":{"text":"Cafe"}}]}`))
default:
t.Fatalf("unexpected path: %s", r.URL.Path)
}
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"route",
"coffee",
"--from", "A",
"--to", "B",
"--api-key", "test-key",
"--base-url", server.URL,
"--routes-base-url", server.URL,
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "Route waypoints") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
if stderr.Len() != 0 {
t.Fatalf("unexpected stderr: %s", stderr.String())
}
}
func TestRunRouteValidationError(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"route",
"coffee",
"--from", "A",
"--to", "B",
"--mode", "FLY",
"--api-key", "test-key",
}, &stdout, &stderr)
if exitCode != 2 {
t.Fatalf("expected validation error exit code 2, got %d", exitCode)
}
}
func TestRunRouteMissingFrom(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"route",
"coffee",
"--to", "B",
"--api-key", "test-key",
}, &stdout, &stderr)
if exitCode != 2 {
t.Fatalf("expected validation error exit code 2, got %d", exitCode)
}
}
func TestRunDetailsJSON(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/places/place-1" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
_, _ = w.Write([]byte(`{"id": "place-1"}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"details",
"place-1",
"--api-key", "test-key",
"--base-url", server.URL,
"--json",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "\"place_id\"") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
}
func TestRunDetailsWithReviews(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("X-Goog-FieldMask"), "reviews") {
t.Fatalf("expected reviews in field mask: %s", r.Header.Get("X-Goog-FieldMask"))
}
_, _ = w.Write([]byte(`{"id": "place-1", "reviews": [{"name": "reviews/1"}]}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"details",
"place-1",
"--api-key", "test-key",
"--base-url", server.URL,
"--reviews",
"--json",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "\"reviews\"") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
}
func TestRunDetailsWithPhotos(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("X-Goog-FieldMask"), "photos") {
t.Fatalf("expected photos in field mask: %s", r.Header.Get("X-Goog-FieldMask"))
}
_, _ = w.Write([]byte(`{"id": "place-1", "photos": [{"name": "places/place-1/photos/photo-1"}]}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"details",
"place-1",
"--api-key", "test-key",
"--base-url", server.URL,
"--photos",
"--json",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "\"photos\"") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
}
func TestRunDetailsHuman(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`{"id": "place-2", "displayName": {"text": "Park"}}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"details",
"place-2",
"--api-key", "test-key",
"--base-url", server.URL,
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "Park") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
}
func TestRunPhotoJSON(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/places/place-1/photos/photo-1/media" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
_, _ = w.Write([]byte(`{"photoUri": "https://example.com/photo.jpg"}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"photo",
"places/place-1/photos/photo-1",
"--api-key", "test-key",
"--base-url", server.URL,
"--json",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "\"photo_uri\"") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
}
func TestRunPhotoHuman(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`{"photoUri": "https://example.com/photo.jpg"}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"photo",
"places/place-1/photos/photo-1",
"--api-key", "test-key",
"--base-url", server.URL,
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "photo.jpg") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
if stderr.Len() != 0 {
t.Fatalf("unexpected stderr: %s", stderr.String())
}
}
func TestRunResolveHuman(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != placesSearchPath {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
_, _ = w.Write([]byte(`{"places": [{"id": "loc-1", "displayName": {"text": "Downtown"}}]}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"resolve",
"Downtown",
"--api-key", "test-key",
"--base-url", server.URL,
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if !strings.Contains(stdout.String(), "Downtown") {
t.Fatalf("unexpected stdout: %s", stdout.String())
}
if stderr.Len() != 0 {
t.Fatalf("unexpected stderr: %s", stderr.String())
}
}
func TestRunResolveJSON(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`{"places": [{"id": "loc-2"}]}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"resolve",
"Downtown",
"--api-key", "test-key",
"--base-url", server.URL,
"--json",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if stderr.Len() != 0 {
t.Fatalf("unexpected stderr: %s", stderr.String())
}
results := decodeJSONArray(t, stdout.String())
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d (stdout=%s)", len(results), stdout.String())
}
if results[0]["place_id"] != "loc-2" {
t.Fatalf("unexpected result payload: %#v", results[0])
}
}
func TestRunVersion(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{"--version"}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if strings.TrimSpace(stdout.String()) != Version {
t.Fatalf("unexpected version: %s", stdout.String())
}
}
func TestRunMissingCommand(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{}, &stdout, &stderr)
if exitCode == 0 {
t.Fatalf("expected non-zero exit code")
}
}
func TestRunParseError(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{"search", "--api-key", "x"}, &stdout, &stderr)
if exitCode == 0 {
t.Fatalf("expected parse error")
}
}
func TestRunLocationBiasError(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{"search", "coffee", "--lat", "1", "--api-key", "x"}, &stdout, &stderr)
if exitCode != 2 {
t.Fatalf("expected validation error exit code 2, got %d", exitCode)
}
}
func TestRunNearbyLocationRestrictionError(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{"nearby", "--lat", "1", "--api-key", "x"}, &stdout, &stderr)
if exitCode != 2 {
t.Fatalf("expected validation error exit code 2, got %d", exitCode)
}
}
func TestRunHelp(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{"--help"}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
if stdout.Len() == 0 {
t.Fatalf("expected help output")
}
}
func TestVersionFlagIsBool(t *testing.T) {
var flag VersionFlag
if !flag.IsBool() {
t.Fatalf("expected IsBool true")
}
}
func TestRunSearchJSONWithNextPageToken(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != placesSearchPath {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
_, _ = w.Write([]byte(`{"places": [{"id": "abc"}], "nextPageToken": "token123"}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"search",
"coffee",
"--api-key", "test-key",
"--base-url", server.URL,
"--json",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d (stdout=%s stderr=%s)", exitCode, stdout.String(), stderr.String())
}
results := decodeJSONArray(t, stdout.String())
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d (stdout=%s)", len(results), stdout.String())
}
if strings.Contains(stdout.String(), "next_page_token") {
t.Fatalf("unexpected next_page_token in stdout: %s", stdout.String())
}
if !strings.Contains(stderr.String(), "next_page_token: token123") {
t.Fatalf("expected next_page_token in stderr, got: %s", stderr.String())
}
}
func TestRunNearbyJSONWithNextPageToken(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/places:searchNearby" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
_, _ = w.Write([]byte(`{"places": [{"id": "abc"}], "nextPageToken": "nearby-token"}`))
}))
defer server.Close()
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := Run([]string{
"nearby",
"--lat", "1",
"--lng", "2",
"--radius-m", "3",
"--api-key", "test-key",
"--base-url", server.URL,
"--json",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d", exitCode)
}
results := decodeJSONArray(t, stdout.String())
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d (stdout=%s)", len(results), stdout.String())
}
if strings.Contains(stdout.String(), "next_page_token") {
t.Fatalf("unexpected next_page_token in stdout: %s", stdout.String())
}
if !strings.Contains(stderr.String(), "next_page_token: nearby-token") {
t.Fatalf("expected next_page_token in stderr, got: %s", stderr.String())
}
}
func TestWriteJSONError(t *testing.T) {
err := writeJSON(&bytes.Buffer{}, map[string]any{"bad": func() {}})
if err == nil {
t.Fatalf("expected json error")
}
}
func TestWriteJSON(t *testing.T) {
var out bytes.Buffer
if err := writeJSON(&out, map[string]string{"ok": "true"}); err != nil {
t.Fatalf("writeJSON error: %v", err)
}
if !strings.Contains(out.String(), "\"ok\"") {
t.Fatalf("unexpected json output: %s", out.String())
}
}
func decodeJSONArray(t *testing.T, payload string) []map[string]any {
t.Helper()
var items []map[string]any
if err := json.Unmarshal([]byte(strings.TrimSpace(payload)), &items); err != nil {
t.Fatalf("expected JSON array output, got error: %v (payload=%s)", err, payload)
}
return items
}
func TestHandleError(t *testing.T) {
if code := handleError(&bytes.Buffer{}, nil); code != 0 {
t.Fatalf("expected 0")
}
if code := handleError(&bytes.Buffer{}, goplaces.ValidationError{Field: "x", Message: "bad"}); code != 2 {
t.Fatalf("expected validation exit 2")
}
if code := handleError(&bytes.Buffer{}, goplaces.ErrMissingAPIKey); code != 2 {
t.Fatalf("expected missing api key exit 2")
}
if code := handleError(&bytes.Buffer{}, errors.New("boom")); code != 1 {
t.Fatalf("expected generic exit 1")
}
}