Skip to content

Commit

Permalink
Add establishMetaMaskRepository + OutputLogger
Browse files Browse the repository at this point in the history
In order to analyze a project, that project must be cloned first along
with the MetaMask module template. So, there must be some function that
checks for whether a repo has been cloned yet and clones it if not.

This commit introduces code to implement the steps involved in doing
this. It also introduces a utility class for sending messages to
standard out and standard error streams, as well as a version that can
be used in tests (which fakes out the streams).
  • Loading branch information
mcmire committed Nov 12, 2023
1 parent 7f0c625 commit fd47215
Show file tree
Hide file tree
Showing 15 changed files with 2,003 additions and 11 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"prettier": "^2.7.1",
"prettier-plugin-packagejson": "^2.3.0",
"rimraf": "^3.0.2",
"stdio-mock": "^1.2.0",
"ts-jest": "^28.0.7",
"ts-node": "^10.7.0",
"typedoc": "^0.23.15",
Expand Down
5 changes: 5 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* The number of milliseconds in an hour, used to determine when to pull the
* latest changes for previously cached repositories.
*/
export const ONE_HOUR = 60 * 60 * 1000;
325 changes: 325 additions & 0 deletions src/establish-metamask-repository.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
import execa from 'execa';
import path from 'path';

import { establishMetaMaskRepository } from './establish-metamask-repository';
import { FakeOutputLogger } from '../tests/fake-output-logger';
import type { PrimaryExecaFunction } from '../tests/helpers';
import { fakeDateOnly, withinSandbox } from '../tests/helpers';
import { setupToolWithMockRepository } from '../tests/setup-tool-with-mock-repository';

jest.mock('execa');

const execaMock = jest.mocked<PrimaryExecaFunction>(execa);

