xtablo-source/docs/ENV_TEST_SETUP.md

276 lines
7.8 KiB
Markdown
Raw Permalink Normal View History

2025-11-10 07:53:03 +00:00
# Environment Configuration for Testing
**Date:** 2025-11-08
**Status:** ✅ Completed
## Overview
Modified the API testing infrastructure to use environment variables from `.env.test` instead of fetching secrets from Google Secrets Manager. This enables tests to run without Google Cloud credentials, improves test execution speed, and allows offline testing.
## Changes Made
### 1. Created/Updated `.env.test`
Added all secret environment variables to `apps/api/.env.test`:
```bash
# Test environment configuration
# These values are used in tests and don't need to be real secrets
SUPABASE_URL=https://mhcafqvzbrrwvahpvvzd.supabase.co
# Secrets normally loaded from Google Secrets Manager
SUPABASE_SERVICE_ROLE_KEY=test-service-role-key
SUPABASE_CONNECTION_STRING=test-connection-string
SUPABASE_CA_CERT=test-ca-cert
STREAM_CHAT_API_SECRET=test-stream-secret
STRIPE_SECRET_KEY=test-stripe-key
STRIPE_WEBHOOK_SECRET=test-webhook-secret
EMAIL_CLIENT_SECRET=test-email-secret
EMAIL_REFRESH_TOKEN=test-refresh-token
R2_ACCESS_KEY_ID=test-r2-access-key
R2_SECRET_ACCESS_KEY=test-r2-secret-key
# Non-secret environment variables
STREAM_CHAT_API_KEY=t5vvvddteapa
XTABLO_URL="http://localhost:5173"
CORS_ORIGIN="http://localhost:5173,http://localhost:5174"
R2_ACCOUNT_ID="test-account-id"
TASKS_SECRET="test-tasks-secret"
EMAIL_USER="test@xtablo.com"
EMAIL_CLIENT_ID="test-client-id"
```
### 2. Modified `src/config.ts`
Updated the `createConfig()` function to:
- Accept an optional `secrets` parameter (previously required)
- Detect test mode via `NODE_ENV=test`
- Load secrets from environment variables in test mode
- Use Google Secrets Manager in non-test modes
```typescript
export function createConfig(secrets?: Secrets): AppConfig {
const NODE_ENV = (process.env.NODE_ENV || "development") as
| "development"
| "production"
| "staging"
| "test";
dotenv.config({ path: `.env.${NODE_ENV}` });
// In test mode, use environment variables directly instead of secrets
const isTestMode = NODE_ENV === "test";
// Base configuration
const baseConfig: AppConfig = {
// ...
SUPABASE_SERVICE_ROLE_KEY: isTestMode
? validateEnvVar("SUPABASE_SERVICE_ROLE_KEY", process.env.SUPABASE_SERVICE_ROLE_KEY)
: secrets!.supabaseServiceRoleKey,
// ... (similar pattern for all secrets)
};
// ...
}
```
### 3. Updated All Test Files
Simplified test initialization in all test files:
**Before:**
```typescript
describe("Test Suite", () => {
MiddlewareManager.initialize(
createConfig({
supabaseServiceRoleKey: "test",
supabaseConnectionString: "test",
supabaseCaCert: "test",
streamChatApiSecret: "test",
stripeSecretKey: "test",
stripeWebhookSecret: "test",
emailClientSecret: "test",
emailRefreshToken: "test",
r2AccessKeyId: "test",
r2SecretAccessKey: "test",
})
);
// ...
});
```
**After:**
```typescript
describe("Test Suite", () => {
// In test mode, createConfig() reads from .env.test
MiddlewareManager.initialize(createConfig());
// ...
});
```
### 4. Updated Files
-`apps/api/.env.test` - Added all secret environment variables
-`apps/api/src/config.ts` - Made secrets optional, added test mode detection
-`apps/api/src/__tests__/auth/auth.test.ts` - Simplified initialization
-`apps/api/src/__tests__/invite/invite.test.ts` - Simplified initialization
-`apps/api/src/__tests__/maybeAuth/maybeAuth.test.ts` - Simplified initialization
-`apps/api/src/__tests__/notes/notes.test.ts` - Simplified initialization
-`apps/api/src/__tests__/public/public.test.ts` - Simplified initialization
-`apps/api/src/__tests__/stripe/stripe.test.ts` - Simplified initialization
-`apps/api/src/__tests__/tablo/tablo.test.ts` - Simplified initialization
-`apps/api/src/__tests__/tablo_data/tablo_data.test.ts` - Simplified initialization
-`apps/api/src/__tests__/tasks/tasks.test.ts` - Simplified initialization
-`apps/api/src/__tests__/user/user.test.ts` - Simplified initialization
## Benefits
### 1. No Cloud Credentials Required
- Tests no longer need Google Cloud service account credentials
- CI/CD pipelines simplified
- New developers can run tests immediately after cloning
### 2. Faster Test Execution
- Eliminated network calls to Google Secrets Manager
- Tests start immediately without waiting for secret fetching
- Reduced test execution time
### 3. Offline Testing
- Tests work without internet connection
- Developers can test while traveling or on unstable networks
### 4. Consistent Test Environment
- All developers and CI/CD use identical test secrets
- Eliminates environment-specific test failures
- Reproducible test results
### 5. Simplified Test Code
- Cleaner test initialization
- Less boilerplate in each test file
- Easier to maintain
## Test Results
After implementing these changes:
**Total:** 85 tests
**Passing:** 81 tests ✓
**Failing:** 4 tests (pre-existing module initialization issue)
All tests run successfully with the new `.env.test` configuration. The console output shows:
```
✓ Configuration loaded successfully
```
This confirms that the configuration is being loaded properly from `.env.test`.
## Security Considerations
### ⚠️ Important Notes
1. **`.env.test` contains dummy values only**
- All secret values are set to "test" or similar placeholders
- These are NOT real credentials and cannot access production systems
2. **`.env.test` is ignored by Git**
- The file is included in `.gitignore` (via `*.env*` pattern)
- Real secrets are never committed to the repository
3. **Production secrets remain secure**
- Production and staging still use Google Secrets Manager
- Only test environment uses `.env.test`
- Real credentials are never exposed in test environment
## Usage
### Running Tests
Tests automatically use `.env.test` when `NODE_ENV=test`:
```bash
# Run all tests (NODE_ENV=test is already in package.json)
cd apps/api
pnpm test
# Run specific test file
NODE_ENV=test pnpm tsx --test src/__tests__/user/user.test.ts
# Watch mode
pnpm test:watch
```
### Adding New Secrets
When adding a new secret to the application:
1. Add it to `src/secrets.ts`:
```typescript
export type Secrets = {
// ... existing secrets
newSecret: string;
};
export async function loadSecrets(): Promise<Secrets> {
const secrets = {
// ... existing secrets
newSecret: await fetchSecret("new-secret"),
};
return secrets;
}
```
2. Update `src/config.ts`:
```typescript
const baseConfig: AppConfig = {
// ...
NEW_SECRET: isTestMode
? validateEnvVar("NEW_SECRET", process.env.NEW_SECRET)
: secrets!.newSecret,
};
```
3. Add to `.env.test`:
```bash
NEW_SECRET=test-new-secret
```
4. Add to `.env.development`, `.env.staging`, `.env.production` if needed
## Backward Compatibility
The changes maintain backward compatibility:
- Production code continues to use Google Secrets Manager
- Development mode can still use environment variables or secrets
- Only test mode has changed behavior
- No changes required to deployment configuration
## Related Documentation
- [API Test Suite Documentation](./API_TESTS.md)
- [Book Slot Hook Migration](./BOOK_SLOT_HOOK_MIGRATION.md)
- [API Shared Types Migration](./API_SHARED_TYPES_MIGRATION.md)
## Future Improvements
1. **Mock External Services**
- Mock Supabase client for more realistic test responses
- Mock StreamChat for testing chat functionality
- Mock Stripe for testing payment flows
2. **Test Data Fixtures**
- Create reusable test data factories
- Standardize test user profiles
- Consistent test event data
3. **Integration Tests**
- Add tests with real (test) database connections
- Test full request/response cycles
- Verify data persistence
4. **CI/CD Optimization**
- Run tests in parallel
- Cache test results
- Generate coverage reports