Skip to main content
Glama
index.test.ts13.8 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import type { MedplumSourceInfraConfig } from '@medplum/core'; import { App } from 'aws-cdk-lib'; import { unlink, writeFile } from 'fs/promises'; import { resolve } from 'path'; import { normalizeInfraConfig } from './config'; import { main, MedplumStack } from './index'; async function writeConfig(filename: string, config: any): Promise<string> { const resolvedPath = resolve(filename); await writeFile(resolvedPath, JSON.stringify(config, null, 2), { encoding: 'utf-8' }); return resolvedPath; } describe('Infra', () => { const baseConfig = { name: 'unittest', accountNumber: '647991932601', region: 'us-east-1', domainName: 'medplum.com', apiPort: 8103, apiDomainName: 'api.medplum.com', apiSslCertArn: 'arn:aws:acm:us-east-1:647991932601:certificate/08bf1daf-3a2b-4cbe-91a0-739b4364a1ec', appDomainName: 'app.medplum.com', appSslCertArn: 'arn:aws:acm:us-east-1:647991932601:certificate/fd21b628-b2c0-4a5d-b4f5-b5c9a6d63b1a', storageBucketName: 'medplum-storage', storageDomainName: 'storage.medplum.com', storageSslCertArn: 'arn:aws:acm:us-east-1:647991932601:certificate/19d85245-0a1d-4bf5-9789-23082b1a15fc', storagePublicKey: '-----BEGIN PUBLIC KEY-----\n-----END PUBLIC KEY-----', maxAzs: 2, rdsInstances: 1, desiredServerCount: 1, serverImage: 'medplum/medplum-server:staging', serverMemory: 512, serverCpu: 256, loadBalancerLoggingEnabled: true, loadBalancerLoggingBucket: 'medplum-logs-us-east-1', loadBalancerLoggingPrefix: 'elb', clamscanEnabled: true, clamscanLoggingBucket: 'medplum-logs-us-east-1', clamscanLoggingPrefix: 'clamscan', }; beforeEach(() => { console.log = jest.fn(); }); test('Missing config', async () => { expect(() => main()).not.toThrow(); }); test('Synth stack', async () => { const filename = await writeConfig('./medplum.test.config.json', { ...baseConfig, stackName: 'MedplumUnitTestStack', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Multi region stack', async () => { const filename = await writeConfig('./medplum.multiregion.config.json', { ...baseConfig, name: 'multiregion', stackName: 'MedplumMultiRegionStack', region: 'ap-southeast-1', domainName: 'ap-southeast-1.medplum.com', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('ECR image', async () => { const filename = await writeConfig('./medplum.customvpc.config.json', { ...baseConfig, name: 'customvpc', stackName: 'MedplumCustomVpcStack', serverImage: '647991932601.dkr.ecr.us-east-1.amazonaws.com/medplum-server:staging', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Custom VPC', async () => { // Create a temp config file const filename = await writeConfig('./medplum.customvpc.config.json', { ...baseConfig, name: 'customvpc', stackName: 'MedplumCustomVpcStack', vpcId: 'vpc-0fc3a4d0600000000', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Custom RDS instance type', async () => { const filename = await writeConfig('./medplum.customRdsInstanceType.config.json', { ...baseConfig, name: 'customRdsInstanceType', stackName: 'MedplumCustomRdsInstanceTypeStack', rdsInstanceType: 't3.micro', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Custom RDS secrets', async () => { const filename = await writeConfig('./medplum.customRdsSecrets.config.json', { ...baseConfig, name: 'customRdsSecrets', stackName: 'MedplumCustomRdsSecretsStack', rdsSecretsArn: 'arn:aws:secretsmanager:s-east-1:647991932601:secret:SecretName-6RandomCharacters', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Skip DNS', async () => { const filename = await writeConfig('./medplum.skipDns.config.json', { ...baseConfig, name: 'skipDns', stackName: 'MedplumSkipDnsStack', skipDns: true, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Add DataDog container', async () => { const filename = await writeConfig('./medplum.datadog.config.json', { ...baseConfig, name: 'datadog', stackName: 'MedplumDataDogStack', additionalContainers: [ { name: 'datadog-agent', image: 'datadog/agent:latest', environment: { DD_SITE: 'datadoghq.com', DD_API_KEY: 'YOUR_DATADOG_API_KEY', }, }, ], }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('API in private subnet', async () => { const filename = await writeConfig('./medplum.test.config.json', { ...baseConfig, stackName: 'MedplumUnitTestStack', apiInternetFacing: false, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Disable app-api proxy', async () => { const filename = await writeConfig('./medplum.test.config.json', { ...baseConfig, stackName: 'MedplumUnitTestStack', appApiProxy: false, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Custom cacheNodeType', async () => { const filename = await writeConfig('./medplum.cacheNodeType.config.json', { ...baseConfig, stackName: 'MedplumCacheNodeTypeStack', cacheNodeType: 'cache.m4.2xlarge', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('RDS reader instance', async () => { const filename = await writeConfig('./medplum.reader.config.json', { ...baseConfig, stackName: 'MedplumReaderStack', rdsInstances: 2, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('RDS proxy', async () => { const filename = await writeConfig('./medplum.rdsproxy.config.json', { ...baseConfig, stackName: 'MedplumRdsProxyStack', rdsProxyEnabled: true, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Existing signing key', async () => { const filename = await writeConfig('./medplum.signingKey.config.json', { ...baseConfig, name: 'signingKey', stackName: 'MedplumSigningKeyStack', signingKeyId: 'K1234', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('CloudTrail alarms', async () => { const filename = await writeConfig('./medplum.cloudtrail.config.json', { ...baseConfig, name: 'cloudtrail', stackName: 'MedplumCloudTrailStack', cloudTrailAlarms: { logGroupName: 'cloudtrail-logs', logGroupCreate: true, snsTopicName: 'cloudtrail-alarms', }, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Override hosted zone name', async () => { const filename = await writeConfig('./medplum.hostedzone.config.json', { ...baseConfig, name: 'cloudtrail', stackName: 'MedplumHostedZoneStack', domainName: 'foo.medplum.com', hostedDomainName: 'foo.medplum.com', apiDomainName: 'api.foo.medplum.com', appDomainName: 'app.foo.medplum.com', storageDomainName: 'storage.foo.medplum.com', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Autoscaling', async () => { const filename = await writeConfig('./medplum.autoscaling.config.json', { ...baseConfig, name: 'autoscaling', stackName: 'MedplumAutoscalingTestStack', fargateAutoScaling: { minCapacity: 1, maxCapacity: 10, targetUtilizationPercent: 50, scaleInCooldown: 60, scaleOutCooldown: 60, }, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Custom security groups', async () => { const filename = await writeConfig('./medplum.custom-security-groups.config.json', { ...baseConfig, name: 'custom-security-groups', stackName: 'MedplumCustomSecurityGroupsStack', cacheSecurityGroupId: 'sg-0fc3', loadBalancerSecurityGroupId: 'sg-0fc4', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('IP Set rules', async () => { const filename = await writeConfig('./medplum.ipset.config.json', { ...baseConfig, name: 'ipset', stackName: 'MedplumIpSetStack', apiWafIpSetArn: 'arn:aws:wafv2:us-east-1:647991932601:ipset/MedplumIpSet', appWafIpSetArn: 'arn:aws:wafv2:us-east-1:647991932601:ipset/MedplumIpSet', storageWafIpSetArn: 'arn:aws:wafv2:us-east-1:647991932601:ipset/MedplumIpSet', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('rdsPersistentParameterGroups', async () => { const filename = await writeConfig('./medplum.pgpersistentparams.config.json', { ...baseConfig, stackName: 'MedplumPGStep1Stack', rdsPersistentParameterGroups: true, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('rdsIdsMajorVersionSuffix without rdsPersistentParameterGroups fails', async () => { const config: MedplumSourceInfraConfig = { ...baseConfig, stackName: 'MedplumPGStep1Stack', rdsIdsMajorVersionSuffix: true, vpcId: '', apiWafIpSetArn: '', appWafIpSetArn: '', signingKeyId: '', storageWafIpSetArn: '', baseUrl: '', rdsInstanceType: '', }; const app = new App(); const normalizedConifg = await normalizeInfraConfig(config); expect(() => new MedplumStack(app, normalizedConifg)).toThrow( 'rdsPersistentParameterGroups must be true when rdsIdsMajorVersionSuffix is true' ); }); test('rdsPersistentParameterGroups and rdsIdsMajorVersionSuffix', async () => { const filename = await writeConfig('./medplum.PersistentAndIds.config.json', { ...baseConfig, stackName: 'MedplumPGStep1Stack', rdsPersistentParameterGroups: true, rdsIdsMajorVersionSuffix: true, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Create WAF logging group', async () => { const filename = await writeConfig('./medplum.createWafLogGroup.config.json', { ...baseConfig, name: 'createWafLogGroup', stackName: 'MedplumCreateWafLogGroupStack', wafLogGroupName: 'waf-logs', wafLogGroupCreate: true, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Use existing WAF logging group', async () => { const filename = await writeConfig('./medplum.existingWafLogGroup.config.json', { ...baseConfig, name: 'existingWafLogGroup', stackName: 'MedplumExistingWafLogGroupStack', wafLogGroupName: 'waf-logs', wafLogGroupCreate: true, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Use FireLens and Datadog', async () => { const filename = await writeConfig('./medplum.fireLens.config.json', { ...baseConfig, name: 'fireLens', stackName: 'MedplumFireLensGroupStack', fireLens: { enabled: true, logDriverConfig: { Name: 'datadog', Host: 'http-intake.logs.datadoghq.com', TLS: 'on', compress: 'gzip', apikey: 'YOUR_DATADOG_API_KEY', dd_service: 'my-fargate-app', dd_source: 'nginx', dd_tags: 'environment:dev,project:firelens-example', provider: 'ecs', }, logRouterConfig: { type: 'fluentbit', options: { enableECSLogMetadata: true, }, }, }, }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Use containerInsightsV2', async () => { const filename = await writeConfig('./medplum.containerInsightsV2.config.json', { ...baseConfig, name: 'containerInsightsV2', stackName: 'MedplumContainerInsightsV2Stack', containerInsightsV2: 'enhanced', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); test('Use containerRegistryCredentialsSecretArn', async () => { const filename = await writeConfig('./medplum.containerRegistryCredentialsSecretArn.config.json', { ...baseConfig, name: 'containerRegistryCredentialsSecretArn', stackName: 'MedplumContainerRegistryCredentialsSecretArnStack', containerRegistryCredentialsSecretArn: 'arn:aws:secretsmanager:us-east-1:123456789:secret:TestCredentials-sHDBJc', }); await expect(main({ config: filename })).resolves.not.toThrow(); await unlink(filename); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server