Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
50ac20e
adding migrations
piersolh Sep 14, 2025
749cdab
adding back example env
piersolh Sep 14, 2025
b144c4b
Merge pull request #1 from Code-4-Community/HP-adding-migrations
piersolh Sep 14, 2025
f3d586e
adding comments to explain type error
piersolh Sep 17, 2025
9dc4a94
Merge pull request #24 from Code-4-Community/HP-adding-migrations
piersolh Sep 17, 2025
7cbd8a8
put interceptor decorators in auth controller and module
chnnick Sep 24, 2025
e47cf24
added new application entity and new enums
ItsEricSun Sep 30, 2025
b14ae2a
setting up scaffolding for disciplines entity
piersolh Oct 1, 2025
4cef618
adding seeding and creating a disciplines enum
piersolh Oct 1, 2025
683505a
fixing typo
piersolh Oct 1, 2025
d0cc629
add initial aws module
rayyanmridha Oct 1, 2025
d990085
added admin controller, entity, module, and service
ostepan8 Oct 1, 2025
ab3e30c
small changes to ensure functionality
ostepan8 Oct 1, 2025
0d93119
fix s3 403 error
maxn990 Oct 2, 2025
2962fa2
make sure test doesn't run automatically
rayyanmridha Oct 2, 2025
25719d0
Merge pull request #35 from Code-4-Community/create-application-entity
piersolh Oct 5, 2025
a3ad5b4
Merge pull request #36 from Code-4-Community/HP-create-discipline-entity
piersolh Oct 5, 2025
47af7aa
add get and post Application routes and some clean up of migration fi…
ItsEricSun Oct 8, 2025
0cf8822
controller and service tests for cognito
chnnick Oct 8, 2025
9759078
add application with ID not found error
ItsEricSun Oct 8, 2025
d15bb85
add application not found to tests
ItsEricSun Oct 8, 2025
c64a30f
Merge branch 'main' into create-get-and-post-Application-routes
ItsEricSun Oct 8, 2025
bbdd87e
updated pr for comments
ostepan8 Oct 8, 2025
ce1d5a7
changed the update to only update the email
ostepan8 Oct 9, 2025
3ddea89
make migration dynamic
ostepan8 Oct 9, 2025
28a2dd9
GET and POST Learner Routes + Testing
chnnick Oct 14, 2025
6847ad4
got rid of currentUserInterceptor in controller tests, not sure if ne…
chnnick Oct 14, 2025
5ddac0d
create remove and edit application routes
ItsEricSun Oct 15, 2025
d8b2c29
add tests for new service methods
ItsEricSun Oct 15, 2025
20a224f
install ses
rayyanmridha Oct 27, 2025
48980ca
added patch routes for learner
yumi520 Oct 28, 2025
efa4830
added learner tests
yumi520 Oct 28, 2025
7843b52
Amazon SES tempalte
bodhiYG Oct 31, 2025
0ee55f0
clean up files, add controller, endpoint & tests
rayyanmridha Nov 2, 2025
849129b
remove unnecessary changes
rayyanmridha Nov 2, 2025
be901d4
update env template
rayyanmridha Nov 2, 2025
16c2a80
Merge pull request #31 from Code-4-Community/nc-19-cognito
piersolh Nov 13, 2025
59127a2
Merge pull request #44 from Code-4-Community/rm-15-awsS3Entity
piersolh Nov 13, 2025
7e64ff8
Added AWS bucket name and AWS region as example fields
SamNie2027 Dec 17, 2025
6d58fa1
fix s3 bugs, merge into main
piersolh Dec 17, 2025
6b11c95
Merge pull request #45 from Code-4-Community/admin-scaffolding
piersolh Dec 17, 2025
a82a17b
Merge branch 'main' into nc-19-cognitoTESTS
piersolh Dec 17, 2025
370e08a
Merge pull request #53 from Code-4-Community/nc-19-cognitoTESTS
piersolh Dec 17, 2025
15d81e6
Merge branch 'main' into create-get-and-post-Application-routes
piersolh Dec 17, 2025
ca08357
Merge pull request #54 from Code-4-Community/create-get-and-post-Appl…
piersolh Dec 17, 2025
a0d3c9d
Merge branch 'main' into nc-42-learner
piersolh Dec 17, 2025
87c6fb4
Merge pull request #55 from Code-4-Community/nc-42-learner
piersolh Dec 17, 2025
629eddb
Merge branch 'main' into create-remove-application-&-edit-application…
piersolh Dec 17, 2025
79f163b
Merge pull request #56 from Code-4-Community/create-remove-applicatio…
piersolh Dec 17, 2025
82e0f89
Merge branch 'main' into bg-rm-58-aws-ses
piersolh Dec 17, 2025
12aeda2
update packages
piersolh Dec 17, 2025
3a6f22c
Merge pull request #62 from Code-4-Community/bg-rm-58-aws-ses
piersolh Dec 17, 2025
300e8b1
Merge branch 'main' into yc/49/learner
piersolh Dec 17, 2025
8a633f7
Merge pull request #65 from Code-4-Community/yc/49/learner
piersolh Dec 17, 2025
91f7b8b
Merge branch 'main' into env-improvements
piersolh Dec 17, 2025
5d1a392
Merge pull request #89 from Code-4-Community/env-improvements
piersolh Dec 17, 2025
cc85b52
fix env
piersolh Dec 17, 2025
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 change: 0 additions & 1 deletion apps/backend/src/app.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';

