Skip to content

Commit

Permalink
tests(parameters): add comments to SecretsProvider e2e test (#1282)
Browse files Browse the repository at this point in the history
* fix import to src, instead of lib

* clearing cache to fix forceFetch test case

* add more comments, create dedicated resource for test 7

* Update packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts

---------

Co-authored-by: Andrea Amorosi <dreamorosi@gmail.com>
  • Loading branch information
Alexander Schueren and dreamorosi committed Feb 9, 2023
1 parent 61080da commit ba921c1
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Context } from 'aws-lambda';
import { SecretsProvider } from '../../lib/secrets';
import { TinyLogger } from '../helpers/tinyLogger';
import { SecretsGetOptionsInterface } from '../../lib/types';
import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager';
import { middleware } from '../helpers/sdkMiddlewareRequestCounter';
import { SecretsProvider } from '../../src/secrets';
import { SecretsGetOptionsInterface } from '../../src/types';

const logger = new TinyLogger();
const defaultProvider = new SecretsProvider();
Expand All @@ -14,6 +14,7 @@ const secretNameBinary = process.env.SECRET_NAME_BINARY || '';
const secretNameObjectWithSuffix = process.env.SECRET_NAME_OBJECT_WITH_SUFFIX || '';
const secretNameBinaryWithSuffix = process.env.SECRET_NAME_BINARY_WITH_SUFFIX || '';
const secretNamePlainChached = process.env.SECRET_NAME_PLAIN_CACHED || '';
const secretNamePlainForceFetch = process.env.SECRET_NAME_PLAIN_FORCE_FETCH || '';

// Provider test 8, 9
const customClient = new SecretsManagerClient({});
Expand Down Expand Up @@ -42,23 +43,23 @@ const _call_get = async (paramName: string, testName: string, options?: SecretsG

export const handler = async (_event: unknown, _context: Context): Promise<void> => {

// Test 1 get single param as plaintext
// Test 1 get single secret as plaintext
await _call_get(secretNamePlain, 'get-plain');

// Test 2 get single param with transform json
// Test 2 get single secret with transform json
await _call_get(secretNameObject, 'get-transform-json', { transform: 'json' });

// Test 3 get single param with transform binary
// Test 3 get single secret with transform binary
await _call_get(secretNameBinary, 'get-transform-binary', { transform: 'binary' });

// Test 4 get single param with transform auto json
// Test 4 get single secret with transform auto json
await _call_get(secretNameObjectWithSuffix, 'get-transform-auto-json', { transform: 'auto' });

// Test 5 get single param with transform auto binary
// Test 5 get single secret with transform auto binary
await _call_get(secretNameBinaryWithSuffix, 'get-transform-auto-binary', { transform: 'auto' });

// Test 6
// get parameter twice with middleware, which counts number of SDK requests, we check later if we only called SecretManager API once
// get secret twice with middleware, which counts number of SDK requests, we check later if we only called SecretManager API once
try {
middleware.counter = 0;
await providerWithMiddleware.get(secretNamePlainChached);
Expand All @@ -74,11 +75,12 @@ export const handler = async (_event: unknown, _context: Context): Promise<void>
});
}
// Test 7
// get parameter twice, but force fetch 2nd time, we count number of SDK requests and check that we made two API calls
// get secret twice, but force fetch 2nd time, we count number of SDK requests and check that we made two API calls
try {
middleware.counter = 0;
await providerWithMiddleware.get(secretNamePlainChached);
await providerWithMiddleware.get(secretNamePlainChached, { forceFetch: true });
providerWithMiddleware.clearCache();
await providerWithMiddleware.get(secretNamePlainForceFetch);
await providerWithMiddleware.get(secretNamePlainForceFetch, { forceFetch: true });
logger.log({
test: 'get-plain-force',
value: middleware.counter // should be 2
Expand Down
88 changes: 61 additions & 27 deletions packages/parameters/tests/e2e/secretsProvider.class.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,51 @@ const runtime: string = process.env.RUNTIME || 'nodejs18x';
if (!isValidRuntimeKey(runtime)) {
throw new Error(`Invalid runtime key: ${runtime}`);
}

const uuid = v4();
const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'secretsProvider');
const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'secretsProvider');
const lambdaFunctionCodeFile = 'secretsProvider.class.test.functionCode.ts';

const secretNamePlain = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlain');
const secretNamePlainCached = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlainCached');
const secretNameObject = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject');
const secretNameBinary = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretBinary');
const secretNameObjectWithSuffix = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject.json');
const secretNameBinaryWithSuffix = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject.binary');

const invocationCount = 1;

const integTestApp = new App();
let stack: Stack;

/**
* Collection of e2e tests for SecretsProvider utility.
*
* Test 1: create a secret with plain text value, fetch it with no additional options
* Test 2: create a secret with json value, fetch it using `transform: 'json'` option
* Test 3: create a secret with base64 encoded value (technicaly string), fetch it using `transform: 'binary'` option
* Test 4: create a secret with json value and secret name ends with .json, fetch it using `transform: 'auto'` option
* Test 5: create a secret with base64 encoded value (technicaly string) and secert name ends with .binary, fetch it using `transform: 'auto'` option
* Test 6: create a secret with plain text value, fetch it twice, check that value was cached, the number of SDK calls should be 1
* Test 7: create a secret with plain text value, fetch it twice, second time with `forceFetch: true` option, check that value was not cached, the number of SDK calls should be 2
*
* For tests 6 and 7 we use our own AWS SDK custom middleware plugin `sdkMiddlewareRequestCounter.ts`
*
* Adding new test:
* Please keep the state clean, and create dedicated resource for your test, don't reuse resources from other tests.
* Pass the necessary information to lambda function by using enviroment variables
* Make sure to add the right permissions to the lambda function to access the resources. We use our `ResourceAccessGranter` to add permissions.
*
*/
describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () => {

const uuid = v4();
let invocationLogs: InvocationLogs[];
const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'secretsProvider');
const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'secretsProvider');
const lambdaFunctionCodeFile = 'secretsProvider.class.test.functionCode.ts';

