@mintlify/previewing
Version:
Preview Mintlify docs locally
221 lines (220 loc) • 9.58 kB
JavaScript
import fse from 'fs-extra';
import { pipeline } from 'node:stream/promises';
import tar from 'tar';
import { mockProcessExit } from 'vitest-mock-process';
import * as constants from '../constants.js';
import { downloadTargetMint } from '../local-preview/client.js';
import * as utils from '../util.js';
vi.mock('fs-extra', () => {
const mocks = {
existsSync: vi.fn().mockReturnValue(true),
moveSync: vi.fn().mockReturnValue(undefined),
ensureDirSync: vi.fn().mockReturnValue(undefined),
removeSync: vi.fn().mockReturnValue(undefined),
writeFileSync: vi.fn().mockReturnValue(undefined),
createWriteStream: vi.fn().mockReturnValue({ write: vi.fn(), end: vi.fn() }),
};
return {
...mocks,
default: mocks,
};
});
vi.mock('../util.js', () => ({
buildLogger: vi.fn().mockReturnValue({
text: '',
succeed: vi.fn(),
fail: vi.fn(),
warn: vi.fn(),
start: vi.fn(),
stop: vi.fn(),
stopAndPersist: vi.fn(),
}),
restoreMintlifyLast: vi.fn().mockReturnValue(undefined),
getTarUrl: vi.fn().mockReturnValue('https://asdfghjkl.cloudfront.net/mint-1.0.1.tar.gz'),
}));
vi.mock('node:stream/promises', () => ({
pipeline: vi.fn(),
}));
vi.mock('got', () => {
const mocks = {
stream: vi.fn().mockReturnValue({ pipe: vi.fn(), on: vi.fn() }),
};
return {
...mocks,
default: mocks,
};
});
vi.mock('tar', () => {
const mocks = {
x: vi.fn(),
};
return {
...mocks,
default: mocks,
};
});
const buildLoggerMock = vi.mocked(utils.buildLogger);
const restoreMintlifyLastMock = vi.mocked(utils.restoreMintlifyLast);
const pipelineMock = vi.mocked(pipeline);
const getTarUrlMock = vi.mocked(utils.getTarUrl);
const writeFileSyncMock = vi.mocked(fse.writeFileSync);
const tarMock = vi.mocked(tar);
const existsSyncMock = vi.mocked(fse.existsSync);
const moveSyncMock = vi.mocked(fse.moveSync);
const removeSyncMock = vi.mocked(fse.removeSync);
const processExitMock = mockProcessExit();
describe('downloadTargetMint', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('downloads and extracts a new version happy path', async () => {
const logger = buildLoggerMock();
const targetMintVersion = '1.0.1';
const versionString = '1.0.0';
pipelineMock.mockResolvedValue(undefined);
await downloadTargetMint({
logger,
targetVersion: targetMintVersion,
existingVersion: versionString,
});
// Verify backup was created
expect(existsSyncMock).toHaveBeenCalledWith(constants.DOT_MINTLIFY);
expect(moveSyncMock).toHaveBeenCalledWith(constants.DOT_MINTLIFY, constants.DOT_MINTLIFY_LAST, {
overwrite: true,
});
expect(fse.ensureDirSync).toHaveBeenCalledWith(constants.DOT_MINTLIFY);
// Verify download and extraction
expect(getTarUrlMock).toHaveBeenCalledWith(targetMintVersion);
expect(pipelineMock).toHaveBeenCalled();
expect(tarMock.x).toHaveBeenCalled();
expect(logger.text).toBe('Extracting Mintlify framework...');
// Verify cleanup
expect(removeSyncMock).toHaveBeenCalledWith(constants.TAR_PATH);
expect(removeSyncMock).toHaveBeenCalledWith(constants.DOT_MINTLIFY_LAST);
expect(writeFileSyncMock).toHaveBeenCalledWith(constants.VERSION_PATH, targetMintVersion);
// Verify no restore was needed
expect(restoreMintlifyLastMock).not.toHaveBeenCalled();
});
it('downloads and extracts a specific client version happy path', async () => {
const logger = buildLoggerMock();
const versionString = '1.0.0';
const clientVersion = '2.0.0';
pipelineMock.mockResolvedValue(undefined);
await downloadTargetMint({
logger,
targetVersion: clientVersion,
existingVersion: versionString,
});
// Verify backup was created
expect(existsSyncMock).toHaveBeenCalledWith(constants.DOT_MINTLIFY);
expect(moveSyncMock).toHaveBeenCalledWith(constants.DOT_MINTLIFY, constants.DOT_MINTLIFY_LAST, {
overwrite: true,
});
expect(fse.ensureDirSync).toHaveBeenCalledWith(constants.DOT_MINTLIFY);
// Verify download and extraction
expect(getTarUrlMock).toHaveBeenCalledWith(clientVersion);
expect(pipelineMock).toHaveBeenCalled();
expect(tarMock.x).toHaveBeenCalled();
expect(logger.text).toBe('Extracting Mintlify framework...');
// Verify cleanup
expect(removeSyncMock).toHaveBeenCalledWith(constants.TAR_PATH);
expect(removeSyncMock).toHaveBeenCalledWith(constants.DOT_MINTLIFY_LAST);
expect(writeFileSyncMock).toHaveBeenCalledWith(constants.VERSION_PATH, clientVersion);
// Verify no restore was needed
expect(restoreMintlifyLastMock).not.toHaveBeenCalled();
});
describe('downloadTargetMint with backup version', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('fails to download new version', async () => {
const logger = buildLoggerMock();
const targetMintVersion = '1.0.1';
const versionString = '1.0.0';
pipelineMock.mockRejectedValue(new Error('connection timed out'));
await downloadTargetMint({
logger,
targetVersion: targetMintVersion,
existingVersion: versionString,
});
// Verify backup was created
expect(existsSyncMock).toHaveBeenCalledWith(constants.DOT_MINTLIFY);
expect(moveSyncMock).toHaveBeenCalledWith(constants.DOT_MINTLIFY, constants.DOT_MINTLIFY_LAST, {
overwrite: true,
});
expect(fse.ensureDirSync).toHaveBeenCalledWith(constants.DOT_MINTLIFY);
// Verify download fail
expect(logger.warn).toHaveBeenCalledWith('Failed to download Mintlify framework version 1.0.1, Error: connection timed out, falling back to existing version: 1.0.0');
// Verify use backup version
expect(restoreMintlifyLastMock).toHaveBeenCalled();
// Verify no extraction
expect(tarMock.x).not.toHaveBeenCalled();
// Verify no cleanup
expect(removeSyncMock).not.toHaveBeenCalled();
expect(writeFileSyncMock).not.toHaveBeenCalled();
});
it('fails to extract new version', async () => {
const logger = buildLoggerMock();
const targetMintVersion = '1.0.1';
const versionString = '1.0.0';
pipelineMock.mockResolvedValue(undefined);
tarMock.x.mockImplementation(() => {
throw new Error('Zlib error');
});
await downloadTargetMint({
logger,
targetVersion: targetMintVersion,
existingVersion: versionString,
});
// Verify backup was created
expect(existsSyncMock).toHaveBeenCalledWith(constants.DOT_MINTLIFY);
expect(moveSyncMock).toHaveBeenCalledWith(constants.DOT_MINTLIFY, constants.DOT_MINTLIFY_LAST, {
overwrite: true,
});
expect(fse.ensureDirSync).toHaveBeenCalledWith(constants.DOT_MINTLIFY);
// Verify download success
expect(pipelineMock).toHaveBeenCalled();
// Verify extraction fail
expect(logger.warn).toHaveBeenCalledWith('Failed to extract Mintlify framework version 1.0.1, Error: Zlib error, using existing version: 1.0.0');
// Verify use backup version
expect(restoreMintlifyLastMock).toHaveBeenCalled();
// Verify no cleanup
expect(removeSyncMock).not.toHaveBeenCalled();
expect(writeFileSyncMock).not.toHaveBeenCalled();
});
});
describe('downloadTargetMint without backup version', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('fails to download new version with no fallback', async () => {
const logger = buildLoggerMock();
const targetMintVersion = '1.0.1';
const versionString = null;
pipelineMock.mockRejectedValue(new Error('connection timed out'));
await downloadTargetMint({
logger,
targetVersion: targetMintVersion,
existingVersion: versionString,
});
expect(logger.fail).toHaveBeenCalledWith('Failed to download Mintlify framework version 1.0.1, Error: connection timed out');
expect(processExitMock).toHaveBeenCalledWith(1);
});
it('fails to extract new version with no fallback', async () => {
const logger = buildLoggerMock();
const targetMintVersion = '1.0.1';
const versionString = null;
pipelineMock.mockResolvedValue(undefined);
tarMock.x.mockImplementation(() => {
throw new Error('Zlib error');
});
await downloadTargetMint({
logger,
targetVersion: targetMintVersion,
existingVersion: versionString,
});
expect(logger.fail).toHaveBeenCalledWith('Failed to extract Mintlify framework version 1.0.1, Error: Zlib error');
expect(processExitMock).toHaveBeenCalledWith(1);
});
});
});