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
54 changes: 54 additions & 0 deletions cli/__tests__/lib.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,60 @@ describe('poller.js', () => {
jest.clearAllMocks();
});

test('a rejected token stops the poller with a message that says to check the token, not that it is revoked', async () => {
const authError = Object.assign(new Error('unauthorized'), { status: 401 });
const mockGet = jest.fn().mockRejectedValue(authError);
createClient.mockReturnValue({ get: mockGet, post: jest.fn() });

const errors = [];
startPoller({
instanceUrl: 'http://localhost:5000',
token: 'cm_test',
agentName: 'my-agent',
intervalMs: 1,
onEvent: async () => ({ outcome: 'acknowledged' }),
onError: (err) => errors.push(err),
});

await new Promise((r) => setTimeout(r, 50));
const stopMsg = errors.map((e) => e.message).find((m) => /stopping poller/.test(m));
// The three sources `agent connect` reads, in order: --token, COMMONLY_TOKEN, saved login.
expect(stopMsg).toMatch(/--token, then COMMONLY_TOKEN, then your saved login/);
expect(stopMsg).toMatch(/commonly login/);
// connect never reads the token file or detach: naming them would send the reader to the wrong place.
expect(stopMsg).not.toMatch(/detach/);
expect(stopMsg).not.toMatch(/tokens\//);
expect(stopMsg).not.toMatch(/likely revoked/);
});

const rejectedMessage = async (loginInstance) => {
const authError = Object.assign(new Error('unauthorized'), { status: 401 });
createClient.mockReturnValue({ get: jest.fn().mockRejectedValue(authError), post: jest.fn() });
const errors = [];
startPoller({
instanceUrl: 'http://localhost:5000',
token: 'cm_test',
agentName: 'my-agent',
intervalMs: 1,
loginInstance,
onEvent: async () => ({ outcome: 'acknowledged' }),
onError: (err) => errors.push(err),
});
await new Promise((r) => setTimeout(r, 50));
return errors.map((e) => e.message).find((m) => /stopping poller/.test(m));
};

test('the login hint names the --instance connect ran with, so it refreshes that profile', async () => {
const stopMsg = await rejectedMessage('dev');
expect(stopMsg).toMatch(/run commonly login --instance dev\./);
});

test('with no --instance, the login hint is the bare commonly login', async () => {
const stopMsg = await rejectedMessage(undefined);
expect(stopMsg).toMatch(/run commonly login\./);
expect(stopMsg).not.toMatch(/--instance/);
});

const makeEvent = (id = 'evt-1') => ({
_id: id,
type: 'heartbeat',
Expand Down
1 change: 1 addition & 0 deletions cli/src/commands/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -3713,6 +3713,7 @@ Docs:
agentName: opts.name,
instanceId: opts.instanceId,
intervalMs: parseInt(opts.interval, 10),
loginInstance: opts.instance,
onEvent: async (event) => {
const ts = new Date().toTimeString().slice(0, 8);
process.stdout.write(`[${ts}] ${event.type.padEnd(18)}`);
Expand Down
5 changes: 4 additions & 1 deletion cli/src/lib/poller.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const startPoller = ({
agentName,
instanceId = 'default',
intervalMs = 5000,
loginInstance, // the --instance the connect ran with, so the login hint names it
onEvent, // async (event) => { outcome, content? }
onError, // (err) => void
}) => {
Expand Down Expand Up @@ -139,7 +140,9 @@ export const startPoller = ({
if (consecutiveAuthErrors >= MAX_AUTH_ERRORS) {
onError?.(new Error(
`Runtime token rejected ${consecutiveAuthErrors} times in a row — stopping poller. `
+ `The token is likely revoked.`,
+ `This connection takes its key from --token, then COMMONLY_TOKEN, then your saved login. `
+ `If the key was rotated or revoked, re-run with the new key in --token or COMMONLY_TOKEN; `
+ `with neither set, run commonly login${loginInstance ? ` --instance ${loginInstance}` : ''}.`,
));
running = false;
return;
Expand Down
Loading