Reorganize repo: backend/, deploy/, docs/ layout + AGENTS.md
Restructure the monorepo into clear top-level directories: - backend/: Go module root (cmd, internal, pkg, configs, migrations, docs/swagger, scripts, tests, go.mod, Makefile, .air.toml) - deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd - docs/: project documentation + reports/ (moved from repo root) - AGENTS.md: new AI coding-agent guide at repo root Update all references to the new layout: - Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root) - docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile, env_file ../../.env, volume mounts ../../backend:/app - deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile - CI: working-directory: backend for go commands, file deploy/docker/Dockerfile, coverage path backend/coverage.out, health_check backend/scripts/ - backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../ - README: architecture tree, quickstart, config paths updated Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh, gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to preserve history. Build, vet, SQLite tests, and docker compose config verified.
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
# GoChat Test Coverage Report
|
||||
|
||||
> Generated: Initial template
|
||||
> Target: 80%+ coverage across all modules
|
||||
> Reference: Chatwoot RSpec coverage patterns mapped to Go testing conventions
|
||||
|
||||
## Coverage Targets Per Module
|
||||
|
||||
| Module | Target Coverage | Priority | Status |
|
||||
|--------|----------------|----------|--------|
|
||||
| `internal/model` | 90% | High | Pending |
|
||||
| `internal/repository` | 85% | High | Pending |
|
||||
| `internal/service` | 80% | High | Pending |
|
||||
| `internal/auth` | 85% | High | Pending |
|
||||
| `internal/handler` | 75% | Medium | Pending |
|
||||
| `internal/middleware` | 80% | Medium | Pending |
|
||||
| `internal/channel` | 75% | Medium | Pending |
|
||||
| `internal/channel/provider` | 70% | Medium | Pending |
|
||||
| `internal/config` | 80% | Low | Pending |
|
||||
| `internal/app` | 60% | Low | Pending |
|
||||
| `internal/router` | 70% | Low | Pending |
|
||||
| `internal/ws` | 75% | Medium | Pending |
|
||||
| `internal/pubsub` | 75% | Medium | Pending |
|
||||
| `pkg/crypto` | 90% | High | Pending |
|
||||
| `pkg/logger` | 80% | Medium | Pending |
|
||||
|
||||
## Module Coverage Details
|
||||
|
||||
### internal/model (Target: 90%)
|
||||
|
||||
Model tests focus on:
|
||||
- CRUD operations via GORM (Create, Read, Update, Delete)
|
||||
- Association integrity (foreign keys, belongs-to, has-many)
|
||||
- Validation rules (required fields, unique constraints, format checks)
|
||||
- Soft delete behavior (DeletedAt field, recovery)
|
||||
- Custom types (JSONB permissions, enum fields)
|
||||
- Edge cases (null fields, empty strings, boundary values)
|
||||
|
||||
Key test files:
|
||||
- `internal/model/base_test.go` — Base model fields, timestamps, soft delete
|
||||
- `internal/model/user_test.go` — User CRUD, role validation, email uniqueness
|
||||
- `internal/model/account_test.go` — Account CRUD, locale/timezone defaults
|
||||
- `internal/model/conversation_test.go` — Conversation status transitions, assignee
|
||||
- `internal/model/message_test.go` — Message types, content validation, privacy
|
||||
- `internal/model/custom_role_test.go` — Permission matrix parsing, role CRUD
|
||||
- `internal/model/contact_test.go` — Contact identification, channel binding
|
||||
|
||||
### internal/repository (Target: 85%)
|
||||
|
||||
Repository tests focus on:
|
||||
- BaseRepository generic CRUD operations
|
||||
- Specialized repository query methods (FindByAccount, FindByStatus, etc.)
|
||||
- Pagination (offset/limit)
|
||||
- Error handling (record not found, duplicate key)
|
||||
- Soft-delete scoped queries
|
||||
- Transaction support
|
||||
|
||||
Key test files:
|
||||
- `internal/repository/repository_test.go` — BaseRepository[T] generic CRUD
|
||||
- `internal/repository/user_repo_test.go` — FindByEmail, FindByAccount
|
||||
- `internal/repository/conversation_repo_test.go` — FindByAccount, FindByStatus, FindByAssignee
|
||||
- `internal/repository/message_repo_test.go` — FindByConversation, Search
|
||||
- `internal/repository/account_repo_test.go` — Account CRUD operations
|
||||
|
||||
### internal/service (Target: 80%)
|
||||
|
||||
Service tests focus on:
|
||||
- Business logic correctness
|
||||
- Input validation
|
||||
- Error handling and propagation
|
||||
- Cross-service coordination
|
||||
- Authorization checks (RBAC integration)
|
||||
- AI/Copilot features (Captain, LLM Provider)
|
||||
|
||||
Key test files:
|
||||
- `internal/service/auth_service_test.go` — Login, Register, Refresh, Logout flows
|
||||
- `internal/service/conversation_service_test.go` — CRUD, status transitions, assignment
|
||||
- `internal/service/message_service_test.go` — Create, Search, privacy controls
|
||||
- `internal/service/rbac_service_test.go` — Role assignment, permission checking, custom roles
|
||||
- `internal/service/captain_service_test.go` — Captain Assistant CRUD, Document, Scenario
|
||||
- `internal/service/copilot_service_test.go` — Thread, Message, AI reply suggestions
|
||||
- `internal/service/llm_provider_test.go` — LLM Provider interface compliance
|
||||
|
||||
### internal/auth (Target: 85%)
|
||||
|
||||
Auth tests focus on:
|
||||
- JWT token generation and validation
|
||||
- Refresh token lifecycle
|
||||
- Permission system (administrator, agent, custom_role)
|
||||
- Policy context construction
|
||||
- MFA (TOTP) verification
|
||||
- OAuth provider integration
|
||||
|
||||
Key test files:
|
||||
- `internal/auth/jwt_test.go` — Token pair generation, expiry, validation
|
||||
- `internal/auth/permission_test.go` — Permission sets for each role level
|
||||
- `internal/auth/policy_test.go` — PolicyContext authorization checks
|
||||
- `internal/auth/mfa_test.go` — TOTP generation and verification
|
||||
- `internal/auth/oauth_test.go` — OAuth provider flow
|
||||
|
||||
### internal/handler (Target: 75%)
|
||||
|
||||
Handler tests focus on:
|
||||
- HTTP request/response correctness
|
||||
- Route parameter binding
|
||||
- Authentication middleware integration
|
||||
- Response format (JSON structure, status codes)
|
||||
- Error response formatting
|
||||
|
||||
Key test files:
|
||||
- `internal/handler/auth/auth_handler_test.go` — Auth endpoints
|
||||
- `internal/handler/api_v1/conversation_handler_test.go` — Conversation API
|
||||
- `internal/handler/api_v1/message_handler_test.go` — Message API
|
||||
- `internal/handler/webhook/webhook_handler_test.go` — Webhook processing
|
||||
- `internal/handler/ws/ws_handler_test.go` — WebSocket connection
|
||||
|
||||
### internal/middleware (Target: 80%)
|
||||
|
||||
Middleware tests focus on:
|
||||
- Authentication extraction from headers
|
||||
- Role-based access control enforcement
|
||||
- CORS configuration
|
||||
- Rate limiting behavior
|
||||
- Request logging
|
||||
|
||||
### internal/channel (Target: 75%)
|
||||
|
||||
Channel tests focus on:
|
||||
- Channel provider interface compliance
|
||||
- Webhook processing pipeline
|
||||
- Message broker routing
|
||||
- Provider-specific configuration validation
|
||||
- Incoming/outgoing message transformation
|
||||
|
||||
### pkg/crypto (Target: 90%)
|
||||
|
||||
Crypto tests focus on:
|
||||
- Password hashing (bcrypt)
|
||||
- JWT token generation and validation
|
||||
- Token expiry handling
|
||||
- Invalid token rejection
|
||||
|
||||
### pkg/logger (Target: 80%)
|
||||
|
||||
Logger tests focus on:
|
||||
- Log level filtering
|
||||
- Structured logging output
|
||||
- Context-aware logging
|
||||
|
||||
## Test Strategy Matrix
|
||||
|
||||
| Test Type | Scope | Tool | Count Target |
|
||||
|-----------|-------|------|-------------|
|
||||
| Unit Tests | Single function/method | `go test` | 200+ |
|
||||
| Integration Tests | Service + Repository | `go test` with SQLite | 50+ |
|
||||
| E2E Tests | Full HTTP flow | httptest + SQLite | 30+ |
|
||||
| Benchmark Tests | Performance | `go test -bench` | 20+ |
|
||||
| Mock Tests | External dependencies | httptest mock server | 15+ |
|
||||
|
||||
## Coverage Collection Method
|
||||
|
||||
```bash
|
||||
# Run all tests with coverage
|
||||
go test -coverprofile=coverage.out -covermode=atomic ./...
|
||||
|
||||
# View per-function coverage
|
||||
go tool cover -func=coverage.out
|
||||
|
||||
# Generate HTML report
|
||||
go tool cover -html=coverage.out -o coverage.html
|
||||
|
||||
# Run benchmarks
|
||||
go test -bench=. -benchmem ./internal/service/ ./internal/repository/ ./pkg/crypto/
|
||||
```
|
||||
|
||||
## Coverage Quality Gates
|
||||
|
||||
- **Critical modules** (model, repository, auth, crypto): Must achieve 85%+ coverage
|
||||
- **Business logic** (service, middleware): Must achieve 80%+ coverage
|
||||
- **HTTP layer** (handler, channel): Must achieve 75%+ coverage
|
||||
- **Infrastructure** (app, config, router): Must achieve 60%+ coverage
|
||||
- **Overall project**: Must achieve 80%+ average coverage
|
||||
|
||||
## Chatwoot Test Pattern Mapping
|
||||
|
||||
| Chatwoot Pattern | Go Equivalent | Coverage Focus |
|
||||
|-----------------|---------------|---------------|
|
||||
| `spec/models/` (RSpec model specs) | `internal/model/*_test.go` | Model CRUD + validations |
|
||||
| `spec/services/` (service_object specs) | `internal/service/*_test.go` | Business logic |
|
||||
| `spec/controllers/` (controller specs) | `internal/handler/*_test.go` | HTTP API responses |
|
||||
| `spec/policies/` (Pundit policy specs) | `internal/auth/*_test.go` | RBAC permission checks |
|
||||
| `spec/integration/` (integration specs) | `tests/e2e/*_test.go` | Full flow scenarios |
|
||||
| `spec/jobs/` (Sidekiq job specs) | `internal/worker/*_test.go` | Background task processing |
|
||||
| FactoryBot fixtures | SQLite in-memory GORM seeds | Test data setup |
|
||||
| Shoulda Matchers | testify/assert + custom validators | Assertion helpers |
|
||||
|
||||
## Running the Report
|
||||
|
||||
```bash
|
||||
# Quick unit test coverage
|
||||
./scripts/coverage/generate_report.sh --quick
|
||||
|
||||
# Full coverage with HTML report
|
||||
./scripts/coverage/generate_report.sh --html
|
||||
|
||||
# Include E2E tests
|
||||
./scripts/coverage/generate_report.sh --e2e --html
|
||||
|
||||
# Include benchmarks
|
||||
./scripts/coverage/generate_report.sh --bench --html
|
||||
```
|
||||
|
||||
## Continuous Improvement
|
||||
|
||||
Coverage targets should be reviewed quarterly. As new features are added:
|
||||
1. Each new module must have tests before merge
|
||||
2. Coverage must not decrease on existing modules
|
||||
3. Critical paths (auth, payments, data integrity) require 90%+ coverage
|
||||
4. Use `go test -race` to catch concurrent access issues
|
||||
@@ -0,0 +1,198 @@
|
||||
#!/bin/bash
|
||||
# generate_report.sh — Run all tests, collect coverage, generate HTML report
|
||||
# Reference: Chatwoot's test coverage tracking approach mapped to Go tooling
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/coverage/generate_report.sh # Full coverage report
|
||||
# ./scripts/coverage/generate_report.sh --quick # Quick report (unit tests only)
|
||||
# ./scripts/coverage/generate_report.sh --html # Generate HTML report
|
||||
#
|
||||
# Output:
|
||||
# - coverage.out: Raw coverage profile
|
||||
# - coverage.html: HTML coverage report (with --html)
|
||||
# - coverage_summary.txt: Per-package coverage summary
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
COVERAGE_DIR="$SCRIPT_DIR"
|
||||
|
||||
# Colors for terminal output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}=== GoChat Test Coverage Report Generator ===${NC}"
|
||||
echo ""
|
||||
|
||||
# Parse arguments
|
||||
QUICK_MODE=false
|
||||
HTML_MODE=false
|
||||
E2E_MODE=false
|
||||
BENCHMARK_MODE=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--quick) QUICK_MODE=true ;;
|
||||
--html) HTML_MODE=true ;;
|
||||
--e2e) E2E_MODE=true ;;
|
||||
--bench) BENCHMARK_MODE=true ;;
|
||||
--help)
|
||||
echo "Usage: $0 [--quick] [--html] [--e2e] [--bench]"
|
||||
echo ""
|
||||
echo " --quick Run unit tests only (skip E2E and benchmarks)"
|
||||
echo " --html Generate HTML coverage report"
|
||||
echo " --e2e Include E2E tests in coverage"
|
||||
echo " --bench Include benchmark tests"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown argument: $arg"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Ensure coverage output directory exists
|
||||
mkdir -p "$COVERAGE_DIR"
|
||||
|
||||
COVERAGE_FILE="$COVERAGE_DIR/coverage.out"
|
||||
COVERAGE_HTML="$COVERAGE_DIR/coverage.html"
|
||||
COVERAGE_SUMMARY="$COVERAGE_DIR/coverage_summary.txt"
|
||||
|
||||
# Clean previous coverage data
|
||||
rm -f "$COVERAGE_FILE" "$COVERAGE_HTML" "$COVERAGE_SUMMARY"
|
||||
|
||||
echo -e "${YELLOW}Running test suite with coverage profiling...${NC}"
|
||||
|
||||
# Build the test command
|
||||
TEST_PKGS=(
|
||||
"./internal/model/..."
|
||||
"./internal/repository/..."
|
||||
"./internal/service/..."
|
||||
"./internal/auth/..."
|
||||
"./internal/handler/..."
|
||||
"./internal/middleware/..."
|
||||
"./internal/channel/..."
|
||||
"./internal/channel/provider/..."
|
||||
"./internal/config/..."
|
||||
"./internal/app/..."
|
||||
"./internal/router/..."
|
||||
"./internal/ws/..."
|
||||
"./internal/pubsub/..."
|
||||
"./pkg/crypto/..."
|
||||
"./pkg/logger/..."
|
||||
)
|
||||
|
||||
if [ "$E2E_MODE" = true ]; then
|
||||
TEST_PKGS+=("./tests/e2e/...")
|
||||
fi
|
||||
|
||||
# Run tests with coverage
|
||||
echo -e "${BLUE}Running unit tests...${NC}"
|
||||
FAIL_COUNT=0
|
||||
|
||||
for pkg in "${TEST_PKGS[@]}"; do
|
||||
echo -e " Testing: ${GREEN}$pkg${NC}"
|
||||
|
||||
# Check if package has test files
|
||||
HAS_TESTS=$(find "$PROJECT_ROOT/${pkg%/...}" -name "*_test.go" 2>/dev/null | head -1)
|
||||
if [ -z "$HAS_TESTS" ]; then
|
||||
echo -e " ${YELLOW}No test files found — skipping${NC}"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Run tests with coverage
|
||||
PKG_COVERAGE="$COVERAGE_DIR/pkg_$(echo "$pkg" | tr '/' '_' | sed 's/\.\.\.//g').out"
|
||||
|
||||
if go test -coverprofile="$PKG_COVERAGE" -covermode=atomic -v "$pkg" 2>&1 | tee "$COVERAGE_DIR/test_output_$(echo "$pkg" | tr '/' '_').log"; then
|
||||
echo -e " ${GREEN}PASS${NC}"
|
||||
else
|
||||
echo -e " ${RED}FAIL${NC}"
|
||||
FAIL_COUNT=$((FAIL_COUNT + 1))
|
||||
fi
|
||||
|
||||
# Append package coverage to main coverage file
|
||||
if [ -f "$PKG_COVERAGE" ]; then
|
||||
cat "$PKG_COVERAGE" >> "$COVERAGE_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# Generate per-package coverage summary
|
||||
echo -e "${YELLOW}Generating coverage summary...${NC}"
|
||||
echo "# GoChat Coverage Summary" > "$COVERAGE_SUMMARY"
|
||||
echo "# Generated: $(date)" >> "$COVERAGE_SUMMARY"
|
||||
echo "" >> "$COVERAGE_SUMMARY"
|
||||
|
||||
for pkg in "${TEST_PKGS[@]}"; do
|
||||
PKG_COVERAGE="$COVERAGE_DIR/pkg_$(echo "$pkg" | tr '/' '_' | sed 's/\.\.\.//g').out"
|
||||
if [ -f "$PKG_COVERAGE" ]; then
|
||||
COVERAGE_PCT=$(go tool cover -func="$PKG_COVERAGE" 2>/dev/null | tail -1 | awk '{print $3}')
|
||||
echo " $pkg: $COVERAGE_PCT" >> "$COVERAGE_SUMMARY"
|
||||
echo -e " ${GREEN}$pkg${NC}: ${COVERAGE_PCT}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# Calculate overall coverage
|
||||
if [ -f "$COVERAGE_FILE" ]; then
|
||||
OVERALL_COVERAGE=$(go tool cover -func="$COVERAGE_FILE" 2>/dev/null | tail -1 | awk '{print $3}')
|
||||
echo -e "${BLUE}Overall coverage: ${GREEN}${OVERALL_COVERAGE}${NC}"
|
||||
echo "" >> "$COVERAGE_SUMMARY"
|
||||
echo "Overall: $OVERALL_COVERAGE" >> "$COVERAGE_SUMMARY"
|
||||
fi
|
||||
|
||||
# Generate HTML report if requested
|
||||
if [ "$HTML_MODE" = true ] && [ -f "$COVERAGE_FILE" ]; then
|
||||
echo -e "${YELLOW}Generating HTML coverage report...${NC}"
|
||||
go tool cover -html="$COVERAGE_FILE" -o "$COVERAGE_HTML"
|
||||
echo -e "${GREEN}HTML report saved to: $COVERAGE_HTML${NC}"
|
||||
fi
|
||||
|
||||
# Run benchmark tests if requested
|
||||
if [ "$BENCHMARK_MODE" = true ]; then
|
||||
echo ""
|
||||
echo -e "${YELLOW}Running benchmark tests...${NC}"
|
||||
|
||||
BENCHMARK_PKGS=(
|
||||
"./internal/service/..."
|
||||
"./internal/repository/..."
|
||||
"./pkg/crypto/..."
|
||||
)
|
||||
|
||||
BENCHMARK_FILE="$COVERAGE_DIR/benchmark_results.txt"
|
||||
|
||||
for pkg in "${BENCHMARK_PKGS[@]}"; do
|
||||
echo -e " Benchmarking: ${GREEN}$pkg${NC}"
|
||||
go test -bench=. -benchmem -run=^$ "$pkg" 2>&1 | tee -a "$BENCHMARK_FILE"
|
||||
done
|
||||
|
||||
echo -e "${GREEN}Benchmark results saved to: $BENCHMARK_FILE${NC}"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo -e "${BLUE}=== Report Summary ===${NC}"
|
||||
echo -e " Coverage profile: $COVERAGE_FILE"
|
||||
echo -e " Coverage summary: $COVERAGE_SUMMARY"
|
||||
if [ "$HTML_MODE" = true ]; then
|
||||
echo -e " HTML report: $COVERAGE_HTML"
|
||||
fi
|
||||
if [ "$BENCHMARK_MODE" = true ]; then
|
||||
echo -e " Benchmark results: $BENCHMARK_FILE"
|
||||
fi
|
||||
echo -e " Failed packages: $FAIL_COUNT"
|
||||
echo ""
|
||||
|
||||
if [ "$FAIL_COUNT" -gt 0 ]; then
|
||||
echo -e "${RED}Some tests failed. Please review the logs.${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}All tests passed!${NC}"
|
||||
exit 0
|
||||
fi
|
||||
Reference in New Issue
Block a user