Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"event-emitter": "^0.3.5",
"ext": "^1.7.0",
"fast-glob": "^3.3.3",
"fs-extra": "^11.3.4",
"https-proxy-agent": "^9.0.0",
"js-yaml": "^4.1.0",
"log": "^6.3.1",
Expand Down
4 changes: 2 additions & 2 deletions src/utils/fs/fileExists.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

const fse = require('fs-extra');
const fs = require('node:fs').promises;

const fileExists = async (filePath) => {
try {
const stats = await fse.lstat(filePath);
const stats = await fs.lstat(filePath);
return stats.isFile();
} catch {
return false;
Expand Down
4 changes: 2 additions & 2 deletions src/utils/fs/fileExistsSync.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

const fse = require('fs-extra');
const fs = require('node:fs');

const fileExistsSync = (filePath) => {
try {
const stats = fse.lstatSync(filePath);
const stats = fs.lstatSync(filePath);
return stats.isFile();
} catch {
return false;
Expand Down
4 changes: 2 additions & 2 deletions src/utils/fs/readFile.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

const fse = require('fs-extra');
const fs = require('node:fs').promises;
const parseFile = require('./parseFile');

const readFile = async (filePath, options = {}) => {
const contents = await fse.readFile(filePath, 'utf8');
const contents = await fs.readFile(filePath, 'utf8');
return parseFile(filePath, contents, options);
};

Expand Down
4 changes: 2 additions & 2 deletions src/utils/fs/readFileSync.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

const fse = require('fs-extra');
const fs = require('node:fs');
const parseFile = require('./parseFile');

const readFileSync = (filePath, options = {}) => {
const contents = fse.readFileSync(filePath, 'utf8');
const contents = fs.readFileSync(filePath, 'utf8');
return parseFile(filePath, contents, options);
};

Expand Down
6 changes: 3 additions & 3 deletions src/utils/fs/writeFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const path = require('path');
const YAML = require('js-yaml');
const fse = require('fs-extra');
const fs = require('node:fs').promises;
const isJsonPath = require('./isJsonPath');
const isYamlPath = require('./isYamlPath');

Expand All @@ -17,8 +17,8 @@ const formatContents = (filePath, contents, options) => {
};

const writeFile = async (filePath, contents = '', options = {}) => {
await fse.ensureDir(path.dirname(filePath));
await fse.writeFile(filePath, formatContents(filePath, contents, options));
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, formatContents(filePath, contents, options));
};

module.exports = writeFile;
23 changes: 23 additions & 0 deletions test/lib/fs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

const fs = require('node:fs');
const fsp = fs.promises;
const path = require('node:path');

const ensureDir = (dirPath) => fsp.mkdir(dirPath, { recursive: true });

const remove = (targetPath) => fsp.rm(targetPath, { recursive: true, force: true });

const removeSync = (targetPath) => fs.rmSync(targetPath, { recursive: true, force: true });

const outputFile = async (filePath, contents, options) => {
await ensureDir(path.dirname(filePath));
await fsp.writeFile(filePath, contents, options);
};

const outputFileSync = (filePath, contents, options) => {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, contents, options);
};

module.exports = { ensureDir, outputFile, outputFileSync, remove, removeSync };
5 changes: 2 additions & 3 deletions test/lib/process-tmp-dir.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict';

const { mkdirSync, realpathSync } = require('fs');
const { removeSync } = require('fs-extra');
const { mkdirSync, realpathSync, rmSync } = require('fs');
const path = require('path');
const os = require('os');
const crypto = require('crypto');
Expand All @@ -28,7 +27,7 @@ module.exports = (function self() {

process.on('exit', () => {
try {
removeSync(module.exports);
rmSync(module.exports, { recursive: true, force: true });
} catch (error) {
if (rmTmpDirIgnorableErrorCodes.has(error.code)) return;
throw error;
Expand Down
12 changes: 12 additions & 0 deletions test/lib/skip-on-disabled-symlinks-in-windows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict';

module.exports = (error, context, afterCallback) => {
if (error.code !== 'EPERM' || process.platform !== 'win32') return;
Comment thread
GrahamCampbell marked this conversation as resolved.

if (!context || typeof context.skip !== 'function') {
throw new TypeError('Passed context is not a valid Mocha context');
}

Comment thread
GrahamCampbell marked this conversation as resolved.
if (afterCallback) afterCallback();
context.skip();
};
24 changes: 12 additions & 12 deletions test/unit/components/framework/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ const fs = require('node:fs').promises;
const path = require('path');
const proxyquire = require('proxyquire');
const chai = require('chai');
const fse = require('fs-extra');
const sinon = require('sinon');
const Context = require('../../../../src/Context');
const ComponentContext = require('../../../../src/ComponentContext');
const { validateComponentInputs } = require('../../../../src/configuration/validate');
const { configSchema } = require('../../../../components/framework/configuration');
const ServerlessFramework = require('../../../../components/framework');
const { outputFile, remove } = require('../../../lib/fs');

const expect = chai.expect;

Expand Down Expand Up @@ -836,7 +836,7 @@ describe('test/unit/components/framework/index.test.js', () => {
const serviceDir = await fs.mkdtemp(path.join(process.cwd(), 'cache-hash-skip-'));

try {
await fse.outputFile(path.join(serviceDir, 'handler.js'), 'module.exports = 1;\n');
await outputFile(path.join(serviceDir, 'handler.js'), 'module.exports = 1;\n');

const spawnStub = sinon.stub();
const FrameworkComponent = proxyquire('../../../../components/framework/index.js', {
Expand All @@ -857,7 +857,7 @@ describe('test/unit/components/framework/index.test.js', () => {

expect(spawnStub).to.not.have.been.called;
} finally {
await fse.remove(serviceDir);
await remove(serviceDir);
}
});

Expand All @@ -866,7 +866,7 @@ describe('test/unit/components/framework/index.test.js', () => {

try {
const filePath = path.join(serviceDir, 'handler.js');
await fse.outputFile(filePath, 'module.exports = 1;\n');
await outputFile(filePath, 'module.exports = 1;\n');

const spawnStub = sinon.stub();
spawnStub.onFirstCall().returns(createSpawnExecution({ stderr: 'deployed' }));
Expand All @@ -891,22 +891,22 @@ describe('test/unit/components/framework/index.test.js', () => {
context.state.inputs = inputs;
context.state.cacheHash = await component.calculateCacheHash();

await fse.outputFile(filePath, 'module.exports = 2;\n');
await outputFile(filePath, 'module.exports = 2;\n');

await component.deploy();

expect(spawnStub).to.be.calledTwice;
expect(context.state.cacheHash).to.equal(await component.calculateCacheHash());
} finally {
await fse.remove(serviceDir);
await remove(serviceDir);
}
});

it('expands literal directory cache patterns when calculating hashes', async () => {
const serviceDir = await fs.mkdtemp(path.join(process.cwd(), 'cache-hash-dir-'));

try {
await fse.outputFile(path.join(serviceDir, 'src', 'handler.js'), 'module.exports = 1;\n');
await outputFile(path.join(serviceDir, 'src', 'handler.js'), 'module.exports = 1;\n');

const context = await getContext();
const directoryPatternComponent = new ServerlessFramework('id', context, {
Expand All @@ -922,7 +922,7 @@ describe('test/unit/components/framework/index.test.js', () => {
await globPatternComponent.calculateCacheHash()
);
} finally {
await fse.remove(serviceDir);
await remove(serviceDir);
}
});

Expand All @@ -931,9 +931,9 @@ describe('test/unit/components/framework/index.test.js', () => {

try {
await Promise.all([
fse.outputFile(path.join(serviceDir, 'keep.js'), 'keep\n'),
fse.outputFile(path.join(serviceDir, 'ignored', 'drop.js'), 'drop\n'),
fse.outputFile(path.join(serviceDir, 'ignored', 'reinclude.js'), 'reinclude\n'),
outputFile(path.join(serviceDir, 'keep.js'), 'keep\n'),
outputFile(path.join(serviceDir, 'ignored', 'drop.js'), 'drop\n'),
outputFile(path.join(serviceDir, 'ignored', 'reinclude.js'), 'reinclude\n'),
]);

const context = await getContext();
Expand All @@ -950,7 +950,7 @@ describe('test/unit/components/framework/index.test.js', () => {
await explicitPatternComponent.calculateCacheHash()
);
} finally {
await fse.remove(serviceDir);
await remove(serviceDir);
}
});
});
71 changes: 71 additions & 0 deletions test/unit/lib/skip-on-disabled-symlinks-in-windows.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use strict';

const sinon = require('sinon');
const { expect } = require('chai');

const skipOnDisabledSymlinksInWindows = require('../../lib/skip-on-disabled-symlinks-in-windows');

describe('test/unit/lib/skip-on-disabled-symlinks-in-windows.test.js', () => {
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, 'platform');

const setPlatform = (platform) => {
Object.defineProperty(process, 'platform', {
configurable: true,
value: platform,
});
};

beforeEach(() => {
setPlatform('win32');
});

afterEach(() => {
Object.defineProperty(process, 'platform', originalPlatformDescriptor);
});

it('skips Windows EPERM symlink failures and runs the cleanup callback', () => {
const context = { skip: sinon.stub() };
const afterCallback = sinon.stub();

skipOnDisabledSymlinksInWindows({ code: 'EPERM' }, context, afterCallback);

expect(afterCallback).to.have.been.calledOnce;
expect(context.skip).to.have.been.calledOnce;
});

it('skips Windows EPERM symlink failures without a cleanup callback', () => {
const context = { skip: sinon.stub() };

skipOnDisabledSymlinksInWindows({ code: 'EPERM' }, context);

expect(context.skip).to.have.been.calledOnce;
});

it('does nothing for non-Windows EPERM errors', () => {
const context = { skip: sinon.stub() };
const afterCallback = sinon.stub();
setPlatform('linux');

skipOnDisabledSymlinksInWindows({ code: 'EPERM' }, context, afterCallback);

expect(afterCallback).to.not.have.been.called;
expect(context.skip).to.not.have.been.called;
});

it('does nothing for Windows non-EPERM errors', () => {
const context = { skip: sinon.stub() };
const afterCallback = sinon.stub();

skipOnDisabledSymlinksInWindows({ code: 'ENOENT' }, context, afterCallback);

expect(afterCallback).to.not.have.been.called;
expect(context.skip).to.not.have.been.called;
});

it('rejects invalid Mocha contexts for Windows EPERM errors', () => {
expect(() => skipOnDisabledSymlinksInWindows({ code: 'EPERM' }, {})).to.throw(
TypeError,
'Passed context is not a valid Mocha context'
);
});
});
6 changes: 3 additions & 3 deletions test/unit/src/configuration/read.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ const expect = chai.expect;

const path = require('path');
const fsp = require('fs').promises;
const fse = require('fs-extra');
const readConfiguration = require('../../../../src/configuration/read');
const { ensureDir, remove } = require('../../../lib/fs');

describe('test/unit/src/configuration/read.test.js', () => {
let configurationPath;
Expand Down Expand Up @@ -62,7 +62,7 @@ describe('test/unit/src/configuration/read.test.js', () => {
});

it('should read "serverless-compose.ts"', async () => {
await fse.ensureDir('node_modules');
await ensureDir('node_modules');
try {
await fsp.writeFile('node_modules/ts-node.js', 'module.exports.register = () => null;');
configurationPath = 'serverless-compose.ts';
Expand All @@ -73,7 +73,7 @@ describe('test/unit/src/configuration/read.test.js', () => {
await fsp.writeFile(configurationPath, `module.exports = ${JSON.stringify(configuration)}`);
expect(await readConfiguration(path.resolve(configurationPath))).to.deep.equal(configuration);
} finally {
await fse.remove('node_modules');
await remove('node_modules');
}
});

Expand Down
12 changes: 6 additions & 6 deletions test/unit/src/configuration/resolve-path.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
const fs = require('node:fs').promises;
const os = require('node:os');
const path = require('node:path');
const fse = require('fs-extra');
const { expect } = require('chai');

const resolveConfigurationPath = require('../../../../src/configuration/resolve-path');
const { ensureDir, outputFile, remove } = require('../../../lib/fs');

describe('test/unit/src/configuration/resolve-path.test.js', () => {
let tmpDir;
Expand All @@ -16,26 +16,26 @@ describe('test/unit/src/configuration/resolve-path.test.js', () => {
});

afterEach(async () => {
await fse.remove(tmpDir);
await remove(tmpDir);
});

it('resolves the first existing compose config by supported extension order', async () => {
const yamlPath = path.join(tmpDir, 'serverless-compose.yaml');
const jsonPath = path.join(tmpDir, 'serverless-compose.json');
const ymlPath = path.join(tmpDir, 'serverless-compose.yml');

await fse.outputFile(jsonPath, '{}');
await fse.outputFile(yamlPath, 'services: {}\n');
await outputFile(jsonPath, '{}');
await outputFile(yamlPath, 'services: {}\n');

expect(await resolveConfigurationPath(tmpDir)).to.equal(yamlPath);

await fse.outputFile(ymlPath, 'services: {}\n');
await outputFile(ymlPath, 'services: {}\n');

expect(await resolveConfigurationPath(tmpDir)).to.equal(ymlPath);
});

it('ignores directories named like compose config files', async () => {
await fse.ensureDir(path.join(tmpDir, 'serverless-compose.yml'));
await ensureDir(path.join(tmpDir, 'serverless-compose.yml'));

await expect(resolveConfigurationPath(tmpDir)).to.eventually.be.rejected.and.have.property(
'code',
Expand Down
Loading