Skip to content

Implement unit tests for evaluator/criteria.ts #7

@BekahHW

Description

@BekahHW

Description

The src/evaluator/criteria.ts file contains the core scoring logic for RepoReady (158 lines) but has no test coverage. This file includes critical functions like calculateRating(), generateRecommendations(), and all evaluation criteria that determine repository scores.

Current State

  • ❌ No tests for evaluationCriteria array (12 criteria functions)
  • ❌ No tests for calculateRating() function
  • ❌ No tests for generateRecommendations() function
  • ✅ Jest is configured in the project
  • ✅ TypeScript interfaces are well-defined in src/types/index.ts

Acceptance Criteria

Test Coverage Goals

  • 100% function coverage for all exported functions
  • 90%+ line coverage for the entire file
  • All evaluation criteria tested with various repository states
  • Edge cases covered (empty data, missing fields, etc.)

Specific Tests Required

calculateRating() Function

  • Test all rating thresholds (Excellent: 90-100%, Good: 75-89%, etc.)
  • Test boundary values (89.9 vs 90.0)
  • Test extreme values (0, 100, negative numbers)

generateRecommendations() Function

  • Test with no failed criteria (should return empty array)
  • Test with multiple failed criteria (should prioritize by weight)
  • Test with all criteria failed (should limit to top 5)
  • Test recommendation text accuracy for each criteria type

Individual Evaluation Criteria

  • Test each of the 12 criteria functions with mock repository data
  • Test edge cases for each criteria (empty strings, null values, etc.)
  • Verify weight assignments are correct

Implementation Suggestions

Test File Structure

// src/__tests__/evaluator/criteria.test.ts
import {
  evaluationCriteria,
  calculateRating,
  generateRecommendations
} from '../../evaluator/criteria';
import { RepositoryInfo } from '../../types';

// Mock repository data factory
function createMockRepo(overrides: Partial<RepositoryInfo> = {}): RepositoryInfo {
  return {
    owner: 'test-owner',
    repo: 'test-repo',
    name: 'test-repo',
    description: 'A test repository for unit testing',
    topics: ['test', 'typescript'],
    hasReadme: true,
    hasContributing: true,
    hasCodeOfConduct: true,
    hasLicense: true,
    hasIssueTemplates: true,
    hasPullRequestTemplate: true,
    hasGoodFirstIssues: true,
    hasHelpWantedIssues: true,
    isArchived: false,
    defaultBranch: 'main',
    ...overrides
  };
}

describe('Evaluation Criteria', () => {
  describe('calculateRating', () => {
    it('returns "Excellent" for 90-100%', () => {
      expect(calculateRating(100)).toBe('Excellent');
      expect(calculateRating(95)).toBe('Excellent');
      expect(calculateRating(90)).toBe('Excellent');
    });
    
    it('returns "Good" for 75-89%', () => {
      expect(calculateRating(89)).toBe('Good');
      expect(calculateRating(80)).toBe('Good');
      expect(calculateRating(75)).toBe('Good');
    });
    
    // Add more boundary tests...
  });
  
  describe('generateRecommendations', () => {
    it('returns empty array when all criteria pass', () => {
      const results = [
        { criteria: 'README File', passed: true, weight: 20 }
      ];
      expect(generateRecommendations(results)).toEqual([]);
    });
    
    it('prioritizes recommendations by weight', () => {
      const results = [
        { criteria: 'README File', passed: false, weight: 20 },
        { criteria: 'Repository Name', passed: false, weight: 5 }
      ];
      const recommendations = generateRecommendations(results);
      expect(recommendations[0]).toContain('README');
    });
  });
  
  describe('Individual Criteria', () => {
    describe('Repository Name criteria', () => {
      const nameCriteria = evaluationCriteria.find(c => c.name === 'Repository Name')!;
      
      it('passes for descriptive names', () => {
        const repo = createMockRepo({ name: 'awesome-project' });
        expect(nameCriteria.check(repo)).toBe(true);
      });
      
      it('fails for generic names', () => {
        const repo = createMockRepo({ name: 'test' });
        expect(nameCriteria.check(repo)).toBe(false);
      });
      
      it('fails for very short names', () => {
        const repo = createMockRepo({ name: 'ab' });
        expect(nameCriteria.check(repo)).toBe(false);
      });
    });
    
    // Test each criteria similarly...
  });
});

Key Test Scenarios

Perfect Repository (All Criteria Pass)

const perfectRepo = createMockRepo();
// Should score 100% and get "Excellent" rating

Minimal Repository (Most Criteria Fail)

const minimalRepo = createMockRepo({
  description: '',
  topics: [],
  hasReadme: false,
  hasContributing: false,
  // ... set most to false
});

Edge Case Repository

const edgeRepo = createMockRepo({
  name: '',  // Empty name
  description: 'x'.repeat(1000),  // Very long description
  topics: new Array(50).fill('tag'),  // Many topics
});

Files to Create

  • src/__tests__/evaluator/criteria.test.ts
  • src/__tests__/helpers/mockData.ts (optional, for shared test data)

Current Code Analysis

Functions to Test

  1. calculateRating(percentage: number) - 5 rating categories
  2. generateRecommendations(results: Array<...>) - Priority-based recommendations
  3. evaluationCriteria[].check() - 12 individual criteria functions

Test Data Requirements

  • Mock RepositoryInfo objects with various states
  • Test results arrays with different pass/fail combinations
  • Boundary value test cases

Benefits

  • 🎯 Ensures scoring accuracy and consistency
  • 🐛 Catches edge cases and potential bugs
  • 🔧 Makes refactoring safer
  • 📊 Validates recommendation logic
  • 🚀 Builds confidence for contributors

Resources

Estimated Effort

Medium - Requires understanding the scoring logic and creating comprehensive test cases.


Perfect for contributors who want to ensure the core evaluation logic is bulletproof! 🎯

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions