Skip to content

Commit

Permalink
update repo-mock to init local tuf cache (#282)
Browse files Browse the repository at this point in the history
Signed-off-by: Brian DeHamer <bdehamer@github.com>
Co-authored-by: Eugene <108841108+ejahnGithub@users.noreply.github.com>
  • Loading branch information
bdehamer and ejahnGithub committed Apr 13, 2023
1 parent 19febca commit c13096d
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/early-clocks-play.md
@@ -0,0 +1,5 @@
---
'@tufjs/repo-mock': minor
---

New function to set-up repo mock and initialize local cache
28 changes: 27 additions & 1 deletion packages/repo-mock/src/__tests__/index.test.ts
@@ -1,6 +1,7 @@
import { Metadata, MetadataKind } from '@tufjs/models';
import fs from 'fs';
import http from 'http';
import { clearMock, mockRepo } from '../index';
import tufmock, { clearMock, mockRepo } from '../index';

describe('mockRepo', () => {
const baseURL = 'http://localhost:8000';
Expand Down Expand Up @@ -73,6 +74,31 @@ describe('mockRepo', () => {
});
});

describe('default', () => {
const subject = tufmock([]);

it('creates a cache directory', () => {
expect(subject.cachePath).toBeTruthy();

const stat = fs.statSync(subject.cachePath);
expect(stat.isDirectory()).toBeTruthy();
});

it('inits the cache directory with the root metadata', () => {
const rootPath = `${subject.cachePath}/root.json`;
const stat = fs.statSync(rootPath);
expect(stat.isFile()).toBeTruthy();
});

describe('teardown', () => {
it('removes the cache directory', () => {
subject.teardown();

expect(() => fs.statSync(subject.cachePath)).toThrow();
});
});
});

async function fetch(url: string): Promise<string> {
return new Promise((resolve, reject) => {
http
Expand Down
73 changes: 66 additions & 7 deletions packages/repo-mock/src/index.ts
@@ -1,16 +1,31 @@
import fs from 'fs';
import nock from 'nock';
import os from 'os';
import path from 'path';
import { KeyPair } from './key';
import {
createRootMeta,
createSnapshotMeta,
createTargetsMeta,
createTimestampMeta,
} from './metadata';
import { collectTargets, Target } from './target';
import { Target, collectTargets } from './target';

export type { Target } from './target';

export function mockRepo(baseURL: string, targets: Target[]): string {
interface MockRepoOptions {
baseURL?: string;
metadataPathPrefix?: string;
targetPathPrefix?: string;
}

export function mockRepo(
baseURL: string,
targets: Target[],
options: Omit<MockRepoOptions, 'baseURL'> = {}
): string {
const metadataPrefix = options.metadataPathPrefix ?? '/metadata';
const targetPrefix = options.targetPathPrefix ?? '/targets';
const keyPair = new KeyPair();

// Translate the input targets into TUF TargetFile objects
Expand All @@ -22,15 +37,23 @@ export function mockRepo(baseURL: string, targets: Target[]): string {
const timestampMeta = createTimestampMeta(snapshotMeta, keyPair);
const rootMeta = createRootMeta(keyPair);

// Calculate paths for all of the metadata files
const rootPath = `${metadataPrefix}/1.root.json`;
const timestampPath = `${metadataPrefix}/timestamp.json`;
const snapshotPath = `${metadataPrefix}/snapshot.json`;
const targetsPath = `${metadataPrefix}/targets.json`;

// Mock the metadata endpoints
nock(baseURL).get('/metadata/1.root.json').reply(200, rootMeta);
nock(baseURL).get('/metadata/timestamp.json').reply(200, timestampMeta);
nock(baseURL).get('/metadata/snapshot.json').reply(200, snapshotMeta);
nock(baseURL).get('/metadata/targets.json').reply(200, targetsMeta);
nock(baseURL).get(rootPath).reply(200, rootMeta);
nock(baseURL).get(timestampPath).reply(200, timestampMeta);
nock(baseURL).get(snapshotPath).reply(200, snapshotMeta);
nock(baseURL).get(targetsPath).reply(200, targetsMeta);

// Mock the target endpoints
targets.forEach((target) => {
nock(baseURL).get(`/targets/${target.name}`).reply(200, target.content);
nock(baseURL)
.get(`${targetPrefix}/${target.name}`)
.reply(200, target.content);
});

// Mock a 404 response for non-existent metadata/target files
Expand All @@ -42,3 +65,39 @@ export function mockRepo(baseURL: string, targets: Target[]): string {
export function clearMock() {
nock.cleanAll();
}

class Scope {
private readonly targets: Target[];
private readonly options: MockRepoOptions;
public readonly baseURL: string;
public readonly cachePath: string;

constructor(targets: Target[], options: MockRepoOptions = {}) {
this.targets = targets;
this.options = options;

this.baseURL =
options.baseURL ??
`http://${Math.random().toString(36).substring(2)}.com`;
this.cachePath = fs.mkdtempSync(path.join(os.tmpdir(), 'tuf-cache-test-'));
this.reset();
}

public reset() {
clearMock();
const rootJSON = mockRepo(this.baseURL, this.targets, this.options);
fs.writeFileSync(path.join(this.cachePath, 'root.json'), rootJSON);
}

public teardown() {
clearMock();
fs.rmSync(this.cachePath, { recursive: true });
}
}

export default (targets: Target | Target[], options: MockRepoOptions = {}) => {
if (!Array.isArray(targets)) {
targets = [targets];
}
return new Scope(targets, options);
};

0 comments on commit c13096d

Please sign in to comment.