import { AppController } from './app.controller';
import { AppService } from './app.service';

Expand Down
28 changes: 24 additions & 4 deletions apps/backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,32 @@ import { TypeOrmModule } from '@nestjs/typeorm';

import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TaskModule } from './task/task.module';
import { AWSS3Module } from './aws-s3/aws-s3.module';
import AppDataSource from './data-source';
import { UtilModule } from './util/util.module';
import { ApplicationsController } from './applications/applications.controller';
import { ApplicationsService } from './applications/applications.service';
import { Application } from './applications/application.entity';
import { AdminsModule } from './users/admins.module';
import { Admin } from './users/admin.entity';
import { ConfigModule } from '@nestjs/config';

@Module({
imports: [TypeOrmModule.forRoot(AppDataSource.options), TaskModule],
controllers: [AppController],
providers: [AppService],
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: '../../.env',
}),
TypeOrmModule.forRoot({
...AppDataSource.options,
entities: [Admin],
}),
UtilModule,
AdminsModule,
AWSS3Module,
TypeOrmModule.forFeature([Application]),
],
controllers: [AppController, ApplicationsController],
providers: [AppService, ApplicationsService],
})
export class AppModule {}
48 changes: 48 additions & 0 deletions apps/backend/src/applications/application.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

import { AppStatus, ExperienceType, InterestArea, School } from './types';

@Entity('application')
export class Application {
@PrimaryGeneratedColumn()
appId!: number;

@Column({ type: 'enum', enum: AppStatus, default: AppStatus.APP_SUBMITTED })
appStatus!: AppStatus;

@Column({ type: 'varchar' })
daysAvailable!: string;

@Column({ type: 'enum', enum: ExperienceType })
experienceType!: ExperienceType;

@Column('text', { array: true, default: [] })
fileUploads!: string[];

@Column({ type: 'enum', enum: InterestArea })
interest!: InterestArea;

@Column({ type: 'varchar' })
license!: string;

@Column({ type: 'boolean', default: false })
isInternational!: boolean;

@Column({ type: 'boolean', default: false })
isLearner!: boolean;

@Column({ type: 'varchar' })
phone!: string;

@Column({ type: 'enum', enum: School })
school!: School;

@Column({ type: 'boolean', default: false, nullable: true })
referred?: boolean;

@Column({ type: 'varchar', nullable: true })
referredEmail?: string;

@Column({ type: 'int' })
weeklyHours!: number;
}
290 changes: 290 additions & 0 deletions apps/backend/src/applications/application.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { NotFoundException } from '@nestjs/common';
import { ApplicationsService } from './applications.service';
import { Application } from './application.entity';
import { CreateApplicationDto } from './dto/create-application.request.dto';
import { AppStatus, ExperienceType, InterestArea, School } from './types';

