Skip to content
Merged
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
43 changes: 43 additions & 0 deletions .github/workflows/backend-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Backend Tests

on:
pull_request:
branches: [ main ]
paths:
- 'backend/**'

jobs:
test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./backend

steps:
- uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: './backend/package-lock.json'

- name: Install dependencies
run: npm ci

- name: Run linting
run: npm run lint

- name: Run tests
run: npm test

- name: Generate test summary
if: always()
run: |
echo "# Backend Test Results" >> $GITHUB_STEP_SUMMARY
if [ ${{ job.status }} == "success" ]; then
echo "✅ All tests passed successfully!" >> $GITHUB_STEP_SUMMARY
else
echo "❌ Some tests failed. Please check the logs for details." >> $GITHUB_STEP_SUMMARY
fi
41 changes: 41 additions & 0 deletions .github/workflows/frontend-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Frontend Tests

on:
pull_request:
branches: [ main ]
paths:
- 'frontend/**'

jobs:
test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend

steps:
- uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: './frontend/package-lock.json'

- name: Install dependencies
run: npm ci

- name: Run tests
env:
VITE_BASE_URL_API: https://jsonplaceholder.typicode.com
VITE_BUILD_DATE: 1970-01-01
VITE_BUILD_TIME: 00:00:00
VITE_BUILD_TS: 1970-01-01T00:00:00+0000
VITE_BUILD_COMMIT_SHA: test
VITE_BUILD_ENV_CODE: test
VITE_BUILD_WORKFLOW_RUNNER: test
VITE_BUILD_WORKFLOW_NAME: test
VITE_BUILD_WORKFLOW_RUN_NUMBER: 1
VITE_BUILD_WORKFLOW_RUN_ATTEMPT: 1
run: npm test
3 changes: 0 additions & 3 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

cd frontend && npm run lint
79 changes: 38 additions & 41 deletions backend/src/iac/backend-stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ describe('BackendStack', () => {
beforeEach(() => {
app = new cdk.App();
stackProps = {
env: { account: '123456789012', region: 'us-east-1' }
env: { account: '123456789012', region: 'us-east-1' },
};

// Create both staging and production stacks for testing
stagingStack = new BackendStack(app, 'StagingStack', {
...stackProps,
environment: 'staging'
environment: 'staging',
});
stagingTemplate = Template.fromStack(stagingStack);

productionStack = new BackendStack(app, 'ProductionStack', {
...stackProps,
environment: 'production'
environment: 'production',
});
productionTemplate = Template.fromStack(productionStack);
});
Expand All @@ -40,9 +40,9 @@ describe('BackendStack', () => {
Tags: Match.arrayWith([
{
Key: 'Name',
Value: Match.stringLikeRegexp('AIMedicalReportVPC')
}
])
Value: Match.stringLikeRegexp('AIMedicalReportVPC'),
},
]),
});

// Verify subnet count for staging (2 AZs = 4 subnets)
Expand All @@ -60,9 +60,9 @@ describe('BackendStack', () => {
ClusterSettings: [
{
Name: 'containerInsights',
Value: 'enabled'
}
]
Value: 'enabled',
},
],
});
});

Expand All @@ -78,25 +78,25 @@ describe('BackendStack', () => {
Environment: Match.arrayWith([
{ Name: 'NODE_ENV', Value: 'staging' },
{ Name: 'PERPLEXITY_MODEL', Value: 'sonar' },
{ Name: 'PERPLEXITY_MAX_TOKENS', Value: '2048' }
{ Name: 'PERPLEXITY_MAX_TOKENS', Value: '2048' },
]),
LogConfiguration: Match.objectLike({
LogDriver: 'awslogs'
LogDriver: 'awslogs',
}),
PortMappings: [
{
ContainerPort: 3000,
Protocol: 'tcp'
}
]
})
])
Protocol: 'tcp',
},
],
}),
]),
});

// Test production task definition has higher resources
productionTemplate.hasResourceProperties('AWS::ECS::TaskDefinition', {
Cpu: '512',
Memory: '1024'
Memory: '1024',
});
});

Expand All @@ -107,14 +107,14 @@ describe('BackendStack', () => {
DesiredCount: 1,
NetworkConfiguration: Match.objectLike({
AwsvpcConfiguration: {
AssignPublicIp: 'DISABLED'
}
})
AssignPublicIp: 'DISABLED',
},
}),
});

// Test production service has higher count
productionTemplate.hasResourceProperties('AWS::ECS::Service', {
DesiredCount: 2
DesiredCount: 2,
});
});

Expand All @@ -125,13 +125,13 @@ describe('BackendStack', () => {
// Production should have auto-scaling
productionTemplate.hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', {
MinCapacity: 2,
MaxCapacity: 10
MaxCapacity: 10,
});

