Skip to content

Commit

Permalink
update relevancy tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateusz Duda committed May 10, 2024
1 parent 746bc21 commit 608667b
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 83 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module.exports = {
transformIgnorePatterns: ['<rootDir>/node_modules/'],
roots: ["<rootDir>/test/", "<rootDir>/src/"],
collectCoverage: true,
collectCoverageFrom: ["**/src/**", "!**/node_modules/**"],
collectCoverageFrom: ["src/**", "!**/node_modules/**"],
coverageDirectory: './coverage',
coverageReporters: ['json', 'lcovonly', 'text', 'clover'],
testPathIgnorePatterns: ['/_utils/'],
Expand Down
19 changes: 14 additions & 5 deletions src/Commands/runVerifyUnpushedCommitsCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,20 @@ export async function verifyUnpushedCommits(args: Array<string>, root: string, u
return VerificationStatus.VERIFIED;
}

if ([...commitsVerificationInfo.values()].some(info => !info.isSkipped && !info.isMergeCommit && !info.includesOnlyIgnoredFiles && !info.hasRelevancy)) {
console.log(`[Scope tags] All commits are verified, but found no relevancy data. To add relevancy use:\n`);
console.log("\nTo add relevancy use\n\n\tnpx scope --add\n\n");
return VerificationStatus.NOT_VERIFIED;
}
// if ([...commitsVerificationInfo.entries()].some(([commitSHA, info]) => {

for (const [commitSHA, info] of [...commitsVerificationInfo.entries()]) {
if (!info.isSkipped && !info.isMergeCommit && !info.includesOnlyIgnoredFiles) {
const commit = await repository.getCommitByHash(commitSHA);

const filesWithoutRelevancy = info.filesToTag.filter(file => !info.relevancy.some(relevancyInfo => relevancyInfo.path === file.newPath));

console.log(`Commit '${commit.summary()}' not verified, found no relevancy for required files:\n`);
filesWithoutRelevancy.forEach(file => console.log(`- ${file.newPath}`));

return VerificationStatus.NOT_VERIFIED;
}
};

return VerificationStatus.VERIFIED;
}
12 changes: 5 additions & 7 deletions src/Git/GitRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,8 @@ export class GitRepository {
* https://git-scm.com/docs/git-diff
*/


// exec(`pwd && echo $PAGER && cd ${this._root} && git diff ${commit.sha()}^ ${commit.sha()} --name-status`, (error: any, stdout: any, stderr: any) => {
// console.debug("STDOUT:", stdout, ", STDERR:", stderr);
// });

const nameStatusOutput = execSync(`cd ${this._root} && git --no-pager diff ${commit.sha()}~ ${commit.sha()} --name-status`).toString().trim().split('\n');

console.debug(nameStatusOutput);

/**
* Has the following format:
* 1 1 .vscode/launch.json
Expand Down Expand Up @@ -312,6 +305,7 @@ export class GitRepository {
includesOnlyIgnoredFiles: false,
isMergeCommit: false,
hasRelevancy: false,
relevancy: [],
};

// Check if commit should be skipped
Expand Down Expand Up @@ -350,6 +344,10 @@ export class GitRepository {
// Check relevancy
commitInfo.hasRelevancy = relevancyManager.doesCommitMessageHaveRelevancyData(commit.message());

if (commitInfo.hasRelevancy) {
commitInfo.relevancy = relevancyManager.convertCommitMessageToRelevancyData(commit)
}

return commitInfo;
}

Expand Down
2 changes: 2 additions & 0 deletions src/Git/Types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Commit } from "nodegit/commit";
import { CommitMessageRelevancyInfo } from "../Relevancy/RelevancyManager";

export type FilePath = string;

Expand Down Expand Up @@ -48,4 +49,5 @@ export type VerificationInfo = {
hasRelevancy: boolean,
isMergeCommit: boolean,
includesOnlyIgnoredFiles: boolean,
relevancy: Array<CommitMessageRelevancyInfo>,
}
87 changes: 54 additions & 33 deletions src/Relevancy/RelevancyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class RelevancyManager {
} as CommitMessageRelevancyInfo;
});

// Merge relevancies - if some are duplicates, select those with higher relevancy
// Merge relevancies - if some are duplicates, select those with higher relevancy - TODO: This should be testable -> add test
const allRelevancies: CommitMessageRelevancyInfo[] = relevancyArray.concat(relevancyArrayFromCurrentCommit);
const mergedRelevancies: CommitMessageRelevancyInfo[] = [];

Expand Down Expand Up @@ -139,34 +139,47 @@ export class RelevancyManager {
}

public convertCommitMessageToRelevancyData(commit: Commit, replaceCurrentCommitSHA = true): Array<CommitMessageRelevancyInfo> {
const commitMessage = commit.message();

const prefixStartIndex = commitMessage.indexOf(RelevancyManager.COMMIT_MSG_PREFIX);
const relevancyEndIndex = commitMessage.lastIndexOf(RelevancyManager.COMMIT_MSG_PREFIX);

if (prefixStartIndex === -1) {
throw new Error(`Commit message '${commitMessage}' does not include relevancy info`);
if (!this.doesCommitMessageHaveRelevancyData(commit.message())) {
throw new Error(`Commit message '${commit.message()}' does not include correct relevancy data`);
}

const relevancyJSON = commitMessage.substring(prefixStartIndex + RelevancyManager.COMMIT_MSG_PREFIX.length, relevancyEndIndex);
const info: CommitMessageRelevancyInfo[] = [];

let relevancyInfo = [];
// Check every line, as commit could have multiple relevancies

try {
relevancyInfo = JSON.parse(relevancyJSON) as Array<CommitMessageRelevancyInfo>;
} catch (e) {
throw new Error(`Could not parse relevancy data from commit message: '${commitMessage}', found relevancy data: '${relevancyJSON}'`);
}
let currentLine = 0;

// Replace current commit' sha
if (replaceCurrentCommitSHA) {
relevancyInfo.forEach(info => {
if (info.commit === RelevancyManager.CURRENT_COMMIT) {
info.commit = commit.sha();
}
});
for (const line of commit.message().split("\n")) {
currentLine++;

const prefixStartIndex = line.indexOf(RelevancyManager.COMMIT_MSG_PREFIX);
const relevancyEndIndex = line.lastIndexOf(RelevancyManager.COMMIT_MSG_PREFIX);

if (prefixStartIndex === -1 || relevancyEndIndex === -1 || prefixStartIndex === relevancyEndIndex) {
continue;
}

const relevancyJSON = line.substring(prefixStartIndex + RelevancyManager.COMMIT_MSG_PREFIX.length, relevancyEndIndex);

try {
const parsedRelevancy = JSON.parse(relevancyJSON) as Array<CommitMessageRelevancyInfo>;

parsedRelevancy.forEach(relevancy => {
// Replace current commit' sha
if (replaceCurrentCommitSHA) {
if (relevancy.commit === RelevancyManager.CURRENT_COMMIT) {
relevancy.commit = commit.sha();
}
}
info.push(relevancy)
});
} catch (e) {
throw new Error(`Could not parse relevancy data from line ${currentLine}: '${line}', found relevancy data: '${relevancyJSON}'`);
}
}
return relevancyInfo;

return info;
}

// public addRelevancyFromCommit(fileDataRelevancy: Map<FileData, Relevancy>, commit: Commit) {
Expand All @@ -177,22 +190,30 @@ export class RelevancyManager {
// }

public doesCommitMessageHaveRelevancyData(commitMessage: string): boolean {
const prefixStartIndex = commitMessage.indexOf(RelevancyManager.COMMIT_MSG_PREFIX);
const relevancyEndIndex = commitMessage.lastIndexOf(RelevancyManager.COMMIT_MSG_PREFIX);

if (prefixStartIndex === -1 || relevancyEndIndex === -1 || prefixStartIndex === relevancyEndIndex) {
return false;
}
// Check every line, as commit could have multiple relevancies

let commitMessageHasRelevancy = false;

for (const line of commitMessage.split("\n")) {
const prefixStartIndex = line.indexOf(RelevancyManager.COMMIT_MSG_PREFIX);
const relevancyEndIndex = line.lastIndexOf(RelevancyManager.COMMIT_MSG_PREFIX);

const relevancyJSON = commitMessage.substring(prefixStartIndex + RelevancyManager.COMMIT_MSG_PREFIX.length, relevancyEndIndex);
if (prefixStartIndex === -1 || relevancyEndIndex === -1 || prefixStartIndex === relevancyEndIndex) {
continue;
}

const relevancyJSON = line.substring(prefixStartIndex + RelevancyManager.COMMIT_MSG_PREFIX.length, relevancyEndIndex);

try {
JSON.parse(relevancyJSON);
return true;
} catch (e) {
return false;
try {
JSON.parse(relevancyJSON);
commitMessageHasRelevancy = true;
} catch (e) {
return false;
}
}

return commitMessageHasRelevancy;
}

public loadRelevancyMapFromCommits(commits: Commit[]): RelevancyMap {
Expand Down
137 changes: 100 additions & 37 deletions test/commits/relevancy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,45 @@ const checkRelevancyAndFileData = (info: CommitMessageRelevancyInfo, fileData: F
expect(info.commit).toBe(expectedCommitSHA);
}

const mockFileData1: FileData = {
oldPath: "src/basic/commands/command.ts",
newPath: "src/basic/commands/command.ts",
change: GitDeltaType.ADDED,
linesAdded: 120,
linesRemoved: 10,
commitedIn: {
sha: () => "sha"
} as Commit
};

const mockFileData2: FileData = {
oldPath: "assets/basic/assets/asset.jpg",
newPath: "assets/basic/assets/asset.jpg",
change: GitDeltaType.MODIFIED,
linesAdded: 5,
linesRemoved: 0,
commitedIn: {
sha: () => "sha"
} as Commit
};

const mockFileData3: FileData = {
oldPath: "config/basic/config/data.log",
newPath: "config/basic/config/data.log",
change: GitDeltaType.DELETED,
linesAdded: 0,
linesRemoved: 1050,
commitedIn: {
sha: () => "sha"
} as Commit
};

const mockRelevancyData = new Map<FileData, Relevancy>([
[mockFileData1, Relevancy.LOW],
[mockFileData2, Relevancy.MEDIUM],
[mockFileData3, Relevancy.HIGH],
]);

describe("Relevancy manager tests", () => {

it("Reports no relevancy when there is no relevancy in commit message", async () => {
Expand Down Expand Up @@ -47,41 +86,40 @@ describe("Relevancy manager tests", () => {
})
})

it("Correctly encodes relevancy data in a commit", async () => {
it("Reports relevancy when there is correct relevancy encoded", async () => {
const relevancyManager = new RelevancyManager();

const mockFileData1: FileData = {
oldPath: "src/basic/commands/command.ts",
newPath: "src/basic/commands/command.ts",
change: GitDeltaType.ADDED,
linesAdded: 120,
linesRemoved: 10,
commitedIn: {
sha: () => "sha"
} as Commit
};

const mockFileData2: FileData = {
oldPath: "assets/basic/assets/asset.jpg",
newPath: "assets/basic/assets/asset.jpg",
change: GitDeltaType.MODIFIED,
linesAdded: 5,
linesRemoved: 0,
commitedIn: {
sha: () => "sha"
} as Commit
};

const mockFileData3: FileData = {
oldPath: "config/basic/config/data.log",
newPath: "config/basic/config/data.log",
change: GitDeltaType.DELETED,
linesAdded: 0,
linesRemoved: 1050,
commitedIn: {
sha: () => "sha"
} as Commit
};
const commitMessagesWithRelevancy = [
`[TEST-1234] There is correct relevancy data in this commit message
__relevancy__[]__relevancy__`,
`[TEST - 1234] There is correct relevancy data in this commit message
__relevancy__[{"path":"src/basic/commands/command.ts","relevancy":"LOW","commit":"__current__"}]__relevancy__
`,
`[TEST-1234] This is a commit message with multiple relevancies
__relevancy__[{"path":"src/basic/commands/command.ts","relevancy":"LOW","commit":"__current__"}]__relevancy__
__relevancy__[{"path":"assets/basic/assets/asset.jpg","relevancy":"MEDIUM","commit":"__current__"},{"path":"config/basic/config/data.log","relevancy":"HIGH","commit":"__current__"}]__relevancy__
`,
];

commitMessagesWithRelevancy.forEach(message => {
const hasRelevancy = relevancyManager.doesCommitMessageHaveRelevancyData(message);

if (!hasRelevancy) {
console.debug(message);
}

expect(hasRelevancy).toBe(true);
})
})


it("Correctly encodes relevancy data in a commit", async () => {
const relevancyManager = new RelevancyManager();

const mockRelevancyData = new Map<FileData, Relevancy>([
[mockFileData1, Relevancy.LOW],
Expand All @@ -96,12 +134,8 @@ describe("Relevancy manager tests", () => {
sha: () => "sha",
} as Commit;

mockCommit.message

const generatedMessage = relevancyManager.convertRelevancyDataToCommitMessage(mockRelevancyData, mockCommit);

console.debug(generatedMessage);

expect(relevancyManager.doesCommitMessageHaveRelevancyData(generatedMessage)).toBe(true);

// Read relevancy back
Expand All @@ -117,4 +151,33 @@ describe("Relevancy manager tests", () => {
checkRelevancyAndFileData(generatedRelevancyData[1], mockFileData2, mockRelevancyData.get(mockFileData2), "sha");
checkRelevancyAndFileData(generatedRelevancyData[2], mockFileData3, mockRelevancyData.get(mockFileData3), "sha");
})

it("Correctly reads multiple relevancies from a merge commit", async () => {
const relevancyManager = new RelevancyManager();

const mockCommitWithGeneratedRelevancy: Commit = {
message: () => `[TEST-1234] This is a commit message with multiple relevancies
__relevancy__[{"path":"src/basic/commands/command.ts","relevancy":"LOW","commit":"__current__"}]__relevancy__
__relevancy__[{"path":"assets/basic/assets/asset.jpg","relevancy":"MEDIUM","commit":"__current__"},{"path":"config/basic/config/data.log","relevancy":"HIGH","commit":"__current__"}]__relevancy__
`,
sha: () => "sha",
} as Commit;

expect(relevancyManager.doesCommitMessageHaveRelevancyData(mockCommitWithGeneratedRelevancy.message())).toBe(true);

// Read relevancy back

const generatedRelevancyData: CommitMessageRelevancyInfo[] = relevancyManager.convertCommitMessageToRelevancyData(mockCommitWithGeneratedRelevancy, true);

expect(generatedRelevancyData.length).toBe(3);

checkRelevancyAndFileData(generatedRelevancyData[0], mockFileData1, mockRelevancyData.get(mockFileData1), "sha");
checkRelevancyAndFileData(generatedRelevancyData[1], mockFileData2, mockRelevancyData.get(mockFileData2), "sha");
checkRelevancyAndFileData(generatedRelevancyData[2], mockFileData3, mockRelevancyData.get(mockFileData3), "sha");
})
});

0 comments on commit 608667b

Please sign in to comment.