Skip to content

Commit

Permalink
[TEAMMATES#12048] v9: Skeleton implementation (TEAMMATES#12056)
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelfangjw authored and hhdqirui committed Feb 11, 2023
1 parent 9c46277 commit 522e93a
Show file tree
Hide file tree
Showing 42 changed files with 1,949 additions and 41 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ src/web/dist/*
src/web/webtools/*
filestorage-dev/*
datastore-dev/datastore/*
postgres-data/

!.gitkeep

Expand Down
53 changes: 51 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ apply plugin: "pmd"
apply plugin: "com.github.spotbugs"
apply plugin: "jacoco"
apply plugin: "cz.habarta.typescript-generator"
apply plugin: "org.liquibase.gradle"

def checkstyleVersion = "10.3.2"
def pmdVersion = "6.48.0"
Expand All @@ -26,11 +27,14 @@ buildscript {
exclude group: "org.gradle"
}
classpath "com.google.guava:guava:31.1-jre"
classpath "org.liquibase:liquibase-gradle-plugin:2.1.1"
}
}

configurations {
staticAnalysis

liquibaseRuntime.extendsFrom testImplementation
}

repositories {
Expand Down Expand Up @@ -69,13 +73,18 @@ dependencies {
implementation("org.eclipse.jetty:jetty-webapp")
implementation("org.eclipse.jetty:jetty-annotations")
implementation("org.jsoup:jsoup:1.15.2")
implementation("org.hibernate.orm:hibernate-core:6.1.6.Final")
implementation("org.postgresql:postgresql:42.5.2")

testAnnotationProcessor(testng)

testImplementation("com.tngtech.archunit:archunit:0.11.0")
testImplementation("junit:junit:4.13.2")
testImplementation("org.seleniumhq.selenium:selenium-java:4.3.0")
testImplementation(testng)
testImplementation("org.testcontainers:postgresql:1.17.6")
testImplementation("org.liquibase:liquibase-core:4.19.0")
testImplementation("org.mockito:mockito-core:5.1.1")
// For supporting authorization code flow locally
testImplementation("com.google.oauth-client:google-oauth-client-jetty:1.34.1")
// For using Gmail API
Expand All @@ -88,6 +97,8 @@ dependencies {
exclude group: "org.apache.jmeter", module: "bom"
}

liquibaseRuntime("info.picocli:picocli:4.7.1")
liquibaseRuntime(sourceSets.main.output)
}

sourceSets {
Expand All @@ -106,19 +117,33 @@ sourceSets {
srcDir "src/test/java"
srcDir "src/e2e/java"
srcDir "src/lnp/java"
srcDir "src/it/java"
srcDir "src/client/java"
include "**/*.java"
}
resources {
srcDir "src/test/resources"
srcDir "src/e2e/resources"
srcDir "src/lnp/resources"
srcDir "src/it/resources"
srcDir "src/client/resources"
exclude "**/*.java"
}
}
}

liquibase {
activities {
main {
searchPath "${projectDir}"
changeLogFile "src/main/resources/db/changelog/db.changelog-root.xml"
url project.properties['liquibaseDbUrl']
username project.properties['liquibaseUsername']
password project.properties['liquibasePassword']
}
}
}

