Skip to content

Commit 87bc9f5

Browse files
committed
Replace legacy e2e suite with owner-only CLI contract tests
1 parent 0319f41 commit 87bc9f5

35 files changed

Lines changed: 1746 additions & 4967 deletions

.env.test.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# e2e test credentials template.
2+
# Copy to .env.test and fill in real tokens (that file is git-excluded).
3+
4+
FIZZY_TEST_TOKEN=fizzy_your_token_here
5+
FIZZY_TEST_ACCOUNT=1234567
6+
FIZZY_TEST_API_URL=https://app.fizzy.do

Makefile

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
.PHONY: test test-unit test-e2e test-go test-file test-run build clean tidy help \
1+
.PHONY: test test-unit test-e2e e2e test-go test-file e2e-file test-run e2e-run build clean tidy help \
22
check-toolchain fmt fmt-check vet lint tidy-check race-test vuln secrets \
33
replace-check security check release-check release tools \
44
surface-snapshot surface-check lint-actions
55

66
BINARY := $(CURDIR)/bin/fizzy
7+
8+
# Load local test credentials if present (file is git-excluded via .git/info/exclude).
9+
-include .env.test
10+
export FIZZY_TEST_TOKEN FIZZY_TEST_ACCOUNT FIZZY_TEST_API_URL
711
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
812
LDFLAGS := -X main.version=$(VERSION)
913

@@ -20,10 +24,13 @@ help:
2024
@echo "Usage:"
2125
@echo " make build Build the CLI"
2226
@echo " make test-unit Run unit tests (no API required)"
23-
@echo " make test-e2e Run e2e tests (requires API credentials)"
24-
@echo " make test Alias for test-e2e"
25-
@echo " make test-file Run a specific e2e test file"
26-
@echo " make test-run Run a specific e2e test by name"
27+
@echo " make e2e Run owner-only CLI contract e2e tests"
28+
@echo " make test-e2e Alias for e2e"
29+
@echo " make test Alias for e2e"
30+
@echo " make e2e-file Run a specific CLI contract e2e test file"
31+
@echo " make test-file Alias for e2e-file"
32+
@echo " make e2e-run Run a specific CLI contract e2e test by name"
33+
@echo " make test-run Alias for e2e-run"
2734
@echo " make clean Remove build artifacts"
2835
@echo " make tidy Tidy dependencies"
2936
@echo ""
@@ -45,17 +52,19 @@ help:
4552
@echo " make tools Install dev tools"
4653
@echo ""
4754
@echo "Environment variables (required for e2e tests):"
48-
@echo " FIZZY_TEST_TOKEN API token"
49-
@echo " FIZZY_TEST_ACCOUNT Account slug"
50-
@echo " FIZZY_TEST_API_URL API base URL (default: https://app.fizzy.do)"
51-
@echo " FIZZY_TEST_USER_ID User ID for user update/deactivate tests (optional)"
55+
@echo " FIZZY_TEST_TOKEN API token"
56+
@echo " FIZZY_TEST_ACCOUNT Account slug"
57+
@echo " FIZZY_TEST_API_URL API base URL (default: https://app.fizzy.do)"
58+
@echo " FIZZY_TEST_BINARY Prebuilt binary path (optional)"
59+
@echo " FIZZY_E2E_KEEP_FIXTURE Set to 1 to skip final fixture teardown"
60+
@echo " FIZZY_E2E_TEARDOWN_DELAY Delay teardown by N seconds"
5261
@echo ""
5362
@echo "Examples:"
5463
@echo " make build"
5564
@echo " make test-unit"
5665
@echo " export FIZZY_TEST_TOKEN=your-token"
5766
@echo " export FIZZY_TEST_ACCOUNT=your-account"
58-
@echo " make test-e2e"
67+
@echo " make e2e"
5968

6069
# Toolchain guard — fails fast when PATH go and GOROOT go disagree
6170
check-toolchain:
@@ -80,28 +89,33 @@ test-unit: check-toolchain
8089
go test -v ./internal/...
8190

8291
# Run e2e tests (requires API credentials)
83-
test-e2e: build
92+
e2e: build
8493
@if [ -z "$$FIZZY_TEST_TOKEN" ]; then echo "Error: FIZZY_TEST_TOKEN not set"; exit 1; fi
8594
@if [ -z "$$FIZZY_TEST_ACCOUNT" ]; then echo "Error: FIZZY_TEST_ACCOUNT not set"; exit 1; fi
86-
FIZZY_TEST_BINARY=$(BINARY) go test -v ./e2e/tests/...
95+
FIZZY_TEST_BINARY=$(BINARY) go test -v -timeout 10m ./e2e/cli_tests/...
96+
97+
test-e2e: e2e
8798

88-
# Alias for test-e2e
89-
test: test-e2e
90-
test-go: test-e2e
99+
test: e2e
100+
test-go: e2e
91101

92-
# Run a single test file (e.g., make test-file FILE=board)
93-
test-file: build
94-
@if [ -z "$(FILE)" ]; then echo "Usage: make test-file FILE=board"; exit 1; fi
102+
# Run a single test file (e.g., make e2e-file FILE=crud_board)
103+
e2e-file: build
104+
@if [ -z "$(FILE)" ]; then echo "Usage: make e2e-file FILE=crud_board"; exit 1; fi
95105
@if [ -z "$$FIZZY_TEST_TOKEN" ]; then echo "Error: FIZZY_TEST_TOKEN not set"; exit 1; fi
96106
@if [ -z "$$FIZZY_TEST_ACCOUNT" ]; then echo "Error: FIZZY_TEST_ACCOUNT not set"; exit 1; fi
97-
FIZZY_TEST_BINARY=$(BINARY) go test -v ./e2e/tests/$(FILE)_test.go
107+
FIZZY_TEST_BINARY=$(BINARY) go test -v ./e2e/cli_tests/$(FILE)_test.go
98108

99-
# Run a single test by name (e.g., make test-run NAME=TestBoardCRUD)
100-
test-run: build
101-
@if [ -z "$(NAME)" ]; then echo "Usage: make test-run NAME=TestBoardCRUD"; exit 1; fi
109+
test-file: e2e-file
110+
111+
# Run a single test by name (e.g., make e2e-run NAME=TestBoardList)
112+
e2e-run: build
113+
@if [ -z "$(NAME)" ]; then echo "Usage: make e2e-run NAME=TestBoardList"; exit 1; fi
102114
@if [ -z "$$FIZZY_TEST_TOKEN" ]; then echo "Error: FIZZY_TEST_TOKEN not set"; exit 1; fi
103115
@if [ -z "$$FIZZY_TEST_ACCOUNT" ]; then echo "Error: FIZZY_TEST_ACCOUNT not set"; exit 1; fi
104-
FIZZY_TEST_BINARY=$(BINARY) go test -v -run $(NAME) ./e2e/tests/...
116+
FIZZY_TEST_BINARY=$(BINARY) go test -v -run $(NAME) ./e2e/cli_tests/...
117+
118+
test-run: e2e-run
105119

106120
# Format Go source
107121
fmt:

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,20 @@ fizzy skill install
188188
```bash
189189
make build # Build binary
190190
make test-unit # Run unit tests (no API required)
191-
make test-e2e # Run e2e tests (requires FIZZY_TEST_TOKEN, FIZZY_TEST_ACCOUNT)
191+
make e2e # Run owner-only CLI contract e2e suite
192+
make e2e-run NAME=TestBoardList
192193
```
193194

195+
E2E requirements:
196+
- `FIZZY_TEST_TOKEN`
197+
- `FIZZY_TEST_ACCOUNT`
198+
- optional: `FIZZY_TEST_API_URL`
199+
- optional: `FIZZY_TEST_BINARY`
200+
201+
Useful local inspection modes:
202+
- `FIZZY_E2E_KEEP_FIXTURE=1 make e2e`
203+
- `FIZZY_E2E_TEARDOWN_DELAY=120 make e2e`
204+
194205
## License
195206

196207
[MIT](LICENSE)

e2e/cli_tests/account_user_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package clitests
2+
3+
import (
4+
"strconv"
5+
"testing"
6+
)
7+
8+
func TestAccountShow(t *testing.T) {
9+
assertOK(t, newHarness(t).Run("account", "show"))
10+
}
11+
12+
func TestAccountSettingsUpdateWithCurrentName(t *testing.T) {
13+
h := newHarness(t)
14+
show := h.Run("account", "show")
15+
assertOK(t, show)
16+
currentName := show.GetDataString("name")
17+
if currentName == "" {
18+
t.Skip("account show returned no name")
19+
}
20+
assertOK(t, h.Run("account", "settings-update", "--name", currentName))
21+
}
22+
23+
func TestAccountEntropyWithCurrentValue(t *testing.T) {
24+
h := newHarness(t)
25+
show := h.Run("account", "show")
26+
assertOK(t, show)
27+
days := show.GetDataInt("auto_postpone_period_in_days")
28+
if days == 0 {
29+
days = 7
30+
}
31+
assertOK(t, h.Run("account", "entropy", "--auto_postpone_period_in_days", strconv.Itoa(days)))
32+
}
33+
34+
func TestAccountJoinCodeShow(t *testing.T) {
35+
assertOK(t, newHarness(t).Run("account", "join-code-show"))
36+
}
37+
38+
func TestAccountExportCreateShow(t *testing.T) {
39+
h := newHarness(t)
40+
create := h.Run("account", "export-create")
41+
assertOK(t, create)
42+
exportID := create.GetDataString("id")
43+
if exportID == "" {
44+
exportID = mapValueString(create.GetDataMap(), "id")
45+
}
46+
if exportID == "" {
47+
t.Fatal("expected export ID in export-create response")
48+
}
49+
show := h.Run("account", "export-show", exportID)
50+
assertOK(t, show)
51+
if got := mapValueString(show.GetDataMap(), "id"); got != exportID {
52+
t.Fatalf("expected export-show id %q, got %q", exportID, got)
53+
}
54+
if got := mapValueString(show.GetDataMap(), "status"); got == "" {
55+
t.Fatal("expected export status in export-show response")
56+
}
57+
}
58+
59+
func TestUserList(t *testing.T) {
60+
result := newHarness(t).Run("user", "list")
61+
assertOK(t, result)
62+
if result.GetDataArray() == nil {
63+
t.Fatal("expected array response")
64+
}
65+
}
66+
67+
func TestUserShowAndUpdateOwnProfile(t *testing.T) {
68+
h := newHarness(t)
69+
userID := currentUserID(t, h)
70+
show := h.Run("user", "show", userID)
71+
assertOK(t, show)
72+
currentName := show.GetDataString("name")
73+
if currentName == "" {
74+
t.Skip("user show returned no name")
75+
}
76+
assertOK(t, h.Run("user", "update", userID, "--name", currentName))
77+
}
78+
79+
func TestUserAvatarUpdateAndRemove(t *testing.T) {
80+
h := newHarness(t)
81+
userID := currentUserID(t, h)
82+
fixturePath := fixtureFile(t, "test_image.png")
83+
84+
show := h.Run("user", "show", userID)
85+
assertOK(t, show)
86+
avatarURL := show.GetDataString("avatar_url")
87+
if avatarURL == "" {
88+
t.Skip("user show returned no avatar_url")
89+
}
90+
initiallyAttached := avatarRedirects(t, avatarURL)
91+
if initiallyAttached {
92+
t.Cleanup(func() {
93+
assertOK(t, newHarness(t).Run("user", "update", userID, "--avatar", fixturePath))
94+
if !avatarRedirects(t, avatarURL) {
95+
t.Fatal("expected avatar to be restored")
96+
}
97+
})
98+
}
99+
100+
assertOK(t, h.Run("user", "update", userID, "--avatar", fixturePath))
101+
if !avatarRedirects(t, avatarURL) {
102+
t.Fatal("expected uploaded avatar endpoint to redirect to an image blob")
103+
}
104+
105+
assertOK(t, h.Run("user", "avatar-remove", userID))
106+
if avatarRedirects(t, avatarURL) {
107+
t.Fatal("expected avatar endpoint to fall back to generated SVG after removal")
108+
}
109+
}

