fix: resolve all 44 failing CI E2E tests (#64)
* fix: resolve all 44 failing CI E2E tests - Bypass non-critical rate limiters in test env (MC_DISABLE_RATE_LIMIT=1) to prevent 429s when 165 tests share the same IP bucket - Make admin seed idempotent (INSERT OR IGNORE) to fix UNIQUE constraint race when multiple Next.js workers initialize concurrently - Add distinct x-forwarded-for headers to login-flow tests so they never share the critical login rate-limit bucket with other test suites - Add missing 018_token_usage migration that the heartbeat POST handler depends on, fixing the 500 on inline token reporting * docs: update README with latest features and test count - Update migration count from 15 to 18 - Update E2E test count from 146 to 165 - Move Direct CLI, OpenAPI docs, and GitHub sync to completed roadmap - Add Direct CLI and GitHub sync feature descriptions - Add /api/connect and /api/github to API reference - Remove resolved known limitation (vitest stubs) - Update repo description * fix: prevent build-time admin seed with wrong credentials in CI Move `cp .env.test .env` before `pnpm build` in CI workflow so env vars are present during build. Add NEXT_PHASE guard to skip seed during build as belt-and-suspenders — env vars may not be available at build time. Root cause: `next build` imports db.ts, triggering seedAdminUserFromEnv() with undefined AUTH_USER/AUTH_PASS, seeding user `admin` instead of `testadmin`. Runtime seed then sees count > 0 and skips. Tests login as `testadmin` which doesn't exist → 401.
This commit is contained in:
parent
d6879c66c1
commit
b2703b37d5
|
|
@ -5,3 +5,4 @@ AUTH_SECRET=test-legacy-secret
|
|||
MC_ALLOW_ANY_HOST=1
|
||||
MC_COOKIE_SECURE=
|
||||
MC_COOKIE_SAMESITE=lax
|
||||
MC_DISABLE_RATE_LIMIT=1
|
||||
|
|
|
|||
|
|
@ -39,12 +39,12 @@ jobs:
|
|||
- name: Unit tests
|
||||
run: pnpm test
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Prepare E2E environment
|
||||
run: cp .env.test .env
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm exec playwright install --with-deps
|
||||
|
||||
|
|
|
|||
29
README.md
29
README.md
|
|
@ -61,7 +61,6 @@ Initial login is seeded from `AUTH_USER` / `AUTH_PASS` on first run.
|
|||
### Known Limitations
|
||||
|
||||
- **CSP still includes `unsafe-inline`** — `unsafe-eval` has been removed, but inline styles remain for framework compatibility
|
||||
- **Vitest stubs need real assertions** — unit test files exist but most are placeholder stubs
|
||||
|
||||
### Security Considerations
|
||||
|
||||
|
|
@ -87,6 +86,12 @@ Token usage dashboard with per-model breakdowns, trend charts, and cost analysis
|
|||
### Background Automation
|
||||
Scheduled tasks for database backups, stale record cleanup, and agent heartbeat monitoring. Configurable via UI or API.
|
||||
|
||||
### Direct CLI Integration
|
||||
Connect Claude Code, Codex, or any CLI tool directly to Mission Control without requiring a gateway. Register connections, send heartbeats with inline token reporting, and auto-register agents.
|
||||
|
||||
### GitHub Issues Sync
|
||||
Inbound sync from GitHub repositories with label and assignee mapping. Synced issues appear on the task board alongside agent-created tasks.
|
||||
|
||||
### Integrations
|
||||
Outbound webhooks with delivery history, configurable alert rules with cooldowns, and multi-gateway connection management. Optional 1Password CLI integration for secret management.
|
||||
|
||||
|
|
@ -108,7 +113,7 @@ mission-control/
|
|||
│ ├── lib/
|
||||
│ │ ├── auth.ts # Session + API key auth, RBAC
|
||||
│ │ ├── db.ts # SQLite (better-sqlite3, WAL mode)
|
||||
│ │ ├── migrations.ts # 15 schema migrations
|
||||
│ │ ├── migrations.ts # 18 schema migrations
|
||||
│ │ ├── scheduler.ts # Background task scheduler
|
||||
│ │ ├── webhooks.ts # Outbound webhook delivery
|
||||
│ │ └── websocket.ts # Gateway WebSocket client
|
||||
|
|
@ -128,7 +133,7 @@ mission-control/
|
|||
| Charts | Recharts 3 |
|
||||
| Real-time | WebSocket + Server-Sent Events |
|
||||
| Auth | scrypt hashing, session tokens, RBAC |
|
||||
| Testing | Vitest + Playwright (146 E2E tests) |
|
||||
| Testing | Vitest + Playwright (165 E2E tests) |
|
||||
|
||||
## Authentication
|
||||
|
||||
|
|
@ -233,6 +238,18 @@ All endpoints require authentication unless noted. Full reference below.
|
|||
| `GET/POST/PUT/DELETE` | `/api/alerts` | admin | Alert rules |
|
||||
| `GET/POST/PUT/DELETE` | `/api/gateways` | admin | Gateway connections |
|
||||
| `GET/PUT/DELETE/POST` | `/api/integrations` | admin | Integration management |
|
||||
| `POST` | `/api/github` | admin | Trigger GitHub Issues sync |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Direct CLI</strong></summary>
|
||||
|
||||
| Method | Path | Role | Description |
|
||||
|--------|------|------|-------------|
|
||||
| `POST` | `/api/connect` | operator | Register direct CLI connection |
|
||||
| `GET` | `/api/connect` | viewer | List active connections |
|
||||
| `DELETE` | `/api/connect` | operator | Disconnect CLI session |
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -339,13 +356,15 @@ See [open issues](https://github.com/builderz-labs/mission-control/issues) for p
|
|||
- [x] Export endpoint row limits ([#43](https://github.com/builderz-labs/mission-control/issues/43))
|
||||
- [x] Fill in Vitest unit test stubs with real assertions
|
||||
|
||||
- [x] Direct CLI integration — connect tools like Codex, Claude Code, or custom CLIs directly without requiring a gateway ([#61](https://github.com/builderz-labs/mission-control/pull/61))
|
||||
- [x] OpenAPI 3.1 documentation with Scalar UI ([#60](https://github.com/builderz-labs/mission-control/pull/60))
|
||||
- [x] GitHub Issues sync — inbound sync with label/assignee mapping ([#63](https://github.com/builderz-labs/mission-control/pull/63))
|
||||
|
||||
**Up next:**
|
||||
|
||||
- [ ] Agent-agnostic gateway support — connect any orchestration framework (OpenClaw, ZeroClaw, OpenFang, NeoBot, IronClaw, etc.), not just OpenClaw
|
||||
- [ ] Direct CLI integration — connect tools like Codex, Claude Code, or custom CLIs directly without requiring a gateway
|
||||
- [ ] Native macOS app (Electron or Tauri)
|
||||
- [ ] First-class per-agent cost breakdowns — dedicated panel with per-agent token usage and spend (currently derivable from per-session data)
|
||||
- [ ] OpenAPI / Swagger documentation
|
||||
- [ ] Webhook retry with exponential backoff
|
||||
- [ ] OAuth approval UI improvements
|
||||
- [ ] API token rotation UI
|
||||
|
|
|
|||
|
|
@ -74,6 +74,9 @@ function initializeSchema() {
|
|||
interface CountRow { count: number }
|
||||
|
||||
function seedAdminUserFromEnv(dbConn: Database.Database): void {
|
||||
// Skip seeding during `next build` — env vars may not be available yet
|
||||
if (process.env.NEXT_PHASE === 'phase-production-build') return
|
||||
|
||||
const count = (dbConn.prepare('SELECT COUNT(*) as count FROM users').get() as CountRow).count
|
||||
if (count > 0) return
|
||||
|
||||
|
|
@ -82,7 +85,7 @@ function seedAdminUserFromEnv(dbConn: Database.Database): void {
|
|||
const displayName = username.charAt(0).toUpperCase() + username.slice(1)
|
||||
|
||||
dbConn.prepare(`
|
||||
INSERT INTO users (username, display_name, password_hash, role)
|
||||
INSERT OR IGNORE INTO users (username, display_name, password_hash, role)
|
||||
VALUES (?, ?, ?, ?)
|
||||
`).run(username, displayName, hashPassword(password), 'admin')
|
||||
|
||||
|
|
|
|||
|
|
@ -477,6 +477,24 @@ const migrations: Migration[] = [
|
|||
CREATE INDEX IF NOT EXISTS idx_github_syncs_created_at ON github_syncs(created_at);
|
||||
`)
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '018_token_usage',
|
||||
up: (db) => {
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS token_usage (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
model TEXT NOT NULL,
|
||||
session_id TEXT NOT NULL,
|
||||
input_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
output_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_token_usage_session_id ON token_usage(session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_token_usage_created_at ON token_usage(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_token_usage_model ON token_usage(model);
|
||||
`)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ test.describe('Login Flow', () => {
|
|||
|
||||
test('login API returns session cookie on success', async ({ request }) => {
|
||||
const res = await request.post('/api/auth/login', {
|
||||
data: { username: 'testadmin', password: 'testpass123' }
|
||||
data: { username: 'testadmin', password: 'testpass123' },
|
||||
headers: { 'x-forwarded-for': '10.88.88.1' }
|
||||
})
|
||||
expect(res.status()).toBe(200)
|
||||
|
||||
|
|
@ -38,7 +39,8 @@ test.describe('Login Flow', () => {
|
|||
test('session cookie grants API access', async ({ request }) => {
|
||||
// Login to get a session
|
||||
const loginRes = await request.post('/api/auth/login', {
|
||||
data: { username: 'testadmin', password: 'testpass123' }
|
||||
data: { username: 'testadmin', password: 'testpass123' },
|
||||
headers: { 'x-forwarded-for': '10.88.88.2' }
|
||||
})
|
||||
expect(loginRes.status()).toBe(200)
|
||||
|
||||
|
|
@ -50,7 +52,7 @@ test.describe('Login Flow', () => {
|
|||
|
||||
// Use the session cookie to access /api/auth/me
|
||||
const meRes = await request.get('/api/auth/me', {
|
||||
headers: { 'cookie': `mc-session=${sessionToken}` }
|
||||
headers: { 'cookie': `mc-session=${sessionToken}`, 'x-forwarded-for': '10.88.88.2' }
|
||||
})
|
||||
expect(meRes.status()).toBe(200)
|
||||
const body = await meRes.json()
|
||||
|
|
|
|||
Loading…
Reference in New Issue