Skip to content
Open
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
1,371 changes: 1,130 additions & 241 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"build": "npm run clean && tsc -b src",
"format": "prettier -w .",
"release": "release-it --only-version",
"test": "TS_NODE_PROJECT=tests node --test --test-concurrency=none -r ts-node/register tests/**[!build]/index.test.ts",
"test": "TS_NODE_PROJECT=tests node --test --test-concurrency=none -r ts-node/register tests/cloudfront/index.test.ts",
"test:build": "npm run build && tstyche build",
"prepare": "husky"
},
Expand All @@ -46,6 +46,7 @@
"devDependencies": {
"@aws-sdk/client-acm": "^3.782.0",
"@aws-sdk/client-application-auto-scaling": "^3.758.0",
"@aws-sdk/client-cloudfront": "^3.948.0",
"@aws-sdk/client-cloudwatch-logs": "^3.767.0",
"@aws-sdk/client-ec2": "^3.767.0",
"@aws-sdk/client-ecs": "^3.766.0",
Expand All @@ -62,6 +63,7 @@
"husky": "^9.1.7",
"ioredis": "^5.8.1",
"lint-staged": "^16.1.2",
"mime": "^4.1.0",
"nanospinner": "^1.2.2",
"pathe": "^2.0.3",
"prettier": "^3.4.2",
Expand Down
3 changes: 2 additions & 1 deletion tests/automation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { createSpinner } from 'nanospinner';
export async function deploy(args: InlineProgramArgs): Promise<OutputMap> {
const spinner = createSpinner('Deploying stack...').start();
const stack = await LocalWorkspace.createOrSelectStack(args);
await stack.setConfig('aws:region', { value: 'us-east-2' });
// TODO: accept value from AWS_REGION env once certificate is updated to receive region
await stack.setConfig('aws:region', { value: 'us-east-1' });
const up = await stack.up({ logToStdErr: true });
spinner.success({ text: 'Stack deployed' });

Expand Down
86 changes: 86 additions & 0 deletions tests/cloudfront/certificate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { it } from 'node:test';
import * as assert from 'node:assert';
import { ListResourceRecordSetsCommand } from '@aws-sdk/client-route-53';
import { request } from 'undici';
import status from 'http-status';
import { CloudFrontTestContext } from './test-context';
import { backOff } from '../util';

export function testCloudFrontWithCertificate(ctx: CloudFrontTestContext) {
it('should not create certificate when certificate is provided', () => {
const cf = ctx.outputs!.cfWithCertificate;

assert.ok(
cf.acmCertificate === undefined,
'Certificate should not be created',
);
});

it('should create alias record(s) when certificate is provided', async () => {
const cf = ctx.outputs!.cfWithCertificate;
// Ensure FQDN, i.e., end with dot if not
const domainName = ctx.config.certificateDomain.replace(/([^.])$/, '$1.');

const command = new ListResourceRecordSetsCommand({
HostedZoneId: ctx.config.hostedZoneId,
MaxItems: 1000,
});
const response = await ctx.clients.route53.send(command);
const record = response.ResourceRecordSets?.find(
record => record.Name === domainName && record.Type === 'A',
);

assert.ok(record, 'Alias record should exists');
assert.strictEqual(
record.AliasTarget?.DNSName,
`${cf.distribution.domainName}.`,
'Alias record should target CF distribution',
);
});

it('should configure viewer certificate when certificate is provided', () => {
const cf = ctx.outputs!.cfWithCertificate;
const certificate = ctx.outputs!.certificate;
const { viewerCertificate } = cf.distribution;

assert.strictEqual(
viewerCertificate.acmCertificateArn,
certificate.certificate.arn,
'Viewer certificate should match provided certificate',
);
assert.strictEqual(
viewerCertificate.sslSupportMethod,
'sni-only',
'Viewer certificate should have correct SSL supported method',
);
assert.strictEqual(
viewerCertificate.minimumProtocolVersion,
'TLSv1.2_2021',
'Viewer certificate should have correct minimum protocol version',
);
});

it('should configure aliases when certificate is provided', () => {
const cf = ctx.outputs!.cfWithCertificate;

assert.deepStrictEqual(
cf.distribution.aliases,
[ctx.config.certificateDomain],
'Aliases should be correctly configured',
);
});

it('should have reachable distribution when certificate is provided', async () => {
const url = `https://${ctx.config.certificateDomain}`;

await backOff(async () => {
const response = await request(url);

assert.strictEqual(
response.statusCode,
status.OK,
`Distribution should respond with status code 200, got ${response.statusCode}`,
);
});
});
}
83 changes: 83 additions & 0 deletions tests/cloudfront/domain.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { it } from 'node:test';
import * as assert from 'node:assert';
import { ListResourceRecordSetsCommand } from '@aws-sdk/client-route-53';
import { request } from 'undici';
import status from 'http-status';
import { CloudFrontTestContext } from './test-context';
import { backOff } from '../util';

export function testCloudFrontWithDomain(ctx: CloudFrontTestContext) {
it('should create certificate when domain is provided', () => {
const cf = ctx.outputs!.cfWithDomain;

assert.ok(cf.acmCertificate, 'Certificate should be created');
});

it('should create alias record when domain is provided', async () => {
const cf = ctx.outputs!.cfWithDomain;
// Ensure FQDN, i.e., end with dot if not
const domainName = ctx.config.domainName.replace(/([^.])$/, '$1.');

const command = new ListResourceRecordSetsCommand({
HostedZoneId: ctx.config.hostedZoneId,
MaxItems: 1000,
});
const response = await ctx.clients.route53.send(command);

const record = response.ResourceRecordSets?.find(
record => record.Name === domainName && record.Type === 'A',
);

assert.ok(record, 'Alias record should exists');
assert.strictEqual(
record.AliasTarget?.DNSName,
`${cf.distribution.domainName}.`,
'Alias record should target CF distribution',
);
});

it('should configure viewer certificate when domain is provided', () => {
const cf = ctx.outputs!.cfWithDomain;
const { viewerCertificate } = cf.distribution;

assert.strictEqual(
viewerCertificate.acmCertificateArn,
cf.acmCertificate?.certificate.arn,
'Viewer certificate should match created certificate',
);
assert.strictEqual(
viewerCertificate.sslSupportMethod,
'sni-only',
'Viewer certificate should have correct SSL supported method',
);
assert.strictEqual(
viewerCertificate.minimumProtocolVersion,
'TLSv1.2_2021',
'Viewer certificate should have correct minimum protocol version',
);
});

it('should configure aliases when domain is provided', () => {
const cf = ctx.outputs!.cfWithDomain;

assert.deepStrictEqual(
cf.distribution.aliases,
[ctx.config.domainName],
'Aliases should be correctly configured',
);
});

it('should have reachable distribution when domain is provided', async () => {
const url = `https://${ctx.config.domainName}`;

await backOff(async () => {
const response = await request(url);

assert.strictEqual(
response.statusCode,
status.OK,
`Distribution should respond with status code 200, got ${response.statusCode}`,
);
});
});
}
Loading