Skip to content

Commit

Permalink
Test: Add some testcases
Browse files Browse the repository at this point in the history
  • Loading branch information
Alfex4936 committed Oct 13, 2024
1 parent 242a6ca commit 200fbe5
Show file tree
Hide file tree
Showing 12 changed files with 365 additions and 19 deletions.
57 changes: 57 additions & 0 deletions .github/workflows/spring-codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: CodeCov for Spring Boot

on:
push:
paths:
- '**/*.java' # Triggers the workflow only when .java files are changed
workflow_dispatch: # Allows manual triggering of the workflow

permissions:
contents: read # Grants read access to repository contents

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # Cancels any in-progress runs of the same workflow and branch

jobs:
jacoco:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
# Checkout the repository
- uses: actions/checkout@v4

# Set up JDK 22
- name: Set up JDK 22
uses: actions/setup-java@v3
with:
java-version: "22"
distribution: "corretto"

# Set permissions for /tmp directory (if necessary)
- name: Set permissions for /tmp directory
run: sudo chmod -R 777 /tmp

# Decode and set up secrets
- name: Setup secrets
run: |
echo "${{ secrets.APPLICATION_YML }}" | base64 --decode > src/main/resources/application-secret.yml
echo "${{ secrets.V1_SQL }}" | base64 --decode > src/main/resources/db/migration/V1__Create_festivals_table.sql
echo "${{ secrets.BAD_WORD_LIST }}" | base64 --decode > src/main/resources/badwords.txt
echo "${{ secrets.BAD_WORD_LIST }}" | base64 --decode > src/test/resources/badwords.txt
# Ensure Maven Wrapper is executable (if using Maven Wrapper)
- name: Ensure Maven Wrapper is executable
run: chmod +x mvnw

# Build and Run Tests with Maven (including JaCoCo)
- name: Build with Maven
run: ./mvnw clean verify -DskipTests=false

