Skip to content

Development Guide

This guide covers the development workflow for ocmonica.

Prerequisites

Quick Start

Initial Setup

# Install dependencies and initialize config
task setup

# Or manually:
task install      # Install Go dependencies
task web:install  # Install Node.js dependencies
task config:init  # Create config files from examples

Development Mode

Run both backend and frontend together:

task dev:full

This will start: - Backend server on http://localhost:8080 - Frontend dev server on http://localhost:3000

Press Ctrl+C to stop both servers.

Run Servers Separately

Backend only:

task dev

Frontend only:

task web:dev

Hot Reload Development

Ocmonica supports hot reload for both backend and frontend during development, allowing you to see changes instantly without manual rebuilds.

Quick Start

# Local development with full hot reload (recommended)
task dev:watch:full

# Or Docker with observability stack
task docker:dev

Backend Hot Reload with Air

Air watches your Go files and automatically rebuilds/restarts the server when changes are detected.

Install (included in task setup):

go install github.com/air-verse/air@latest

Run:

# Backend only
task dev:watch

# Both backend and frontend
task dev:watch:full

What Gets Watched: - All .go files in cmd/, internal/, pkg/ - Configuration files: .toml, .yaml

Rebuild Time: ~2-3 seconds for typical changes Build Output: Compiled to tmp/main (gitignored) Configuration: .air.toml in project root

Frontend Hot Reload with Next.js

Next.js has hot reload built-in with Turbopack enabled by default.

# Frontend only
task web:dev

# Or use dev:watch:full for both backend and frontend
task dev:watch:full

What Gets Watched: All files in web/src/ Reload Time: < 1 second (hot module replacement) No configuration needed: Works out of the box

Docker Development (For Full Stack Testing)

Run the complete development stack in Docker with hot reload enabled:

# Start all services (foreground)
task docker:dev

# Or start in background
task docker:dev:up

# View logs
task docker:dev:logs

# View specific service logs
task docker:dev:logs:backend
task docker:dev:logs:frontend

# Stop containers
task docker:dev:down

# Clean up (stop + remove volumes)
task docker:dev:clean

Services Started: - Backend (Go + Air): http://localhost:8080 - Frontend (Next.js): http://localhost:3000 - Jaeger UI: http://localhost:16686 - Prometheus: http://localhost:9091 - Grafana: http://localhost:3001 (admin/admin)

How It Works: - Source code is mounted as Docker volumes - Air watches Go files and rebuilds in container - Next.js watches TypeScript/React files with polling - Changes on host → automatically reflected in container

Troubleshooting

Backend not rebuilding

Symptoms: Code changes don't trigger rebuild

Solutions: - Check Air is running (you should see colored output) - Verify .air.toml exists in project root - Check tmp/ directory is created and writable - Look for errors in build-errors.log - Ensure file is in a watched directory (cmd/, internal/, pkg/)

Frontend not hot reloading in Docker

Symptoms: Changes to TypeScript/React files not reflected in browser

Solutions: - Verify WATCHPACK_POLLING=true in docker-compose environment - Check browser console for errors - Ensure node_modules is excluded from volume mount (check docker-compose.dev.yml) - Hard refresh browser: Cmd+Shift+R (Mac) / Ctrl+Shift+R (Windows/Linux) - Restart frontend container: docker-compose -f docker-compose.dev.yml restart frontend

Slow rebuilds

Symptoms: Air takes > 5 seconds to rebuild

Possible causes: - Large codebase (expected for first build) - Too many files changing at once - Disk I/O bottleneck

Solutions: - First build is always slower (go mod download, etc.) - Subsequent builds should be fast (< 3 seconds) - Adjust delay in .air.toml if builds trigger too frequently (default: 1000ms) - For Docker on Mac/Windows, file watching can be slower due to virtualization - Consider using local development (task dev:watch:full) for faster iteration

Port conflicts

Symptoms: Error "port already in use" or "address already in use"

