Skip to content

Commit

Permalink
test: add end-to-end tests
Browse files Browse the repository at this point in the history
  • Loading branch information
coderbyheart committed Apr 5, 2024
1 parent 836b786 commit fa12cff
Show file tree
Hide file tree
Showing 13 changed files with 1,525 additions and 14 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/test-and-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,63 @@ env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

permissions:
contents: write
issues: write
id-token: write

jobs:
tests:
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: "20.x"
cache: "npm"

- name: Authenticate with NPM registry
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc

- name: Install dependencies
run: npm ci --no-audit

- name: Compile
run: npx tsc

- name: Check source code with eslint
run: npx eslint .

- name: Check if source code is properly formatted
run: npx prettier -c ./

- name: Test
run: npm test

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
# The role is set up via https://github.com/bifravst/ci
# secrets.AWS_ACCOUNT_ID_CI is an organization secret
role-to-assume: |
arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID_CI }}:role/${{ github.repository_owner }}-ci-${{ github.event.repository.name }}
# vars.AWS_REGION_CI is an organization variable
aws-region: ${{ vars.AWS_REGION_CI }}

- name: Generate Stack ID
run: |
RANDOM_STRING=`node -e "const crypto = require('crypto'); process.stdout.write(crypto.randomBytes(Math.ceil(8 * 0.5)).toString('hex').slice(0, 8));"`
echo "STACK_NAME=lh-${RANDOM_STRING}" >> $GITHUB_ENV
- run: npx cdk deploy

- name: Run end-to-end tests
run: npx tsx --test e2e.spec.ts

- run: npx cdk destroy -f

- name: Semantic release
continue-on-error: true
run: npx semantic-release
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules/
npm-debug.log
dist/
/node_modules/
/npm-debug.log
/dist/
/cdk.out/
3 changes: 3 additions & 0 deletions cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "npx tsx --no-warnings cdk/e2e.ts"
}
1 change: 1 addition & 0 deletions cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Provides the infrastructure to run the end-to-end tests.
17 changes: 17 additions & 0 deletions cdk/TestApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { App } from 'aws-cdk-lib'
import { TestStack } from './TestStack.js'

export class TestApp extends App {
public constructor(
id: string,
args: ConstructorParameters<typeof TestStack>[2],
) {
super({
context: {
isTest: true,
},
})

new TestStack(this, id, args)
}
}
66 changes: 66 additions & 0 deletions cdk/TestStack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
App,
CfnOutput,
Duration,
aws_lambda as Lambda,
Stack,
} from 'aws-cdk-lib'
import { LambdaLogGroup, LambdaSource } from '../src/cdk.js'
import type { PackedLayer } from '../src/layer.js'
import type { TestLambdas } from './packTestLambdas.js'

export class TestStack extends Stack {
public constructor(
parent: App,
id: string,
{
lambdaSources,
layer,
}: {
lambdaSources: TestLambdas
layer: PackedLayer
},
) {
super(parent, id, {})

const baseLayer = new Lambda.LayerVersion(this, 'baseLayer', {
layerVersionName: `${Stack.of(this).stackName}-baseLayer`,
code: new LambdaSource(this, {
id: 'baseLayer',
zipFile: layer.layerZipFile,
hash: layer.hash,
}).code,
compatibleArchitectures: [Lambda.Architecture.ARM_64],
compatibleRuntimes: [Lambda.Runtime.NODEJS_20_X],
})

const fn = new Lambda.Function(this, 'fn', {
handler: lambdaSources.test.handler,
architecture: Lambda.Architecture.ARM_64,
runtime: Lambda.Runtime.NODEJS_20_X,
timeout: Duration.seconds(1),
memorySize: 1792,
code: new LambdaSource(this, lambdaSources.test).code,
description: 'Returns a ULID',
environment: {
NODE_NO_WARNINGS: '1',
},
layers: [baseLayer],
...new LambdaLogGroup(this, 'fnLogs'),
})

const url = fn.addFunctionUrl({
authType: Lambda.FunctionUrlAuthType.NONE,
})

new CfnOutput(this, 'lambdaURL', {
exportName: `${this.stackName}:lambdaURL`,
description: 'API endpoint',
value: url.url,
})
}
}

export type StackOutputs = {
lambdaURL: string
}
7 changes: 7 additions & 0 deletions cdk/baseLayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { packLayer, type PackedLayer } from '../src/layer.js'

export const pack = async (): Promise<PackedLayer> =>
packLayer({
id: 'baseLayer',
dependencies: ['id128'],
})
12 changes: 12 additions & 0 deletions cdk/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { TestApp } from './TestApp.js'
import { pack as packBaseLayer } from './baseLayer.js'
import { packTestLambdas } from './packTestLambdas.js'
import { fromEnv } from '@nordicsemiconductor/from-env'
const { stackName } = fromEnv({
stackName: 'STACK_NAME',
})(process.env)

new TestApp(stackName, {
lambdaSources: await packTestLambdas(),
layer: await packBaseLayer(),
})
7 changes: 7 additions & 0 deletions cdk/lambda.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { APIGatewayProxyResultV2 } from 'aws-lambda'
import id128 from 'id128'

export const handler = async (): Promise<APIGatewayProxyResultV2> => ({

Check warning on line 4 in cdk/lambda.ts

View workflow job for this annotation

GitHub Actions / tests

Async arrow function 'handler' has no 'await' expression
statusCode: 201,
body: id128.Ulid.generate().toCanonical(),
})
10 changes: 10 additions & 0 deletions cdk/packTestLambdas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { PackedLambda } from '../src/packLambda.js'
import { packLambdaFromPath } from '../src/packLambdaFromPath.js'

export type TestLambdas = {
test: PackedLambda
}

export const packTestLambdas = async (): Promise<TestLambdas> => ({
test: await packLambdaFromPath('test', 'cdk/lambda.ts'),
})
22 changes: 22 additions & 0 deletions e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { describe, it } from 'node:test'
import assert from 'node:assert/strict'
import { stackOutput } from '@nordicsemiconductor/cloudformation-helpers'
import { CloudFormationClient } from '@aws-sdk/client-cloudformation'
import type { StackOutputs } from './cdk/TestStack.js'
import { fromEnv } from '@nordicsemiconductor/from-env'

void describe('end-to-end tests', () => {
void it('should return an ULID', async () => {
const { stackName } = fromEnv({
stackName: 'STACK_NAME',
})(process.env)
const { lambdaURL } = await stackOutput(
new CloudFormationClient({}),
)<StackOutputs>(stackName)

const res = await fetch(new URL(lambdaURL))
assert.equal(res.ok, true)
assert.equal(res.status, 201)
assert.match(await res.text(), /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/)
})
})
Loading

0 comments on commit fa12cff

Please sign in to comment.