Skip to content

Commit

Permalink
added tests for database file
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateusz Duda committed May 22, 2024
1 parent 7c35981 commit c1ec1e7
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 29 deletions.
4 changes: 2 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
transformIgnorePatterns: ['<rootDir>/node_modules/'],
transformIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/__temporaryTestData__/'],
roots: ["<rootDir>/test/", "<rootDir>/src/"],
collectCoverage: true,
collectCoverage: false,
collectCoverageFrom: ["src/**", "!**/node_modules/**"],
coverageDirectory: './coverage',
coverageReporters: ['json', 'lcovonly', 'text', 'clover'],
Expand Down
44 changes: 21 additions & 23 deletions src/Git/GitRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ export class GitRepository {
}

public getFileDataUsingNativeGitCommand(commit: Commit): FileData[] {

/**
* Has the following format:
* M src/Git/GitRepository.ts
Expand Down Expand Up @@ -134,8 +133,14 @@ export class GitRepository {
`cd ${this._root} && git update-index && git diff-tree --no-commit-id --numstat -r ${commit.sha()}`
).toString().trim().split('\n');

if (nameStatusOutput.length !== numstatOutput.length) {
throw new Error(`Output of git-diff --name-status does not match one for --no-commit-id for commit ${commit.sha()}`);
if (nameStatusOutput.length === 0) {
console.debug(nameStatusOutput);
throw new Error(`Output of git-diff --name-status is empty for commit ${commit.sha()}`);
}

if (numstatOutput.length === 0) {
console.debug(numstatOutput);
throw new Error(`Output of git-diff --numstat is empty for commit ${commit.sha()}`);
}

const statusesToGitDeltaTypeMap = new Map<string, GitDeltaType>([
Expand All @@ -154,19 +159,27 @@ export class GitRepository {

const fileDataArray: FileData[] = [];

numstatOutput.forEach((numStatLine: string, i: number) => {
nameStatusOutput.forEach((nameStatusLine: string, i: number) => {
const [changeType, relatedFilePath, optionalRenamedPath] = nameStatusLine.split('\t');

const numStatLine = numstatOutput.find(output => output.includes(relatedFilePath));

if (!numStatLine) {
throw new Error(`No matching git-diff --name-status for file ${relatedFilePath}, output is ${nameStatusOutput}`);
}

const [linesAdded, linesRemoved, filePath] = numStatLine.split('\t');

const [changeType, relatedFilePath, optionalRenamedPath] = nameStatusOutput[i].split('\t');
let ourChangeType = statusesToGitDeltaTypeMap.get(changeType[0]) || GitDeltaType.UNREADABLE;

if (filePath !== relatedFilePath) {
throw new Error(`Error while processing git-diff: File path '${filePath}' does not match one of '${relatedFilePath}'`);
if (optionalRenamedPath) {
ourChangeType = GitDeltaType.RENAMED;
}

fileDataArray.push({
oldPath: filePath,
newPath: optionalRenamedPath || filePath,
change: statusesToGitDeltaTypeMap.get(changeType[0]) || GitDeltaType.UNREADABLE,
change: ourChangeType,
linesAdded: parseInt(linesAdded),
linesRemoved: parseInt(linesRemoved),
commitedIn: commit
Expand Down Expand Up @@ -251,27 +264,12 @@ export class GitRepository {

// Mostly from https://github.com/nodegit/nodegit/blob/master/examples/add-and-commit.js
public async commitFiles(commitMessage: string, filePaths: string[]): Promise<Oid> {
const { execSync } = require('child_process');

const repository = await this._getRepository();
// const index = await repository.refreshIndex();

// for (const filePath of filePaths) {
// await index.addByPath(filePath);
// }

// await index.write();

// const oid = await index.writeTree();

// const parent = await repository.getHeadCommit();
const author = Signature.now("Scott Chacon", "[email protected]");
const committer = Signature.now("Scott A Chacon", "[email protected]");

const commitId = await repository.createCommitOnHead(filePaths, author, committer, commitMessage);

repository.createCommitOnHead

return commitId;
}

Expand Down
29 changes: 28 additions & 1 deletion src/Scope/FileTagsDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,15 @@ export class FileTagsDatabase implements IJSONFileDatabase<FileTagsDatabase> {
this._fileTagsDatabaseData.ignoredFiles[ignoredFileIndex] = newPath;
}

private _deleteFileFromDatabase(filePath: string) {
delete this._fileTagsDatabaseData.files[filePath];
}

private _deleteIgnoredFileFromDatabase(filePath: string) {
const index = this._fileTagsDatabaseData.ignoredFiles.indexOf(filePath);
this._fileTagsDatabaseData.ignoredFiles.splice(index, 1);
}

// Returns files to be tagged
public updateDatabaseBasedOnChanges(fileDataArray: FileData[]): FileData[] {
fileDataArray.forEach(fileData => {
Expand All @@ -352,7 +361,11 @@ export class FileTagsDatabase implements IJSONFileDatabase<FileTagsDatabase> {
}
// If file deleted - remove from database
if (fileData.change === GitDeltaType.DELETED) {

if (this.isFileInDatabase(fileData.oldPath)) {
this._deleteFileFromDatabase(fileData.oldPath);
} else if (this.isIgnored(fileData.oldPath)) {
this._deleteIgnoredFileFromDatabase(fileData.oldPath);
}
}
});
return fileDataArray.filter(fileData => fileData.change !== GitDeltaType.DELETED);
Expand All @@ -361,4 +374,18 @@ export class FileTagsDatabase implements IJSONFileDatabase<FileTagsDatabase> {
public getPath(): string {
return FileTagsDatabase.PATH;
}

public get fileCount(): number {
if (!this._loaded) {
throw new Error("[FileTagsDatabase] Use .load() before reading file count");
}
return Object.keys(this._fileTagsDatabaseData.files).length;
}

public get ignoredFilesCount(): number {
if (!this._loaded) {
throw new Error("[FileTagsDatabase] Use .load() before reading ignored files count");
}
return this._fileTagsDatabaseData.ignoredFiles.length;
}
}
2 changes: 1 addition & 1 deletion test/_repo
104 changes: 104 additions & 0 deletions test/scope/databaseFile.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { GitRepository } from "../../src/Git/GitRepository";
import { GitDeltaType } from "../../src/Git/Types";
import { FileTagsDatabase } from "../../src/Scope/FileTagsDatabase";
import { cloneMockRepositoryToFolder, commitFilesUsingGitNatively, deleteFiles, makeUniqueFolderForTest, renameFiles } from "../utils/utils";

describe("Database file", () => {
it("After files are deleted, the information about them is purged from database", async () => {
const FOLDER_PATH = makeUniqueFolderForTest();
const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH);

const savedDatabase = new FileTagsDatabase(REPO_PATH);

savedDatabase.load();

const filesToDelete = [
"src/tagged-file.js",
"src/file-ignored-by-database.js"
];

deleteFiles(filesToDelete, REPO_PATH);
commitFilesUsingGitNatively(filesToDelete, REPO_PATH, "delete commit");

const repository = new GitRepository(REPO_PATH);

const unpushedCommits = await repository.getUnpushedCommits();
expect(unpushedCommits.length).toBe(1);

const commit = unpushedCommits[0];

const fileDataArray = await repository.getFileDataForCommits([commit], true);

expect(fileDataArray.length).toBe(2);

expect(fileDataArray[0].change).toBe(GitDeltaType.DELETED);
expect(fileDataArray[1].change).toBe(GitDeltaType.DELETED);

const newDatabase = new FileTagsDatabase(REPO_PATH);

newDatabase.load();
newDatabase.updateDatabaseBasedOnChanges(fileDataArray);

// Check if the files were deleted

expect(savedDatabase.isFileInDatabase("src/tagged-file.js")).toBe(true);
expect(newDatabase.isFileInDatabase("src/tagged-file.js")).toBe(false);

expect(savedDatabase.isIgnored("src/file-ignored-by-database.js")).toBe(true);
expect(newDatabase.isIgnored("src/file-ignored-by-database.js")).toBe(false);

expect(newDatabase.fileCount).toBe(savedDatabase.fileCount - 1);
expect(newDatabase.ignoredFilesCount).toBe(savedDatabase.ignoredFilesCount - 1);
});

it("After files are renamed, the information about them is purged from database", async () => {
const FOLDER_PATH = makeUniqueFolderForTest();
const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH);

const savedDatabase = new FileTagsDatabase(REPO_PATH);

savedDatabase.load();

const filesToRename = [
["src/tagged-file.js", "src/tagged-file-renamed.js"],
["src/file-ignored-by-database.js", "src/file-ignored-by-database-renamed.js"],
];

renameFiles(filesToRename, REPO_PATH);
commitFilesUsingGitNatively(filesToRename.map(files => files[1]), REPO_PATH, "rename commit");

const repository = new GitRepository(REPO_PATH);

const unpushedCommits = await repository.getUnpushedCommits();
expect(unpushedCommits.length).toBe(1);

const commit = unpushedCommits[0];

const fileDataArray = await repository.getFileDataForCommits([commit], true);

expect(fileDataArray.length).toBe(2);

expect(fileDataArray[0].change).toBe(GitDeltaType.RENAMED);
expect(fileDataArray[1].change).toBe(GitDeltaType.RENAMED);

const newDatabase = new FileTagsDatabase(REPO_PATH);

newDatabase.load();
newDatabase.updateDatabaseBasedOnChanges(fileDataArray);

// Check if the files were deleted

expect(savedDatabase.isFileInDatabase("src/tagged-file.js")).toBe(true);
expect(savedDatabase.isFileInDatabase("src/tagged-file-renamed.js")).toBe(false);
expect(newDatabase.isFileInDatabase("src/tagged-file.js")).toBe(false);
expect(newDatabase.isFileInDatabase("src/tagged-file-renamed.js")).toBe(true);

expect(savedDatabase.isIgnored("src/file-ignored-by-database.js")).toBe(true);
expect(savedDatabase.isIgnored("src/file-ignored-by-database-renamed.js")).toBe(false);
expect(newDatabase.isIgnored("src/file-ignored-by-database.js")).toBe(false);
expect(newDatabase.isIgnored("src/file-ignored-by-database-renamed.js")).toBe(true);

expect(newDatabase.fileCount).toBe(savedDatabase.fileCount);
expect(newDatabase.ignoredFilesCount).toBe(savedDatabase.ignoredFilesCount);
});
});
42 changes: 40 additions & 2 deletions test/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { resolve, join, sep, posix } from "path";
import { appendFileSync, existsSync, mkdirSync } from "fs";

import { MOCK_REMOTE_URL, MOCK_REPOSITORY, TEST_DATA_FOLDER } from "./globals";

import { v4 as uuidv4 } from 'uuid';
import { GitRepository } from "../../src/Git/GitRepository";
import rimraf from "rimraf";
import { execSync } from "child_process";

// Mocked repository

Expand All @@ -29,7 +30,6 @@ export const assertTemporaryFolderExists = () => {
* @returns {string} Generated repository path
*/
export const makeUniqueFolderForTest = (): string => {

const testID = uuidv4();
const tempFolderPath = join(TEST_DATA_FOLDER, testID);
mkdirSync(join(TEST_DATA_FOLDER, testID));
Expand Down Expand Up @@ -125,3 +125,41 @@ export const commitModitication = async (

return repository;
};

export const deleteFiles = (
fileNames: string[],
repositoryPath: string,
): void => {
for (const fileName of fileNames) {
const filePath = join(repositoryPath, fileName);
if (existsSync(filePath)) {
rimraf.sync(filePath);
} else {
console.debug(`[Delete files] File ${filePath} does not exist`)
}
}
};

export const renameFiles = (
fileNames: string[][],
repositoryPath: string,
): void => {
for (const fileName of fileNames) {
const filePathBefore = join(repositoryPath, fileName[0]);
const filePathAfter = join(repositoryPath, fileName[1]);
if (existsSync(filePathBefore)) {
execSync(`cd ${repositoryPath} && git mv ${fileName[0]} ${fileName[1]}`);
} else {
console.debug(`[Rename files] File ${filePathBefore} does not exist`);
}
}
};

// Nodegit does not work well in Jest with deletion / renaming
export const commitFilesUsingGitNatively = (
fileNames: string[],
repositoryPath: string,
commitMessage = "test commit"
) => {
execSync(`cd ${repositoryPath} && git add ${fileNames.join(' ')} && git commit -m "${commitMessage}"`);
}

0 comments on commit c1ec1e7

Please sign in to comment.