Check what's using a port:

# Mac/Linux
lsof -i :8080
lsof -i :3000

# Windows
netstat -ano | findstr :8080

Solutions: - Stop other services using those ports - Or modify ports in docker-compose.dev.yml

Docker volume permission issues

Symptoms: Permission denied errors in container logs

Solutions: - Ensure your user has write permissions to project directory - On Linux, you may need to adjust the container user (currently using root in dev Dockerfile) - Check Docker Desktop settings (Mac/Windows)

Debugging with Chrome DevTools

Both local and Docker development setups support Node.js debugging via Chrome DevTools for the Next.js frontend. This allows you to debug server-side code including Server Components, API routes, and middleware.

Enable Debugging

Debugging is enabled by default: - Local: npm run dev from web/ directory - Docker: task docker:dev from project root - Port 9229 exposed for debugger connection

Attach Chrome DevTools

  1. Navigate to chrome://inspect in Chrome
  2. Click "Configure..." and ensure localhost:9229 is in the list
  3. Under "Remote Target", click "inspect" for the Node.js process
  4. DevTools window opens with full debugging capabilities

Debugging Features

Set Breakpoints: Click line numbers in Sources tab to set breakpoints Inspect Variables: Hover over variables to see values Call Stack: View the full call stack when paused Console: Use console.log() - output appears in DevTools console Performance: Profile server-side rendering performance Hot Reload: Debugger persists through hot-reload cycles

What Can You Debug?

✅ Server Components (default in app/ directory) ✅ API Routes (app/api/ directory) ✅ Middleware (middleware.ts) ✅ Server Actions ✅ Data fetching in Server Components

❌ Client Components ('use client' directive) - use browser DevTools instead ❌ Browser event handlers - use browser DevTools

Debugging Ports

  • 3000 - Next.js dev server
  • 9229 - Node.js debugger (Chrome DevTools)

Troubleshooting

Connection refused: - Ensure dev server is running - Verify port 9229 is not blocked by firewall - Check chrome://inspect has localhost:9229 configured

Breakpoints not hitting: - Verify you're debugging server code, not client components - Check Source Maps are enabled in DevTools settings - Ensure file path in DevTools matches your filesystem

Port conflict (9229 in use): - Stop other Node.js debuggers - Check with: lsof -i :9229 (Mac/Linux) - Kill process: lsof -ti :9229 | xargs kill -9

Docker connection issues: - Verify port mapping: docker port ocmonica-frontend-dev - Check docker-compose.dev.yml has port 9229:9229 mapped - Restart frontend container if needed

See web/CLAUDE.md for detailed debugging guide.

When to Use Which Approach

Scenario Recommended Command Why
Daily backend development task dev:watch Fastest iteration, minimal overhead
Daily full-stack development task dev:watch:full Both services with hot reload locally
Testing with observability task docker:dev Full stack with Jaeger/Prometheus/Grafana
Quick frontend changes task web:dev Frontend only, no backend needed
Testing production build task build-all Verify production configuration
CI/CD pipeline Production builds No hot reload needed

Performance Tips

  1. Use local development for fastest iteration: Docker adds overhead for file watching
  2. Keep changes focused: Editing many files at once can slow down rebuilds
  3. Use .air.toml delay setting: 1 second default prevents rebuilding on every keystroke
  4. Exclude unnecessary directories: Check .air.toml excludes (vendor, gen, etc.)
  5. On Mac/Windows: Consider increasing Docker resources (CPU/memory) for better performance

Project Structure

ocmonica/
├── cmd/server/              # Backend entry point
├── internal/                # Private application code
│   ├── api/                # API handlers (REST & gRPC)
│   ├── service/            # Business logic
│   ├── repository/         # Data access
│   └── models/             # Domain models
├── pkg/                    # Public libraries
│   ├── config/            # Configuration management
│   └── utils/             # Utility functions
├── proto/                  # Protobuf definitions
├── gen/                    # Generated Go code (gitignored)
├── web/                    # TypeScript frontend
│   ├── src/
│   │   ├── app/           # Next.js pages
│   │   ├── components/    # React components
│   │   ├── lib/           # Utilities and API clients
│   │   ├── types/         # TypeScript types
│   │   └── gen/           # Generated TS code (gitignored)
│   └── public/            # Static assets
├── Taskfile.yaml           # Task definitions
└── README.md

