Complete guide to setting up a reliable test gate for any project — covers stack detection, 4 core test files, script wiring, secret hygiene, and Cloudflare Workers/Pages patterns. Use when starting a new project, adding CI to an existing one, or when "tests pass but production breaks." Companion to cm-safe-deploy and cm-project-bootstrap.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add aibot88/sec_skill_store --skill cm-test-gate --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Cm Test Gate?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/aibot88-cm-test-gate)More formats (shields.io, HTML) on the badges page.
---
name: cm-test-gate
description: Complete guide to setting up a reliable test gate for any project — covers stack detection, 4 core test files, script wiring, secret hygiene, and Cloudflare Workers/Pages patterns. Use when starting a new project, adding CI to an existing one, or when "tests pass but production breaks." Companion to cm-safe-deploy and cm-project-bootstrap.
---
# cm-test-gate: Multi-Layer Test Gate Setup
## Overview
A deployment process without a test gate is just shipping code and praying. The `test:gate` script is your first line of defense before deployment. A test gate MUST verify four things: frontend component safety, backend API behavior, core business logic, and i18n synchronization.
**Core assumption:** The most dangerous errors are syntax flaws, variable shadowing, or import failures that tests often skip if they only check logic.
**Violating the letter of this process is violating the spirit of quality engineering.**
## The Protocol
When setting up a test gate for a project, follow these 5 phases in order.
### Phase 1: Stack Detection and Environment Setup
**Goal:** Identify the framework and install the correct testing dependencies.
1. **Detect Stack:**
- Check `package.json` for framework (React, Vue, Svelte, static HTML) and build tool (Vite, Next.js).
- Check for `wrangler.json(c)` (Cloudflare Workers/Pages).
- Check for Tailwind, PostCSS, or specific UI libraries.
2. **Install Dependencies (Example: Vite/Vitest):**
```bash
# Install vitest and related tools
npm install -D vitest jsdom @testing-library/react @testing-library/jest-dom
# (Adjust based on framework: e.g., @testing-library/svelte)
```
3. **Configure File:**
- Create `vitest.config.ts` (or `.js`):
```typescript
import { defineConfig } from 'vitest/config'
// Import framework plugin (e.g., react(), svelte())
export default defineConfig({
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./test/setup.ts'], // Optional
},
})
```
### Phase 2: The 4 Core Test Files
A complete `test:gate` must cover four distinct layers. Do not combine these files.
#### Layer 1: Frontend Safety (`frontend-safety.test.ts`)
This layer prevents white screens and catastrophic syntax errors in the browser. Emphasize parsing and template rendering over logical assertions.
*Use the exact implementation from `cm-quality-gate` regarding the 4 corruption checks.*
```typescript
import { test, expect } from 'vitest';
import fs from 'fs';
import path from 'path';
test('app.js does not contain catastrophic syntax corruption', () => {
// 1. Read the raw file
const content = fs.readFileSync(path.resolve(__dirname, '../public/static/app.js'), 'utf-8');
// 2. Syntax Validation (Check for broken template literals)
// ❌ Bug #1: Single-quote wrapping template string
expect(content).not.toMatch(/=\s*'[^']*\$\{t\(/);
// 3. Delimiter consistency
// ❌ Bug #4: Mismatched delimiters
expect(content).not.toMatch(/t\('[^']*\`/);
expect(content).not.toMatch(/t\(\`[^']*'\)/);
// 4. HTML structure integrity
// ❌ Bug #2: Spaces inside tags or broken closers
expect(content).not.toMatch(/<\s+[a-zA-Z]/); // e.g., "< div"
expect(content).not.toMatch(/<\/\s+[a-zA-Z]/); // e.g., "</ div"
expect(content).not.toMatch(/--\s+>/); // e.g., "text-- >"
});
```
#### Layer 2: API Routes (`api-routes.test.ts`)
This layer ensures backend endpoints respond correctly and handle JSON properly.
*Example for a generic fetch wrapper or specific Next.js/Worker handler:*
```typescript
import { test, expect } from 'vitest';
test('API mock test', async () => {
// Test your server handlers directly
// Ensure 200 OK for valid inputs and 400 for errors
expect(true).toBe(true);
});
```
#### Layer 3: Business Logic (`business-logic.test.ts`)
This layer tests pure functions: calculations, validations, and data transformations.
```typescript
import { test, expect } from 'vitest';
test('Calculates score correctly', () => {
// const result = calculateScore(input);
// expect(result).toBe(expected);
expect(true).toBe(true);
});
```
#### Layer 4: i18n Synchronization (`i18n-sync.test.ts`)
This layer guarantees that language files are complete and identical in structure.
```typescript
import { test, expect } from 'vitest';
import fs from 'fs';
import path from 'path';
test('i18n files have identical key counts', () => {
const langDir = path.resolve(__dirname, '../public/static/i18n');
const langs = ['vi.json', 'en.json', 'th.json', 'ph.json'];
const countKeys = (obj: any): number => {
let count = 0;
for (const k in obj) {
if (typeof obj[k] === 'object' && obj[k] !== null) {
count += countKeys(obj[k]);
} else {
count++;
}
}
return count;
};
let baseCount = -1;
for (const file of langs) {
if (!fs.existsSync(path.join(langDir, file))) continue;
const data = JSON.parse(fs.readFileSync(path.join(langDir, file), 'utf-8'));
const count = countKeys(data);
if (baseCount === -1) {
baseCount = count;
} else {
expect(count, `File ${file} has a different key count`).toBe(baseCount);
}
}
});
```
#### Layer 5: Security Scan (`security-scan.test.ts`)
This layer prevents secrets from being committed to the repository. Powered by `cm-secret-shield` patterns.
```typescript
import { test, expect } from 'vitest';
import fs from 'fs';
import { execSync } from 'child_process';
test('no secret files tracked by git', () => {
const tracked = execSync('git ls-files', { encoding: 'utf-8' });
const badFiles = ['.env', '.dev.vars', '.env.local', '.env.production'];
const found = badFiles.filter(f => tracked.split('\n').includes(f));
expect(found, `Secret files tracked: ${found.join(', ')}`).toEqual([]);
});
test('.gitignore contains required security patterns', () => {
const gitignore = fs.readFileSync('.gitignore', 'utf-8');
expect(gitignore).toContain('.env');
expect(gitignore).toContain('.dev.vars');
});
test('no hardcoded secrets in source files', () => {
const dangerousPatterns = [
/SERVICE_KEY\s*[=:]\s*['"][a-zA-Z0-9/+=]{20,}/g,
/PRIVATE_KEY\s*[=:]\s*['"][a-zA-Z0-9/+=]{20,}/g,
/-----BEGIN.*PRIVATE KEY-----/g,
];
const srcDir = 'src';
if (!fs.existsSync(srcDir)) return;
const files = fs.readdirSync(srcDir).filter(f => f.endsWith('.ts') || f.endsWith('.js'));
for (const file of files) {
const content = fs.readFileSync(`${srcDir}/${file}`, 'utf-8');
for (const pattern of dangerousPatterns) {
expect(content, `${file} contains potential secret`).not.toMatch(pattern);
}
}
});
```
### Phase 3: Script Wiring
Wire these tests into `package.json` to make them easily executable by CI or other skills.
```json
{
"scripts": {
"test": "vitest",
"test:gate": "vitest run --reporter=verbose",
"test:security": "snyk test && aikido-api-client scan-release $npm_package_name $(git rev-parse HEAD) --minimum-severity-level=HIGH",
"test:watch": "vitest watch"
}
}
```
> **Security Gate Check:** The `test:security` script runs the Snyk dependency check and the Aikido release scan in parallel. See `cm-security-gate` for advanced SAST/IaC flags.
### Phase 4: Secret Hygiene and Ignore Configuration
**NEVER commit `.env` or `.dev.vars`.** Ensure tests do not expose actual production secrets.
1. **Check `.gitignore`:**
```bash
grep -E "node_modules|\.env|\.dev\.vars" .gitignore
# Must exist, if not, add them.
```
2. **Define Mock Env:**
Create a `.env.test` file (this CAN be committed) with safe, mock values if needed by the test environment.
### Phase 5: Verification
Run the test gate to prove it works before declaring the task complete.
```bash
npm run test:gate
```
## Integration with Other Skills
| Skill | Relationship |
|---|---|
| `cm-safe-deploy` | `test:gate` is Gate 2 in the safe deploy pipeline. |
| `cm-project-bootstrap` | Should invoke `cm-test-gate` during Phase 7 (Infrastructure Setup). |
| `cm-safe-i18n` | Relies on the i18n tests set up in Phase 2, Layer 4. |
| `cm-secret-shield` | Layer 5 security scan uses Secret Shield patterns. |
## Red Flags - STOP and Fix
- Setting up tests but not creating the `test:gate` run script.
- Combining all tests into one massive `app.test.js` file.
- Skipping the `frontend-safety.test.ts` layer for SPA/monolith projects.
- Using real production database credentials in the test setup.
- Ignoring test failures and proceeding anyway.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!