tasks.withType(cz.habarta.typescript.generator.gradle.GenerateTask) {
jsonLibrary = "jackson2"
optionalAnnotations = [
Expand Down Expand Up @@ -513,8 +538,8 @@ task lnpTests(type: Test) {
}
}

task componentTests(type: Test) {
description "Runs the full unit and integration test suite."
task unitTests(type: Test) {
description "Runs the full unit test suite."
group "Test"
useTestNG()
options.suites "src/test/resources/testng-component.xml"
Expand All @@ -531,6 +556,30 @@ task componentTests(type: Test) {
}
}

task integrationTests(type: Test) {
description "Runs the full integration test suite."
group "Test"
useTestNG()
options.suites "src/it/resources/testng-it.xml"
options.useDefaultListeners = true
ignoreFailures false
maxHeapSize = "1g"
reports.html.required = false
reports.junitXml.required = false
jvmArgs "-ea", "-Xss2m", "-Dfile.encoding=UTF-8"
afterTest afterTestClosure
afterSuite checkTestNgFailureClosure
testLogging {
events "passed"
}
}

task componentTests(type: Test) {
description "Runs the full unit and integration test suite."
group "Test"
dependsOn unitTests, integrationTests
}

task e2eTests {
description "Runs the full E2E test suite and retries failed test up to ${numOfTestRetries} times."
group "Test"
Expand Down
11 changes: 11 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,14 @@ services:
context: solr
ports:
- 8983:8983
postgres:
image: postgres:15.1-alpine
restart: always
volumes:
- ./postgres-data:/var/lib/postgresql/data
ports:
- 5432:5432
environment:
POSTGRES_USER: teammates
POSTGRES_PASSWORD: teammates
POSTGRES_DB: teammates
6 changes: 6 additions & 0 deletions gradle.template.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,9 @@ org.gradle.daemon=false
# Use this property if you want to use a specific Cloud SDK installation.
# If this property is not set, the latest Cloud SDK will always be used.
# cloud.sdk.home=/Users/visenze/Documents/google-cloud-sdk

# Liquibase
liquibaseTaskPrefix=liquibase
liquibaseDbUrl=jdbc:postgresql://localhost:5432/teammates
liquibaseUsername=teammates
liquibasePassword=teammates
74 changes: 74 additions & 0 deletions src/it/java/teammates/it/storage/sqlapi/CoursesDbIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package teammates.it.storage.sqlapi;

import org.testng.annotations.Test;

import teammates.common.exception.EntityAlreadyExistsException;
import teammates.common.exception.EntityDoesNotExistException;
import teammates.it.test.BaseTestCaseWithSqlDatabaseAccess;
import teammates.storage.sqlapi.CoursesDb;
import teammates.storage.sqlentity.Course;

/**
* SUT: {@link CoursesDb}.
*/
public class CoursesDbIT extends BaseTestCaseWithSqlDatabaseAccess {

private final CoursesDb coursesDb = CoursesDb.inst();

@Test
public void testCreateCourse() throws Exception {
______TS("Create course, does not exists, succeeds");

Course course = new Course.CourseBuilder("course-id")
.withName("course-name")
.withInstitute("teammates")
.build();

coursesDb.createCourse(course);

Course actualCourse = coursesDb.getCourse("course-id");
verifyEquals(course, actualCourse);

______TS("Create course, already exists, execption thrown");

Course identicalCourse = new Course.CourseBuilder("course-id")
.withName("course-name")
.withInstitute("teammates")
.build();
assertNotSame(course, identicalCourse);

assertThrows(EntityAlreadyExistsException.class, () -> coursesDb.createCourse(identicalCourse));
}

@Test
public void testUpdateCourse() throws Exception {
______TS("Update course, does not exists, exception thrown");

Course course = new Course.CourseBuilder("course-id")
.withName("course-name")
.withInstitute("teammates")
.build();

assertThrows(EntityDoesNotExistException.class, () -> coursesDb.updateCourse(course));

______TS("Update course, already exists, update successful");

coursesDb.createCourse(course);
course.setName("new course name");

coursesDb.updateCourse(course);
Course actual = coursesDb.getCourse("course-id");
verifyEquals(course, actual);

______TS("Update detached course, already exists, update successful");

// same id, different name
Course detachedCourse = new Course.CourseBuilder("course-id")
.withName("course")
.withInstitute("teammates")
.build();

coursesDb.updateCourse(detachedCourse);
verifyEquals(course, detachedCourse);
}
}
4 changes: 4 additions & 0 deletions src/it/java/teammates/it/storage/sqlapi/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Contains test cases for {@link teammates.storage.search} package.
*/
package teammates.it.storage.sqlapi;
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package teammates.it.test;

import org.testcontainers.containers.PostgreSQLContainer;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;

import teammates.common.util.HibernateUtil;
import teammates.common.util.JsonUtils;
import teammates.sqllogic.api.Logic;
import teammates.sqllogic.core.LogicStarter;
import teammates.storage.sqlentity.BaseEntity;
import teammates.storage.sqlentity.Course;
import teammates.test.BaseTestCase;

/**
* Base test case for tests that access the database.
*/
@Test(singleThreaded = true)
public class BaseTestCaseWithSqlDatabaseAccess extends BaseTestCase {
/**
* Test container.
*/
protected static final PostgreSQLContainer<?> PGSQL = new PostgreSQLContainer<>("postgres:15.1-alpine");

private final Logic logic = Logic.inst();

@BeforeSuite
public static void setUpClass() throws Exception {
PGSQL.start();
DbMigrationUtil.resetDb(PGSQL.getJdbcUrl(), PGSQL.getUsername(), PGSQL.getPassword());
HibernateUtil.buildSessionFactory(PGSQL.getJdbcUrl(), PGSQL.getUsername(), PGSQL.getPassword());

LogicStarter.initializeDependencies();
}

@AfterSuite
public static void tearDownClass() throws Exception {
PGSQL.close();
}

@BeforeMethod
public void setUp() throws Exception {
HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().begin();
DbMigrationUtil.resetDb(PGSQL.getJdbcUrl(), PGSQL.getUsername(), PGSQL.getPassword());
}

@AfterMethod
public void tearDown() {
HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();
}

/**
* Verifies that two entities are equal.
*/
protected void verifyEquals(BaseEntity expected, BaseEntity actual) {
if (expected instanceof Course) {
Course expectedCourse = (Course) expected;
Course actualCourse = (Course) actual;
equalizeIrrelevantData(expectedCourse, actualCourse);
assertEquals(JsonUtils.toJson(expectedCourse), JsonUtils.toJson(actualCourse));
}
}

/**
* Verifies that the given entity is present in the database.
*/
protected void verifyPresentInDatabase(BaseEntity expected) {
BaseEntity actual = getEntity(expected);
verifyEquals(expected, actual);
}

private BaseEntity getEntity(BaseEntity entity) {
if (entity instanceof Course) {
return logic.getCourse(((Course) entity).getId());
} else {
throw new RuntimeException("Unknown entity type!");
}
}

private void equalizeIrrelevantData(Course expected, Course actual) {
// Ignore time field as it is stamped at the time of creation in testing
expected.setCreatedAt(actual.getCreatedAt());
expected.setUpdatedAt(actual.getUpdatedAt());
}

}
43 changes: 43 additions & 0 deletions src/it/java/teammates/it/test/DbMigrationUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package teammates.it.test;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.HashMap;
import java.util.Map;

import liquibase.Liquibase;
import liquibase.Scope;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.resource.DirectoryResourceAccessor;

/**
* Utility class with methods to apply sql migrations in tests.
*/
public final class DbMigrationUtil {

private DbMigrationUtil() {
// prevent instantiation
}

/**
* Drop all tables and re-apply migrations.
*/
public static void resetDb(String dbUrl, String username, String password) throws Exception {
Map<String, Object> config = new HashMap<>();
File file = new File(System.getProperty("user.dir"));

Scope.child(config, () -> {
Connection conn = DriverManager.getConnection(dbUrl, username, password);
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(conn));
try (Liquibase liquibase = new Liquibase("src/main/resources/db/changelog/db.changelog-root.xml",
new DirectoryResourceAccessor(file), database)) {
liquibase.dropAll();
liquibase.update();
}
conn.close();
});
}
}
Loading

0 comments on commit 522e93a

Please sign in to comment.