Skip to content

Commit

Permalink
test(region-provider): add extensive unit tests for region-provider (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
trivikr committed Jun 17, 2020
1 parent d055da7 commit 5f61141
Show file tree
Hide file tree
Showing 6 changed files with 360 additions and 218 deletions.
132 changes: 50 additions & 82 deletions packages/region-provider/src/defaultProvider.spec.ts
Original file line number Diff line number Diff line change
@@ -1,104 +1,72 @@
import { defaultProvider } from "./defaultProvider";
import { ProviderError } from "@aws-sdk/property-provider";
import { fromEnv } from "./fromEnv";
import { fromSharedConfigFiles } from "./fromSharedConfigFiles";
import { chain, memoize } from "@aws-sdk/property-provider";
import {
defaultProvider,
RegionProviderConfiguration
} from "./defaultProvider";

jest.mock("./fromEnv", () => {
const envProvider = jest.fn();
return {
fromEnv: jest.fn().mockReturnValue(envProvider)
};
});
import { fromEnv, EnvConfiguration } from "./fromEnv";
jest.mock("./fromEnv", () => ({
fromEnv: jest.fn()
}));

jest.mock("./fromSharedConfigFiles", () => {
const iniProvider = jest.fn();
return {
fromSharedConfigFiles: jest.fn().mockReturnValue(iniProvider)
};
});
import {
fromSharedConfigFiles,
SharedConfigInit
} from "./fromSharedConfigFiles";
jest.mock("./fromSharedConfigFiles", () => ({
fromSharedConfigFiles: jest.fn()
}));

beforeEach(() => {
(fromEnv() as any).mockClear();
(fromSharedConfigFiles() as any).mockClear();
(fromEnv as any).mockClear();
(fromSharedConfigFiles as any).mockClear();
});
jest.mock("@aws-sdk/property-provider", () => ({
chain: jest.fn(),
memoize: jest.fn()
}));

describe("defaultProvider", () => {
it("should stop after the environmental provider if a region has been found", async () => {
const region = "foo";
(fromEnv() as any).mockImplementation(() => Promise.resolve(region));
const configuration: RegionProviderConfiguration = {
profile: "profile",
environmentVariableName: "environmentVariableName"
};

expect(await defaultProvider()()).toEqual(region);
expect((fromEnv() as any).mock.calls.length).toBe(1);
expect((fromSharedConfigFiles() as any).mock.calls.length).toBe(0);
afterEach(() => {
jest.clearAllMocks();
});

it("should continue on to the ini provider if no environment variable region has been found", async () => {
const region = "foo";
it("passes fromEnv() and fromSharedConfigFiles() to chain", () => {
const mockFromEnvReturn = "mockFromEnvReturn";
(fromEnv as jest.Mock).mockReturnValueOnce(mockFromEnvReturn);

(fromEnv() as any).mockImplementation(() =>
Promise.reject(new ProviderError("Nothing here!"))
const mockFromSharedConfigFilesReturn = "mockFromSharedConfigFilesReturn";
(fromSharedConfigFiles as jest.Mock).mockReturnValueOnce(
mockFromSharedConfigFilesReturn
);
(fromSharedConfigFiles() as any).mockImplementation(() =>
Promise.resolve(region)
);

expect(await defaultProvider()()).toEqual(region);
expect((fromEnv() as any).mock.calls.length).toBe(1);
expect((fromSharedConfigFiles() as any).mock.calls.length).toBe(1);
});

it("should pass configuration on to the env provider", async () => {
const envConfig: EnvConfiguration = {
environmentVariableName: "foo"
};

(fromEnv() as any).mockImplementation(() => Promise.resolve("region"));
(fromEnv as any).mockClear();

await expect(defaultProvider(envConfig)()).resolves;

expect((fromEnv as any).mock.calls.length).toBe(1);
expect((fromEnv as any).mock.calls[0][0]).toBe(envConfig);
});
defaultProvider(configuration);

it("should pass configuration on to the ini provider", async () => {
const iniConfig: SharedConfigInit = {
profile: "foo",
filepath: "/home/user/.secrets/credentials.ini",
configFilepath: "/home/user/.secrets/credentials.ini"
};
expect(fromEnv).toHaveBeenCalledTimes(1);
expect(fromEnv).toHaveBeenCalledWith(configuration);
expect(fromSharedConfigFiles).toHaveBeenCalledTimes(1);
expect(fromSharedConfigFiles).toHaveBeenCalledWith(configuration);

(fromEnv() as any).mockImplementation(() =>
Promise.reject(new ProviderError("Keep moving!"))
expect(chain).toHaveBeenCalledTimes(1);
expect(chain).toHaveBeenCalledWith(
mockFromEnvReturn,
mockFromSharedConfigFilesReturn
);
(fromSharedConfigFiles() as any).mockImplementation(() =>
Promise.resolve("region")
);

(fromSharedConfigFiles as any).mockClear();

await expect(defaultProvider(iniConfig)()).resolves;

expect((fromSharedConfigFiles as any).mock.calls.length).toBe(1);
expect((fromSharedConfigFiles as any).mock.calls[0][0]).toBe(iniConfig);
});

it("should return the same promise across invocations", async () => {
const region = "foo";
(fromEnv() as any).mockImplementation(() => Promise.resolve(region));
it("passes output of chain to memoize", () => {
const mockChainReturn = "mockChainReturn";
(chain as jest.Mock).mockReturnValueOnce(mockChainReturn);

const provider = defaultProvider();
defaultProvider(configuration);

expect(await provider()).toEqual(region);
expect(chain).toHaveBeenCalledTimes(1);
expect(memoize).toHaveBeenCalledTimes(1);
expect(memoize).toHaveBeenCalledWith(mockChainReturn);
});

expect(provider()).toBe(provider());
it("returns output memoize", () => {
const mockMemoizeReturn = "mockMemoizeReturn";
(memoize as jest.Mock).mockReturnValueOnce(mockMemoizeReturn);

expect(await provider()).toEqual(region);
expect((fromEnv() as any).mock.calls.length).toBe(1);
expect(defaultProvider(configuration)).toEqual(mockMemoizeReturn);
});
});
9 changes: 3 additions & 6 deletions packages/region-provider/src/defaultProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import { Provider } from "@aws-sdk/types";

export type RegionProviderConfiguration = EnvConfiguration & SharedConfigInit;

export function defaultProvider(
export const defaultProvider = (
configuration: RegionProviderConfiguration = {}
): Provider<string> {
return memoize(
chain(fromEnv(configuration), fromSharedConfigFiles(configuration))
);
}
): Provider<string> =>
memoize(chain(fromEnv(configuration), fromSharedConfigFiles(configuration)));
81 changes: 63 additions & 18 deletions packages/region-provider/src/fromEnv.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,81 @@ import { ProviderError } from "@aws-sdk/property-provider";
describe("fromEnv", () => {
const ENV_CUSTOM = "AWS_DEFAULT_REGION";
const envRegion = process.env[ENV_REGION];
const customEnv = process.env[ENV_CUSTOM];
const envCustom = process.env[ENV_CUSTOM];
const mockEnvRegion = "mockEnvRegion";
const mockEnvCustom = "mockEnvCustom";

const getProviderError = (envVarName: string) =>
new ProviderError(
`No value defined for the ${envVarName} environment variable`
);

beforeEach(() => {
delete process.env[ENV_REGION];
delete process.env[ENV_CUSTOM];
});

afterAll(() => {
process.env[ENV_CUSTOM] = customEnv;
process.env[ENV_CUSTOM] = envCustom;
process.env[ENV_REGION] = envRegion;
});

it(`should read from the ${ENV_REGION} environment variable`, async () => {
process.env[ENV_REGION] = "us-north-12";
expect(await fromEnv()()).toBe(process.env[ENV_REGION]);
});
describe("when no value is passed", () => {
it(`returns value in '${ENV_REGION}' env var when set`, () => {
process.env[ENV_REGION] = mockEnvRegion;
return expect(fromEnv()()).resolves.toBe(mockEnvRegion);
});

it(`should reject the promise is ${ENV_REGION} is not set`, async () => {
await expect(fromEnv()()).rejects.toMatchObject(
new ProviderError(
`No value defined for the AWS_REGION environment variable`
)
);
it(`throws when '${ENV_REGION}' env var is not set`, () => {
return expect(fromEnv()()).rejects.toMatchObject(
getProviderError(ENV_REGION)
);
});
});

it("should allow specifying custom environment variables", async () => {
process.env[ENV_CUSTOM] = "eu-central-35";
expect(process.env[ENV_REGION]).toBeUndefined();
expect(await fromEnv({ environmentVariableName: ENV_CUSTOM })()).toBe(
process.env[ENV_CUSTOM]
);
describe(`when custom env var '${ENV_CUSTOM}' is passed`, () => {
describe(`returns value in '${ENV_CUSTOM}' env var when set`, () => {
const environmentVariableName = ENV_CUSTOM;

beforeEach(() => {
process.env[environmentVariableName] = mockEnvCustom;
expect(process.env[ENV_REGION]).toBeUndefined();
});

it(`when '${ENV_REGION}' is set`, () => {
process.env[ENV_REGION] = mockEnvRegion;
return expect(fromEnv({ environmentVariableName })()).resolves.toBe(
mockEnvCustom
);
});

it(`when '${ENV_REGION}' is not set`, () => {
return expect(fromEnv({ environmentVariableName })()).resolves.toBe(
mockEnvCustom
);
});
});

describe(`throws when '${ENV_CUSTOM}' env var is not set`, () => {
const environmentVariableName = ENV_CUSTOM;

beforeEach(() => {
delete process.env[environmentVariableName];
expect(process.env[ENV_REGION]).toBeUndefined();
});

it(`when '${ENV_REGION}' is set`, () => {
process.env[ENV_REGION] = mockEnvRegion;
return expect(
fromEnv({ environmentVariableName })()
).rejects.toMatchObject(getProviderError(environmentVariableName));
});

it(`when '${ENV_REGION}' is not set`, () => {
return expect(
fromEnv({ environmentVariableName })()
).rejects.toMatchObject(getProviderError(environmentVariableName));
});
});
});
});
22 changes: 10 additions & 12 deletions packages/region-provider/src/fromEnv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,15 @@ export interface EnvConfiguration {
environmentVariableName?: string;
}

export function fromEnv({
export const fromEnv = ({
environmentVariableName = ENV_REGION
}: EnvConfiguration = {}): Provider<string> {
return async () => {
const envRegion = process.env[environmentVariableName];
if (envRegion) {
return envRegion;
}
}: EnvConfiguration = {}): Provider<string> => async () => {
const envRegion = process.env[environmentVariableName];
if (envRegion) {
return envRegion;
}

throw new ProviderError(
`No value defined for the ${environmentVariableName} environment variable`
);
};
}
throw new ProviderError(
`No value defined for the ${environmentVariableName} environment variable`
);
};

0 comments on commit 5f61141

Please sign in to comment.