{
  "dimension": "testing",
  "prefix": "TEST",
  "description": "Rules for detecting testing issues including coverage gaps, test quality, and mock usage",
  "rules": [
    {
      "id": "missing-assertion",
      "category": "test-quality",
      "severity": "high",
      "pattern": "(?:it|test)\\s*\\([^)]+,\\s*(?:async\\s*)?\\(\\)\\s*=>\\s*\\{[^}]*\\}\\s*\\)",
      "patternType": "regex",
      "negativePatterns": ["expect", "assert", "should", "toBe", "toEqual"],
      "description": "Test case without assertions always passes and provides no value",
      "recommendation": "Add assertions to verify expected behavior. Each test should have at least one meaningful assertion",
      "fixExample": "// Before\nit('should process data', async () => {\n  await processData(input);\n});\n\n// After\nit('should process data', async () => {\n  const result = await processData(input);\n  expect(result.success).toBe(true);\n  expect(result.data).toHaveLength(3);\n});"
    },
    {
      "id": "hardcoded-test-data",
      "category": "test-maintainability",
      "severity": "low",
      "pattern": "expect\\s*\\([^)]+\\)\\.toBe\\s*\\(['\"][^'\"]{20,}['\"]\\)",
      "patternType": "regex",
      "description": "Long hardcoded strings in assertions are brittle and hard to maintain",
      "recommendation": "Use snapshots for large outputs, or extract expected values to test fixtures",
      "fixExample": "// Before\nexpect(result).toBe('very long expected string that is hard to maintain...');\n\n// After\nexpect(result).toMatchSnapshot();\n// or\nconst expected = loadFixture('expected-output.json');\nexpect(result).toEqual(expected);"
    },
    {
      "id": "no-error-test",
      "category": "coverage-gap",
      "severity": "medium",
      "pattern": "describe\\s*\\([^)]+",
      "patternType": "regex",
      "negativePatterns": ["throw", "reject", "error", "fail", "catch"],
      "description": "Test suite may be missing error path testing. Error handling is critical for reliability",
      "recommendation": "Add tests for error cases: invalid input, network failures, edge cases",
      "fixExample": "// Add error path tests\nit('should throw on invalid input', () => {\n  expect(() => processData(null)).toThrow('Invalid input');\n});\n\nit('should handle network failure', async () => {\n  mockApi.mockRejectedValue(new Error('Network error'));\n  await expect(fetchData()).rejects.toThrow('Network error');\n});"
    },
    {
      "id": "test-implementation-detail",
      "category": "test-quality",
      "severity": "medium",
      "pattern": "toHaveBeenCalledWith|toHaveBeenCalledTimes",
      "patternType": "includes",
      "description": "Testing implementation details (call counts, exact parameters) makes tests brittle to refactoring",
      "recommendation": "Prefer testing observable behavior and outcomes over internal implementation",
      "fixExample": "// Before - brittle\nexpect(mockService.process).toHaveBeenCalledTimes(3);\nexpect(mockService.process).toHaveBeenCalledWith('exact-arg');\n\n// After - behavior-focused\nexpect(result.items).toHaveLength(3);\nexpect(result.processed).toBe(true);"
    },
    {
      "id": "skip-test",
      "category": "test-coverage",
      "severity": "high",
      "pattern": "it\\.skip|test\\.skip|xit|xdescribe|describe\\.skip",
      "patternType": "regex",
      "description": "Skipped tests indicate untested code paths or broken functionality",
      "recommendation": "Fix or remove skipped tests. If temporarily skipped, add TODO comment with issue reference",
      "fixExample": "// Before\nit.skip('should handle edge case', () => { ... });\n\n// After - either fix it\nit('should handle edge case', () => {\n  // fixed implementation\n});\n\n// Or document why skipped\n// TODO(#123): Re-enable after API migration\nit.skip('should handle edge case', () => { ... });"
    }
  ]
}
