Skip to content

Commit

Permalink
Merge pull request #333 from ls1intum/code-cleanup
Browse files Browse the repository at this point in the history
Add Integration Tests
  • Loading branch information
fabian-emilius authored Oct 30, 2024
2 parents 1b25ff9 + d53a623 commit ebc9211
Show file tree
Hide file tree
Showing 20 changed files with 1,147 additions and 128 deletions.
27 changes: 13 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,23 @@ ThesisTrack was developed as part of this [bachelor's thesis](docs/files/ba-thes
## User Documentation

#### Student
- Submit Thesis Application
- Edit Thesis Application
- Upload Proposal
- Upload Thesis Files
- Create Presentation Draft
- Manage User Settings
- [Submit Thesis Application](https://live.rbg.tum.de/w/artemisintro/53217)
- [Edit Thesis Application](https://live.rbg.tum.de/w/artemisintro/53218)
- [Upload Proposal](https://live.rbg.tum.de/w/artemisintro/53219)
- [Upload Thesis Files](https://live.rbg.tum.de/w/artemisintro/53220)
- [Create Presentation Draft](https://live.rbg.tum.de/w/artemisintro/53215)
- [Manage User Settings](https://live.rbg.tum.de/w/artemisintro/53216)

#### Advisor
- Create Thesis Topic
- Review Applications
- Review Proposal
- Add Comments
- Schedule Presentation
- Submit Thesis Assessment
- [Create Thesis Topic](https://live.rbg.tum.de/w/artemisintro/53209)
- [Review Applications](https://live.rbg.tum.de/w/artemisintro/53212)
- [Review Proposal](https://live.rbg.tum.de/w/artemisintro/53213)
- [Add Comments](https://live.rbg.tum.de/w/artemisintro/53211)
- [Schedule Presentation](https://live.rbg.tum.de/w/artemisintro/53214)
- [Submit Thesis Assessment](https://live.rbg.tum.de/w/artemisintro/53208)

#### Supervisor
- Add Final Grade + Complete Thesis
- Thesis Overview
- [Add Final Grade + Complete Thesis](https://live.rbg.tum.de/w/artemisintro/53221)

## Developer Documentation

Expand Down
186 changes: 93 additions & 93 deletions client/package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@
"react-router-dom": "6.27.0"
},
"devDependencies": {
"@eslint/compat": "1.2.1",
"@eslint/compat": "1.2.2",
"@eslint/eslintrc": "3.1.0",
"@eslint/js": "9.13.0",
"@types/react": "18.3.12",
"@types/react-avatar-editor": "^13.0.3",
"@types/react-dom": "18.3.1",
"@typescript-eslint/eslint-plugin": "8.11.0",
"@typescript-eslint/parser": "8.11.0",
"@typescript-eslint/eslint-plugin": "8.12.2",
"@typescript-eslint/parser": "8.12.2",
"clean-webpack-plugin": "4.0.0",
"compression-webpack-plugin": "11.1.0",
"copy-webpack-plugin": "12.0.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const ApplicationsFilters = (props: IApplicationsFiltersProps) => {
onChange={(value) => {
setFilters((prev) => ({
...prev,
topics: value.length > 0 ? value : undefined,
topics: value,
}))
}}
searchable
Expand All @@ -67,7 +67,7 @@ const ApplicationsFilters = (props: IApplicationsFiltersProps) => {
onChange={(value) => {
setFilters((prev) => ({
...prev,
types: value.length > 0 ? value : undefined,
types: value,
}))
}}
searchable
Expand All @@ -86,7 +86,7 @@ const ApplicationsFilters = (props: IApplicationsFiltersProps) => {
onChange={(value) => {
setFilters((prev) => ({
...prev,
states: value.length > 0 ? (value as ApplicationState[]) : undefined,
states: value as ApplicationState[],
}))
}}
searchable
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/ThesesFilters/ThesesFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const ThesesFilters = () => {
onChange={(x) =>
setFilters((prev) => ({
...prev,
types: x.length > 0 ? x : undefined,
types: x,
}))
}
/>
Expand All @@ -69,7 +69,7 @@ const ThesesFilters = () => {
onChange={(x) =>
setFilters((prev) => ({
...prev,
states: x.length > 0 ? (x as ThesisState[]) : undefined,
states: x as ThesisState[],
}))
}
/>
Expand Down
2 changes: 1 addition & 1 deletion client/src/pages/DashboardPage/DashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const DashboardPage = () => {
<Title order={1}>Dashboard</Title>
<MyTasksSection />
<ThesesProvider
hideIfEmpty={!managementAccess}
hideIfEmpty
defaultStates={
managementAccess
? [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { UploadFileType } from '../../../../config/types'
import { Button, Center, Group, Input, Table, Text } from '@mantine/core'
import { formatDate } from '../../../../utils/format'
import AuthenticatedFilePreviewButton from '../../../../components/AuthenticatedFilePreviewButton/AuthenticatedFilePreviewButton'
import { DownloadSimple, Eye, X } from 'phosphor-react'
import { DownloadSimple, Eye, Trash } from 'phosphor-react'
import AuthenticatedFileDownloadButton from '../../../../components/AuthenticatedFileDownloadButton/AuthenticatedFileDownloadButton'
import AvatarUser from '../../../../components/AvatarUser/AvatarUser'
import { useState } from 'react'
Expand Down Expand Up @@ -83,7 +83,7 @@ const FileHistoryTable = (props: IFileHistoryTableProps) => {
</AuthenticatedFileDownloadButton>
{row.onDelete && (
<Button loading={loading} size='xs' onClick={() => onDelete(row)}>
<X />
<Trash />
</Button>
)}
</Group>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import AvatarUser from '../../../../components/AvatarUser/AvatarUser'
import { formatDate } from '../../../../utils/format'
import { doRequest } from '../../../../requests/request'
import { ApiError } from '../../../../requests/handler'
import { X } from 'phosphor-react'
import { Trash } from 'phosphor-react'

interface IThesisFeedbackOverviewProps {
type: string
Expand Down Expand Up @@ -99,7 +99,7 @@ const ThesisFeedbackOverview = (props: IThesisFeedbackOverviewProps) => {
{access.advisor && (
<Center>
<Button size='xs' loading={deleting} onClick={() => deleteFeedback(item)}>
<X />
<Trash />
</Button>
</Center>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ const ApplicationsProvider = (props: PropsWithChildren<IApplicationsProviderProp
const adjustedFilters = useMemo(() => {
const copiedFilters = { ...filters }

if (showOnlyAssignedTopics && typeof copiedFilters.topics === 'undefined' && topics) {
if (showOnlyAssignedTopics && typeof copiedFilters.topics === 'undefined') {
copiedFilters.topics = [
'NO_TOPIC',
...topics
...(topics ?? [])
.filter(
(topic) =>
topic.supervisors.some((supervisor) => supervisor.userId === user.userId) ||
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ services:
db:
image: 'postgres:15.2-alpine'
container_name: thesis-track-db
volumes:
- ./db_backups:/db_backups
environment:
- POSTGRES_USER=thesis-track-postgres
- POSTGRES_PASSWORD=thesis-track-postgres
Expand Down
2 changes: 1 addition & 1 deletion server/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM gradle:8.9.0-jdk21 AS build
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
RUN gradle build --no-daemon
RUN gradle build -x test --no-daemon

FROM eclipse-temurin:21

Expand Down
12 changes: 12 additions & 0 deletions server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,14 @@ dependencies {
implementation "org.mnode.ical4j:ical4j:4.0.4"
implementation "com.itextpdf:itext-core:8.0.5"
implementation "com.itextpdf:html2pdf:5.0.5"
implementation "com.auth0:java-jwt:4.4.0"

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.mockito:mockito-junit-jupiter'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:postgresql'

testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
compileOnly "org.projectlombok:lombok"
Expand All @@ -48,4 +51,13 @@ dependencies {

test {
useJUnitPlatform()

maxParallelForks = 1
forkEvery = 0

testLogging {
events "PASSED", "FAILED", "SKIPPED"
}

systemProperty 'spring.profiles.active', 'test'
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,10 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers(HttpMethod.GET, "/v2/calendar/**").permitAll()
.requestMatchers(HttpMethod.GET, "/v2/avatars/**").permitAll()
.anyRequest().authenticated()
);

http.oauth2ResourceServer(server -> {
server.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter));
});
)
.oauth2ResourceServer(server -> {
server.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter));
});

return http.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package thesistrack.ls1.controller;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import thesistrack.ls1.constants.ApplicationState;
import thesistrack.ls1.controller.payload.AcceptApplicationPayload;
import thesistrack.ls1.controller.payload.CreateApplicationPayload;
import thesistrack.ls1.controller.payload.UpdateApplicationCommentPayload;
import thesistrack.ls1.mock.BaseIntegrationTest;

import java.time.Instant;
import java.util.List;
import java.util.UUID;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@Testcontainers
class ApplicationControllerTest extends BaseIntegrationTest {
@Container
static PostgreSQLContainer<?> dbContainer = new PostgreSQLContainer<>(
"postgres:16-alpine"
);

@BeforeAll
static void startDatabase() {
dbContainer.start();
}

@AfterAll
static void stopDatabase() {
dbContainer.stop();
}

@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
dbContainer.start();

registry.add("spring.datasource.url", dbContainer::getJdbcUrl);
registry.add("spring.datasource.username", dbContainer::getUsername);
registry.add("spring.datasource.password", dbContainer::getPassword);
}

@Test
void createApplication_Success() throws Exception {
CreateApplicationPayload payload = new CreateApplicationPayload(
null,
"Test Thesis",
"MASTER",
Instant.now(),
"Test motivation"
);

mockMvc.perform(MockMvcRequestBuilders.post("/v2/applications")
.header("Authorization", createRandomAdminAuthentication())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(payload))
)
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.thesisTitle").value("Test Thesis"))
.andExpect(MockMvcResultMatchers.jsonPath("$.thesisType").value("MASTER"))
.andExpect(MockMvcResultMatchers.jsonPath("$.motivation").value("Test motivation"))
.andExpect(MockMvcResultMatchers.jsonPath("$.state").value(ApplicationState.NOT_ASSESSED.getValue()));
}

@Test
void updateApplication_Success() throws Exception {
String authorization = createRandomAdminAuthentication();
UUID applicationId = createTestApplication(authorization, "Application");
CreateApplicationPayload updatePayload = new CreateApplicationPayload(
null,
"Updated Thesis",
"BACHELOR",
Instant.now(),
"Updated motivation"
);

mockMvc.perform(MockMvcRequestBuilders.put("/v2/applications/" + applicationId)
.header("Authorization", authorization)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(updatePayload))
)
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.thesisTitle").value("Updated Thesis"))
.andExpect(MockMvcResultMatchers.jsonPath("$.thesisType").value("BACHELOR"))
.andExpect(MockMvcResultMatchers.jsonPath("$.motivation").value("Updated motivation"))
.andExpect(MockMvcResultMatchers.jsonPath("$.state").value(ApplicationState.NOT_ASSESSED.getValue()));
}

@Test
void updateApplicationComment_Success() throws Exception {
UUID applicationId = createTestApplication(createRandomAdminAuthentication(), "Application");

UpdateApplicationCommentPayload payload = new UpdateApplicationCommentPayload("Test comment");

mockMvc.perform(MockMvcRequestBuilders.put("/v2/applications/" + applicationId + "/comment")
.header("Authorization", createRandomAdminAuthentication())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(payload))
)
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.comment").value("Test comment"));
}

@Test
void acceptApplication_Success() throws Exception {
UUID applicationId = createTestApplication(createRandomAdminAuthentication(), "Application");
UUID advisorId = createTestUser("advisor", List.of("advisor"));
UUID supervisorId = createTestUser("supervisor", List.of("supervisor"));

AcceptApplicationPayload payload = new AcceptApplicationPayload(
"Final Thesis Title",
"MASTER",
List.of(advisorId),
List.of(supervisorId),
true,
true
);

mockMvc.perform(MockMvcRequestBuilders.put("/v2/applications/" + applicationId + "/accept")
.header("Authorization", createRandomAdminAuthentication())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(payload)))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$[0].state").value(ApplicationState.ACCEPTED.getValue()));
}
}
Loading

0 comments on commit ebc9211

Please sign in to comment.