Common Development Tasks

Building

# Build backend binary
task build

# Build frontend for production
task web:build

# Build everything
task build-all

Testing

# Run all Go tests
task test

# Run tests with coverage report
task test:coverage

# Test specific layers
task service:test
task db:test
task config:test

Code Quality

# Run all CI checks (format, lint, test, security, build)
task ci

# Run all quality checks (lint, test, security)
task quality:all

# Format Go code
task fmt

# Lint Go code
task lint

# Auto-fix linting issues
task lint:fix

# Lint frontend code
task web:lint

Security Scanning

The project uses gosec for Go security analysis and Trivy for dependency vulnerability scanning.

Installation

# Install gosec security scanner
task security:install

Running Security Scans

# Quick security scan (console output)
task security:scan

# Generate detailed security reports (JSON, HTML, text)
task security:report

# Generate SARIF report for GitHub Code Scanning
task security:sarif

# Run comprehensive quality report (includes security scan)
task quality:report

Understanding Security Findings

gosec Rule IDs: - G103: Use of unsafe calls (low severity) - Common in generated protobuf code - G115: Integer overflow conversion (high severity) - Check int to int32 conversions - G201: SQL string formatting (medium severity) - Potential SQL injection - G301: File/directory permissions (medium severity) - Expect 0750 or less - G304: Path traversal (medium severity) - Variable file paths

Severity Levels: - HIGH: Address immediately - potential security vulnerability - MEDIUM: Review and fix - may pose security risk - LOW: Informational - audit but may be acceptable

CI/CD Integration

Security scans run automatically on every push and pull request via GitHub Actions:

  1. gosec - Scans Go code for security issues
  2. Trivy - Scans for dependency vulnerabilities

Results are uploaded to GitHub Security tab (Code Scanning Alerts).

Excluding False Positives

If gosec reports a false positive that's been validated as safe:

//nolint:gosec // G304: Path is from validated database record
file, err := os.Open(fullPath)

IMPORTANT: Always include a comment explaining WHY the finding is safe. Never suppress without justification.

Security Report Location

Reports are generated in ./reports/ directory: - gosec-report.json - Machine-readable JSON format - gosec-report.html - Human-readable HTML report - gosec-report.txt - Console-friendly text format - gosec.sarif - SARIF format for GitHub integration

The reports/ directory is gitignored.

Git Hooks

Overview

ocmonica uses Lefthook for git hooks to automatically: - Format code before commit - Run linters on changed files - Validate protobuf files - Enforce commit message format (conventional commits) - Maintain code quality standards

Hooks are fast (typically < 5 seconds) and run in parallel.

Installation

Hooks are installed automatically during task setup. To install manually:

# Install Lefthook binary
go install github.com/evilmartians/lefthook@latest

# Install hooks into .git/hooks/
lefthook install

Configuration

Hooks are configured in lefthook.yml at project root.

Key configuration highlights: - Parallel execution for speed - File filtering via glob patterns (only run on relevant files) - Auto-fixing with stage_fixed: true (auto-stage fixed files) - Skip conditions for merge/rebase operations - Minimal output (only shows errors, not successes)

What Runs When?

On git commit (pre-commit hook):

Check Files Command Auto-fix
Go imports *.go goimports -w ✅ Yes
Go format *.go go fmt ✅ Yes
Go lint (fast) *.go golangci-lint --fast ❌ No
Protobuf lint proto/**/*.proto buf lint ❌ No
Protobuf format proto/**/*.proto buf format -w ✅ Yes
TypeScript lint web/**/*.{ts,tsx,js,jsx} npm run lint ⚠️ Partial
Commit message All commits Conventional commits check ❌ No