describe('establishMetaMaskRepository', () => {
beforeEach(() => {
fakeDateOnly();
});

afterEach(() => {
jest.useRealTimers();
});

describe('given the path to an existing directory that is not a Git repository', () => {
it('throws', async () => {
await withinSandbox(async ({ directoryPath: sandboxDirectoryPath }) => {
const outputLogger = new FakeOutputLogger();

await expect(
establishMetaMaskRepository({
repositoryReference: sandboxDirectoryPath,
workingDirectoryPath: sandboxDirectoryPath,
cachedRepositoriesDirectoryPath: sandboxDirectoryPath,
outputLogger,
}),
).rejects.toThrow(
`"${sandboxDirectoryPath}" is not a Git repository, cannot proceed.`,
);
});
});
});

describe('given the path to an existing repository relative to the working directory', () => {
it('does not pull the latest changes', async () => {
await withinSandbox(async ({ directoryPath: sandboxDirectoryPath }) => {
const workingDirectoryPath = path.join(sandboxDirectoryPath, 'working');
const { cachedRepositoriesDirectoryPath, repository } =
await setupToolWithMockRepository({
execaMock,
sandboxDirectoryPath,
repository: {
create: true,
parentDirectoryPath: workingDirectoryPath,
},
});
const outputLogger = new FakeOutputLogger();

await establishMetaMaskRepository({
repositoryReference: repository.name,
workingDirectoryPath,
cachedRepositoriesDirectoryPath,
outputLogger,
});

expect(execaMock).not.toHaveBeenNthCalledWith(3, 'git', ['pull'], {
cwd: repository.directoryPath,
});
});
});

it('returns information about the repository, even if the default branch is not selected', async () => {
const fetchHeadModifiedDate = new Date('2023-01-01T00:00:00Z');

await withinSandbox(async ({ directoryPath: sandboxDirectoryPath }) => {
const workingDirectoryPath = path.join(sandboxDirectoryPath, 'working');
const { cachedRepositoriesDirectoryPath, repository } =
await setupToolWithMockRepository({
execaMock,
sandboxDirectoryPath,
repository: {
name: 'some-repo',
create: true,
parentDirectoryPath: workingDirectoryPath,
commandMocks: {
'git symbolic-ref HEAD': () => ({
result: {
stdout: 'refs/heads/some-branch',
},
}),
'git rev-parse --verify main': () => ({
error: new Error('not found'),
}),
'git rev-parse --verify master': () => ({
result: {
stdout: '',
},
}),
},
fetchHead: { modifiedDate: fetchHeadModifiedDate },
},
validRepositories: [],
});
const outputLogger = new FakeOutputLogger();

const metaMaskRepository = await establishMetaMaskRepository({
repositoryReference: 'some-repo',
workingDirectoryPath,
cachedRepositoriesDirectoryPath,
outputLogger,
});

expect(metaMaskRepository).toMatchObject({
shortname: 'some-repo',
directoryPath: repository.directoryPath,
defaultBranchName: 'master',
currentBranchName: 'some-branch',
lastFetchedDate: fetchHeadModifiedDate,
});
});
});
});

describe('given the name of a known MetaMask repository', () => {
describe('if the repository has already been cloned', () => {
it('throws if the default branch is not selected', async () => {
await withinSandbox(async ({ directoryPath: sandboxDirectoryPath }) => {
const { cachedRepositoriesDirectoryPath, repository } =
await setupToolWithMockRepository({
execaMock,
sandboxDirectoryPath,
repository: {
create: true,
commandMocks: {
'git symbolic-ref HEAD': () => ({
result: {
stdout: 'refs/heads/NOT-main',
},
}),
'git rev-parse --verify main': () => ({
result: {
stdout: '',
},
}),
},
},
});
const outputLogger = new FakeOutputLogger();

await expect(
establishMetaMaskRepository({
repositoryReference: repository.name,
workingDirectoryPath: sandboxDirectoryPath,
cachedRepositoriesDirectoryPath,
outputLogger,
}),
).rejects.toThrow(
`Error establishing repository "${repository.directoryPath}": The default branch "main" does not seem to be selected. You'll need to return it to this branch manually.`,
);
});
});

it('pulls the default branch', async () => {
await withinSandbox(async ({ directoryPath: sandboxDirectoryPath }) => {
const { cachedRepositoriesDirectoryPath, repository } =
await setupToolWithMockRepository({
execaMock,
sandboxDirectoryPath,
repository: {
create: true,
},
});
const outputLogger = new FakeOutputLogger();

await establishMetaMaskRepository({
repositoryReference: repository.name,
workingDirectoryPath: sandboxDirectoryPath,
cachedRepositoriesDirectoryPath,
outputLogger,
});

expect(execaMock).toHaveBeenNthCalledWith(4, 'git', ['pull'], {
cwd: repository.directoryPath,
});
});
});

it('returns information about the repository', async () => {
const now = new Date('2023-01-01T00:00:00Z');
jest.setSystemTime(now);

await withinSandbox(async ({ directoryPath: sandboxDirectoryPath }) => {
const { cachedRepositoriesDirectoryPath, repository } =
await setupToolWithMockRepository({
execaMock,
sandboxDirectoryPath,
repository: {
name: 'some-repo',
create: true,
commandMocks: {
'git symbolic-ref HEAD': () => ({
result: {
stdout: 'refs/heads/main',
},
}),
'git rev-parse --verify main': () => ({
result: {
stdout: '',
},
}),
},
},
});
const outputLogger = new FakeOutputLogger();

const metaMaskRepository = await establishMetaMaskRepository({
repositoryReference: 'some-repo',
workingDirectoryPath: sandboxDirectoryPath,
cachedRepositoriesDirectoryPath,
outputLogger,
});

expect(metaMaskRepository).toMatchObject({
currentBranchName: 'main',
defaultBranchName: 'main',
directoryPath: repository.directoryPath,
shortname: 'some-repo',
lastFetchedDate: now,
});
});
});
});

describe('if the repository has not already been cloned', () => {
it('clones the repository', async () => {
await withinSandbox(async ({ directoryPath: sandboxDirectoryPath }) => {
const { cachedRepositoriesDirectoryPath, repository } =
await setupToolWithMockRepository({
execaMock,
sandboxDirectoryPath,
validRepositories: [
{
name: 'some-repo',
fork: false,
archived: false,
},
],
repository: {
name: 'some-repo',
create: false,
},
});
const outputLogger = new FakeOutputLogger();

await establishMetaMaskRepository({
repositoryReference: 'some-repo',
workingDirectoryPath: sandboxDirectoryPath,
cachedRepositoriesDirectoryPath,
outputLogger,
});

expect(execaMock).toHaveBeenNthCalledWith(2, 'gh', [
'repo',
'clone',
`MetaMask/some-repo`,
repository.directoryPath,
]);
});
});

it('returns information about the repository', async () => {
const now = new Date('2023-01-01T01:00:01Z');
jest.setSystemTime(now);

await withinSandbox(async ({ directoryPath: sandboxDirectoryPath }) => {
const { cachedRepositoriesDirectoryPath, repository } =
await setupToolWithMockRepository({
execaMock,
sandboxDirectoryPath,
validRepositories: [
{
name: 'some-repo',
fork: false,
archived: false,
},
],
repository: {
name: 'some-repo',
create: false,
commandMocks: {
'git symbolic-ref HEAD': () => ({
result: {
stdout: 'refs/heads/master',
},
}),
'git rev-parse --verify main': () => ({
error: new Error('not found'),
}),
'git rev-parse --verify master': () => ({
result: {
stdout: '',
},
}),
},
},
});
const outputLogger = new FakeOutputLogger();

const metaMaskRepository = await establishMetaMaskRepository({
repositoryReference: 'some-repo',
workingDirectoryPath: sandboxDirectoryPath,
cachedRepositoriesDirectoryPath,
outputLogger,
});

expect(metaMaskRepository).toMatchObject({
currentBranchName: 'master',
defaultBranchName: 'master',
directoryPath: repository.directoryPath,
shortname: 'some-repo',
lastFetchedDate: now,
});
});
});
});
});
});
Loading

0 comments on commit fd47215

Please sign in to comment.