const invocationCount = 1;

const integTestApp = new App();
let stack: Stack;

beforeAll(async () => {

// use unique names for each test to keep a clean state
const secretNamePlain = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlain');
const secretNameObject = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject');
const secretNameBinary = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretBinary');
const secretNameObjectWithSuffix = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject.json');
const secretNameBinaryWithSuffix = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretObject.binary');
const secretNamePlainCached = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlainCached');
const secretNamePlainForceFetch = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'testSecretPlainForceFetch');

// creates the test fuction that uses powertools secret provider we want to test
// pass env vars with secret names we want to fetch
stack = createStackWithLambdaFunction({
app: integTestApp,
stackName: stackName,
Expand All @@ -61,6 +83,7 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () =>
SECRET_NAME_OBJECT_WITH_SUFFIX: secretNameObjectWithSuffix,
SECRET_NAME_BINARY_WITH_SUFFIX: secretNameBinaryWithSuffix,
SECRET_NAME_PLAIN_CACHED: secretNamePlainCached,
SECRET_NAME_PLAIN_FORCE_FETCH: secretNamePlainForceFetch,
},
runtime: runtime
});
Expand Down Expand Up @@ -99,7 +122,14 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () =>
secretStringValue: SecretValue.unsafePlainText('foo')
});

Aspects.of(stack).add(new ResourceAccessGranter([ secretString, secretObject, secretBinary, secretObjectWithSuffix, secretBinaryWithSuffix, secretStringCached ]));
const secretStringForceFetch = new Secret(stack, 'testSecretStringForceFetch', {
secretName: secretNamePlainForceFetch,
secretStringValue: SecretValue.unsafePlainText('foo')
});

// add secrets here to grant lambda permisisons to access secrets
Aspects.of(stack).add(new ResourceAccessGranter([
secretString, secretObject, secretBinary, secretObjectWithSuffix, secretBinaryWithSuffix, secretStringCached, secretStringForceFetch ]));

await deployStack(integTestApp, stack);

Expand All @@ -108,7 +138,8 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () =>
}, SETUP_TIMEOUT);

describe('SecretsProvider usage', () => {
it('should retrieve a single parameter', async () => {

it('should retrieve a secret as plain string', async () => {

const logs = invocationLogs[0].getFunctionLogs();
const testLog = InvocationLogs.parseFunctionLog(logs[0]);
Expand All @@ -119,7 +150,7 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () =>
});
}, TEST_CASE_TIMEOUT);

it('should retrieve a single parameter with transform json', async () => {
it('should retrieve a secret using transform json option', async () => {
const logs = invocationLogs[0].getFunctionLogs();
const testLog = InvocationLogs.parseFunctionLog(logs[1]);

Expand All @@ -129,7 +160,7 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () =>
});
}, TEST_CASE_TIMEOUT);

it('should retrieve single param with transform binary', async () => {
it('should retrieve a secret using transform binary option', async () => {
const logs = invocationLogs[0].getFunctionLogs();
const testLog = InvocationLogs.parseFunctionLog(logs[2]);

Expand All @@ -140,17 +171,18 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () =>
}, TEST_CASE_TIMEOUT);
});

it('should retrieve single param with transform auto json', async () => {
it('should retrieve a secret using transform auto option with implicit json', async () => {
const logs = invocationLogs[0].getFunctionLogs();
const testLog = InvocationLogs.parseFunctionLog(logs[3]);

// result should be a json object
expect(testLog).toStrictEqual({
test: 'get-transform-auto-json',
value: { foo: 'bar' }
});
}, TEST_CASE_TIMEOUT);

it('should retrieve single param wit transform auto binary', async () => {
it('should retrieve a secret using transform auto option with implicit binary', async () => {
const logs = invocationLogs[0].getFunctionLogs();
const testLog = InvocationLogs.parseFunctionLog(logs[4]);

Expand All @@ -160,23 +192,25 @@ describe(`parameters E2E tests (SecretsProvider) for runtime: ${runtime}`, () =>
});
});

it('should retrieve single parameter cached', async () => {
it('should retrieve a secret twice with cached value', async () => {
const logs = invocationLogs[0].getFunctionLogs();
const testLogFirst = InvocationLogs.parseFunctionLog(logs[5]);

// we fetch twice, but we expect to make an API call only once
expect(testLogFirst).toStrictEqual({
test: 'get-plain-cached',
value: 1
});
});

it('should retrieve single parameter twice without caching', async () => {
it('should retrieve a secret twice with forceFetch second time', async () => {
const logs = invocationLogs[0].getFunctionLogs();
const testLogFirst = InvocationLogs.parseFunctionLog(logs[6]);

// we fetch twice, 2nd time with forceFetch: true flag, we expect two api calls
expect(testLogFirst).toStrictEqual({
test: 'get-plain-force',
value: 1
value: 2
});
});

Expand Down

0 comments on commit ba921c1

Please sign in to comment.