describe('ApplicationsService', () => {
let service: ApplicationsService;
let repository: Repository<Application>;

const mockRepository = {
find: jest.fn(),
findOne: jest.fn(),
save: jest.fn(),
create: jest.fn(),
delete: jest.fn(),
remove: jest.fn(),
};

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ApplicationsService,
{
provide: getRepositoryToken(Application),
useValue: mockRepository,
},
],
}).compile();

service = module.get<ApplicationsService>(ApplicationsService);
repository = module.get<Repository<Application>>(
getRepositoryToken(Application),
);
});

afterEach(() => {
jest.clearAllMocks();
});

it('should be defined', () => {
expect(service).toBeDefined();
});

describe('findAll', () => {
it('should return an array of applications', async () => {
const mockApplications: Application[] = [
{
appId: 1,
appStatus: AppStatus.APP_SUBMITTED,
daysAvailable: 'Monday, Tuesday',
experienceType: ExperienceType.BS,
fileUploads: [],
interest: InterestArea.NURSING,
license: null,
isInternational: false,
isLearner: false,
phone: '123-456-7890',
school: School.HARVARD_MEDICAL_SCHOOL,
referred: false,
referredEmail: null,
weeklyHours: 20,
},
];

mockRepository.find.mockResolvedValue(mockApplications);

const result = await service.findAll();

expect(repository.find).toHaveBeenCalled();
expect(result).toEqual(mockApplications);
});
});

describe('findById', () => {
it('should return a single application', async () => {
const mockApplication: Application = {
appId: 1,
appStatus: AppStatus.APP_SUBMITTED,
daysAvailable: 'Monday, Tuesday',
experienceType: ExperienceType.BS,
fileUploads: [],
interest: InterestArea.NURSING,
license: null,
isInternational: false,
isLearner: false,
phone: '123-456-7890',
school: School.HARVARD_MEDICAL_SCHOOL,
referred: false,
referredEmail: null,
weeklyHours: 20,
};

mockRepository.findOne.mockResolvedValue(mockApplication);

const result = await service.findById(1);

expect(repository.findOne).toHaveBeenCalledWith({ where: { appId: 1 } });
expect(result).toEqual(mockApplication);
});

it('should throw NotFoundException when application is not found', async () => {
const nonExistentId = 999;

mockRepository.findOne.mockResolvedValue(null);

await expect(service.findById(nonExistentId)).rejects.toThrow(
new NotFoundException(`Application with ID ${nonExistentId} not found`),
);

expect(repository.findOne).toHaveBeenCalledWith({
where: { appId: nonExistentId },
});
});
});

describe('create', () => {
it('should create and save a new application', async () => {
const createApplicationDto: CreateApplicationDto = {
appStatus: AppStatus.APP_SUBMITTED,
daysAvailable: 'Monday, Tuesday',
experienceType: ExperienceType.BS,
fileUploads: [],
interest: InterestArea.NURSING,
license: null,
isInternational: false,
isLearner: false,
phone: '123-456-7890',
school: School.HARVARD_MEDICAL_SCHOOL,
referred: false,
referredEmail: null,
weeklyHours: 20,
};

const savedApplication: Application = {
appId: 1,
...createApplicationDto,
};

mockRepository.save.mockResolvedValue(savedApplication);

const result = await service.create(createApplicationDto);

expect(repository.save).toHaveBeenCalled();
expect(result).toEqual(savedApplication);
});
});