Generated code excluded: Files in gen/ directory are automatically skipped.

On git push (pre-push hook):

Check Command Purpose
Full Go lint golangci-lint run All linters (not just fast)
Go tests go test -short -race ./... Run test suite
Security scan gosec ./... Security vulnerability check
Frontend build npm run build Ensure TypeScript compiles

All checks run only on staged files for pre-commit, and all files for pre-push.

Running Hooks Manually

# Run pre-commit checks on staged files
task hooks:run

# Or directly with Lefthook
lefthook run pre-commit

# Run on all files (not just staged)
lefthook run pre-commit --all-files

# Run pre-push checks
lefthook run pre-push

# Run specific hook
lefthook run go-lint-fast

Skipping Hooks

When to Skip: - Emergency hotfixes - WIP commits on feature branch - Experimenting with code - When you know the hook will fail and want to fix it later

How to Skip:

# Method 1: Git flag (works with any hook tool)
git commit --no-verify -m "WIP: work in progress"

# Method 2: Lefthook env var (skip all hooks)
LEFTHOOK=0 git commit -m "skip all hooks"

# Method 3: Skip specific hook
LEFTHOOK_EXCLUDE=go-lint-fast git commit -m "skip just linting"

# Method 4: Skip on merge/rebase (automatic)
# Some hooks automatically skip during merge/rebase

⚠️ Important: - Never skip hooks on commits to main branch - CI will catch issues, but hooks prevent bad code from being pushed - Use --no-verify sparingly - it's a last resort

Commit Message Format

Hooks enforce Conventional Commits format:

Format: <type>(<scope>): <description>

Examples:

git commit -m "feat(auth): add JWT token refresh"
git commit -m "fix(api): resolve null pointer in file handler"
git commit -m "docs: update README with installation steps"
git commit -m "refactor(service): extract user validation logic"
git commit -m "test(repository): add tests for file CRUD operations"

Valid types: - feat - New feature - fix - Bug fix - docs - Documentation changes - style - Code style changes (formatting, missing semicolons, etc.) - refactor - Code refactoring (no functionality change) - test - Adding or updating tests - chore - Maintenance tasks (dependencies, build, etc.) - perf - Performance improvements - ci - CI/CD changes - build - Build system changes - revert - Revert a previous commit

Scope is optional but recommended: - auth - Authentication/authorization - api - API handlers - service - Service layer - repository - Data access layer - config - Configuration - web - Frontend

Troubleshooting

Hooks not running:

# Reinstall hooks
lefthook install

# Check if hooks are installed
ls -la .git/hooks/pre-commit

# Should show a symlink or script

Slow commits:

# See which hook is slow (verbose mode)
lefthook run pre-commit -v

# If a specific hook is slow, skip it
LEFTHOOK_EXCLUDE=go-security git commit -m "WIP"

# Or move slow checks to pre-push in lefthook.yml

Hook fails on valid code:

# Run hook manually to debug
lefthook run pre-commit -v

# Run the specific tool that's failing
golangci-lint run --fast ./...
buf lint
npm run lint

# Check if file is excluded
# (Generated files in gen/ should be excluded)

Conventional commit validation fails:

# Your commit message must match the format
# Bad:  "updated code"
# Good: "feat: add new feature"

# Fix by amending your commit message
git commit --amend -m "feat(api): add user registration endpoint"

Disable hooks permanently (not recommended):

# Uninstall hooks
lefthook uninstall

# To re-enable later
lefthook install

Node.js not found in pre-commit:

# Ensure Node.js is in your PATH
which node
which npm

# If not, add to your shell profile (~/.bashrc, ~/.zshrc)
export PATH="/usr/local/bin:$PATH"

Go tools not found:

# Install required Go tools
task setup

# Or manually
go install golang.org/x/tools/cmd/goimports@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

