Skip to content

Commit

Permalink
Merge pull request #85 from rtjord/dev
Browse files Browse the repository at this point in the history
Updated rating tool and extended timeouts
  • Loading branch information
rtjord authored Dec 4, 2024
2 parents 5d34fc1 + 1e1189d commit eb2d442
Show file tree
Hide file tree
Showing 15 changed files with 499 additions and 53 deletions.
3 changes: 2 additions & 1 deletion backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions backend/src/common/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,13 @@ export interface PackageRating {
BusFactor: number;
ResponsiveMaintainer: number;
LicenseScore: number;
GoodPinningPractice: number;
PullRequest: number;
NetScore: number;
RampUpLatency: number;
CorrectnessLatency: number;
BusFactorLatency: number;
ResponsiveMaintainerLatency: number;
LicenseScoreLatency: number;
GoodPinningPracticeLatency: number;
PullRequestLatency: number;
NetScoreLatency: number;
}
Expand Down Expand Up @@ -92,4 +90,4 @@ export interface PackageTableRow {
JSProgram?: string; // JavaScript program for sensitive modules
standaloneCost?: number; // Standalone cost, excluding dependencies
totalCost?: number; // Total cost including dependencies if applicable
}
}
8 changes: 4 additions & 4 deletions backend/src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ export async function getScores(token: string, url: string): Promise<metricData>
ResponsiveMaintainer_Latency: -1,
License: -1,
License_Latency: -1,
GoodPinningPractice: -1,
GoodPinningPracticeLatency: -1,
PullRequest: -1,
PullRequestLatency: 0-1
GoodPinningPractice: -1,
GoodPinningPractice_Latency: -1,
PullRequest_Latency: -1
};
console.log('Error calculating score:', error);
return emptyResult;
Expand All @@ -103,4 +103,4 @@ export async function getRepoUrl(url: string): Promise<string> {
export function extractFieldFromPackageJson(packageJson: string, field: string) {
const metadata = JSON.parse(packageJson);
return metadata[field];
}
}
2 changes: 1 addition & 1 deletion backend/src/handlers/PackageRate/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,4 @@ export interface PackageTableRow {
JSProgram?: string; // JavaScript program for sensitive modules
standaloneCost: number; // Standalone cost, excluding dependencies
Rating: PackageRating;
}
}
80 changes: 78 additions & 2 deletions backend/src/services/rate/__tests__/gitAnalysis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const fakeRepoData: repoData = {
licenses: [],
numberOfCommits: -1,
numberOfLines: -1,
pullRequestMetrics: undefined,
documentation: {
hasReadme: false,
numLines: -1,
Expand All @@ -31,7 +32,8 @@ const fakeRepoData: repoData = {
licenses: -1,
numberOfCommits: -1,
numberOfLines: -1,
documentation: -1
documentation: -1,
pullRequests: -1
}
};

Expand Down Expand Up @@ -109,6 +111,80 @@ describe('gitAnalysisClass', () => {
it('should have a gitData with values', async () => {
const result = await gitAnalysisInstance.runTasks(url);
expect(result).not.toBe(fakeRepoData);
}, 10000);
}, 30000);

// In gitAnalysis.test.ts

describe('Pull Request Analysis', () => {
it('should fetch pull request metrics', async () => {
await gitAnalysisInstance.fetchPullRequests(fakeRepoData);
expect(fakeRepoData.pullRequestMetrics).toBeDefined();
if (fakeRepoData.pullRequestMetrics) {
expect(fakeRepoData.pullRequestMetrics.reviewedFraction).toBeGreaterThanOrEqual(0);
expect(fakeRepoData.pullRequestMetrics.reviewedFraction).toBeLessThanOrEqual(1);
expect(fakeRepoData.pullRequestMetrics.totalAdditions).toBeGreaterThanOrEqual(0);
expect(fakeRepoData.pullRequestMetrics.reviewedAdditions).toBeGreaterThanOrEqual(0);
}
}, 30000);

it('should handle repositories with no access or invalid repos', async () => {
const invalidRepo = {
...fakeRepoData,
repoUrl: 'https://github.com/definitely-not-real/non-existent-repo',
repoOwner: 'definitely-not-real',
repoName: 'non-existent-repo'
};

// Temporarily reduce exponential backoff for this test
const originalBackoff = gitAnalysisInstance.exponentialBackoff;
gitAnalysisInstance.exponentialBackoff = async (requestFn) => {
try {
return await requestFn();
} catch (error) {
throw error;
}
};

await gitAnalysisInstance.fetchPullRequests(invalidRepo);

// Restore original exponential backoff
gitAnalysisInstance.exponentialBackoff = originalBackoff;

expect(invalidRepo.pullRequestMetrics).toEqual({
totalAdditions: 0,
reviewedAdditions: 0,
reviewedFraction: 0
});
}, 10000); // Reduced timeout since we're bypassing backoff

it('should handle API errors gracefully', async () => {
const badRepo = {
...fakeRepoData,
repoOwner: '', // Invalid owner
repoName: '' // Invalid name
};

// Temporarily reduce exponential backoff for this test
const originalBackoff = gitAnalysisInstance.exponentialBackoff;
gitAnalysisInstance.exponentialBackoff = async (requestFn) => {
try {
return await requestFn();
} catch (error) {
throw error;
}
};

await gitAnalysisInstance.fetchPullRequests(badRepo);

// Restore original exponential backoff
gitAnalysisInstance.exponentialBackoff = originalBackoff;

expect(badRepo.pullRequestMetrics).toEqual({
totalAdditions: 0,
reviewedAdditions: 0,
reviewedFraction: 0
});
}, 10000); // Reduced timeout since we're bypassing backoff
});

});
98 changes: 94 additions & 4 deletions backend/src/services/rate/__tests__/metricCalc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ const fakeRepoData: repoData = {
numberOfContributors: 400,
numberOfOpenIssues: 10,
numberOfClosedIssues: 20,
lastCommitDate: "Sat Mar 09 2024",
lastCommitDate: "Sat Nov 09 2024",
licenses: [''],
numberOfCommits: 1200,
numberOfLines: 600,
pullRequestMetrics: {
totalAdditions: 1000,
reviewedAdditions: 800,
reviewedFraction: 0.8
},
documentation: {
hasReadme: true,
numLines: 1000,
Expand All @@ -27,7 +32,8 @@ const fakeRepoData: repoData = {
licenses: 0,
numberOfCommits: 0,
numberOfLines: 0,
documentation: 0
documentation: 0,
pullRequests: 0
}
};

Expand All @@ -43,6 +49,7 @@ const invalidData: repoData = {
licenses: [''],
numberOfCommits: -1,
numberOfLines: -1,
pullRequestMetrics: undefined,
documentation: {
hasReadme: false,
numLines: -1,
Expand All @@ -57,7 +64,8 @@ const invalidData: repoData = {
licenses: -1,
numberOfCommits: -1,
numberOfLines: -1,
documentation: -1
documentation: -1,
pullRequests: -1
}
};

Expand Down Expand Up @@ -241,8 +249,89 @@ describe('metricCalcClass', () => {
expect(netScore).toEqual(0);
});

describe('calculatePinnedDependencies', () => {
it('should return 1.0 for a repo with no dependencies', () => {
fakeRepoData.dependencies = [];
const pinnedDependencies = metricClass.calculatePinnedDependencies(fakeRepoData);
expect(pinnedDependencies).toEqual(1.0);
});

it('should calculate the fraction of pinned dependencies correctly', () => {
fakeRepoData.dependencies = [
{ name: 'dep1', version: '1.0.0' },
{ name: 'dep2', version: '2.3.x' },
{ name: 'dep3', version: '^1.2.3' },
];
const pinnedDependencies = metricClass.calculatePinnedDependencies(fakeRepoData);
expect(pinnedDependencies).toEqual(0.667);
});

it('should return 0 for invalid dependencies', () => {
fakeRepoData.dependencies = undefined;
const pinnedDependencies = metricClass.calculatePinnedDependencies(fakeRepoData);
expect(pinnedDependencies).toEqual(0);
});
});

describe('Pull Request Metrics', () => {
beforeEach(() => {
// Reset fakeRepoData for each test
fakeRepoData.pullRequestMetrics = {
totalAdditions: 1000,
reviewedAdditions: 800,
reviewedFraction: 0.8
};
});

it('should calculate PR score correctly with valid metrics', () => {
const prScore = metricClass.calculatePullRequestScore(fakeRepoData);
expect(prScore).toEqual(0.8);
});

it('should return 0 for undefined PR metrics', () => {
fakeRepoData.pullRequestMetrics = undefined;
const prScore = metricClass.calculatePullRequestScore(fakeRepoData);
expect(prScore).toEqual(0);
});

it('should return 0 for zero total additions', () => {
fakeRepoData.pullRequestMetrics = {
totalAdditions: 0,
reviewedAdditions: 0,
reviewedFraction: 0
};
const prScore = metricClass.calculatePullRequestScore(fakeRepoData);
expect(prScore).toEqual(0);
});

it('should return correct latency value', () => {
fakeRepoData.latency.pullRequests = 2000; // 2 seconds in milliseconds
const latency = metricClass.getPullRequestLatency(fakeRepoData.latency);
expect(latency).toEqual(2.0);
});

it('should include PR score in net score calculation', () => {
// Set up a scenario where we know the expected output
fakeRepoData.licenses = ['MIT'];
fakeRepoData.pullRequestMetrics = {
totalAdditions: 1000,
reviewedAdditions: 1000,
reviewedFraction: 1.0
};

const netScore = metricClass.calculateNetScore(fakeRepoData);
expect(netScore).toBeGreaterThan(0);
expect(netScore).toBeLessThanOrEqual(1);
});
});

// Test the overall getValue function
it('Return correct values from getValue method', () => {
fakeRepoData.dependencies = [
{ name: 'dep1', version: '1.0.0' },
{ name: 'dep2', version: '2.3.x' },
];

const result = metricClass.getValue(fakeRepoData);
expect(result).toHaveProperty('URL', fakeRepoData.repoUrl);
expect(result).toHaveProperty('NetScore');
Expand All @@ -251,5 +340,6 @@ describe('metricCalcClass', () => {
expect(result).toHaveProperty('RampUp');
expect(result).toHaveProperty('ResponsiveMaintainer');
expect(result).toHaveProperty('License');
expect(result).toHaveProperty('GoodPinningPractice');
});
});
});
57 changes: 55 additions & 2 deletions backend/src/services/rate/__tests__/npmAnalysis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ describe('npmAnalysis', () => {
licenses: -1,
numberOfCommits: -1,
numberOfLines: -1,
documentation: -1
documentation: -1,
pullRequests: -1
}
};