e2e/cli_tests/crud_board_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package clitests
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
"time"
7+
8+
"github.com/basecamp/fizzy-cli/e2e/harness"
9+
)
10+
11+
func TestBoardList(t *testing.T) {
12+
h := newHarness(t)
13+
result := h.Run("board", "list")
14+
assertOK(t, result)
15+
if result.GetDataArray() == nil {
16+
t.Fatal("expected array response")
17+
}
18+
}
19+
20+
func TestBoardListAll(t *testing.T) {
21+
assertOK(t, newHarness(t).Run("board", "list", "--all"))
22+
}
23+
24+
func TestBoardListPaginated(t *testing.T) {
25+
assertOK(t, newHarness(t).Run("board", "list", "--page", "1"))
26+
}
27+
28+
func TestBoardShow(t *testing.T) {
29+
result := newHarness(t).Run("board", "show", fixture.BoardID)
30+
assertOK(t, result)
31+
if got := result.GetDataString("id"); got != fixture.BoardID {
32+
t.Fatalf("expected board id %q, got %q", fixture.BoardID, got)
33+
}
34+
}
35+
36+
func TestBoardShowNotFound(t *testing.T) {
37+
assertResult(t, newHarness(t).Run("board", "show", "nonexistent-board-id-99999"), harness.ExitNotFound)
38+
}
39+
40+
func TestBoardCreateUpdateDelete(t *testing.T) {
41+
h := newHarness(t)
42+
boardID := createBoard(t, h)
43+
result := h.Run("board", "update", boardID, "--name", fmt.Sprintf("Updated Board %d", time.Now().UnixNano()))
44+
assertOK(t, result)
45+
46+
deleteResult := h.Run("board", "delete", boardID)
47+
assertOK(t, deleteResult)
48+
if !deleteResult.GetDataBool("deleted") {
49+
t.Fatal("expected deleted=true")
50+
}
51+
}
52+
53+
func TestBoardPublishUnpublish(t *testing.T) {
54+
h := newHarness(t)
55+
boardID := createBoard(t, h)
56+
publish := h.Run("board", "publish", boardID)
57+
assertOK(t, publish)
58+
if publish.GetDataString("public_url") == "" {
59+
t.Fatal("expected public_url in publish response")
60+
}
61+
assertOK(t, h.Run("board", "unpublish", boardID))
62+
}
63+
64+
func TestBoardEntropy(t *testing.T) {
65+
h := newHarness(t)
66+
boardID := createBoard(t, h)
67+
assertOK(t, h.Run("board", "entropy", boardID, "--auto_postpone_period_in_days", "7"))
68+
}
69+
70+
func TestBoardViews(t *testing.T) {
71+
h := newHarness(t)
72+
assertOK(t, h.Run("board", "closed", "--board", fixture.BoardID))
73+
assertOK(t, h.Run("board", "postponed", "--board", fixture.BoardID))
74+
assertOK(t, h.Run("board", "stream", "--board", fixture.BoardID))
75+
}
76+
77+
func TestBoardInvolvement(t *testing.T) {
78+
h := newHarness(t)
79+
boardID := createBoard(t, h)
80+
assertOK(t, h.Run("board", "involvement", boardID, "--involvement", "watching"))
81+
assertOK(t, h.Run("board", "involvement", boardID, "--involvement", "access_only"))
82+
}

0 commit comments

Comments
 (0)