Before Lab 3.2 — Quality Gates: Type Checking and Tests
Before Lab 3.2 — Quality Gates: Type Checking and Tests
Before running security scans or building artifacts, the pipeline should verify the application is correct enough to ship.
What are quality gates?
Quality gates are automated checks that must pass before the pipeline continues:
If type checking fails, there's no point running a 10-minute security scan.
TypeScript type checking
TypeScript's type system catches errors at compile time instead of runtime:
// This fails type checking before deployment
function getUser(id: string): User {
return id; // Error: Type 'string' is not assignable to type 'User'
}
// This passes type checking
function getUser(id: string): User {
return database.findUser(id); // OK: returns User
}Run type checking without emitting files:
npm run typecheck
# or
tsc --noEmitType checking is fast (usually under 10 seconds) and catches:
- Type mismatches
- Missing properties
- Incorrect function signatures
- Null/undefined access errors
Unit tests
Unit tests verify individual functions in isolation:
// Unit test example
describe("Configuration", () => {
it("should reject invalid port numbers", () => {
expect(() => config.setPort(-1)).toThrow();
expect(() => config.setPort(99999)).toThrow();
});
it("should accept valid port numbers", () => {
expect(() => config.setPort(3000)).not.toThrow();
});
});What unit tests prove:
- Individual functions work correctly
- Edge cases are handled
- Error conditions are caught
- Business logic is correct
What unit tests do NOT prove:
- The API endpoint works
- The database connection works
- The deployed application works
Integration tests
Integration tests verify that components work together:
// Integration test example
describe("API Integration Tests", () => {
it("should return health status", async () => {
const response = await fetch("http://localhost:3000/api/health");
expect(response.status).toBe(200);
const data = await response.json();
expect(data.status).toBe("healthy");
});
});What integration tests prove:
- Routes are registered correctly
- Middleware is applied
- Request/response flow works
- Multiple components work together
What integration tests do NOT prove:
- The deployed Azure environment works
- Authentication works in production
- Network access restrictions work
The test pyramid
Most tests should be unit tests. Integration tests verify connections. End-to-end tests are expensive and slow.
E2E Tests
Critical user journeys across the entire system
Integration Tests
Verify component interactions and data flow
Unit Tests
Fast, isolated testing of individual components
Test Pyramid Explained
The test pyramid shows the ideal distribution of tests. Most tests should be unit tests (fast, cheap, isolated). Fewer integration tests (verify connections). Even fewer E2E tests (slow, expensive).
Unit Tests (70%)
- Fast (milliseconds per test)
- Low cost - cheap to write and run
- Isolated - test individual functions
- High maintenance - stable when tests are well-designed
- Example: Testing a pure function that validates email format
Integration Tests (20%)
- Medium speed (seconds per test)
- Medium cost - requires test infrastructure
- Verifies connections - tests API endpoints, database queries
- Moderate maintenance - more fragile than unit tests
- Example: Testing an API endpoint that returns user data
E2E Tests (10%)
- Slow (minutes per test suite)
- High cost - requires full environment
- Verifies complete flows - tests user journeys
- High maintenance - most fragile
- Example: Testing a complete purchase flow in the browser
Git Hooks (Bonus)
Git hooks are scripts that run automatically at specific points in the Git workflow. They can serve as an additional quality gate at the commit level, before code even reaches the CI/CD pipeline.
Common Git hooks
| Hook | When it runs | Typical use |
|---|---|---|
pre-commit | Before commit message is written | Run linting, type checks, formatting |
pre-push | Before pushing to remote | Run all tests before pushing |
commit-msg | After commit message is written | Enforce commit message format |
post-merge | After merge completes | Run dependency install, rebuild |
Example: Pre-commit hook for quality checks
Create .git/hooks/pre-commit:
#!/bin/bash
# Run type checking before commit
npm run typecheck || exit 1
# Optionally run quick unit tests
npm run test:unit -- --testPathIgnorePatterns integration || exit 1Or use a Git hooks framework like Husky (for npm projects):
npm install --save-dev husky
npx husky add .husky/pre-commit "npm run typecheck"Benefits and limitations
| Benefits | Limitations |
|---|---|
| Catch errors before they reach CI | Hooks are local only (dev team only) |
| Faster feedback (no waiting for CI) | Not enforced by default (developers can skip) |
| Encourages developer discipline | Can slow down local development |
| Reduces CI queue time | Needs maintenance when dependencies change |
Git hooks vs. CI/CD gates
Git hooks provide fast local feedback and encourage good habits, but CI/CD gates provide enforced quality checks that apply to everyone. Both have a place in a mature quality process.
Why quality gates come before security gates
Compare these two scenarios:
Scenario A: Security scans first
1. Semgrep scan (5 minutes) → Pass
2. Trivy scan (3 minutes) → Pass
3. TypeScript check → Fail (type error in new code)
4. Total wasted time: 8 minutesScenario B: Quality gates first
1. TypeScript check (10 seconds) → Fail
2. Pipeline stops immediately
3. Total wasted time: 10 secondsRun fast checks first. Fail early.

Quality gates vs security gates
Quality gates verify correctness (does the code work?). Security gates verify safety (is the code exploitable?).
| Aspect | Quality Gates | Security Gates |
|---|---|---|
| Purpose | Verify correctness | Verify safety |
| Tools | TypeScript, Node test runner | Semgrep, Trivy, Checkov, ZAP |
| What they check | Type errors, business logic, API contracts | Vulnerabilities, secrets, misconfigurations, runtime exploits |
| Failure mode | Code doesn't work as intended | Code works but is exploitable |
| Speed | Fast (seconds to minutes) | Slower (minutes) |
| When to run | Before security gates (fail fast) | After quality gates pass |
| Example failure | TypeError: Cannot read property 'id' of undefined | High severity: SQL injection in user input handler |
Both are essential. Quality gates ensure the application functions; security gates ensure it functions safely.
What passing tests do NOT prove
✓ All tests pass
✓ Type checking passes
✓ Code builds successfully
This does NOT mean:
✗ The application is secure
✗ Dependencies are safe
✗ Infrastructure is configured correctly
✗ The API is protected against attacksThat's why Day 3 has both quality gates AND security gates.
What to look for in Lab 3.2
Lab 3.2 checklist
Lab File
Download Lab 3.2 — Quality Gates PDF