# Upload coverage to Codecov
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: target/jacoco/jacoco.xml
fail_ci_if_error: true
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,5 @@ users can effortlessly discover and explore festivals tailored to their interest
```

> **Note**: Customize queries as per frontend requirements. Utilize GraphQL clients like [Apollo](https://www.apollographql.com/) for efficient data fetching.
[![codecov](https://codecov.io/gh/Alfex4936/K-Festia/graph/badge.svg?token=5O9FFQ3QBN)](https://codecov.io/gh/Alfex4936/K-Festia)
Binary file added kfestia.db
Binary file not shown.
76 changes: 74 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
</parent>
<groupId>csw.korea.festival</groupId>
<artifactId>main</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>main</name>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>K-Festia</name>
<description>Festivals in South Korea</description>
<url/>
<licenses>
Expand All @@ -28,6 +29,7 @@
</scm>
<properties>
<java.version>22</java.version>
<jacoco.version>0.8.12</jacoco.version>
</properties>

<repositories>
Expand Down Expand Up @@ -74,6 +76,13 @@
<version>14.0.0</version>
</dependency>

<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<version>1.3.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
Expand Down Expand Up @@ -249,6 +258,7 @@
</excludes>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
Expand All @@ -258,6 +268,68 @@
<compilerArgs>--enable-preview</compilerArgs>
</configuration>
</plugin>

<!-- Maven Surefire Plugin for Test Execution -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<!-- Pass the JaCoCo argument line for test instrumentation -->
<argLine>${jacocoArgLine} --enable-preview</argLine>
<includes>
<include>**/Test*.java</include>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
<include>**/IT*.java</include>
</includes>
</configuration>
</plugin>

<!-- JaCoCo Maven Plugin for Code Coverage -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions>
<!-- Prepare the JaCoCo agent before tests are run -->
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>${project.build.directory}/jacoco.exec</destFile>
<propertyName>jacocoArgLine</propertyName>
</configuration>
</execution>
<!-- Generate the JaCoCo report after tests are run -->
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/jacoco</outputDirectory>
<reports>
<xml>true</xml> <!-- Ensure XML report is generated -->
<csv>false</csv> <!-- Disable CSV report -->
<html>true</html> <!-- Enable HTML report for visualization -->
</reports>
<excludes>
<exclude>csw/korea/festival/main/common/annotation/**</exclude>
<!-- Exclude all classes ending with 'Config' -->
<exclude>**/*Config.class</exclude>
<exclude>csw/korea/festival/main/common/dto/**</exclude>
<exclude>csw/korea/festival/main/MainApplication.class</exclude>
<exclude>csw/korea/festival/main/festival/repository/**</exclude>
<exclude>csw/korea/festival/main/festival/model/**</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private SynonymMap buildSynonymMap() throws IOException {
}


public static class SimpleKoreanTokenizer extends Tokenizer {
public static final class SimpleKoreanTokenizer extends Tokenizer {
private final CharTermAttribute charTermAttribute = addAttribute(CharTermAttribute.class);
private final OffsetAttribute offsetAttribute = addAttribute(OffsetAttribute.class);
private final PositionIncrementAttribute positionIncrementAttribute = addAttribute(PositionIncrementAttribute.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class FestivalResolver {
* @param size Optional page size. Defaults to 10 if not provided.
* @return Paginated list of festivals matching the criteria.
*/
@RateLimited(key = "festivals", capacity = 1, refillTokens = 100, refillDurationMillis = 60000)
@RateLimited(key = "festivals", capacity = 100, refillTokens = 100, refillDurationMillis = 60000)
@QueryMapping
public FestivalPage festivals(
@Argument String month,
Expand Down
24 changes: 24 additions & 0 deletions src/test/java/csw/korea/festival/main/LuceneConfigurationTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package csw.korea.festival.main;

import csw.korea.festival.main.festival.model.Festival;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.massindexing.MassIndexer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;

@TestConfiguration
public class LuceneConfigurationTest {
@Bean
@Transactional
public MassIndexer indexLuceneData(EntityManager entityManager) {
MassIndexer massIndexer = Search.session(entityManager).massIndexer(Festival.class);
try {
massIndexer.startAndWait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return massIndexer;
}
}
13 changes: 0 additions & 13 deletions src/test/java/csw/korea/festival/main/MainApplicationTests.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package csw.korea.festival.main.config.web;

import io.github.bucket4j.Bucket;
import org.junit.jupiter.api.Test;

import java.time.Duration;

import static org.junit.jupiter.api.Assertions.*;

public class RateLimiterServiceTest {

private final RateLimiterService rateLimiterService = new RateLimiterService();

@Test
public void testResolveBucket_createsBucket() {
String key = "testKey";
long capacity = 10;
long refillTokens = 5;
Duration refillDuration = Duration.ofSeconds(10);

Bucket bucket = rateLimiterService.resolveBucket(key, capacity, refillTokens, refillDuration);
assertNotNull(bucket);
}

@Test
public void testResolveBucket_reuseBucket() {
String key = "testKey";
long capacity = 10;
long refillTokens = 5;
Duration refillDuration = Duration.ofSeconds(10);

Bucket bucket1 = rateLimiterService.resolveBucket(key, capacity, refillTokens, refillDuration);
Bucket bucket2 = rateLimiterService.resolveBucket(key, capacity, refillTokens, refillDuration);

assertSame(bucket1, bucket2); // same bucket is reused
}

@Test
public void testBucketCapacityLimit() {
String key = "testKeyCapacity";
long capacity = 3;
long refillTokens = 1;
Duration refillDuration = Duration.ofMinutes(1);

Bucket bucket = rateLimiterService.resolveBucket(key, capacity, refillTokens, refillDuration);
assertTrue(bucket.tryConsume(1));
assertTrue(bucket.tryConsume(1));
assertTrue(bucket.tryConsume(1));
assertFalse(bucket.tryConsume(1)); // Capacity exceeded
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package csw.korea.festival.main.config.web;

import csw.korea.festival.main.common.annotation.RateLimited;
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Refill;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.Duration;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;

public class RateLimitingAspectTest {

private RateLimitingAspect rateLimitingAspect;

@Mock
private RateLimiterService rateLimiterService;

@Mock
private ProceedingJoinPoint joinPoint;

@Mock
private MethodSignature methodSignature;

@Mock
private HttpServletRequest request;

@BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
rateLimitingAspect = new RateLimitingAspect(rateLimiterService);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));

when(joinPoint.getSignature()).thenReturn(methodSignature);
when(methodSignature.getMethod()).thenAnswer(invocation -> this.getClass().getDeclaredMethod("dummyMethod"));
}

@RateLimited(key = "testKey", capacity = 3, refillTokens = 1, refillDurationMillis = 60000)
public void dummyMethod() {
}

@Test
public void testRateLimitingPass() throws Throwable {
when(rateLimiterService.resolveBucket(anyString(), anyLong(), anyLong(), any(Duration.class)))
.thenReturn(Bucket.builder().addLimit(Bandwidth.classic(3, Refill.greedy(1, Duration.ofMinutes(1)))).build());

rateLimitingAspect.rateLimit(joinPoint, methodSignature.getMethod().getAnnotation(RateLimited.class));
verify(joinPoint, times(1)).proceed();
}

@Test
public void testRateLimitingExceeded() {
Bandwidth limit = Bandwidth
.builder()
.capacity(1)
.refillGreedy(1, Duration.ofMinutes(1))
.build();
Bucket bucket = Bucket.builder().addLimit(limit).build();
when(rateLimiterService.resolveBucket(anyString(), anyLong(), anyLong(), any(Duration.class)))
.thenReturn(bucket);

bucket.tryConsume(1); // Consume the only token available

assertThrows(RateLimitExceededException.class, () ->
rateLimitingAspect.rateLimit(joinPoint, methodSignature.getMethod().getAnnotation(RateLimited.class))
);
}
}
Loading

0 comments on commit 200fbe5

Please sign in to comment.