Available Tasks

task hooks:install      # Install git hooks
task hooks:run          # Run pre-commit hooks manually
task hooks:run-all      # Run pre-commit hooks on all files
task hooks:uninstall    # Remove git hooks

CI/CD Integration

The same hooks run in GitHub Actions CI. See .github/workflows/ci.yml and .github/workflows/lint.yml.

Benefits: - Consistent checks locally and in CI - Faster PR reviews (code is pre-linted) - Fewer CI failures (issues caught before push)

Performance Tips

1. Only lint changed files (already configured) - Pre-commit runs on {staged_files} only - Full checks run in pre-push and CI

2. Use --fast mode for pre-commit (already configured) - Fast linters only (< 1 second) - Full linting in pre-push and CI

3. Skip slow checks for WIP commits

LEFTHOOK_EXCLUDE=go-security git commit -m "WIP: in progress"

4. Parallel execution (already enabled) - All hooks run concurrently - Typically completes in < 5 seconds

Customizing Hooks

To modify hook behavior, edit lefthook.yml:

# Example: Add a new pre-commit check
pre-commit:
  commands:
    my-custom-check:
      glob: "*.go"
      run: ./scripts/my-check.sh {staged_files}
# Example: Skip hook on specific branches
pre-commit:
  commands:
    go-lint-fast:
      skip:
        - ref: experimental/*
# Example: Add environment variables
pre-commit:
  commands:
    test:
      run: go test ./...
      env:
        GO_TEST_VERBOSE: "1"

See Lefthook documentation for advanced configuration.

Developer Onboarding

New team members: 1. Clone repository 2. Run task setup (installs hooks automatically) 3. Make a test commit to verify hooks work 4. Read this documentation

If hooks are too strict: - Use --no-verify for WIP commits on feature branches - Never use --no-verify for commits to main - CI will still catch issues even if hooks are skipped

Protobuf

# Generate Go code from protobuf
task proto:gen

# Generate TypeScript code for frontend
task web:gen

# Lint protobuf files
task proto:lint

# Format protobuf files
task proto:format

# Check for breaking changes
task proto:breaking

Database

# Initialize database directory
task db:init

# Test repository layer
task db:test

Docker

# Build Docker image
task docker:build

# Build and run locally
task docker:run

# Build with git tag
task docker:build:tagged

# Push to registry
task docker:push

Development Workflow

1. Make Changes

Edit code in internal/, cmd/, web/src/, or proto/.

2. Generate Code (if needed)

After modifying protobuf files:

task proto:gen   # Generate Go code
task web:gen     # Generate TypeScript code

3. Run Tests

task test       # Backend tests

4. Check Code Quality

task fmt        # Format
task lint       # Lint

5. Commit Changes

Follow conventional commits:

git add .
git commit -m "feat: add new feature"
git commit -m "fix: resolve bug"
git commit -m "docs: update documentation"

6. Push and Create PR

git push origin feature/your-branch
gh pr create

API Endpoints

Authentication Endpoints

User Authentication: - POST /api/v1/auth/register - Register new user - POST /api/v1/auth/login - Login with username/password - POST /api/v1/auth/refresh - Refresh access token - POST /api/v1/auth/logout - Logout (revoke refresh token) - POST /api/v1/auth/logout-all - Logout from all devices - POST /api/v1/auth/change-password - Change user password

API Key Management: - POST /api/v1/auth/api-keys - Create API key - GET /api/v1/auth/api-keys - List user's API keys - GET /api/v1/auth/api-keys/:id - Get API key details - DELETE /api/v1/auth/api-keys/:id - Revoke API key

User Management: - GET /api/v1/users/me - Get current user profile - GET /api/v1/users/:id - Get user by ID (admin only)

File Management Endpoints (Authenticated)

  • GET /health - Health check
  • GET /api/v1/files - List files
  • POST /api/v1/files - Upload file
  • GET /api/v1/files/:id - Get file metadata
  • GET /api/v1/files/:id/download - Download file
  • DELETE /api/v1/files/:id - Delete file
  • POST /api/v1/directories - Create directory
  • GET /api/v1/directories/:id - Get directory
  • GET /api/v1/directories/:id/children - List directory children
  • GET /api/v1/directories/:id/tree - Get directory tree
  • DELETE /api/v1/directories/:id - Delete directory
  • GET /api/v1/search - Search files

gRPC API (Backend: :8080)

ConnectRPC services available at: - /ocmonica.v1.FileService/* - File operations - /ocmonica.v1.DirectoryService/* - Directory operations - /ocmonica.v1.SearchService/* - Search operations - /ocmonica.v1.AuthService/* - Authentication (planned)

Observability Endpoints

  • GET /metrics - Prometheus metrics endpoint
  • OpenTelemetry traces exported to configured OTLP endpoint

Frontend (Dev: :3000, Prod: :3000)

  • / - File browser interface (planned)

Configuration

Configuration is managed through environment variables and YAML files using Koanf v2.

Development Configuration

Create a config.yaml file (see README for full config):

server:
  host: 0.0.0.0
  port: 8080
  read_timeout: 30s
  write_timeout: 30s

database:
  path: ./data/ocmonica.db

storage:
  base_path: ./data/files
  max_upload_size: 104857600  # 100MB

auth:
  jwt:
    access_token_duration: 15m
    refresh_token_duration: 7d
    rsa_key_size: 4096
  password:
    min_length: 12
    require_upper: true
    require_lower: true
    require_number: true
    require_symbol: true
  registration:
    enabled: true
    default_role: "member"
  api_key:
    enabled: true
    max_keys_per_user: 10

observability:
  tracing:
    enabled: true
    endpoint: "http://localhost:4318"  # Jaeger OTLP endpoint
  metrics:
    enabled: true
    port: 9090

logging:
  level: debug
  format: json

Environment Variables

Override configuration with environment variables:

export SERVER_PORT=8080
export DATABASE_PATH=./data/ocmonica.db
export JWT_ACCESS_TOKEN_DURATION=15m
export REGISTRATION_ENABLED=true
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318

Observability

OpenTelemetry Tracing (Jaeger)

Setup Jaeger locally:

# Run Jaeger all-in-one (Docker)
docker run -d --name jaeger \
  -e COLLECTOR_OTLP_ENABLED=true \
  -p 16686:16686 \
  -p 4318:4318 \
  jaegertracing/all-in-one:latest

# Access Jaeger UI
open http://localhost:16686

Enable tracing in config:

observability:
  tracing:
    enabled: true
    endpoint: "http://localhost:4318"  # OTLP HTTP endpoint

All requests are automatically traced with: - Request/response spans - Database operation spans - Service method spans - Error tracking - Custom attributes (user_id, organization_id, file_id, etc.)

Prometheus Metrics

Access metrics endpoint:

curl http://localhost:9090/metrics

Available metrics: - ocmonica_file_uploads_total - File upload counter - ocmonica_file_downloads_total - File download counter - ocmonica_auth_attempts_total - Authentication attempts - ocmonica_db_query_duration_seconds - Database query latency - ocmonica_search_queries_total - Search query counter - ocmonica_active_users - Active user gauge

Setup Prometheus locally:

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'ocmonica'
    static_configs:
      - targets: ['localhost:9090']

# Run Prometheus
docker run -d --name prometheus \
  -p 9091:9090 \
  -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
  prom/prometheus

Grafana Dashboard (Optional)

# Run Grafana
docker run -d --name grafana \
  -p 3001:3000 \
  grafana/grafana

# Access at http://localhost:3001
# Default: admin/admin

Add Prometheus as datasource: - URL: http://localhost:9091 - Access: Browser

Debugging

Backend Logs

Backend uses structured JSON logging:

# Run with debug level
export LOGGING_LEVEL=debug
task dev

# Or inline
LOGGING_LEVEL=debug task dev

Log fields: - timestamp - ISO 8601 timestamp - level - Log level (debug, info, warn, error) - msg - Log message - trace_id - OpenTelemetry trace ID - span_id - OpenTelemetry span ID - Additional context fields (user_id, file_id, etc.)

Distributed Tracing

View request flow in Jaeger UI: 1. Go to http://localhost:16686 2. Select service: ocmonica 3. Search for traces 4. Click trace to see detailed span breakdown

Frontend Dev Tools

Next.js provides hot reload and detailed error messages in development mode.

Database Inspection

SQLite database is at ./data/ocmonica.db:

sqlite3 ./data/ocmonica.db
.tables
SELECT * FROM files;
SELECT * FROM users;
SELECT * FROM organizations;

Troubleshooting

Port Already in Use

# Kill process on port 8080
lsof -ti:8080 | xargs kill -9

# Kill process on port 3000
lsof -ti:3000 | xargs kill -9

Dependencies Out of Sync

# Backend
go mod tidy
task install

# Frontend
cd web && rm -rf node_modules package-lock.json
npm install

Generated Code Issues

# Regenerate all protobuf code
task proto:gen
task web:gen

Clean Build

task clean
task build-all

IDE Setup

VS Code

Recommended extensions: - Go (golang.go) - ESLint (dbaeumer.vscode-eslint) - Tailwind CSS IntelliSense (bradlc.vscode-tailwindcss) - Buf (bufbuild.vscode-buf)

GoLand / WebStorm

  1. Enable Go modules support
  2. Configure Node.js interpreter
  3. Enable ESLint and Prettier

Testing Strategy

Backend Tests

Test Status: ✅ All tests passing (9/9 packages)

Test Types: - Integration Tests: Full middleware stack with in-memory SQLite - Unit Tests: Service layer business logic with mocked dependencies - Repository Tests: Data access layer with real database operations - Handler Tests: REST and gRPC handlers with authentication

Test Packages:

go test ./...
# All passing:
# ✅ internal/api/grpc
# ✅ internal/api/rest/handlers
# ✅ internal/repository/sqlite
# ✅ internal/service
# ✅ internal/observability/metrics
# ✅ test (integration tests)

Running Tests:

# Run all tests
task test

# Run with coverage
task test:coverage

# Run specific package
go test ./internal/service/...

# Run with race detection
go test -race ./...

# Verbose output
go test -v ./...

Test Setup Pattern: All test files use a common pattern with metrics initialization and in-memory database:

func setupTestHandler(t *testing.T) (*Handler, func()) {
    t.Helper()

    // Initialize metrics (safe for concurrent tests)
    metrics.Init("ocmonica-test")

    // Create in-memory database
    db, err := sql.Open("sqlite", ":memory:")
    if err != nil {
        t.Fatalf("Failed to open database: %v", err)
    }

    // Run migrations
    if err := sqlite.RunMigrations(db); err != nil {
        t.Fatalf("Failed to run migrations: %v", err)
    }

    // Setup repositories and services...

    cleanup := func() {
        _ = db.Close()
    }

    return handler, cleanup
}

Coverage Areas: - ✅ Authentication (register, login, logout, password change) - ✅ API Key management (create, list, revoke) - ✅ Permission checks (RBAC enforcement) - ✅ File operations (upload, download, delete) - ✅ Directory operations (create, list, tree) - ✅ Search functionality - ✅ Multi-tenancy isolation - ✅ Token refresh and validation

Integration Tests

Located in test/ directory: - grpc_auth_integration_test.go - gRPC authentication flow - rbac_integration_test.go - RBAC permission enforcement

Frontend

Currently planned. Future: Vitest/Jest for component tests.

Contributing

  1. Create a feature branch from main
  2. Make your changes following the guidelines above
  3. Ensure all tests pass: task ci
  4. Create a pull request
  5. Wait for CI checks to pass
  6. Request review

Resources