@lerna/publish
Version:
Publish packages in the current project
160 lines (159 loc) • 6.37 kB
JavaScript
var import_prompt = require("./prompt");
var import_otplease = require("./otplease");
jest.mock("./prompt");
import_prompt.promptTextInput.mockResolvedValue("123456");
describe("otplease", () => {
const stdinIsTTY = process.stdin.isTTY;
const stdoutIsTTY = process.stdout.isTTY;
beforeEach(() => {
process.stdin.isTTY = true;
process.stdout.isTTY = true;
});
afterEach(() => {
process.stdin.isTTY = stdinIsTTY;
process.stdout.isTTY = stdoutIsTTY;
});
it("no error", async () => {
const obj = {};
const fn = jest.fn(() => obj);
const result = await (0, import_otplease.otplease)(fn, {});
expect(fn).toHaveBeenCalled();
expect(import_prompt.promptTextInput).not.toHaveBeenCalled();
expect(result).toBe(obj);
});
it("request otp", async () => {
const obj = {};
const fn = jest.fn(makeTestCallback("123456", obj));
const result = await (0, import_otplease.otplease)(fn, {});
expect(fn).toHaveBeenCalledTimes(2);
expect(import_prompt.promptTextInput).toHaveBeenCalled();
expect(result).toBe(obj);
});
it("request otp updates cache", async () => {
const otpCache = { otp: void 0 };
const obj = {};
const fn = jest.fn(makeTestCallback("123456", obj));
const result = await (0, import_otplease.otplease)(fn, {}, otpCache);
expect(fn).toHaveBeenCalledTimes(2);
expect(import_prompt.promptTextInput).toHaveBeenCalled();
expect(result).toBe(obj);
expect(otpCache.otp).toBe("123456");
});
it("uses cache if opts does not have own otp", async () => {
const otpCache = { otp: "654321" };
const obj = {};
const fn = jest.fn(makeTestCallback("654321", obj));
const result = await (0, import_otplease.otplease)(fn, {}, otpCache);
expect(fn).toHaveBeenCalledTimes(1);
expect(import_prompt.promptTextInput).not.toHaveBeenCalled();
expect(result).toBe(obj);
expect(otpCache.otp).toBe("654321");
});
it("uses explicit otp regardless of cache value", async () => {
const otpCache = { otp: "654321" };
const obj = {};
const fn = jest.fn(makeTestCallback("987654", obj));
const result = await (0, import_otplease.otplease)(fn, { otp: "987654" }, otpCache);
expect(fn).toHaveBeenCalledTimes(1);
expect(import_prompt.promptTextInput).not.toHaveBeenCalled();
expect(result).toBe(obj);
expect(otpCache.otp).toBe("654321");
});
it("using cache updated in a different task", async () => {
const otpCache = { otp: void 0 };
const obj = {};
const fn = jest.fn(makeTestCallback("654321", obj));
Promise.resolve().then(() => {
otpCache.otp = "654321";
});
const result = await (0, import_otplease.otplease)(fn, {}, otpCache);
expect(fn).toHaveBeenCalledTimes(2);
expect(import_prompt.promptTextInput).not.toHaveBeenCalled();
expect(result).toBe(obj);
});
it("semaphore prevents overlapping requests for OTP", async () => {
const otpCache = { otp: void 0 };
const obj1 = {};
const fn1 = jest.fn(makeTestCallback("123456", obj1));
const p1 = (0, import_otplease.otplease)(fn1, {}, otpCache);
const obj2 = {};
const fn2 = jest.fn(makeTestCallback("123456", obj2));
const p2 = (0, import_otplease.otplease)(fn2, {}, otpCache);
const [res1, res2] = await Promise.all([p1, p2]);
expect(fn1).toHaveBeenCalledTimes(2);
expect(fn2).toHaveBeenCalledTimes(2);
expect(import_prompt.promptTextInput).toHaveBeenCalledTimes(1);
expect(res1).toBe(obj1);
expect(res2).toBe(obj2);
});
it("strips whitespace from OTP prompt value", async () => {
import_prompt.promptTextInput.mockImplementationOnce((msg, opts) => Promise.resolve(opts.filter(" 121212 ")));
const obj = {};
const fn = jest.fn(makeTestCallback("121212", obj));
const result = await (0, import_otplease.otplease)(fn, {});
expect(result).toBe(obj);
});
it("validates OTP prompt response", async () => {
import_prompt.promptTextInput.mockImplementationOnce(
(msg, opts) => Promise.resolve(opts.validate("i am the very model of a modern major general"))
);
const obj = {};
const fn = jest.fn(makeTestCallback("343434", obj));
await expect((0, import_otplease.otplease)(fn, {})).rejects.toThrow("Must be a valid one-time-password");
});
it("rejects prompt errors", async () => {
import_prompt.promptTextInput.mockImplementationOnce(() => Promise.reject(new Error("poopypants")));
const obj = {};
const fn = jest.fn(makeTestCallback("343434", obj));
await expect((0, import_otplease.otplease)(fn, {})).rejects.toThrow("poopypants");
});
it("re-throws non-EOTP errors", async () => {
const fn = jest.fn(() => {
const err = new Error("not found");
err.code = "E404";
throw err;
});
await expect((0, import_otplease.otplease)(fn, {})).rejects.toThrow("not found");
});
it("re-throws E401 errors that do not contain 'one-time pass' in the body", async () => {
const fn = jest.fn(() => {
const err = new Error("auth required");
err.body = "random arbitrary noise";
err.code = "E401";
throw err;
});
await expect((0, import_otplease.otplease)(fn, {})).rejects.toThrow("auth required");
});
it.each([["stdin"], ["stdout"]])("re-throws EOTP error when %s is not a TTY", async (pipe) => {
const fn = jest.fn(() => {
const err = new Error(`non-interactive ${pipe}`);
err.code = "EOTP";
throw err;
});
process[pipe].isTTY = false;
await expect((0, import_otplease.otplease)(fn)).rejects.toThrow(`non-interactive ${pipe}`);
});
describe("getOneTimePassword()", () => {
it("defaults message argument", async () => {
await (0, import_otplease.getOneTimePassword)();
expect(import_prompt.promptTextInput).toHaveBeenCalledWith(
"This operation requires a one-time password:",
expect.any(Object)
);
});
it("accepts custom message", async () => {
await (0, import_otplease.getOneTimePassword)("foo bar");
expect(import_prompt.promptTextInput).toHaveBeenCalledWith("foo bar", expect.any(Object));
});
});
});
function makeTestCallback(otp, result) {
return (opts) => {
if (opts.otp !== otp) {
const err = new Error(`oops, received otp ${opts.otp}`);
err.code = "EOTP";
throw err;
}
return result;
};
}