second first commit
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
/**
|
||||
* ACM integration tests.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||
import {
|
||||
ACMClient,
|
||||
RequestCertificateCommand,
|
||||
DescribeCertificateCommand,
|
||||
GetCertificateCommand,
|
||||
ListCertificatesCommand,
|
||||
DeleteCertificateCommand,
|
||||
ImportCertificateCommand,
|
||||
ExportCertificateCommand,
|
||||
AddTagsToCertificateCommand,
|
||||
ListTagsForCertificateCommand,
|
||||
RemoveTagsFromCertificateCommand,
|
||||
PutAccountConfigurationCommand,
|
||||
GetAccountConfigurationCommand,
|
||||
} from '@aws-sdk/client-acm';
|
||||
import selfsigned from 'selfsigned';
|
||||
import { makeClient, uniqueName, ACCOUNT, REGION } from './setup';
|
||||
|
||||
async function generateSelfSignedCert(): Promise<{ cert: Buffer; key: Buffer }> {
|
||||
const attrs = [{ name: 'commonName', value: 'test.example.com' }];
|
||||
const pems = await selfsigned.generate(attrs);
|
||||
return { cert: Buffer.from(pems.cert), key: Buffer.from(pems.private) };
|
||||
}
|
||||
|
||||
describe('ACM Certificate Lifecycle', () => {
|
||||
let acm: ACMClient;
|
||||
let certificateArn: string;
|
||||
|
||||
beforeAll(() => {
|
||||
acm = makeClient(ACMClient);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
try {
|
||||
if (certificateArn) {
|
||||
await acm.send(new DeleteCertificateCommand({ CertificateArn: certificateArn }));
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
|
||||
it('should request a certificate', async () => {
|
||||
const domain = `${uniqueName('cert')}.example.com`;
|
||||
const response = await acm.send(
|
||||
new RequestCertificateCommand({ DomainName: domain })
|
||||
);
|
||||
certificateArn = response.CertificateArn!;
|
||||
expect(certificateArn).toMatch(
|
||||
new RegExp(`^arn:aws:acm:${REGION}:${ACCOUNT}:certificate/.+`)
|
||||
);
|
||||
});
|
||||
|
||||
it('should describe a certificate', async () => {
|
||||
const response = await acm.send(
|
||||
new DescribeCertificateCommand({ CertificateArn: certificateArn })
|
||||
);
|
||||
expect(response.Certificate?.DomainName).toBeTruthy();
|
||||
expect(response.Certificate?.Status).toBe('ISSUED');
|
||||
});
|
||||
|
||||
it('should get certificate', async () => {
|
||||
const response = await acm.send(
|
||||
new GetCertificateCommand({ CertificateArn: certificateArn })
|
||||
);
|
||||
expect(response.Certificate).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should list certificates', async () => {
|
||||
const response = await acm.send(new ListCertificatesCommand({}));
|
||||
expect(
|
||||
response.CertificateSummaryList?.some((c) => c.CertificateArn === certificateArn)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should delete a certificate', async () => {
|
||||
await acm.send(new DeleteCertificateCommand({ CertificateArn: certificateArn }));
|
||||
|
||||
await expect(
|
||||
acm.send(new DescribeCertificateCommand({ CertificateArn: certificateArn }))
|
||||
).rejects.toThrow();
|
||||
|
||||
// Already deleted; prevent afterAll from trying again
|
||||
certificateArn = '';
|
||||
});
|
||||
});
|
||||
|
||||
describe('ACM Import/Export', () => {
|
||||
let acm: ACMClient;
|
||||
const arnsToClean: string[] = [];
|
||||
|
||||
beforeAll(() => {
|
||||
acm = makeClient(ACMClient);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
for (const arn of arnsToClean) {
|
||||
try {
|
||||
await acm.send(new DeleteCertificateCommand({ CertificateArn: arn }));
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should import a certificate', async () => {
|
||||
const { cert, key } = await generateSelfSignedCert();
|
||||
const response = await acm.send(
|
||||
new ImportCertificateCommand({
|
||||
Certificate: cert,
|
||||
PrivateKey: key,
|
||||
})
|
||||
);
|
||||
const arn = response.CertificateArn!;
|
||||
arnsToClean.push(arn);
|
||||
expect(arn).toMatch(
|
||||
new RegExp(`^arn:aws:acm:${REGION}:${ACCOUNT}:certificate/.+`)
|
||||
);
|
||||
});
|
||||
|
||||
it('should get imported certificate', async () => {
|
||||
const { cert, key } = await generateSelfSignedCert();
|
||||
const importResp = await acm.send(
|
||||
new ImportCertificateCommand({
|
||||
Certificate: cert,
|
||||
PrivateKey: key,
|
||||
})
|
||||
);
|
||||
const arn = importResp.CertificateArn!;
|
||||
arnsToClean.push(arn);
|
||||
|
||||
const getResp = await acm.send(
|
||||
new GetCertificateCommand({ CertificateArn: arn })
|
||||
);
|
||||
expect(getResp.Certificate).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should export imported certificate', async () => {
|
||||
const { cert, key } = await generateSelfSignedCert();
|
||||
const importResp = await acm.send(
|
||||
new ImportCertificateCommand({
|
||||
Certificate: cert,
|
||||
PrivateKey: key,
|
||||
})
|
||||
);
|
||||
const arn = importResp.CertificateArn!;
|
||||
arnsToClean.push(arn);
|
||||
|
||||
const passphrase = Buffer.from('test-passphrase');
|
||||
const exportResp = await acm.send(
|
||||
new ExportCertificateCommand({
|
||||
CertificateArn: arn,
|
||||
Passphrase: passphrase,
|
||||
})
|
||||
);
|
||||
expect(exportResp.Certificate).toBeTruthy();
|
||||
expect(exportResp.PrivateKey).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fail to export requested certificate', async () => {
|
||||
const domain = `${uniqueName('exp')}.example.com`;
|
||||
const reqResp = await acm.send(
|
||||
new RequestCertificateCommand({ DomainName: domain })
|
||||
);
|
||||
const arn = reqResp.CertificateArn!;
|
||||
arnsToClean.push(arn);
|
||||
|
||||
await expect(
|
||||
acm.send(
|
||||
new ExportCertificateCommand({
|
||||
CertificateArn: arn,
|
||||
Passphrase: Buffer.from('test-passphrase'),
|
||||
})
|
||||
)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ACM Tagging', () => {
|
||||
let acm: ACMClient;
|
||||
let certificateArn: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
acm = makeClient(ACMClient);
|
||||
const domain = `${uniqueName('tag')}.example.com`;
|
||||
const response = await acm.send(
|
||||
new RequestCertificateCommand({ DomainName: domain })
|
||||
);
|
||||
certificateArn = response.CertificateArn!;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
try {
|
||||
if (certificateArn) {
|
||||
await acm.send(new DeleteCertificateCommand({ CertificateArn: certificateArn }));
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
|
||||
it('should add and list tags', async () => {
|
||||
await acm.send(
|
||||
new AddTagsToCertificateCommand({
|
||||
CertificateArn: certificateArn,
|
||||
Tags: [
|
||||
{ Key: 'Environment', Value: 'test' },
|
||||
{ Key: 'Project', Value: 'floci' },
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
const response = await acm.send(
|
||||
new ListTagsForCertificateCommand({ CertificateArn: certificateArn })
|
||||
);
|
||||
const tags = response.Tags ?? [];
|
||||
expect(tags.some((t) => t.Key === 'Environment' && t.Value === 'test')).toBe(true);
|
||||
expect(tags.some((t) => t.Key === 'Project' && t.Value === 'floci')).toBe(true);
|
||||
});
|
||||
|
||||
it('should remove tags', async () => {
|
||||
// Ensure tags exist
|
||||
await acm.send(
|
||||
new AddTagsToCertificateCommand({
|
||||
CertificateArn: certificateArn,
|
||||
Tags: [{ Key: 'ToRemove', Value: 'yes' }],
|
||||
})
|
||||
);
|
||||
|
||||
await acm.send(
|
||||
new RemoveTagsFromCertificateCommand({
|
||||
CertificateArn: certificateArn,
|
||||
Tags: [{ Key: 'ToRemove', Value: 'yes' }],
|
||||
})
|
||||
);
|
||||
|
||||
const response = await acm.send(
|
||||
new ListTagsForCertificateCommand({ CertificateArn: certificateArn })
|
||||
);
|
||||
const tags = response.Tags ?? [];
|
||||
expect(tags.some((t) => t.Key === 'ToRemove')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ACM Account Configuration', () => {
|
||||
let acm: ACMClient;
|
||||
|
||||
beforeAll(() => {
|
||||
acm = makeClient(ACMClient);
|
||||
});
|
||||
|
||||
it('should put and get account configuration', async () => {
|
||||
await acm.send(
|
||||
new PutAccountConfigurationCommand({
|
||||
ExpiryEvents: { DaysBeforeExpiry: 45 },
|
||||
IdempotencyToken: uniqueName('idem'),
|
||||
})
|
||||
);
|
||||
|
||||
const response = await acm.send(new GetAccountConfigurationCommand({}));
|
||||
expect(response.ExpiryEvents?.DaysBeforeExpiry).toBe(45);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ACM Error Handling', () => {
|
||||
let acm: ACMClient;
|
||||
const arnsToClean: string[] = [];
|
||||
|
||||
beforeAll(() => {
|
||||
acm = makeClient(ACMClient);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
for (const arn of arnsToClean) {
|
||||
try {
|
||||
await acm.send(new DeleteCertificateCommand({ CertificateArn: arn }));
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw for non-existent certificate', async () => {
|
||||
const fakeArn = `arn:aws:acm:${REGION}:${ACCOUNT}:certificate/00000000-0000-0000-0000-000000000000`;
|
||||
await expect(
|
||||
acm.send(new DescribeCertificateCommand({ CertificateArn: fakeArn }))
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('should handle request with SANs', async () => {
|
||||
const domain = `${uniqueName('san')}.example.com`;
|
||||
const san = `alt.${domain}`;
|
||||
const response = await acm.send(
|
||||
new RequestCertificateCommand({
|
||||
DomainName: domain,
|
||||
SubjectAlternativeNames: [domain, san],
|
||||
})
|
||||
);
|
||||
const arn = response.CertificateArn!;
|
||||
arnsToClean.push(arn);
|
||||
expect(arn).toBeTruthy();
|
||||
|
||||
const descResp = await acm.send(
|
||||
new DescribeCertificateCommand({ CertificateArn: arn })
|
||||
);
|
||||
const sans = descResp.Certificate?.SubjectAlternativeNames ?? [];
|
||||
expect(sans).toContain(san);
|
||||
});
|
||||
|
||||
it('should fail to import invalid PEM', async () => {
|
||||
await expect(
|
||||
acm.send(
|
||||
new ImportCertificateCommand({
|
||||
Certificate: Buffer.from('not-a-valid-pem'),
|
||||
PrivateKey: Buffer.from('also-not-valid'),
|
||||
})
|
||||
)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user