describe('update', () => {
it('should update application status', async () => {
const mockApplication: Application = {
appId: 1,
appStatus: AppStatus.APP_SUBMITTED,
daysAvailable: 'Monday, Tuesday',
experienceType: ExperienceType.BS,
fileUploads: [],
interest: InterestArea.NURSING,
license: null,
isInternational: false,
isLearner: false,
phone: '123-456-7890',
school: School.HARVARD_MEDICAL_SCHOOL,
referred: false,
referredEmail: null,
weeklyHours: 20,
};

const updatedApplication: Application = {
...mockApplication,
appStatus: AppStatus.IN_REVIEW,
};

mockRepository.findOne.mockResolvedValue(mockApplication);
mockRepository.save.mockResolvedValue(updatedApplication);

const result = await service.update(1, {
appStatus: AppStatus.IN_REVIEW,
});

expect(repository.findOne).toHaveBeenCalledWith({ where: { appId: 1 } });
expect(repository.save).toHaveBeenCalledWith({
...mockApplication,
appStatus: AppStatus.IN_REVIEW,
});
expect(result).toEqual(updatedApplication);
});

it('should update application interest', async () => {
const mockApplication: Application = {
appId: 1,
appStatus: AppStatus.APP_SUBMITTED,
daysAvailable: 'Monday, Tuesday',
experienceType: ExperienceType.BS,
fileUploads: [],
interest: InterestArea.NURSING,
license: null,
isInternational: false,
isLearner: false,
phone: '123-456-7890',
school: School.HARVARD_MEDICAL_SCHOOL,
referred: false,
referredEmail: null,
weeklyHours: 20,
};

const updatedApplication: Application = {
...mockApplication,
interest: InterestArea.HARM_REDUCTION,
};

mockRepository.findOne.mockResolvedValue(mockApplication);
mockRepository.save.mockResolvedValue(updatedApplication);

const result = await service.update(1, {
interest: InterestArea.HARM_REDUCTION,
});

expect(repository.findOne).toHaveBeenCalledWith({ where: { appId: 1 } });
expect(repository.save).toHaveBeenCalledWith({
...mockApplication,
interest: InterestArea.HARM_REDUCTION,
});
expect(result).toEqual(updatedApplication);
});

it('should throw NotFoundException when updating non-existent application', async () => {
const nonExistentId = 999;

mockRepository.findOne.mockResolvedValue(null);

await expect(
service.update(nonExistentId, { appStatus: AppStatus.IN_REVIEW }),
).rejects.toThrow(
new NotFoundException(`Application with ID ${nonExistentId} not found`),
);

expect(repository.findOne).toHaveBeenCalledWith({
where: { appId: nonExistentId },
});
expect(repository.save).not.toHaveBeenCalled();
});
});

describe('delete', () => {
it('should delete an application', async () => {
const mockApplication: Application = {
appId: 1,
appStatus: AppStatus.APP_SUBMITTED,
daysAvailable: 'Monday, Tuesday',
experienceType: ExperienceType.BS,
fileUploads: [],
interest: InterestArea.NURSING,
license: null,
isInternational: false,
isLearner: false,
phone: '123-456-7890',
school: School.HARVARD_MEDICAL_SCHOOL,
referred: false,
referredEmail: null,
weeklyHours: 20,
};

mockRepository.findOne.mockResolvedValue(mockApplication);
mockRepository.remove.mockResolvedValue(mockApplication);

await service.delete(1);

expect(repository.findOne).toHaveBeenCalledWith({ where: { appId: 1 } });
expect(repository.remove).toHaveBeenCalledWith(mockApplication);
});

it('should throw NotFoundException when deleting non-existent application', async () => {
const nonExistentId = 999;

mockRepository.findOne.mockResolvedValue(null);

await expect(service.delete(nonExistentId)).rejects.toThrow(
new NotFoundException(`Application with ID ${nonExistentId} not found`),
);

expect(repository.findOne).toHaveBeenCalledWith({
where: { appId: nonExistentId },
});
expect(repository.remove).not.toHaveBeenCalled();
});
});
});
Loading
Loading