Expand Down Expand Up @@ -115,7 +116,8 @@ describe('npmAnalysis', () => {
licenses: -1,
numberOfCommits: -1,
numberOfLines: -1,
documentation: -1
documentation: -1,
pullRequests: -1
}
};

Expand All @@ -141,6 +143,55 @@ describe('npmAnalysis', () => {
expect(mockLogger.logDebug).toHaveBeenCalledWith(`No commits found in the repository ${mockNpmData.repoUrl} in dir ./repo`);
});
});
describe('analyzeDependencies', () => {
it('should calculate pinned dependency fraction correctly', async () => {
const dir = './testRepo'; // Mock directory
const npmData: npmData = {
repoUrl: 'https://github.com/example/repo',
lastCommitDate: '',
documentation: {
hasReadme: true,
numLines: 100,
hasExamples: false,
hasDocumentation: true,
dependencies: undefined, // Initially undefined
},
latency: {
contributors: -1,
openIssues: -1,
closedIssues: -1,
lastCommitDate: -1,
licenses: -1,
numberOfCommits: -1,
numberOfLines: -1,
documentation: -1,
dependencies: -1,
pullRequests: -1,
},
};

// Mock reading of package.json
jest.spyOn(fs, 'readFile').mockResolvedValue(JSON.stringify({
dependencies: {
'dep1': '1.0.0',
'dep2': '2.3.x',
},
devDependencies: {
'dep3': '^1.2.3',
'dep4': '1.2.5',
},
}));

const instance = new npmAnalysis(mockEnvVars);
await instance.analyzeDependencies(dir, npmData);

expect(npmData.documentation.dependencies).toEqual({
total: 4,
pinned: 3,
fractionPinned: 0.75,
});
});
});

describe('deleteRepo', () => {
it('should delete the repository', async () => {
Expand All @@ -160,4 +211,6 @@ describe('npmAnalysis', () => {
expect(mockLogger.logDebug).toHaveBeenCalledWith('Failed to delete repository in ./repo:');
});
});


});
Loading

0 comments on commit eb2d442

Please sign in to comment.