Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.test.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# e2e test credentials template.
# Copy to .env.test and fill in real tokens (that file is git-excluded).

FIZZY_TEST_TOKEN=fizzy_your_token_here
FIZZY_TEST_ACCOUNT=1234567
FIZZY_TEST_API_URL=https://app.fizzy.do
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ completions/
# Profiling
profiles/
default.pgo

# Local test credentials
.env.test
64 changes: 41 additions & 23 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
.PHONY: test test-unit test-e2e test-go test-file test-run build clean tidy help \
.PHONY: test test-unit test-e2e e2e test-go test-file e2e-file test-run e2e-run build clean tidy help \
check-toolchain fmt fmt-check vet lint tidy-check race-test vuln secrets \
replace-check security check release-check release tools \
surface-snapshot surface-check lint-actions

BINARY := $(CURDIR)/bin/fizzy
FIZZY_TEST_BINARY ?= $(BINARY)

# Load local test credentials if present, but refuse tracked local secret files.
ifneq ($(shell git ls-files --error-unmatch .env.test >/dev/null 2>&1 && echo tracked),)
$(error .env.test is tracked by Git. Remove it from version control and keep local secret files untracked)
endif
-include .env.test
export FIZZY_TEST_TOKEN FIZZY_TEST_ACCOUNT FIZZY_TEST_API_URL FIZZY_TEST_BINARY
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
LDFLAGS := -X main.version=$(VERSION)

Expand All @@ -20,10 +28,13 @@ help:
@echo "Usage:"
@echo " make build Build the CLI"
@echo " make test-unit Run unit tests (no API required)"
@echo " make test-e2e Run e2e tests (requires API credentials)"
@echo " make test Alias for test-e2e"
@echo " make test-file Run a specific e2e test file"
@echo " make test-run Run a specific e2e test by name"
@echo " make e2e Run owner-only CLI contract e2e tests"
@echo " make test-e2e Alias for e2e"
@echo " make test Alias for e2e"
@echo " make e2e-file Run a specific CLI contract e2e test file"
@echo " make test-file Alias for e2e-file"
@echo " make e2e-run Run a specific CLI contract e2e test by name"
@echo " make test-run Alias for e2e-run"
@echo " make clean Remove build artifacts"
@echo " make tidy Tidy dependencies"
@echo ""
Expand All @@ -45,17 +56,19 @@ help:
@echo " make tools Install dev tools"
@echo ""
@echo "Environment variables (required for e2e tests):"
@echo " FIZZY_TEST_TOKEN API token"
@echo " FIZZY_TEST_ACCOUNT Account slug"
@echo " FIZZY_TEST_API_URL API base URL (default: https://app.fizzy.do)"
@echo " FIZZY_TEST_USER_ID User ID for user update/deactivate tests (optional)"
@echo " FIZZY_TEST_TOKEN API token"
@echo " FIZZY_TEST_ACCOUNT Account slug"
@echo " FIZZY_TEST_API_URL API base URL (default: https://app.fizzy.do)"
@echo " FIZZY_TEST_BINARY Prebuilt binary path (optional)"
@echo " FIZZY_E2E_KEEP_FIXTURE Set to 1 to skip final fixture teardown"
@echo " FIZZY_E2E_TEARDOWN_DELAY Delay teardown by N seconds"
@echo ""
@echo "Examples:"
@echo " make build"
@echo " make test-unit"
@echo " export FIZZY_TEST_TOKEN=your-token"
@echo " export FIZZY_TEST_ACCOUNT=your-account"
@echo " make test-e2e"
@echo " make e2e"

# Toolchain guard — fails fast when PATH go and GOROOT go disagree
check-toolchain:
Expand All @@ -80,28 +93,33 @@ test-unit: check-toolchain
go test -v ./internal/...

# Run e2e tests (requires API credentials)
test-e2e: build
e2e: build
@if [ -z "$$FIZZY_TEST_TOKEN" ]; then echo "Error: FIZZY_TEST_TOKEN not set"; exit 1; fi
@if [ -z "$$FIZZY_TEST_ACCOUNT" ]; then echo "Error: FIZZY_TEST_ACCOUNT not set"; exit 1; fi
FIZZY_TEST_BINARY=$(BINARY) go test -v ./e2e/tests/...
go test -v -timeout 10m ./e2e/cli_tests/...

Comment thread
robzolkos marked this conversation as resolved.
# Alias for test-e2e
test: test-e2e
test-go: test-e2e
test-e2e: e2e

# Run a single test file (e.g., make test-file FILE=board)
test-file: build
@if [ -z "$(FILE)" ]; then echo "Usage: make test-file FILE=board"; exit 1; fi
test: e2e
test-go: e2e

# Run a single test file (e.g., make e2e-file FILE=crud_board)
e2e-file: build
@if [ -z "$(FILE)" ]; then echo "Usage: make e2e-file FILE=crud_board"; exit 1; fi
@if [ -z "$$FIZZY_TEST_TOKEN" ]; then echo "Error: FIZZY_TEST_TOKEN not set"; exit 1; fi
@if [ -z "$$FIZZY_TEST_ACCOUNT" ]; then echo "Error: FIZZY_TEST_ACCOUNT not set"; exit 1; fi
FIZZY_TEST_BINARY=$(BINARY) go test -v ./e2e/tests/$(FILE)_test.go
go test -v ./e2e/cli_tests/$(FILE)_test.go

test-file: e2e-file

# Run a single test by name (e.g., make test-run NAME=TestBoardCRUD)
test-run: build
@if [ -z "$(NAME)" ]; then echo "Usage: make test-run NAME=TestBoardCRUD"; exit 1; fi
# Run a single test by name (e.g., make e2e-run NAME=TestBoardList)
e2e-run: build
@if [ -z "$(NAME)" ]; then echo "Usage: make e2e-run NAME=TestBoardList"; exit 1; fi
@if [ -z "$$FIZZY_TEST_TOKEN" ]; then echo "Error: FIZZY_TEST_TOKEN not set"; exit 1; fi
@if [ -z "$$FIZZY_TEST_ACCOUNT" ]; then echo "Error: FIZZY_TEST_ACCOUNT not set"; exit 1; fi
FIZZY_TEST_BINARY=$(BINARY) go test -v -run $(NAME) ./e2e/tests/...
go test -v -run $(NAME) ./e2e/cli_tests/...

test-run: e2e-run

# Format Go source
fmt:
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,20 @@ fizzy skill install
```bash
make build # Build binary
make test-unit # Run unit tests (no API required)
make test-e2e # Run e2e tests (requires FIZZY_TEST_TOKEN, FIZZY_TEST_ACCOUNT)
make e2e # Run owner-only CLI contract e2e suite
make e2e-run NAME=TestBoardList
```

E2E requirements:
- `FIZZY_TEST_TOKEN`
- `FIZZY_TEST_ACCOUNT`
- optional: `FIZZY_TEST_API_URL`
- optional: `FIZZY_TEST_BINARY`

Useful local inspection modes:
- `FIZZY_E2E_KEEP_FIXTURE=1 make e2e`
- `FIZZY_E2E_TEARDOWN_DELAY=120 make e2e`

## License

[MIT](LICENSE)
124 changes: 124 additions & 0 deletions e2e/cli_tests/account_user_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package clitests

import (
"strconv"
"testing"
)

func TestAccountShow(t *testing.T) {
assertOK(t, newHarness(t).Run("account", "show"))
}

func TestAccountSettingsUpdateWithCurrentName(t *testing.T) {
h := newHarness(t)
show := h.Run("account", "show")
assertOK(t, show)
currentName := show.GetDataString("name")
if currentName == "" {
t.Skip("account show returned no name")
}
assertOK(t, h.Run("account", "settings-update", "--name", currentName))
show = h.Run("account", "show")
assertOK(t, show)
if got := show.GetDataString("name"); got != currentName {
t.Fatalf("expected account name %q after settings-update, got %q", currentName, got)
}
}

func TestAccountEntropyWithCurrentValue(t *testing.T) {
h := newHarness(t)
show := h.Run("account", "show")
assertOK(t, show)
days := show.GetDataInt("auto_postpone_period_in_days")
if days == 0 {
days = 7
}
assertOK(t, h.Run("account", "entropy", "--auto_postpone_period_in_days", strconv.Itoa(days)))
show = h.Run("account", "show")
assertOK(t, show)
if got := show.GetDataInt("auto_postpone_period_in_days"); got != days {
t.Fatalf("expected auto_postpone_period_in_days=%d, got %d", days, got)
}
}

func TestAccountJoinCodeShow(t *testing.T) {
assertOK(t, newHarness(t).Run("account", "join-code-show"))
}

func TestAccountExportCreateShow(t *testing.T) {
h := newHarness(t)
create := h.Run("account", "export-create")
assertOK(t, create)
exportID := create.GetDataString("id")
if exportID == "" {
exportID = mapValueString(create.GetDataMap(), "id")
}
if exportID == "" {
t.Fatal("expected export ID in export-create response")
}
show := h.Run("account", "export-show", exportID)
assertOK(t, show)
if got := mapValueString(show.GetDataMap(), "id"); got != exportID {
t.Fatalf("expected export-show id %q, got %q", exportID, got)
}
if got := mapValueString(show.GetDataMap(), "status"); got == "" {
t.Fatal("expected export status in export-show response")
}
}

func TestUserList(t *testing.T) {
result := newHarness(t).Run("user", "list")
assertOK(t, result)
if result.GetDataArray() == nil {
t.Fatal("expected array response")
}
}

func TestUserShowAndUpdateOwnProfile(t *testing.T) {
h := newHarness(t)
userID := currentUserID(t, h)
show := h.Run("user", "show", userID)
assertOK(t, show)
currentName := show.GetDataString("name")
if currentName == "" {
t.Skip("user show returned no name")
}
assertOK(t, h.Run("user", "update", userID, "--name", currentName))
show = h.Run("user", "show", userID)
assertOK(t, show)
if got := show.GetDataString("name"); got != currentName {
t.Fatalf("expected user name %q after update, got %q", currentName, got)
}
}

func TestUserAvatarUpdateAndRemove(t *testing.T) {
h := newHarness(t)
userID := currentUserID(t, h)
fixturePath := fixtureFile(t, "test_image.png")

show := h.Run("user", "show", userID)
assertOK(t, show)
avatarURL := show.GetDataString("avatar_url")
if avatarURL == "" {
t.Skip("user show returned no avatar_url")
}
initiallyAttached := avatarRedirects(t, avatarURL)
if initiallyAttached {
t.Cleanup(func() {
assertOK(t, newHarness(t).Run("user", "update", userID, "--avatar", fixturePath))
if !avatarRedirects(t, avatarURL) {
t.Fatal("expected avatar to be restored")
}
})
}

assertOK(t, h.Run("user", "update", userID, "--avatar", fixturePath))
if !avatarRedirects(t, avatarURL) {
t.Fatal("expected uploaded avatar endpoint to redirect to an image blob")
}

assertOK(t, h.Run("user", "avatar-remove", userID))
if avatarRedirects(t, avatarURL) {
t.Fatal("expected avatar endpoint to fall back to generated SVG after removal")
}
}
Loading
Loading