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
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gofynd/fdk-cli",
"version": "8.0.7",
"version": "8.0.8",
"main": "index.js",
"license": "MIT",
"bin": {
Expand Down
81 changes: 81 additions & 0 deletions src/__tests__/api.interceptor.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
/// <reference types="jest" />
import { addSignatureFn } from '../lib/api/helper/interceptors';
import ConfigStore, { CONFIG_KEYS } from '../lib/Config';

const packageJSON = require('../../package.json');

describe('API request interceptor', () => {
afterEach(() => {
jest.restoreAllMocks();
});

it('adds fdk cli version header before service requests are signed', async () => {
const interceptor = addSignatureFn({});
const config: any = {
Expand All @@ -18,4 +23,80 @@ describe('API request interceptor', () => {
expect(signedConfig.headers['x-fp-date']).toBeDefined();
expect(signedConfig.headers['x-fp-signature']).toBeDefined();
});

it('adds stored region header before fynd service requests are signed', async () => {
jest.spyOn(ConfigStore, 'get').mockImplementation((key: string) => {
if (key === CONFIG_KEYS.REGION) return 'asia-south1';
return undefined;
});
const interceptor = addSignatureFn({});
const config: any = {
url: 'https://api.fyndx1.de/service/panel/authentication/v1.0/oauth/token',
method: 'get',
headers: {},
};

const signedConfig = await interceptor(config);

expect(signedConfig.headers['x-region']).toBe('asia-south1');
expect(signedConfig.headers['x-fp-signature']).toBeDefined();
});

it('prefers request region header over stored region header', async () => {
jest.spyOn(ConfigStore, 'get').mockImplementation((key: string) => {
if (key === CONFIG_KEYS.REGION) return 'chinmay';
return undefined;
});
const interceptor = addSignatureFn({});
const config: any = {
url: 'https://api.fyndx1.de/service/panel/authentication/v1.0/oauth/token',
method: 'get',
headers: {
'x-region': 'asia-south2',
},
};

const signedConfig = await interceptor(config);

expect(signedConfig.headers['x-region']).toBe('asia-south2');
expect(signedConfig.headers['x-fp-signature']).toBeDefined();
});

it('does not add stored region header when request opts out of region', async () => {
jest.spyOn(ConfigStore, 'get').mockImplementation((key: string) => {
if (key === CONFIG_KEYS.REGION) return 'chinmay';
return undefined;
});
const interceptor = addSignatureFn({});
const config: any = {
url: 'https://api.fyndx1.de/service/panel/authentication/v1.0/oauth/token',
method: 'get',
headers: {
'x-region': null,
},
};

const signedConfig = await interceptor(config);

expect(signedConfig.headers['x-region']).toBeUndefined();
expect(signedConfig.headers['x-fp-signature']).toBeDefined();
});

it('does not add stored region header to third-party requests', async () => {
jest.spyOn(ConfigStore, 'get').mockImplementation((key: string) => {
if (key === CONFIG_KEYS.REGION) return 'asia-south1';
return undefined;
});
const interceptor = addSignatureFn({});
const config: any = {
url: 'https://storage.googleapis.com/fdk-upload/signed-url',
method: 'put',
headers: {},
};

const signedConfig = await interceptor(config);

expect(signedConfig.headers['x-region']).toBeUndefined();
expect(signedConfig.headers['x-fp-signature']).toBeUndefined();
});
});
46 changes: 45 additions & 1 deletion src/__tests__/auth.device.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ jest.mock('open', () => ({
default: (...args) => openMock(...args),
}));

jest.mock('../helper/extension_utils', () => ({
getRandomFreePort: jest.fn().mockResolvedValue(43123),
}));

jest.mock('../helper/formatter', () => ({
OutputFormatter: {
link: (value: string) => value,
Expand Down Expand Up @@ -127,6 +131,7 @@ describe('Auth device flow', () => {
expect.objectContaining({
headers: {
'Content-Type': 'application/json',
'x-region': 'asia-south1/development',
},
}),
expect.objectContaining({
Expand All @@ -137,6 +142,9 @@ describe('Auth device flow', () => {
1,
'https://api.fyndx1.de/region/asia-south1/development/service/panel/authentication/v1.0/oauth/device_authorization',
expect.objectContaining({
headers: expect.objectContaining({
'x-region': 'asia-south1/development',
}),
data: expect.objectContaining({
requested_region: 'asia-south1/development',
}),
Expand All @@ -146,6 +154,9 @@ describe('Auth device flow', () => {
2,
'https://api.fyndx1.de/region/asia-south1/development/service/panel/authentication/v1.0/oauth/token',
expect.objectContaining({
headers: expect.objectContaining({
'x-region': 'asia-south1/development',
}),
data: expect.objectContaining({
device_code: 'device-code-region',
}),
Expand All @@ -155,6 +166,7 @@ describe('Auth device flow', () => {
'https://partners.fyndx1.de/partners/organizations/?device_id=device-code-region&region=asia-south1%2Fdevelopment',
);
expect(configStore.get(CONFIG_KEYS.AUTH_TOKEN).access_token).toBe('region-token');
expect(configStore.get(CONFIG_KEYS.REGION)).toBe('asia-south1/development');
});

it('uses device flow and opens verification URL unchanged', async () => {
Expand Down Expand Up @@ -186,14 +198,46 @@ describe('Auth device flow', () => {
},
} as any);

configStore.set(CONFIG_KEYS.REGION, 'asia-south1');

await Auth.login({ host: 'api.fyndx1.de' });

expect(getSpy).toHaveBeenCalled();
expect(getSpy).toHaveBeenCalledWith(
'https://api.fyndx1.de/service/panel/authentication/v1.0/oauth/client-config',
expect.objectContaining({
headers: {
'Content-Type': 'application/json',
'x-region': null,
},
}),
expect.objectContaining({
validateStatus: expect.any(Function),
}),
);
expect(postSpy).toHaveBeenCalledTimes(2);
expect(postSpy).toHaveBeenNthCalledWith(
1,
'https://api.fyndx1.de/service/panel/authentication/v1.0/oauth/device_authorization',
expect.objectContaining({
headers: expect.objectContaining({
'x-region': null,
}),
}),
);
expect(postSpy).toHaveBeenNthCalledWith(
2,
'https://api.fyndx1.de/service/panel/authentication/v1.0/oauth/token',
expect.objectContaining({
headers: expect.objectContaining({
'x-region': null,
}),
}),
);
expect(openMock).toHaveBeenCalledTimes(1);
const openedUrl = openMock.mock.calls[0][0] as string;
expect(openedUrl).toBe('https://partners.fyndx1.de/partners/organizations/?device_id=device-code-basic');
expect(configStore.get(CONFIG_KEYS.AUTH_TOKEN).access_token).toBe('token-1');
expect(configStore.get(CONFIG_KEYS.REGION)).toBeUndefined();
});

it('maps expired_token polling response to CommandError', async () => {
Expand Down
83 changes: 78 additions & 5 deletions src/__tests__/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import {
jest.mock('inquirer');
let program;

jest.mock('../helper/extension_utils', () => ({
getRandomFreePort: jest.fn().mockResolvedValue(43123),
}));

jest.mock('configstore', () => {
const Store =
jest.requireActual('configstore');
Expand All @@ -45,18 +49,30 @@ jest.mock('configstore', () => {
jest.mock('open', () => {
return () => {}
})
export async function login(domain?: string) {
export async function login(domain?: string, region?: string) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // Disable SSL verification
const port = await getRandomFreePort([]);
const app = await startServer(port);
const req = request(app);
if(domain)
await program.parseAsync(['ts-node', './src/fdk.ts', 'login', '--host', domain]);
else
await program.parseAsync(['ts-node', './src/fdk.ts', 'login', '--host', 'api.fyndx1.de']);
const args = ['ts-node', './src/fdk.ts', 'login', '--host', domain || 'api.fyndx1.de'];
if (region) args.push('--region', region);
await parseProgram(args);
return await req.post('/token').send(tokenData);
}

function resetProgramState(command = program) {
command._optionValues = {};
command.args = [];
command.rawArgs = [];
command.processedArgs = [];
command.commands?.forEach((subCommand: any) => resetProgramState(subCommand));
}

async function parseProgram(args: string[]) {
resetProgramState();
await program.parseAsync(args);
}

describe('Auth Commands', () => {
beforeAll(async () => {
setEnv();
Expand Down Expand Up @@ -130,13 +146,70 @@ describe('Auth Commands', () => {
'pr-4fb094006ed3a6d749b69875be0418b83238d078',
);
});
it('Should update region when user selects no for organization change', async () => {
const inquirerMock = mockFunction(inquirer.prompt);
inquirerMock.mockResolvedValue({ confirmChangeOrg: 'No' });
configStore.set(CONFIG_KEYS.REGION, 'asia-south1');

await parseProgram([
'ts-node',
'./src/fdk.ts',
'login',
'--host',
'api.fyndx1.de',
'--region',
'asia-south2',
]);

expect(configStore.get(CONFIG_KEYS.REGION)).toBe('asia-south2');
});
it('Should clear region when user selects no for organization change without region', async () => {
const inquirerMock = mockFunction(inquirer.prompt);
inquirerMock.mockResolvedValue({ confirmChangeOrg: 'No' });
configStore.set(CONFIG_KEYS.REGION, 'asia-south1');

await parseProgram([
'ts-node',
'./src/fdk.ts',
'login',
'--host',
'api.fyndx1.de',
]);

expect(configStore.get(CONFIG_KEYS.REGION)).toBeUndefined();
});
it('Should successfully login with and env should updated', async () => {
configStore.delete(CONFIG_KEYS.AUTH_TOKEN);
configStore.set(CONFIG_KEYS.REGION, 'asia-south1');
await login('api.fynd.com');
expect(configStore.get(CONFIG_KEYS.CURRENT_ENV_VALUE)).toBe('api.fynd.com');
expect(configStore.get(CONFIG_KEYS.AUTH_TOKEN).access_token).toBe(
'pr-4fb094006ed3a6d749b69875be0418b83238d078',
);
expect(configStore.get(CONFIG_KEYS.REGION)).toBeUndefined();
});
it('Should store region after regional partner panel login', async () => {
configStore.delete(CONFIG_KEYS.AUTH_TOKEN);
configStore.delete(CONFIG_KEYS.REGION);
await login('api.fyndx1.de', 'asia-south1');
expect(configStore.get(CONFIG_KEYS.REGION)).toBe('asia-south1');
});
it('Should pass command region while validating host before login', async () => {
configStore.delete(CONFIG_KEYS.AUTH_TOKEN);
configStore.set(CONFIG_KEYS.REGION, 'chinmay');
const getSpy = jest.spyOn(axios, 'get');

await login('api.fyndx1.de', 'asia-south2');

expect(getSpy).toHaveBeenCalledWith(
'https://api.fyndx1.de/service/application/content/_healthz',
{
headers: {
'x-region': 'asia-south2',
},
},
);
expect(configStore.get(CONFIG_KEYS.REGION)).toBe('asia-south2');
});
it('should console active user', async () => {
let consoleWarnSpy: jest.SpyInstance;
Expand Down
Loading
Loading