Skip to content

Commit

Permalink
Improve integration test with unique values to ensure they are overwr…
Browse files Browse the repository at this point in the history
…itten
  • Loading branch information
sethvargo committed Aug 13, 2024
1 parent 7dc254c commit 65fc04e
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 103 deletions.
17 changes: 15 additions & 2 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,25 @@ jobs:
- id: 'deploy'
uses: './'
with:
name: 'https-trigger-${{ github.run_number }}'
name: 'integration-https-trigger-${{ github.run_number }}-${{ github.run_attempt }}'
runtime: 'nodejs22'
entry_point: 'helloWorld'
source_dir: './tests/test-node-func/'
service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}'

- id: 'auth'
uses: 'google-github-actions/auth@v2'
with:
project_id: '${{ vars.PROJECT_ID }}'
workload_identity_provider: '${{ vars.WIF_PROVIDER_NAME }}'

- name: 'test'
run: |-
curl "${{ steps.deploy.outputs.url }}" \
--silent \
--fail-with-body \
--header "Authorization: Bearer ${{ steps.auth.outputs.auth_token }}"
event_trigger:
timeout-minutes: 10
permissions:
Expand All @@ -70,7 +83,7 @@ jobs:
- id: 'deploy'
uses: './'
with:
name: 'event-trigger-${{ github.run_number }}'
name: 'integration-event-trigger-${{ github.run_number }}-${{ github.run_attempt }}'
runtime: 'nodejs22'
entry_point: 'helloWorld'
source_dir: './tests/test-node-func/'
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,16 @@ jobs:
if: ${{ matrix.os == 'ubuntu-latest' }}
run: 'npm run lint'

- uses: 'google-github-actions/auth@v2'
- id: 'auth'
uses: 'google-github-actions/auth@v2'
if: ${{ github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name && github.actor != 'dependabot[bot]' }}
with:
project_id: '${{ vars.PROJECT_ID }}'
workload_identity_provider: '${{ vars.WIF_PROVIDER_NAME }}'
service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}'

