diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a8f76a4..32b14d78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -202,9 +202,21 @@ importers: packages/worker: dependencies: + drizzle-orm: + specifier: ^0.45.0 + version: 0.45.0(@cloudflare/workers-types@4.20251205.0)(@neondatabase/serverless@0.10.4)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(@types/pg@8.15.6)(kysely@0.28.8)(pg@8.16.3)(postgres@3.4.7) + drizzle-zod: + specifier: ^0.8.3 + version: 0.8.3(drizzle-orm@0.45.0(@cloudflare/workers-types@4.20251205.0)(@neondatabase/serverless@0.10.4)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(@types/pg@8.15.6)(kysely@0.28.8)(pg@8.16.3)(postgres@3.4.7))(zod@4.1.13) hono: specifier: ^4.10.4 version: 4.10.4 + postgres: + specifier: ^3.4.7 + version: 3.4.7 + zod: + specifier: ^4.1.13 + version: 4.1.13 devDependencies: '@cloudflare/vitest-pool-workers': specifier: ^0.10.3 diff --git a/testing/flow-test/README.md b/testing/flow-test/README.md new file mode 100644 index 00000000..a77cca61 --- /dev/null +++ b/testing/flow-test/README.md @@ -0,0 +1,34 @@ +# Flow Test Project + +Minimal project for testing flow CLI features. + +## Quick Start + +```bash +f setup # Pull env from 1focus, create .env +f dev # Start dev server +f test # Run test suite +``` + +## Commands + +| Command | Shortcut | Description | +|---------|----------|-------------| +| `f setup` | `f s` | Full setup with 1focus env pull | +| `f dev` | `f d` | Start dev server | +| `f test` | `f t` | Run flow feature tests | +| `f env-pull` | `f env` | Pull env from 1focus | +| `f env-push` | `f envs` | Push local .env to 1focus | +| `f env-show` | `f env1f` | Show env stored in 1focus | +| `f env-local` | `f envl` | Show local .env | + +## Testing Flow Features + +The `f test` command runs: +1. 1focus API connectivity +2. Env endpoint functionality +3. Local .env existence +4. Env push operation +5. Push verification + +All tests should pass for a healthy flow setup. diff --git a/testing/flow-test/flow.toml b/testing/flow-test/flow.toml new file mode 100644 index 00000000..6c1e7300 --- /dev/null +++ b/testing/flow-test/flow.toml @@ -0,0 +1,253 @@ +version = 1 +name = "flow-test" + +[deps] +node = "node" +pnpm = "pnpm" + +# ============================================================================= +# Setup & Dev +# ============================================================================= + +[[tasks]] +name = "setup" +description = "Set up project: pull env from 1focus, install deps" +command = ''' +set -euo pipefail + +ROOT="$(pwd)" +WEB_DIR="$ROOT/packages/web" +ENV_FILE="$WEB_DIR/.env" + +echo "=== Flow Test Setup ===" +echo "" + +# 1. Create .env from example +if [ ! -f "$ENV_FILE" ]; then + cp "$WEB_DIR/.env.example" "$ENV_FILE" 2>/dev/null || touch "$ENV_FILE" + echo "✓ Created .env" +else + echo "✓ .env exists" +fi + +# 2. Pull secrets from 1focus +echo "" +echo "Pulling secrets from 1focus..." +RESPONSE=$(curl -s "https://1f-worker.nikiv.workers.dev/api/v1/env/flow-test" 2>/dev/null || echo "{}") + +if echo "$RESPONSE" | jq -e '.env' > /dev/null 2>&1; then + echo "$RESPONSE" | jq -r '.env | to_entries | .[] | "\(.key)=\(.value)"' | while read line; do + key=$(echo "$line" | cut -d= -f1) + value=$(echo "$line" | cut -d= -f2-) + if grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then + sed -i '' "s|^${key}=.*|${key}=${value}|" "$ENV_FILE" + else + echo "${key}=${value}" >> "$ENV_FILE" + fi + echo " ✓ $key" + done +else + echo " (no env in 1focus yet, using defaults)" +fi + +# 3. Generate local secrets +if ! grep -q "^SECRET_KEY=" "$ENV_FILE" 2>/dev/null; then + SECRET=$(openssl rand -hex 16) + echo "SECRET_KEY=$SECRET" >> "$ENV_FILE" + echo " ✓ SECRET_KEY (generated)" +fi + +# 4. Install deps +echo "" +echo "Installing dependencies..." +cd "$WEB_DIR" +pnpm install --silent 2>/dev/null || npm install --silent 2>/dev/null || echo " (no package.json)" + +echo "" +echo "=== Setup Complete ===" +echo "" +echo "Run 'f dev' to start" +''' +shortcuts = ["s"] + +[[tasks]] +name = "dev" +description = "Start dev server" +command = ''' +cd packages/web +echo "Starting dev server..." +if [ -f package.json ]; then + pnpm dev 2>/dev/null || npm run dev 2>/dev/null || node index.js +else + echo "Dev server would start here" + echo "" + echo "Current .env:" + cat .env 2>/dev/null | grep -v "^#" | grep -v "^$" || echo "(empty)" +fi +''' +shortcuts = ["d"] + +[[tasks]] +name = "test" +description = "Run tests" +command = ''' +set -euo pipefail + +echo "=== Flow Test Suite ===" +echo "" + +PASS=0 +FAIL=0 + +test_pass() { echo " ✓ $1"; PASS=$((PASS + 1)); } +test_fail() { echo " ✗ $1"; FAIL=$((FAIL + 1)); } + +# Test 1: 1focus API +echo "1. Testing 1focus API..." +if curl -sf "https://1f-worker.nikiv.workers.dev/health" > /dev/null; then + test_pass "API reachable" +else + test_fail "API unreachable" +fi + +# Test 2: Env endpoint +echo "2. Testing env endpoint..." +if curl -sf "https://1f-worker.nikiv.workers.dev/api/v1/env/flow-test" | jq -e '.project' > /dev/null; then + test_pass "Env endpoint works" +else + test_fail "Env endpoint failed" +fi + +# Test 3: Local .env +echo "3. Checking local .env..." +if [ -f packages/web/.env ]; then + VARS=$(grep -c "=" packages/web/.env 2>/dev/null || echo "0") + test_pass ".env exists ($VARS vars)" +else + test_fail ".env missing" +fi + +# Test 4: Push +echo "4. Testing env push..." +TEST_VAL="test_$(date +%s)" +PUSH_RESP=$(curl -s -X POST "https://1f-worker.nikiv.workers.dev/api/v1/env/flow-test" -H "Content-Type: application/json" -d "{\"vars\":{\"TEST_VAR\":\"$TEST_VAL\"}}") +if echo "$PUSH_RESP" | grep -q "success"; then + test_pass "Push works" +else + test_fail "Push failed: $PUSH_RESP" +fi + +# Test 5: Verify +echo "5. Verifying push..." +sleep 1 +VERIFY_RESP=$(curl -s "https://1f-worker.nikiv.workers.dev/api/v1/env/flow-test") +if echo "$VERIFY_RESP" | grep -q "TEST_VAR"; then + test_pass "Push verified" +else + test_fail "Verification failed" +fi + +echo "" +echo "=== Results: $PASS passed, $FAIL failed ===" + +if [ "$FAIL" -eq 0 ]; then + echo "All tests passed!" +else + exit 1 +fi +''' +shortcuts = ["t"] + +# ============================================================================= +# Environment Management (via 1focus) +# ============================================================================= + +[[tasks]] +name = "env-pull" +description = "Pull env from 1focus" +command = ''' +set -euo pipefail + +cd packages/web + +echo "Pulling env from 1focus..." +RESPONSE=$(curl -s "https://1f-worker.nikiv.workers.dev/api/v1/env/flow-test") + +if ! echo "$RESPONSE" | jq -e '.env' > /dev/null 2>&1; then + echo "No env found in 1focus" + exit 0 +fi + +[ ! -f .env ] && touch .env + +echo "$RESPONSE" | jq -r '.env | to_entries | .[] | "\(.key)=\(.value)"' | while read line; do + key=$(echo "$line" | cut -d= -f1) + value=$(echo "$line" | cut -d= -f2-) + if grep -q "^${key}=" .env 2>/dev/null; then + sed -i '' "s|^${key}=.*|${key}=${value}|" .env + else + echo "${key}=${value}" >> .env + fi + echo " ✓ $key" +done + +echo "" +echo "Done!" +''' +shortcuts = ["env", "envp"] + +[[tasks]] +name = "env-push" +description = "Push local .env to 1focus" +command = ''' +set -euo pipefail + +cd packages/web + +if [ ! -f .env ]; then + echo "No .env file" + exit 1 +fi + +echo "Pushing env to 1focus..." + +VARS="{" +FIRST=true +while IFS='=' read -r key value || [ -n "$key" ]; do + [[ "$key" =~ ^#.*$ ]] && continue + [[ -z "$key" ]] && continue + + value="${value%\"}" + value="${value#\"}" + + if [ "$FIRST" = true ]; then FIRST=false; else VARS+=","; fi + value=$(echo "$value" | sed 's/\\/\\\\/g; s/"/\\"/g') + VARS+="\"$key\":\"$value\"" +done < .env +VARS+="}" + +curl -s -X POST "https://1f-worker.nikiv.workers.dev/api/v1/env/flow-test" \ + -H "Content-Type: application/json" \ + -d "{\"vars\": $VARS}" | jq . + +echo "" +echo "Done!" +''' +shortcuts = ["envs"] + +[[tasks]] +name = "env-show" +description = "Show env in 1focus" +command = ''' +curl -s "https://1f-worker.nikiv.workers.dev/api/v1/env/flow-test" | jq . +''' +shortcuts = ["env1f"] + +[[tasks]] +name = "env-local" +description = "Show local .env" +command = ''' +echo "=== Local .env ===" +cat packages/web/.env 2>/dev/null | grep -v "^#" | grep -v "^$" || echo "(empty or missing)" +''' +shortcuts = ["envl"] diff --git a/testing/flow-test/packages/web/.env.example b/testing/flow-test/packages/web/.env.example new file mode 100644 index 00000000..a052c25b --- /dev/null +++ b/testing/flow-test/packages/web/.env.example @@ -0,0 +1,12 @@ +# Flow Test Environment +# These will be pulled from 1focus on setup + +# API Keys (pulled from 1focus) +API_KEY= +OPENROUTER_API_KEY= + +# Local secrets (generated on setup) +SECRET_KEY= + +# Database (optional) +DATABASE_URL= diff --git a/testing/flow-test/packages/web/index.js b/testing/flow-test/packages/web/index.js new file mode 100644 index 00000000..579beae3 --- /dev/null +++ b/testing/flow-test/packages/web/index.js @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +const fs = require('fs') +const path = require('path') + +// Load .env +const envPath = path.join(__dirname, '.env') +if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf8') + envContent.split('\n').forEach(line => { + if (line && !line.startsWith('#')) { + const [key, ...valueParts] = line.split('=') + if (key) { + process.env[key.trim()] = valueParts.join('=').trim() + } + } + }) +} + +console.log('=== Flow Test Dev Server ===') +console.log('') +console.log('Environment loaded:') + +const envVars = Object.keys(process.env) + .filter(k => !k.startsWith('_') && !k.startsWith('npm_') && !['PATH', 'HOME', 'USER', 'SHELL', 'TERM', 'LANG', 'PWD', 'OLDPWD', 'SHLVL'].includes(k)) + .sort() + +envVars.forEach(key => { + const value = process.env[key] + const display = value && value.length > 20 ? value.slice(0, 20) + '...' : value + console.log(` ${key}: ${display || '(empty)'}`) +}) + +console.log('') +console.log('Server running... (Ctrl+C to stop)') + +// Keep alive +setInterval(() => {}, 1000) diff --git a/testing/flow-test/packages/web/package.json b/testing/flow-test/packages/web/package.json new file mode 100644 index 00000000..c7f5aa7e --- /dev/null +++ b/testing/flow-test/packages/web/package.json @@ -0,0 +1,9 @@ +{ + "name": "flow-test-web", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "node index.js", + "start": "node index.js" + } +}