productionTemplate.hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', {
TargetTrackingScalingPolicyConfiguration: {
TargetValue: 70
}
TargetValue: 70,
},
});
});
});
Expand All @@ -141,13 +141,13 @@ describe('BackendStack', () => {
// Staging log group with 1 week retention
stagingTemplate.hasResourceProperties('AWS::Logs::LogGroup', {
LogGroupName: '/ecs/AIMedicalReport-staging',
RetentionInDays: 7
RetentionInDays: 7,
});

// Production log group with 1 month retention
productionTemplate.hasResourceProperties('AWS::Logs::LogGroup', {
LogGroupName: '/ecs/AIMedicalReport-production',
RetentionInDays: 30
RetentionInDays: 30,
});
});
});
Expand All @@ -156,7 +156,7 @@ describe('BackendStack', () => {
it('should create a Cognito domain', () => {
stagingTemplate.hasResourceProperties('AWS::Cognito::UserPoolDomain', {
Domain: 'aimedicalreport-auth',
UserPoolId: Match.anyValue()
UserPoolId: Match.anyValue(),
});
});

Expand All @@ -165,12 +165,9 @@ describe('BackendStack', () => {
GenerateSecret: true,
AllowedOAuthFlows: ['code'],
CallbackURLs: Match.arrayWith([
Match.stringLikeRegexp('http://aimedicalreport.example.com/oauth2/idpresponse')
Match.stringLikeRegexp('http://aimedicalreport.example.com/oauth2/idpresponse'),
]),
ExplicitAuthFlows: Match.arrayWith([
'ALLOW_USER_PASSWORD_AUTH',
'ALLOW_USER_SRP_AUTH'
])
ExplicitAuthFlows: Match.arrayWith(['ALLOW_USER_PASSWORD_AUTH', 'ALLOW_USER_SRP_AUTH']),
});
});
});
Expand All @@ -182,10 +179,10 @@ describe('BackendStack', () => {
LoadBalancerAttributes: Match.arrayWith([
{
Key: 'deletion_protection.enabled',
Value: 'false'
}
Value: 'false',
},
]),
Type: 'application'
Type: 'application',
});
});

Expand All @@ -196,7 +193,7 @@ describe('BackendStack', () => {
TargetType: 'ip',
HealthCheckPath: '/health',
HealthCheckTimeoutSeconds: 5,
HealthCheckIntervalSeconds: 30
HealthCheckIntervalSeconds: 30,
});
});

Expand All @@ -211,16 +208,16 @@ describe('BackendStack', () => {
UserPoolArn: Match.anyValue(),
UserPoolClientId: Match.anyValue(),
UserPoolDomain: Match.anyValue(),
OnUnauthenticatedRequest: 'authenticate'
OnUnauthenticatedRequest: 'authenticate',
},
Order: 1
Order: 1,
}),
Match.objectLike({
Type: 'forward',
TargetGroupArn: Match.anyValue(),
Order: 2
})
])
Order: 2,
}),
]),
});
});
});
Expand Down
9 changes: 4 additions & 5 deletions backend/src/iac/backend-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import * as cognito from 'aws-cdk-lib/aws-cognito';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as elbv2_actions from 'aws-cdk-lib/aws-elasticloadbalancingv2-actions';
import * as constructs from 'constructs';
import { Construct } from 'constructs';

interface BackendStackProps extends cdk.StackProps {
environment: string;
}

export class BackendStack extends cdk.Stack {
constructor(scope: constructs.Construct, id: string, props: BackendStackProps) {
constructor(scope: Construct, id: string, props: BackendStackProps) {
super(scope, id, props);

const isProd = props.environment === 'production';
Expand Down Expand Up @@ -82,7 +81,7 @@ export class BackendStack extends cdk.Stack {
const userPool = cognito.UserPool.fromUserPoolId(
this,
`${appName}UserPool`,
'ai-cognito-medical-reports-user-pool'
'ai-cognito-medical-reports-user-pool',
);

// Create a Cognito domain if it doesn't exist
Expand Down Expand Up @@ -165,7 +164,7 @@ export class BackendStack extends cdk.Stack {
defaultAction: new elbv2_actions.AuthenticateCognitoAction({
userPool,
userPoolClient,
userPoolDomain: userPoolDomain.domainName,
userPoolDomain: userPoolDomain,
next: elbv2.ListenerAction.forward([targetGroup]),
onUnauthenticatedRequest: elbv2.UnauthenticatedAction.AUTHENTICATE,
}),
Expand Down
17 changes: 0 additions & 17 deletions backend/src/iac/simple-stack.test.ts

This file was deleted.

15 changes: 0 additions & 15 deletions backend/src/iac/simple-stack.ts

This file was deleted.

Loading