- name: 'npm test'
env:
TEST_AUTHENTICATED: '${{ !!steps.auth.outputs.auth_token }}'
TEST_PROJECT_ID: '${{ vars.PROJECT_ID }}'
TEST_SERVICE_ACCOUNT_EMAIL: '${{ vars.SERVICE_ACCOUNT_EMAIL }}'
TEST_SECRET_VERSION_NAME: '${{ vars.SECRET_VERSION_NAME }}'
Expand Down
63 changes: 47 additions & 16 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -598,22 +598,53 @@ export class CloudFunctionsClient {
computeUpdateMask(cf: CloudFunction): string {
const keys: string[] = [];

const iter = (obj: object, root?: string) => {
for (const [k, v] of Object.entries(obj)) {
if (v === undefined) {
continue;
}

const pth = root ? root + '.' + k : k;
if (typeof v === 'object' && !Array.isArray(v)) {
iter(v, pth);
} else {
keys.push(pth);
}
}
};

iter(cf);
if (cf.name !== undefined) keys.push('name');
if (cf.description !== undefined) keys.push('description');
if (cf.environment !== undefined) keys.push('environment');
if (cf.kmsKeyName !== undefined) keys.push('kmsKeyName');
if (cf.labels !== undefined) keys.push('labels');

if (cf.buildConfig?.runtime !== undefined) keys.push('buildConfig.runtime');
if (cf.buildConfig?.entryPoint !== undefined) keys.push('buildConfig.entryPoint');
if (cf.buildConfig?.source !== undefined) keys.push('buildConfig.source');
if (cf.buildConfig?.dockerRepository !== undefined) keys.push('buildConfig.dockerRepository');
if (cf.buildConfig?.environmentVariables !== undefined)
keys.push('buildConfig.environmentVariables');
if (cf.buildConfig?.serviceAccount !== undefined) keys.push('buildConfig.serviceAccount');
if (cf.buildConfig?.workerPool !== undefined) keys.push('buildConfig.workerPool');

if (cf.serviceConfig?.allTrafficOnLatestRevision !== undefined)
keys.push('serviceConfig.allTrafficOnLatestRevision');
if (cf.serviceConfig?.availableCpu !== undefined) keys.push('serviceConfig.availableCpu');
if (cf.serviceConfig?.availableMemory !== undefined) keys.push('serviceConfig.availableMemory');
if (cf.serviceConfig?.environmentVariables !== undefined)
keys.push('serviceConfig.environmentVariables');
if (cf.serviceConfig?.ingressSettings !== undefined) keys.push('serviceConfig.ingressSettings');
if (cf.serviceConfig?.maxInstanceCount !== undefined)
keys.push('serviceConfig.maxInstanceCount');
if (cf.serviceConfig?.maxInstanceRequestConcurrency !== undefined)
keys.push('serviceConfig.maxInstanceRequestConcurrency');
if (cf.serviceConfig?.minInstanceCount !== undefined)
keys.push('serviceConfig.minInstanceCount');
if (cf.serviceConfig?.secretEnvironmentVariables !== undefined)
keys.push('serviceConfig.secretEnvironmentVariables');
if (cf.serviceConfig?.secretVolumes !== undefined) keys.push('serviceConfig.secretVolumes');
if (cf.serviceConfig?.serviceAccountEmail !== undefined)
keys.push('serviceConfig.serviceAccountEmail');
if (cf.serviceConfig?.timeoutSeconds !== undefined) keys.push('serviceConfig.timeoutSeconds');
if (cf.serviceConfig?.vpcConnector !== undefined) keys.push('serviceConfig.vpcConnector');
if (cf.serviceConfig?.vpcConnectorEgressSettings !== undefined)
keys.push('serviceConfig.vpcConnectorEgressSettings');

if (cf.eventTrigger?.triggerRegion !== undefined) keys.push('eventTrigger.triggerRegion');
if (cf.eventTrigger?.eventType !== undefined) keys.push('eventTrigger.eventType');
if (cf.eventTrigger?.eventFilters !== undefined) keys.push('eventTrigger.eventFilters');
if (cf.eventTrigger?.pubsubTopic !== undefined) keys.push('eventTrigger.pubsubTopic');
if (cf.eventTrigger?.serviceAccountEmail !== undefined)
keys.push('eventTrigger.serviceAccountEmail');
if (cf.eventTrigger?.retryPolicy !== undefined) keys.push('eventTrigger.retryPolicy');
if (cf.eventTrigger?.channel !== undefined) keys.push('eventTrigger.channel');
if (cf.eventTrigger?.service !== undefined) keys.push('eventTrigger.service');

return keys.join(',');
}
Expand Down
126 changes: 85 additions & 41 deletions tests/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ import { zipDir } from '../src/util';

const { TEST_PROJECT_ID, TEST_SERVICE_ACCOUNT_EMAIL, TEST_SECRET_VERSION_NAME } = process.env;
const TEST_LOCATION = 'us-central1';
const TEST_FUNCTION_NAME = 'test-' + crypto.randomBytes(12).toString('hex');
const TEST_SEED = crypto.randomBytes(12).toString('hex').toLowerCase();
const TEST_SEED_UPPER = TEST_SEED.toUpperCase();
const TEST_FUNCTION_NAME = `unit-${TEST_SEED}`;

test(
'lifecycle',
{
concurrency: true,
skip: skipIfMissingEnv('TEST_PROJECT_ID', 'TEST_LOCATION'),
skip: skipIfMissingEnv('TEST_AUTHENTICATED'),
},
async (suite) => {
// Always try to delete the function
Expand All @@ -52,7 +54,7 @@ test(
}
});

suite.test('can create, read, update, and delete', async () => {
await suite.test('can create, read, update, and delete', async () => {
const secret = new SecretName(TEST_SECRET_VERSION_NAME);

const client = new CloudFunctionsClient({
Expand All @@ -75,39 +77,50 @@ test(
name: TEST_FUNCTION_NAME,
description: 'test function',
environment: Environment.GEN_2,
labels: { key1: 'value1', key2: 'value2' },
labels: {
[`label1-${TEST_SEED}`]: `value1_${TEST_SEED}`,
[`label2-${TEST_SEED}`]: `value2_${TEST_SEED}`,
},

buildConfig: {
runtime: 'nodejs22',
entryPoint: 'helloWorld',
source: sourceUploadResp,
environmentVariables: { BUILDKEY1: 'VALUE1', BUILDKEY2: 'VALUE2' },
source: {
storageSource: sourceUploadResp.storageSource,
},
environmentVariables: {
[`BUILD_ENV_KEY1_${TEST_SEED_UPPER}`]: `VALUE1_${TEST_SEED}`,
[`BUILD_ENV_KEY2_${TEST_SEED_UPPER}`]: `VALUE2_${TEST_SEED}`,
},
},

serviceConfig: {
allTrafficOnLatestRevision: true,
availableCpu: '1',
availableMemory: '512m',
environmentVariables: { KEY1: 'VALUE1', KEY2: 'VALUE2' },
availableMemory: '512Mi',
environmentVariables: {
[`SERVICE_ENV_KEY1_${TEST_SEED_UPPER}`]: `VALUE1_${TEST_SEED}`,
[`SERVICE_ENV_KEY2_${TEST_SEED_UPPER}`]: `VALUE2_${TEST_SEED}`,
},
ingressSettings: IngressSettings.ALLOW_ALL,
maxInstanceCount: 5,
minInstanceCount: 2,
secretEnvironmentVariables: [
{
key: 'SECRET1',
key: `SECRET1_${TEST_SEED_UPPER}`,
projectId: secret.project,
secret: secret.name,
version: secret.version,
},
],
secretVolumes: [
{
mountPath: '/etc/secrets/one',
mountPath: `/etc/secrets/one_${TEST_SEED}`,
projectId: secret.project,
secret: secret.name,
versions: [
{
path: '/value1',
path: 'value1',
version: secret.version,
},
],
Expand All @@ -119,45 +132,55 @@ test(
};

// Create
const createResp = await client.create(cf);
const createResp = await client.create(cf, {
onDebug: (f) => {
process.stdout.write('\n\n\n\n');
process.stdout.write(f());
process.stdout.write('\n\n\n\n');
},
});
assert.ok(createResp?.url);

// Read
const getResp = await client.get(cf.name);
assert.ok(getResp.name.endsWith(TEST_FUNCTION_NAME)); // The response is the fully-qualified name
assert.deepStrictEqual(getResp.description, 'test function');
assert.deepStrictEqual(getResp.labels, { key1: 'value1', key2: 'value2' });
assert.deepStrictEqual(getResp.buildConfig.runtime, 'nodejs20');
assert.deepStrictEqual(getResp.labels, {
[`label1-${TEST_SEED}`]: `value1_${TEST_SEED}`,
[`label2-${TEST_SEED}`]: `value2_${TEST_SEED}`,
});
assert.deepStrictEqual(getResp.buildConfig.runtime, 'nodejs22');
assert.deepStrictEqual(getResp.buildConfig.environmentVariables, {
BUILDKEY1: 'VALUE1',
BUILDKEY2: 'VALUE2',
[`BUILD_ENV_KEY1_${TEST_SEED_UPPER}`]: `VALUE1_${TEST_SEED}`,
[`BUILD_ENV_KEY2_${TEST_SEED_UPPER}`]: `VALUE2_${TEST_SEED}`,
});
assert.deepStrictEqual(getResp.buildConfig.entryPoint, 'helloWorld');
assert.deepStrictEqual(getResp.serviceConfig.availableCpu, 1);
assert.deepStrictEqual(getResp.serviceConfig.availableMemory, 512);
assert.deepStrictEqual(getResp.serviceConfig.availableCpu, '1');
assert.deepStrictEqual(getResp.serviceConfig.availableMemory, '512Mi');
assert.deepStrictEqual(getResp.serviceConfig.environmentVariables, {
KEY1: 'VALUE1',
KEY2: 'VALUE2',
LOG_EXECUTION_ID: 'true', // inserted by GCP
[`SERVICE_ENV_KEY1_${TEST_SEED_UPPER}`]: `VALUE1_${TEST_SEED}`,
[`SERVICE_ENV_KEY2_${TEST_SEED_UPPER}`]: `VALUE2_${TEST_SEED}`,
});
assert.deepStrictEqual(getResp.serviceConfig.ingressSettings, 'ALLOW_ALL');
assert.deepStrictEqual(getResp.serviceConfig.maxInstanceCount, 5);
assert.deepStrictEqual(getResp.serviceConfig.minInstanceCount, 2);
assert.deepStrictEqual(getResp.serviceConfig.secretEnvironmentVariables, [
{
key: 'SECRET1',
key: `SECRET1_${TEST_SEED_UPPER}`,
projectId: secret.project,
secret: secret.name,
version: secret.version,
},
]);
assert.deepStrictEqual(getResp.serviceConfig.secretVolumes, [
{
mountPath: '/etc/secrets/one',
mountPath: `/etc/secrets/one_${TEST_SEED}`,
projectId: secret.project,
secret: secret.name,
versions: [
{
path: '/value1',
path: 'value1',
version: secret.version,
},
],
Expand All @@ -175,38 +198,49 @@ test(
const cf2: CloudFunction = {
name: TEST_FUNCTION_NAME,
description: 'test function2',
labels: { key3: 'value3', key4: 'value4' },
labels: {
[`label3-${TEST_SEED}`]: `value3_${TEST_SEED}`,
[`label4-${TEST_SEED}`]: `value4_${TEST_SEED}`,
},

buildConfig: {
runtime: 'nodejs20',
entryPoint: 'helloWorld',
source: sourceUploadUpdateResp,
environmentVariables: { BUILDKEY3: 'VALUE3', BUILDKEY4: 'VALUE4' },
source: {
storageSource: sourceUploadResp.storageSource,
},
environmentVariables: {
[`BUILD_ENV_KEY3_${TEST_SEED_UPPER}`]: `VALUE3_${TEST_SEED}`,
[`BUILD_ENV_KEY4_${TEST_SEED_UPPER}`]: `VALUE4_${TEST_SEED}`,
},
},

serviceConfig: {
allTrafficOnLatestRevision: true,
availableMemory: '256Mi',
environmentVariables: { KEY3: 'VALUE3', KEY4: 'VALUE4' },
availableMemory: '1Gi',
environmentVariables: {
[`SERVICE_ENV_KEY3_${TEST_SEED_UPPER}`]: `VALUE3_${TEST_SEED}`,
[`SERVICE_ENV_KEY4_${TEST_SEED_UPPER}`]: `VALUE4_${TEST_SEED}`,
},
ingressSettings: IngressSettings.ALLOW_INTERNAL_AND_GCLB,
maxInstanceCount: 3,
minInstanceCount: 1,
secretEnvironmentVariables: [
{
key: 'SECRET2',
key: `SECRET2_${TEST_SEED_UPPER}`,
projectId: secret.project,
secret: secret.name,
version: secret.version,
},
],
secretVolumes: [
{
mountPath: '/etc/secrets/two',
mountPath: `/etc/secrets/two_${TEST_SEED}`,
projectId: secret.project,
secret: secret.name,
versions: [
{
path: '/value2',
path: 'value2',
version: secret.version,
},
],
Expand All @@ -217,40 +251,50 @@ test(
},
};

const patchResp = await client.patch(cf2);
const patchResp = await client.patch(cf2, {
onDebug: (f) => {
process.stdout.write('\n\n\n\n');
process.stdout.write(f());
process.stdout.write('\n\n\n\n');
},
});
assert.ok(patchResp.name.endsWith(TEST_FUNCTION_NAME)); // The response is the fully-qualified name
assert.deepStrictEqual(patchResp.description, 'test function2');
assert.deepStrictEqual(patchResp.labels, { key3: 'value3', key4: 'value4' });
assert.deepStrictEqual(patchResp.labels, {
[`label3-${TEST_SEED}`]: `value3_${TEST_SEED}`,
[`label4-${TEST_SEED}`]: `value4_${TEST_SEED}`,
});
assert.deepStrictEqual(patchResp.buildConfig.runtime, 'nodejs20');
assert.deepStrictEqual(patchResp.buildConfig.entryPoint, 'helloWorld');
assert.deepStrictEqual(patchResp.buildConfig.environmentVariables, {
BUILDKEY3: 'VALUE3',
BUILDKEY4: 'VALUE4',
[`BUILD_ENV_KEY3_${TEST_SEED_UPPER}`]: `VALUE3_${TEST_SEED}`,
[`BUILD_ENV_KEY4_${TEST_SEED_UPPER}`]: `VALUE4_${TEST_SEED}`,
});
assert.deepStrictEqual(patchResp.serviceConfig.availableMemory, '256');
assert.deepStrictEqual(patchResp.serviceConfig.availableMemory, '1Gi');
assert.deepStrictEqual(patchResp.serviceConfig.environmentVariables, {
KEY3: 'VALUE3',
KEY4: 'VALUE4',
LOG_EXECUTION_ID: 'true', // inserted by GCP
[`SERVICE_ENV_KEY3_${TEST_SEED_UPPER}`]: `VALUE3_${TEST_SEED}`,
[`SERVICE_ENV_KEY4_${TEST_SEED_UPPER}`]: `VALUE4_${TEST_SEED}`,
});
assert.deepStrictEqual(patchResp.serviceConfig.ingressSettings, 'ALLOW_INTERNAL_AND_GCLB');
assert.deepStrictEqual(patchResp.serviceConfig.maxInstanceCount, 3);
assert.deepStrictEqual(patchResp.serviceConfig.minInstanceCount, 1);
assert.deepStrictEqual(patchResp.serviceConfig.secretEnvironmentVariables, [
{
key: 'SECRET2',
key: `SECRET2_${TEST_SEED_UPPER}`,
projectId: secret.project,
secret: secret.name,
version: secret.version,
},
]);
assert.deepStrictEqual(patchResp.serviceConfig.secretVolumes, [
{
mountPath: '/etc/secrets/two',
mountPath: `/etc/secrets/two_${TEST_SEED}`,
projectId: secret.project,
secret: secret.name,
versions: [
{
path: '/value2',
path: 'value2',
version: secret.version,
},
],
Expand Down
Loading

0 comments on commit 65fc04e

Please sign in to comment.