Files
meteor/packages/webapp/socket_file_tests.js
Nacho Codoñer 232b8a8c8b OSS-459 Posix indirect dependency replacement (#13204)
* implement tests to tweak socket files behaviors and group config

* re-run checks

* better test coverage on UNIX_SOCKET_PATH and UNIX_SOCKET_GROUP usage

* create socker file as part of the tests

* replace @vlasky/whomst dependency to use a unix-standard way to get gid from group name

* ensure test cross-compatibility on unix/macos

* clean

* register cleanup

* ensure close properly the net server instantiated

* temporary comment a test to see if CI passes

* Revert "temporary comment a test to see if CI passes"

This reverts commit 22b6517860.

* verify if new test is the culprit

* establish port

* establish port

* silent one test to see if CI fixed

* activate other test within the testAsyncMulti as may be the culprit on CI failure

* try set permissions as well

* print /etc/group

* use travis group

* ensure use proper group on travis CI to fix tests

* silent get group info logs

---------

Co-authored-by: Denilson <denilsonh2014@gmail.com>
2024-07-10 12:18:41 +02:00

149 lines
4.2 KiB
JavaScript

import { writeFileSync, unlinkSync, statSync, readFileSync } from 'fs';
import { createServer } from 'net';
import { createServer as createServerHttp } from 'http';
import {
removeExistingSocketFile,
registerSocketFileCleanup,
} from './socket_file.js';
import { EventEmitter } from 'events';
import { tmpdir, userInfo, platform } from 'os';
import { main, getGroupInfo } from './webapp_server';
import express from 'express';
const testSocketFile = `${tmpdir()}/socket_file_tests`;
const getChownInfo = async (filePath) => {
try {
const stats = await statSync(filePath);
return { uid: stats.uid, gid: stats.gid };
} catch (error) {
console.error(`Error fetching ownership info for ${filePath}:`, error.message);
return null;
}
};
const isMacOS = () => {
return platform() === 'darwin';
};
const removeTestSocketFile = () => {
try {
unlinkSync(testSocketFile);
} catch (error) {
// Do nothing
}
}
Tinytest.add("socket file - don't remove a non-socket file", test => {
writeFileSync(testSocketFile, "");
test.throws(
() => { removeExistingSocketFile(testSocketFile); },
/An existing file was found/
);
removeTestSocketFile()
});
Tinytest.addAsync(
'socket file - remove a previously existing socket file',
(test, done) => {
removeTestSocketFile();
const server = createServer();
server.listen(testSocketFile);
server.on('listening', Meteor.bindEnvironment(() => {
test.isNotUndefined(statSync(testSocketFile));
removeExistingSocketFile(testSocketFile);
test.throws(
() => { statSync(testSocketFile); },
/ENOENT/
);
server.close();
done();
}));
}
);
Tinytest.add(
'socket file - no existing socket file, nothing to remove',
test => {
removeTestSocketFile();
removeExistingSocketFile(testSocketFile);
}
);
Tinytest.add('socket file - remove socket file on exit', test => {
const testEventEmitter = new EventEmitter();
registerSocketFileCleanup(testSocketFile, testEventEmitter);
['exit', 'SIGINT', 'SIGHUP', 'SIGTERM'].forEach(signal => {
writeFileSync(testSocketFile, "");
test.isNotUndefined(statSync(testSocketFile));
testEventEmitter.emit(signal);
test.throws(
() => { statSync(testSocketFile); },
/ENOENT/
);
});
});
function prepareServer() {
removeTestSocketFile();
removeExistingSocketFile(testSocketFile);
const testEventEmitter = new EventEmitter();
registerSocketFileCleanup(testSocketFile, testEventEmitter);
const server = createServer();
server.listen(testSocketFile);
const app = express();
const httpServer = createServerHttp(app);
return { httpServer, server };
}
function closeServer({ httpServer, server }) {
return new Promise((resolve) => {
httpServer.on(
"listening",
Meteor.bindEnvironment(() => {
process.env.UNIX_SOCKET_PATH = "";
process.env.UNIX_SOCKET_GROUP = "";
removeExistingSocketFile(testSocketFile);
server.close();
httpServer.close();
resolve();
})
);
});
}
testAsyncMulti(
"socket usage - use socket file for inter-process communication",
[
async (test) => {
// use UNIX_SOCKET_PATH
const { httpServer, server } = prepareServer();
process.env.UNIX_SOCKET_PATH = testSocketFile;
const result = await main({ httpServer });
test.equal(result, "DAEMON");
const currentGid = userInfo({ encoding: "utf8" })?.gid;
test.equal((await getChownInfo(testSocketFile))?.gid, currentGid);
return closeServer({ httpServer, server });
},
async (test) => {
// use UNIX_SOCKET_PATH and UNIX_SOCKET_GROUP
const { httpServer, server } = prepareServer();
const groupToUse = Boolean(process.env.TRAVIS) && 'travis' || (isMacOS() ? 'staff' : 'root');
process.env.UNIX_SOCKET_PATH = testSocketFile;
process.env.UNIX_SOCKET_GROUP = groupToUse;
process.env.UNIX_SOCKET_PERMISSIONS = '777';
const result = await main({ httpServer });
test.equal(result, "DAEMON");
test.equal((await getChownInfo(testSocketFile))?.gid, getGroupInfo(process.env.UNIX_SOCKET_GROUP)?.gid);
return closeServer({ httpServer, server });
},
]
);