diff --git a/build.gradle b/build.gradle index f448d003a8fa..4fe78e4f12b2 100644 --- a/build.gradle +++ b/build.gradle @@ -77,7 +77,8 @@ dependencies { implementation("org.jsoup:jsoup:1.15.2") implementation("org.hibernate.orm:hibernate-core:6.1.6.Final") implementation("org.postgresql:postgresql:42.7.2") - implementation("org.hibernate:hibernate-hikaricp:6.1.6.Final") + implementation("org.hibernate.orm:hibernate-agroal:6.1.6.Final") + implementation("io.agroal:agroal-pool:2.1") testAnnotationProcessor(testng) @@ -101,9 +102,7 @@ dependencies { exclude group: "org.apache.jmeter", module: "bom" } - liquibaseRuntime("org.liquibase:liquibase-core:4.19.0") liquibaseRuntime("info.picocli:picocli:4.7.1") - liquibaseRuntime("org.postgresql:postgresql:42.7.2") liquibaseRuntime(sourceSets.main.output) } @@ -138,10 +137,6 @@ sourceSets { } } -if (!project.hasProperty("runList")) { - project.ext.set("runList", "main") -} - liquibase { activities { main { @@ -151,23 +146,7 @@ liquibase { username project.properties['liquibaseUsername'] password project.properties['liquibasePassword'] } - snapshot { - url project.properties['liquibaseDbUrl'] - username project.properties['liquibaseUsername'] - password project.properties['liquibasePassword'] - snapshotFormat "json" - outputFile "liquibase-snapshot.json" - } - diffMain { - searchPath "${projectDir}" - changeLogFile "src/main/resources/db/changelog/db.changelog-new.xml" - referenceUrl project.properties['liquibaseDbUrl'] - referenceUsername project.properties['liquibaseUsername'] - referencePassword project.properties['liquibasePassword'] - url "offline:postgresql?snapshot=liquibase-snapshot.json" - } } - runList = project.ext.runList } tasks.withType(cz.habarta.typescript.generator.gradle.GenerateTask) { diff --git a/docs/_markbind/layouts/default.md b/docs/_markbind/layouts/default.md index e57ee418a494..f92791366a06 100644 --- a/docs/_markbind/layouts/default.md +++ b/docs/_markbind/layouts/default.md @@ -27,7 +27,6 @@ * [Captcha]({{ baseUrl }}/captcha.html) * [Documentation]({{ baseUrl }}/documentation.html) * [Emails]({{ baseUrl }}/emails.html) - * [Unit Testing]({{ baseUrl }}/unit-testing.html) * [End-to-End Testing]({{ baseUrl }}/e2e-testing.html) * [Performance Testing]({{ baseUrl }}/performance-testing.html) * [Accessibility Testing]({{ baseUrl }}/axe-testing.html) diff --git a/docs/development.md b/docs/development.md index aec8c3696caa..703a1ce57b08 100644 --- a/docs/development.md +++ b/docs/development.md @@ -291,9 +291,7 @@ There are two big categories of testing in TEAMMATES: - **Component tests**: white-box unit and integration tests, i.e. they test the application components with full knowledge of the components' internal workings. This is configured in `src/test/resources/testng-component.xml` (back-end) and `src/web/jest.config.js` (front-end). - **E2E (end-to-end) tests**: black-box tests, i.e. they test the application as a whole without knowing any internal working. This is configured in `src/e2e/resources/testng-e2e.xml`. To learn more about E2E tests, refer to this [document](e2e-testing.md). -
- -#### Running tests +#### Running the tests ##### Frontend tests @@ -337,8 +335,6 @@ You can generate the coverage data with `jacocoReport` task after running tests, The report can be found in the `build/reports/jacoco/jacocoReport/` directory. -
- ## Deploying to a staging server > `Staging server` is the server instance you set up on Google App Engine for hosting the app for testing purposes. diff --git a/docs/schema-migration.md b/docs/schema-migration.md deleted file mode 100644 index a88f49ece0fe..000000000000 --- a/docs/schema-migration.md +++ /dev/null @@ -1,34 +0,0 @@ - - title: "Schema Migration" - - -# SQL Schema Migration - -Teammates uses _[Liquibase]_(https://docs.liquibase.com/start/home.html), a database schema change management solution that enables developers to revise and release database changes to production. The maintainers in charge of releases (Release Leader) will be in charge of generating a _Liquibase_ changelog prior to each release to keep the production databases schema in sync with the code. Therefore this section is just for documentation purposes for contributors. - -## Liquibase in Teammates -_Liquibase_ is made available using the [gradle plugin](https://github.com/liquibase/liquibase-gradle-plugin), providing _liquibase_ functions as tasks. Try `gradle tasks | grep "liquibase"` to see all the tasks available. In teammates, change logs (more in the next section) are written in _XML_. - -### Liquibase connection -Amend the `liquibaseDbUrl`, `liquibaseUsername` and `liquibasePassword` in `gradle.properties` to allow the _Liquibase_ plugin to connect your database. - -## Change logs, change sets and change types -A _change log_ is a file that contains a series of _change sets_ (analagous to a transaction) which applies _change types_ (actions). You can refer to this page on liquibase on the types of [change types](https://docs.liquibase.com/change-types/home.html) that can be used. - -## Gradle Activities for Liquibase -Activities in Gradle are a way of specifying different variables provided by gradle to the Liquibase plugin. The argument `runList` provided by `-pRunList=` e.g `./gradlew liquibaseSnapshot -PrunList=snapshot` is used to specify which activity to be used for the Liquibase command. In this case the `liquibaseSnapshot` command is run using the `snapshot` activity. - -Here is a brief description of the activities defined for Liquibase -1. Main: The default activity used by Liquibase commands and is used for running changelogs against a database. This is used by default if a `runList` is not defined -2. Snapshot: Used to specify output format and name for snapshots i.e JSON -3. diffMain: Specify the reference and the target database to generate changelog that contains operations to update reference database to the state of the target database. i.e the reference is the JSON file generated by the snapshot command, this can be replaced with a live database which is used as reference. - -## Generating/ Updating liquibase change logs -1. Ensure `diff-main` activity in `build.gradle` is pointing to the latest release changelog `src/main/resources/db/changelog/db.changelog-.xml` -2. Delete the `postgres-data` folder to clear any old database schemas -3. Run `git checkout ` and -4. Run the server using `./gradlew serverRun` to generate tables found on branch -5. Generate snapshot of database by running `./gradlew liquibaseSnapshot -PrunList=snapshot`, the snapshot will be output to `liquibase-snapshot.json` -6. Checkout your branch and repeat steps 2 and 4 to generate the tables found on your branch -7. Run `./gradlew liquibaseDiffChangeLog -PrunList=diffMain` to generate changeLog to resolve database schema differences - diff --git a/docs/unit-testing.md b/docs/unit-testing.md deleted file mode 100644 index fe4d2786c8ec..000000000000 --- a/docs/unit-testing.md +++ /dev/null @@ -1,206 +0,0 @@ - - title: "Unit Testing" - - -# Unit Testing - -## What is Unit Testing? - -Unit testing is a testing methodology where the objective is to test components in isolation. - -- It aims to ensure all components of the application work as expected, assuming its dependencies are working. -- This is done in TEAMMATES by using mocks to simulate a component's dependencies. - -Frontend Unit tests in TEAMMATES are located in `.spec.ts` files, while Backend Unit tests in TEAMMATES can be found in the package `teammates.test`. - - -## Writing Unit Tests - -### General guidelines - -#### Include only relevant details in tests -When writing unit tests, reduce the amount of noise in the code to make it easier for future developers to follow. - -The code below has a lot of noise in creation of the `studentModel`: - -```javascript -it('displayInviteButton: should display "Send Invite" button when a student has not joined the course', () => { - component.studentModels = [ - { - student: { - name: 'tester', - teamName: 'Team 1', - email: 'tester@tester.com', - joinState: JoinState.NOT_JOINED, - sectionName: 'Tutorial Group 1', - courseId: 'text-exa.demo', - }, - isAllowedToViewStudentInSection: true, - isAllowedToModifyStudent: true, - }, - ]; - - expect(sendInviteButton).toBeTruthy(); -}); -``` - -However, what is important is only the student joinState. We should thus reduce the noise by including only the relevant details: - -```javascript -it('displayInviteButton: should display "Send Invite" button when a student has not joined the course', () => { - component.studentModels = [ - studentModelBuilder - .joinState(JoinState.NOT_JOINED) - .build() - ]; - - expect(sendInviteButton).toBeTruthy(); -}); -``` - -Including only the relevant details in tests makes it easier for future developers to read and understand the purpose of the test. - -#### Favor readability over uniqueness -Since tests don't have tests, it should be easy for developers to manually inspect them for correctness, even at the expense of greater code duplication. - -Take the following test for example: - -```java -@BeforeMethod -public void setUp() { - users = new User[]{new User("alice"), new User("bob")}; -} - -@Test -public void test_register_canRegisterMultipleUsers() { - registerAllUsers(); - for (User user : users) { - assertTrue(forum.hasRegisteredUser(user)); - } -} - -private void registerAllUsers() { - for (User user : users) { - forum.register(user); - } -} -``` - -While the code reduces duplication, it is not as straightforward for a developer to follow. - -A more readable way to write this test would be: -```java -@Test -public void test_register_canRegisterMultipleUsers() { - User user1 = new User("alice"); - User user2 = new User("bob"); - - forum.register(user1); - forum.register(user2); - - assertTrue(forum.hasRegisteredUser(user1)); - assertTrue(forum.hasRegisteredUser(user2)); -} -``` - -By choosing readability over uniqueness in writing unit tests, there is code duplication, but the test flow is easier for a reader to follow. - - -#### Inline mocks in test code - -Inlining mock return values in the unit test itself improves readability: - -```javascript -it('getStudentCourseJoinStatus: should return true if student has joined the course' , () => { - jest.spyOn(courseService, 'getJoinCourseStatus') - .mockReturnValue(of({ hasJoined: true })); - - expect(student.getJoinCourseStatus).toBeTruthy(); -}); -``` - -By injecting the values in the test right before they are used, developers are able to more easily trace the code and understand the test. - -### Frontend - -#### Naming -Unit tests for a function should follow the format: - -`": should ... when/if ..."` - -Example: - -```javascript - it('hasSection: should return false when there are no sections in the course') -``` - -#### Creating test data -To aid with [including only relevant details in tests](#include-only-relevant-details-in-tests), use the builder in `src/web/test-helpers/generic-builder.ts` - -Usage: -```javascript -const instructorModelBuilder = createBuilder({ - email: 'instructor@gmail.com', - name: 'Instructor', - hasSubmittedSession: false, - isSelected: false, -}); - -it('isAllInstructorsSelected: should return false if at least one instructor !isSelected', () => { -component.instructorListInfoTableRowModels = [ - instructorModelBuilder.isSelected(true).build(), - instructorModelBuilder.isSelected(false).build(), - instructorModelBuilder.isSelected(true).build(), -]; - -expect(component.isAllInstructorsSelected).toBeFalsy(); -}); - -``` - -#### Testing event emission -In Angular, child components emit events. To test for event emissions, we've provided a utility function in `src/test-helpers/test-event-emitter` - -Usage: -```javascript -@Output() -deleteCommentEvent: EventEmitter = new EventEmitter(); - -triggerDeleteCommentEvent(index: number): void { - this.deleteCommentEvent.emit(index); -} - -it('triggerDeleteCommentEvent: should emit the correct index to deleteCommentEvent', () => { - let emittedIndex: number | undefined; - testEventEmission(component.deleteCommentEvent, (index) => { emittedIndex = index; }); - - component.triggerDeleteCommentEvent(5); - expect(emittedIndex).toBe(5); -}); -``` - -### Backend - -#### Naming -Unit test names should follow the format: `test__` - -Examples: -```java -public void testGetComment_commentDoesNotExist_returnsNull() -public void testCreateComment_commentDoesNotExist_success() -public void testCreateComment_commentAlreadyExists_throwsEntityAlreadyExistsException() -``` - -#### Creating test data -To aid with [including only relevant details in tests](#include-only-relevant-details-in-tests), use the `getTypicalX` functions in `BaseTestCase`, where X represents an entity. - -Example: -```java -Account account = getTypicalAccount(); -account.setEmail("newemail@teammates.com"); - -Student student = getTypicalStudent(); -student.setName("New Student Name"); -``` - - diff --git a/src/client/java/teammates/client/connector/DatastoreClient.java b/src/client/java/teammates/client/connector/DatastoreClient.java index 6b8f4510fb36..991674b83a76 100644 --- a/src/client/java/teammates/client/connector/DatastoreClient.java +++ b/src/client/java/teammates/client/connector/DatastoreClient.java @@ -37,9 +37,9 @@ protected void doOperationRemotely() { System.out.println("Going to connect to:" + appDomain + ":" + appPort); DatastoreOptions.Builder builder = DatastoreOptions.newBuilder().setProjectId(Config.APP_ID); - // if (ClientProperties.isTargetUrlDevServer()) { - // builder.setHost(ClientProperties.TARGET_URL); - // } + if (ClientProperties.isTargetUrlDevServer()) { + builder.setHost(ClientProperties.TARGET_URL); + } ObjectifyService.init(new ObjectifyFactory(builder.build().getService())); OfyHelper.registerEntityClasses(); diff --git a/src/client/java/teammates/client/scripts/sql/DataMigrationForAccountAndReadNotificationSql.java b/src/client/java/teammates/client/scripts/sql/DataMigrationForAccountAndReadNotificationSql.java index 0d7becb01ec4..2401b05acc24 100644 --- a/src/client/java/teammates/client/scripts/sql/DataMigrationForAccountAndReadNotificationSql.java +++ b/src/client/java/teammates/client/scripts/sql/DataMigrationForAccountAndReadNotificationSql.java @@ -20,11 +20,6 @@ import com.google.cloud.datastore.QueryResults; import com.googlecode.objectify.cmd.Query; -import jakarta.persistence.TypedQuery; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Root; - // import jakarta.persistence.criteria.CriteriaDelete; import teammates.client.connector.DatastoreClient; @@ -131,27 +126,6 @@ private void doMigration(teammates.storage.entity.Account entity) { * Migrates the entity. */ protected void migrateEntity(teammates.storage.entity.Account oldAccount) { - HibernateUtil.beginTransaction(); - - CriteriaBuilder cb = HibernateUtil.getCriteriaBuilder(); - CriteriaQuery cr = cb.createQuery( - teammates.storage.sqlentity.Account.class); - Root root = cr.from(teammates.storage.sqlentity.Account.class); - cr.select(root).where(cb.equal(root.get("googleId"), oldAccount.getGoogleId())); - - TypedQuery query = HibernateUtil.createQuery(cr); - - - boolean isEntityInDb = query.getResultList().size() != 0; - HibernateUtil.commitTransaction(); - - // In db, but somehow not set as migrated. - if (isEntityInDb) { - oldAccount.setMigrated(true); - entitiesOldAccountSavingBuffer.add(oldAccount); - return; - }; - teammates.storage.sqlentity.Account newAccount = new teammates.storage.sqlentity.Account( oldAccount.getGoogleId(), oldAccount.getName(), @@ -162,6 +136,7 @@ protected void migrateEntity(teammates.storage.entity.Account oldAccount) { oldAccount.setMigrated(true); entitiesOldAccountSavingBuffer.add(oldAccount); migrateReadNotification(oldAccount, newAccount); + } private void migrateReadNotification(teammates.storage.entity.Account oldAccount, diff --git a/src/client/java/teammates/client/scripts/sql/MarkIsMigratedForAccounts.java b/src/client/java/teammates/client/scripts/sql/MarkIsMigratedForAccounts.java deleted file mode 100644 index 75e1536b5bd9..000000000000 --- a/src/client/java/teammates/client/scripts/sql/MarkIsMigratedForAccounts.java +++ /dev/null @@ -1,256 +0,0 @@ -package teammates.client.scripts.sql; - -import java.io.IOException; -// CHECKSTYLE.OFF:ImportOrder -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Collectors; - -import com.google.cloud.datastore.Cursor; -import com.google.cloud.datastore.QueryResults; -import com.googlecode.objectify.cmd.Query; - -import jakarta.persistence.TypedQuery; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Order; -import jakarta.persistence.criteria.Root; -import teammates.storage.sqlentity.Account; -import teammates.test.FileHelper; -import teammates.client.connector.DatastoreClient; -import teammates.client.util.ClientProperties; -import teammates.common.util.HibernateUtil; -// CHECKSTYLE.ON:ImportOrder - -/** - * Protected methods may be overriden. - */ -@SuppressWarnings("PMD") -public class MarkIsMigratedForAccounts extends DatastoreClient { - private static final String BASE_LOG_URI = "src/client/java/teammates/client/scripts/log/"; - - /* NOTE - * Before running the verification, please enable hibernate.jdbc.fetch_size in HibernateUtil.java - * for optimized batch-fetching. - */ - - /** - * Batch size to fetch per page. - */ - protected static final int CONST_SQL_FETCH_BASE_SIZE = 1000; - - AtomicLong numberOfScannedKey; - AtomicLong numberOfAffectedEntities; - AtomicLong numberOfUpdatedEntities; - - /** Datastore entity class. */ - protected Class datastoreEntityClass = teammates.storage.entity.Account.class; - - /** SQL entity class. */ - protected Class sqlEntityClass = Account.class; - - private long entitiesVerified = 0; - private long entitiesSetToIsMigrated = 0; - - public MarkIsMigratedForAccounts() { - numberOfScannedKey = new AtomicLong(); - numberOfAffectedEntities = new AtomicLong(); - numberOfUpdatedEntities = new AtomicLong(); - - String connectionUrl = ClientProperties.SCRIPT_API_URL; - String username = ClientProperties.SCRIPT_API_NAME; - String password = ClientProperties.SCRIPT_API_PASSWORD; - - HibernateUtil.buildSessionFactory(connectionUrl, username, password); - } - - private String getLogPrefix() { - return String.format("%s verifying fields:", sqlEntityClass.getName()); - } - - /** - * Generate the Datstore id of entity to compare with on Datastore side. - */ - protected String generateID(Account sqlEntity) { - return sqlEntity.getGoogleId(); - } - - /** - * Lookup data store entities. - */ - protected Map lookupDataStoreEntities(List datastoreEntitiesIds) { - return ofy().load().type(teammates.storage.entity.Account.class).ids(datastoreEntitiesIds); - } - - /** - * Calculate offset. - */ - protected int calculateOffset(int pageNum) { - return (pageNum - 1) * CONST_SQL_FETCH_BASE_SIZE; - } - - /** - * Get number of pages in database table. - */ - protected Query getFilterQuery() { - return ofy().load().type(teammates.storage.entity.Account.class); - } - - /** - * Get number of pages in database table. - */ - private int getNumPages() { - CriteriaBuilder cb = HibernateUtil.getCriteriaBuilder(); - CriteriaQuery countQuery = cb.createQuery(Long.class); - countQuery.select(cb.count(countQuery.from(sqlEntityClass))); - long countResults = HibernateUtil.createQuery(countQuery).getSingleResult().longValue(); - int numPages = (int) (Math.ceil((double) countResults / (double) CONST_SQL_FETCH_BASE_SIZE)); - log(String.format("Has %d entities with %d pages", countResults, numPages)); - - return numPages; - } - - /** - * Sort SQL entities by id in ascending order and return entities on page. - * @param pageNum page in a sorted entities tables - * @return list of SQL entities on page num - */ - protected List lookupSqlEntitiesByPageNumber(int pageNum) { - CriteriaBuilder cb = HibernateUtil.getCriteriaBuilder(); - CriteriaQuery pageQuery = cb.createQuery(sqlEntityClass); - - // sort by id to maintain stable order. - Root root = pageQuery.from(sqlEntityClass); - pageQuery.select(root); - List orderList = new LinkedList<>(); - orderList.add(cb.asc(root.get("id"))); - pageQuery.orderBy(orderList); - - // perform query with pagination - TypedQuery query = HibernateUtil.createQuery(pageQuery); - query.setFirstResult(calculateOffset(pageNum)); - query.setMaxResults(CONST_SQL_FETCH_BASE_SIZE); - - return query.getResultList(); - } - - /** - * Lookup sql side, have all the sql entities for each sql entity, lookup - * datastore entity. - * If does not match, return failure. - */ - protected List> checkAllEntitiesForFailures() { - // WARNING: failures list might lead to OoM if too many entities, - // but okay since will fail anyway. - List> failures = new LinkedList<>(); - int numPages = getNumPages(); - if (numPages == 0) { - log("No entities available for verification"); - return failures; - } - - List setMigratedAccountBuffer = new ArrayList<>(); - - /* Query SQL and compare against datastore */ - for (int currPageNum = 1; currPageNum <= numPages; currPageNum++) { - log(String.format("Scanning Progress %d %%", - (int) ((float) currPageNum / (float) numPages * 100))); - - long startTimeForSql = System.currentTimeMillis(); - List sqlEntities = lookupSqlEntitiesByPageNumber(currPageNum); - long endTimeForSql = System.currentTimeMillis(); - log("Querying for SQL for page " + currPageNum + " took " - + (endTimeForSql - startTimeForSql) + " milliseconds"); - - List datastoreEntitiesIds = sqlEntities.stream() - .map(entity -> generateID(entity)).collect(Collectors.toList()); - - long startTimeForDatastore = System.currentTimeMillis(); - Map datastoreEntities = lookupDataStoreEntities(datastoreEntitiesIds); - long endTimeForDatastore = System.currentTimeMillis(); - log("Querying for Datastore for page " + currPageNum + " took " - + (endTimeForDatastore - startTimeForDatastore) + " milliseconds"); - - entitiesVerified += sqlEntities.size(); - for (Account sqlEntity : sqlEntities) { - teammates.storage.entity.Account datastoreEntity = datastoreEntities.get(generateID(sqlEntity)); - if (datastoreEntity == null) { - entitiesVerified -= 1; - failures.add(new AbstractMap.SimpleEntry(sqlEntity, null)); - continue; - } - - if (!datastoreEntity.isMigrated()) { - datastoreEntity.setMigrated(true); - setMigratedAccountBuffer.add(datastoreEntity); - } - } - - /* Flushing the buffer */ - if (setMigratedAccountBuffer.size() != 0 ) { - long startTimeForDatastoreFlushing = System.currentTimeMillis(); - entitiesSetToIsMigrated += setMigratedAccountBuffer.size(); - ofy().save().entities(setMigratedAccountBuffer).now(); - setMigratedAccountBuffer.clear(); - long endTimeForDatastoreFlushing = System.currentTimeMillis(); - log("Flushing for datastore " + (endTimeForDatastoreFlushing - startTimeForDatastoreFlushing) + " milliseconds"); - } - } - - /* Query datastore and compare against SQL */ - return failures; - } - - /** - * Main function to run to verify isEqual between sql and datastore DBs. - */ - protected void runCheckAllEntities(Class sqlEntityClass, - Class datastoreEntityClass) { - HibernateUtil.beginTransaction(); - long checkStartTime = System.currentTimeMillis(); - List> failedEntities = checkAllEntitiesForFailures(); - - System.out.println("========================================"); - if (!failedEntities.isEmpty()) { - log("Errors detected"); - for (Map.Entry failure : failedEntities) { - log("Sql entity: " + failure.getKey() + " datastore entity: " + failure.getValue()); - } - } else { - log("No errors detected"); - } - - long checkEndTime = System.currentTimeMillis(); - - log("Entity took " + (checkEndTime - checkStartTime) + " milliseconds to verify"); - log("Verified " + entitiesVerified + " SQL entities successfully"); - log("Number of datastore accounts set to isMigrated " + entitiesSetToIsMigrated); - - HibernateUtil.commitTransaction(); - } - - /** - * Log a line. - * @param logLine the line to log - */ - protected void log(String logLine) { - System.out.println(String.format("%s %s", getLogPrefix(), logLine)); - } - - /** - * Run the operation. - */ - protected void doOperation() { - runCheckAllEntities(this.sqlEntityClass, this.datastoreEntityClass); - } - - public static void main(String[] args) { - MarkIsMigratedForAccounts operation = new MarkIsMigratedForAccounts(); - operation.doOperationRemotely(); - } -} diff --git a/src/client/java/teammates/client/scripts/sql/NonCourseMigrationAfterMaintenanceWindow.java b/src/client/java/teammates/client/scripts/sql/NonCourseMigrationAfterMaintenanceWindow.java deleted file mode 100644 index 49b9f3ab8884..000000000000 --- a/src/client/java/teammates/client/scripts/sql/NonCourseMigrationAfterMaintenanceWindow.java +++ /dev/null @@ -1,18 +0,0 @@ -package teammates.client.scripts.sql; - -/** - * Step 8 in the non course migration process. - */ -@SuppressWarnings("PMD") -public class NonCourseMigrationAfterMaintenanceWindow { - - public static void main(String[] args) { - try { - DataMigrationForUsageStatisticsSql.main(args); - VerifyUsageStatisticsAttributes.main(args); - VerifyNonCourseEntityCounts.main(args); - } catch (Exception e) { - System.out.println(e); - } - } -} diff --git a/src/client/java/teammates/client/scripts/sql/NonCourseMigrationBeforeMaintenanceWindow.java b/src/client/java/teammates/client/scripts/sql/NonCourseMigrationBeforeMaintenanceWindow.java deleted file mode 100644 index a76f4282cd8a..000000000000 --- a/src/client/java/teammates/client/scripts/sql/NonCourseMigrationBeforeMaintenanceWindow.java +++ /dev/null @@ -1,18 +0,0 @@ -package teammates.client.scripts.sql; - -/** - * Step 3 in the non course migration process. - */ -@SuppressWarnings("PMD") -public class NonCourseMigrationBeforeMaintenanceWindow { - - public static void main(String[] args) { - try { - DataMigrationForUsageStatisticsSql.main(args); - DataMigrationForAccountRequestSql.main(args); - VerifyAccountRequestAttributes.main(args); - } catch (Exception e) { - System.out.println(e); - } - } -} diff --git a/src/client/java/teammates/client/scripts/sql/NonCourseMigrationDuringMaintenanceWindow.java b/src/client/java/teammates/client/scripts/sql/NonCourseMigrationDuringMaintenanceWindow.java deleted file mode 100644 index f4f53b1d3b96..000000000000 --- a/src/client/java/teammates/client/scripts/sql/NonCourseMigrationDuringMaintenanceWindow.java +++ /dev/null @@ -1,17 +0,0 @@ -package teammates.client.scripts.sql; - -/** - * Step 5 in the non course migration process. - */ -@SuppressWarnings("PMD") -public class NonCourseMigrationDuringMaintenanceWindow { - - public static void main(String[] args) { - try { - DataMigrationForAccountAndReadNotificationSql.main(args); - VerifyAccountAttributes.main(args); - } catch (Exception e) { - System.out.println(e); - } - } -} diff --git a/src/client/java/teammates/client/scripts/sql/PatchDataMigrationForUsageStatisticsSql.java b/src/client/java/teammates/client/scripts/sql/PatchDataMigrationForUsageStatisticsSql.java deleted file mode 100644 index 96474589c155..000000000000 --- a/src/client/java/teammates/client/scripts/sql/PatchDataMigrationForUsageStatisticsSql.java +++ /dev/null @@ -1,293 +0,0 @@ -package teammates.client.scripts.sql; - -// CHECKSTYLE.OFF:ImportOrder -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Collectors; - -import com.google.cloud.datastore.Cursor; -import com.google.cloud.datastore.QueryResults; -import com.googlecode.objectify.cmd.Query; - -import jakarta.persistence.TypedQuery; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Root; - -import teammates.client.connector.DatastoreClient; -import teammates.client.util.ClientProperties; -import teammates.common.util.Const; -import teammates.common.util.HibernateUtil; -import teammates.storage.sqlentity.UsageStatistics; -import teammates.test.FileHelper; - -// CHECKSTYLE.ON:ImportOrder -/** - * Patch Migration Script for Usage Statistics - * - * Batch select datastore usage statistics, and then batch select SQL entities - * with the same start timestamps. - * Compare the size of the two list, if is not equal, find the missing one in - * SQL and migrate it. - */ -@SuppressWarnings("PMD") -public class PatchDataMigrationForUsageStatisticsSql extends DatastoreClient { - // the folder where the cursor position and console output is saved as a file - private static final String BASE_LOG_URI = "src/client/java/teammates/client/scripts/log/"; - - // 100 is the optimal batch size as there won't be too much time interval - // between read and save (if transaction is not used) - // cannot set number greater than 300 - // see - // https://stackoverflow.com/questions/41499505/objectify-queries-setting-limit-above-300-does-not-work - private static final int BATCH_SIZE = 100; - - // Creates the folder that will contain the stored log. - static { - new File(BASE_LOG_URI).mkdir(); - } - - AtomicLong numberOfScannedKey; - AtomicLong numberOfAffectedEntities; - AtomicLong numberOfUpdatedEntities; - - private List entitiesSavingBuffer; - - private PatchDataMigrationForUsageStatisticsSql() { - numberOfScannedKey = new AtomicLong(); - numberOfAffectedEntities = new AtomicLong(); - numberOfUpdatedEntities = new AtomicLong(); - - entitiesSavingBuffer = new ArrayList<>(); - - String connectionUrl = ClientProperties.SCRIPT_API_URL; - String username = ClientProperties.SCRIPT_API_NAME; - String password = ClientProperties.SCRIPT_API_PASSWORD; - - HibernateUtil.buildSessionFactory(connectionUrl, username, password); - } - - public static void main(String[] args) { - new PatchDataMigrationForUsageStatisticsSql().doOperationRemotely(); - } - - /** - * Returns the log prefix. - */ - protected String getLogPrefix() { - return String.format("Usage Statistics Patch Migration:"); - } - - private boolean isPreview() { - return false; - } - - /** - * Returns whether the account has been migrated. - */ - protected boolean isMigrationNeeded(teammates.storage.entity.UsageStatistics entity) { - return true; - } - - /** - * Returns the filter query. - */ - protected Query getFilterQuery() { - return ofy().load().type(teammates.storage.entity.UsageStatistics.class); - } - - private void doMigration(teammates.storage.entity.UsageStatistics entity) { - try { - if (!isMigrationNeeded(entity)) { - return; - } - if (!isPreview()) { - migrateEntity(entity); - } - } catch (Exception e) { - logError("Problem migrating usage stats " + entity); - logError(e.getMessage()); - } - } - - /** - * Migrates the entity. In this case, add entity to buffer. - */ - protected void migrateEntity(teammates.storage.entity.UsageStatistics oldEntity) { - UsageStatistics newEntity = new UsageStatistics( - oldEntity.getStartTime(), - oldEntity.getTimePeriod(), - oldEntity.getNumResponses(), - oldEntity.getNumCourses(), - oldEntity.getNumStudents(), - oldEntity.getNumInstructors(), - oldEntity.getNumAccountRequests(), - oldEntity.getNumEmails(), - oldEntity.getNumSubmissions()); - - entitiesSavingBuffer.add(newEntity); - } - - @Override - protected void doOperation() { - log("Running " + getClass().getSimpleName() + "..."); - log("Preview: " + isPreview()); - - Cursor cursor = readPositionOfCursorFromFile().orElse(null); - if (cursor == null) { - log("Start from the beginning"); - } else { - log("Start from cursor position: " + cursor.toUrlSafe()); - } - - boolean shouldContinue = true; - while (shouldContinue) { - shouldContinue = false; - Query filterQueryKeys = getFilterQuery().limit(BATCH_SIZE); - if (cursor != null) { - filterQueryKeys = filterQueryKeys.startAt(cursor); - } - QueryResults iterator; - - iterator = filterQueryKeys.iterator(); - - while (iterator.hasNext()) { - shouldContinue = true; - - doMigration(iterator.next()); - - numberOfScannedKey.incrementAndGet(); - } - - if (shouldContinue) { - cursor = iterator.getCursorAfter(); - flushEntitiesSavingBuffer(); - savePositionOfCursorToFile(cursor); - log(String.format("Cursor Position: %s", cursor.toUrlSafe())); - log(String.format("Number Of Entity Key Scanned: %d", numberOfScannedKey.get())); - log(String.format("Number Of Entity affected: %d", numberOfAffectedEntities.get())); - log(String.format("Number Of Entity updated: %d", numberOfUpdatedEntities.get())); - } - } - - deleteCursorPositionFile(); - log(isPreview() ? "Preview Completed!" : "Migration Completed!"); - log("Total number of entities: " + numberOfScannedKey.get()); - log("Number of affected entities: " + numberOfAffectedEntities.get()); - log("Number of updated entities: " + numberOfUpdatedEntities.get()); - } - - /** - * Flushes the saving buffer by issuing Cloud SQL save request. - */ - private void flushEntitiesSavingBuffer() { - if (!entitiesSavingBuffer.isEmpty() && !isPreview()) { - log("Checking usage stats in batch..." + entitiesSavingBuffer.size()); - - // batch query from SQL (migrate if startTime in the datastore but not in sql) - List instantList = entitiesSavingBuffer.stream().map(entity -> entity.getStartTime()) - .collect(Collectors.toList()); - - HibernateUtil.beginTransaction(); - CriteriaBuilder cb = HibernateUtil.getCriteriaBuilder(); - CriteriaQuery cr = cb.createQuery( - teammates.storage.sqlentity.UsageStatistics.class); - Root root = cr - .from(teammates.storage.sqlentity.UsageStatistics.class); - - cr.select(root).where(root.get("startTime").in(instantList)); - TypedQuery query = HibernateUtil.createQuery(cr); - List sqlEntitiesFound = query.getResultList(); - if (sqlEntitiesFound.size() != entitiesSavingBuffer.size()) { - Set foundInstants = sqlEntitiesFound.stream().map(entity -> entity.getStartTime()) - .collect(Collectors.toSet()); - for (teammates.storage.sqlentity.UsageStatistics entity : entitiesSavingBuffer) { - if (foundInstants.contains(entity.getStartTime())) { - continue; - } - // entity is not found in SQL - log("Migrating missing usage stats: it's start time is: " + entity.getStartTime().toString()); - numberOfAffectedEntities.incrementAndGet(); - numberOfUpdatedEntities.incrementAndGet(); - HibernateUtil.persist(entity); - } - } - - HibernateUtil.flushSession(); - HibernateUtil.clearSession(); - HibernateUtil.commitTransaction(); - - } - entitiesSavingBuffer.clear(); - } - - /** - * Saves the cursor position to a file so it can be used in the next run. - */ - private void savePositionOfCursorToFile(Cursor cursor) { - try { - FileHelper.saveFile( - BASE_LOG_URI + this.getClass().getSimpleName() + ".cursor", cursor.toUrlSafe()); - } catch (IOException e) { - logError("Fail to save cursor position " + e.getMessage()); - } - } - - /** - * Reads the cursor position from the saved file. - * - * @return cursor if the file can be properly decoded. - */ - private Optional readPositionOfCursorFromFile() { - try { - String cursorPosition = FileHelper.readFile(BASE_LOG_URI + this.getClass().getSimpleName() + ".cursor"); - return Optional.of(Cursor.fromUrlSafe(cursorPosition)); - } catch (IOException | IllegalArgumentException e) { - return Optional.empty(); - } - } - - /** - * Deletes the cursor position file. - */ - private void deleteCursorPositionFile() { - FileHelper.deleteFile(BASE_LOG_URI + this.getClass().getSimpleName() + ".cursor"); - } - - /** - * Logs a comment. - */ - protected void log(String logLine) { - System.out.println(String.format("%s %s", getLogPrefix(), logLine)); - - Path logPath = Paths.get(BASE_LOG_URI + this.getClass().getSimpleName() + ".log"); - try (OutputStream logFile = Files.newOutputStream(logPath, - StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.APPEND)) { - logFile.write((logLine + System.lineSeparator()).getBytes(Const.ENCODING)); - } catch (Exception e) { - System.err.println("Error writing log line: " + logLine); - System.err.println(e.getMessage()); - } - } - - /** - * Logs an error and persists it to the disk. - */ - protected void logError(String logLine) { - System.err.println(logLine); - - log("[ERROR]" + logLine); - } - -} diff --git a/src/client/java/teammates/client/scripts/sql/ReverseDataMigrationForAccount.java b/src/client/java/teammates/client/scripts/sql/ReverseDataMigrationForAccount.java deleted file mode 100644 index d3c77776f027..000000000000 --- a/src/client/java/teammates/client/scripts/sql/ReverseDataMigrationForAccount.java +++ /dev/null @@ -1,148 +0,0 @@ -package teammates.client.scripts.sql; - -// CHECKSTYLE.OFF:ImportOrder -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import jakarta.persistence.TypedQuery; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Order; -import jakarta.persistence.criteria.Root; - -import teammates.client.connector.DatastoreClient; -import teammates.client.util.ClientProperties; -import teammates.common.util.HibernateUtil; -import teammates.storage.sqlentity.AccountRequest; - -// CHECKSTYLE.ON:ImportOrder -/** - * Protected methods may be overriden. - * - * @param Datastore entity - * @param SQL entity - */ -@SuppressWarnings("PMD") -public class ReverseDataMigrationForAccount - extends DatastoreClient { - - /* - * NOTE - * Before running the verification, please enable hibernate.jdbc.fetch_size in - * HibernateUtil.java - * for optimized batch-fetching. - */ - - /** - * Batch size to fetch per page. - */ - protected static final int CONST_SQL_FETCH_BASE_SIZE = 1000; - - // Set the start time to be the time of publishing the release. - // Script will migrate accounts created AFTER this time back to datastore. - private static final String START_TIME_STRING = "2024-03-29 00:48:00.000 +0800"; - - private static final Instant START_TIME = parseStartTime(START_TIME_STRING); - - public ReverseDataMigrationForAccount() { - String connectionUrl = ClientProperties.SCRIPT_API_URL; - String username = ClientProperties.SCRIPT_API_NAME; - String password = ClientProperties.SCRIPT_API_PASSWORD; - - HibernateUtil.buildSessionFactory(connectionUrl, username, password); - } - - private static Instant parseStartTime(String startTimeString) { - if (startTimeString == null) { - return null; - } - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS Z"); - return Instant.from(formatter.parse(startTimeString)); - } - - private Map lookupDataStoreEntities(List datastoreEntitiesIds) { - return ofy().load().type(teammates.storage.entity.Account.class).ids(datastoreEntitiesIds); - } - - private String generateID(teammates.storage.sqlentity.Account sqlEntity) { - return sqlEntity.getGoogleId(); - } - - /** - * Get Account with {@code createdTime} after {@code startTime}. - */ - protected List getNewAccounts() { - CriteriaBuilder cb = HibernateUtil.getCriteriaBuilder(); - CriteriaQuery cr = cb.createQuery( - teammates.storage.sqlentity.Account.class); - Root root = cr.from(teammates.storage.sqlentity.Account.class); - cr.select(root).where(cb.greaterThan(root.get("createdAt"), START_TIME)); - - TypedQuery query = HibernateUtil.createQuery(cr); - return query.getResultList(); - } - - /* - * Reverse migrate accounts to datastore. - */ - protected void reverseMigrateToDatastore() { - // WARNING: failures list might lead to OoM if too many entities, - // but okay since will fail anyway. - System.out.println("===================reverseMigrateToDatastore====================="); - HibernateUtil.beginTransaction(); - List sqlEntities = getNewAccounts(); - HibernateUtil.commitTransaction(); - - List entitiesSavingBuffer = new LinkedList<>(); - int count = 0; - List datastoreEntitiesIds = sqlEntities.stream() - .map(entity -> generateID(entity)).collect(Collectors.toList()); - Map datastoreEntities = lookupDataStoreEntities(datastoreEntitiesIds); - for (teammates.storage.sqlentity.Account sqlEntity : sqlEntities) { - teammates.storage.entity.Account datastoreEntity = datastoreEntities.get(generateID(sqlEntity)); - if (datastoreEntity == null) { - teammates.storage.entity.Account newEntity = new teammates.storage.entity.Account( - sqlEntity.getGoogleId(), - sqlEntity.getName(), sqlEntity.getEmail(), new HashMap(), true); - entitiesSavingBuffer.add(newEntity); - count++; - } else { - continue; - } - - } - log("Saving " + count + " account entities to datastore"); - ofy().save().entities(entitiesSavingBuffer).now(); - } - - private String getLogPrefix() { - return String.format("Account reverse migrating:"); - } - - /** - * Log a line. - * - * @param logLine the line to log - */ - protected void log(String logLine) { - System.out.println(String.format("%s %s", getLogPrefix(), logLine)); - } - - /** - * Run the operation. - */ - protected void doOperation() { - reverseMigrateToDatastore(); - } - - public static void main(String[] args) { - ReverseDataMigrationForAccount script = new ReverseDataMigrationForAccount(); - script.doOperationRemotely(); - } -} diff --git a/src/e2e/java/teammates/e2e/cases/AdminSearchPageE2ETest.java b/src/e2e/java/teammates/e2e/cases/AdminSearchPageE2ETest.java index b73e82c08085..b5ce80693f0d 100644 --- a/src/e2e/java/teammates/e2e/cases/AdminSearchPageE2ETest.java +++ b/src/e2e/java/teammates/e2e/cases/AdminSearchPageE2ETest.java @@ -31,7 +31,7 @@ protected void prepareTestData() { putDocuments(testData); sqlTestData = loadSqlDataBundle("/AdminSearchPageE2ETest_SqlEntities.json"); removeAndRestoreSqlDataBundle(sqlTestData); - putSqlDocuments(sqlTestData); + doPutDocumentsSql(sqlTestData); } @Test diff --git a/src/e2e/java/teammates/e2e/cases/axe/AdminSearchPageAxeTest.java b/src/e2e/java/teammates/e2e/cases/axe/AdminSearchPageAxeTest.java index 732e06ceed31..d45e1d4e16a5 100644 --- a/src/e2e/java/teammates/e2e/cases/axe/AdminSearchPageAxeTest.java +++ b/src/e2e/java/teammates/e2e/cases/axe/AdminSearchPageAxeTest.java @@ -25,7 +25,7 @@ protected void prepareTestData() { putDocuments(testData); sqlTestData = loadSqlDataBundle("/AdminSearchPageE2ETest_SqlEntities.json"); removeAndRestoreSqlDataBundle(sqlTestData); - putSqlDocuments(sqlTestData); + doPutDocumentsSql(sqlTestData); } @Test diff --git a/src/e2e/java/teammates/e2e/cases/sql/AdminSearchPageE2ETest.java b/src/e2e/java/teammates/e2e/cases/sql/AdminSearchPageE2ETest.java deleted file mode 100644 index 780b0f212fdc..000000000000 --- a/src/e2e/java/teammates/e2e/cases/sql/AdminSearchPageE2ETest.java +++ /dev/null @@ -1,185 +0,0 @@ -package teammates.e2e.cases.sql; - -import java.time.Instant; - -import org.testng.annotations.AfterClass; -import org.testng.annotations.Test; - -import teammates.common.util.AppUrl; -import teammates.common.util.Const; -import teammates.e2e.pageobjects.AdminSearchPage; -import teammates.e2e.util.TestProperties; -import teammates.storage.sqlentity.AccountRequest; -import teammates.storage.sqlentity.Course; -import teammates.storage.sqlentity.FeedbackSession; -import teammates.storage.sqlentity.Instructor; -import teammates.storage.sqlentity.Student; - -/** - * SUT: {@link Const.WebPageURIs#ADMIN_SEARCH_PAGE}. - */ -public class AdminSearchPageE2ETest extends BaseE2ETestCase { - - @Override - protected void prepareTestData() { - if (!TestProperties.INCLUDE_SEARCH_TESTS) { - return; - } - testData = removeAndRestoreDataBundle(loadSqlDataBundle("/AdminSearchPageE2ESqlTest.json")); - putDocuments(testData); - } - - @Test - @Override - public void testAll() { - if (!TestProperties.INCLUDE_SEARCH_TESTS) { - return; - } - - AppUrl url = createFrontendUrl(Const.WebPageURIs.ADMIN_SEARCH_PAGE); - AdminSearchPage searchPage = loginAdminToPage(url, AdminSearchPage.class); - - Course course = testData.courses.get("typicalCourse1"); - Student student = testData.students.get("student1InCourse1"); - Instructor instructor = testData.instructors.get("instructor1OfCourse1"); - AccountRequest accountRequest = testData.accountRequests.get("instructor1OfCourse1"); - - ______TS("Typical case: Search student email"); - String searchContent = student.getEmail(); - searchPage.inputSearchContent(searchContent); - searchPage.clickSearchButton(); - String studentDetails = getExpectedStudentDetails(student); - String studentManageAccountLink = getExpectedStudentManageAccountLink(student); - String studentHomePageLink = getExpectedStudentHomePageLink(student); - int numExpandedRows = getExpectedNumExpandedRows(student); - searchPage.verifyStudentRowContent(student, course, studentDetails, studentManageAccountLink, - studentHomePageLink); - searchPage.verifyStudentExpandedLinks(student, numExpandedRows); - - ______TS("Typical case: Reset student google id"); - searchPage.resetStudentGoogleId(student); - student.setGoogleId(null); - searchPage.verifyStudentRowContentAfterReset(student, course); - - ______TS("Typical case: Regenerate registration key for a course student"); - searchPage.clickExpandStudentLinks(); - String originalJoinLink = searchPage.getStudentJoinLink(student); - searchPage.regenerateStudentKey(student); - searchPage.verifyRegenerateStudentKey(student, originalJoinLink); - searchPage.waitForPageToLoad(); - - ______TS("Typical case: Search for instructor email"); - searchPage.clearSearchBox(); - searchContent = instructor.getEmail(); - searchPage.inputSearchContent(searchContent); - searchPage.clickSearchButton(); - String instructorManageAccountLink = getExpectedInstructorManageAccountLink(instructor); - String instructorHomePageLink = getExpectedInstructorHomePageLink(instructor); - searchPage.verifyInstructorRowContent(instructor, course, instructorManageAccountLink, - instructorHomePageLink); - searchPage.verifyInstructorExpandedLinks(instructor); - - ______TS("Typical case: Reset instructor google id"); - searchPage.resetInstructorGoogleId(instructor); - searchPage.verifyInstructorRowContentAfterReset(instructor, course); - - ______TS("Typical case: Regenerate registration key for an instructor"); - searchPage.clickExpandInstructorLinks(); - originalJoinLink = searchPage.getInstructorJoinLink(instructor); - searchPage.regenerateInstructorKey(instructor); - searchPage.verifyRegenerateInstructorKey(instructor, originalJoinLink); - searchPage.waitForPageToLoad(); - - ______TS("Typical case: Search for account request by email"); - searchPage.clearSearchBox(); - searchContent = accountRequest.getEmail(); - searchPage.inputSearchContent(searchContent); - searchPage.clickSearchButton(); - searchPage.verifyAccountRequestRowContent(accountRequest); - searchPage.verifyAccountRequestExpandedLinks(accountRequest); - - ______TS("Typical case: Search common search key"); - searchPage.clearSearchBox(); - searchContent = "Course1"; - searchPage.inputSearchContent(searchContent); - searchPage.clickSearchButton(); - searchPage.verifyStudentRowContentAfterReset(student, course); - searchPage.verifyInstructorRowContentAfterReset(instructor, course); - searchPage.verifyAccountRequestRowContent(accountRequest); - - ______TS("Typical case: Expand and collapse links"); - searchPage.verifyLinkExpansionButtons(student, instructor, accountRequest); - - ______TS("Typical case: Reset account request successful"); - searchContent = "ASearch.instructor1@gmail.tmt"; - searchPage.clearSearchBox(); - searchPage.inputSearchContent(searchContent); - searchPage.clickSearchButton(); - searchPage.clickResetAccountRequestButton(accountRequest); - assertNull(BACKDOOR.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()).getRegisteredAt()); - - ______TS("Typical case: Delete account request successful"); - accountRequest = testData.accountRequests.get("unregisteredInstructor1"); - searchContent = accountRequest.getEmail(); - searchPage.clearSearchBox(); - searchPage.inputSearchContent(searchContent); - searchPage.clickSearchButton(); - searchPage.clickDeleteAccountRequestButton(accountRequest); - assertNull(BACKDOOR.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())); - } - - private String getExpectedStudentDetails(Student student) { - return String.format("%s [%s] (%s)", student.getCourse().getId(), - student.getSection() == null - ? Const.DEFAULT_SECTION - : student.getSection().getName(), student.getTeam().getName()); - } - - private String getExpectedStudentHomePageLink(Student student) { - return student.isRegistered() ? createFrontendUrl(Const.WebPageURIs.STUDENT_HOME_PAGE) - .withUserId(student.getGoogleId()) - .toAbsoluteString() - : ""; - } - - private String getExpectedStudentManageAccountLink(Student student) { - return student.isRegistered() ? createFrontendUrl(Const.WebPageURIs.ADMIN_ACCOUNTS_PAGE) - .withParam(Const.ParamsNames.INSTRUCTOR_ID, student.getGoogleId()) - .toAbsoluteString() - : ""; - } - - private int getExpectedNumExpandedRows(Student student) { - int expectedNumExpandedRows = 2; - for (FeedbackSession sessions : testData.feedbackSessions.values()) { - if (sessions.getCourse().equals(student.getCourse())) { - expectedNumExpandedRows += 1; - if (sessions.getResultsVisibleFromTime().isBefore(Instant.now())) { - expectedNumExpandedRows += 1; - } - } - } - return expectedNumExpandedRows; - } - - private String getExpectedInstructorHomePageLink(Instructor instructor) { - String googleId = instructor.isRegistered() ? instructor.getGoogleId() : ""; - return createFrontendUrl(Const.WebPageURIs.INSTRUCTOR_HOME_PAGE) - .withUserId(googleId) - .toAbsoluteString(); - } - - private String getExpectedInstructorManageAccountLink(Instructor instructor) { - String googleId = instructor.isRegistered() ? instructor.getGoogleId() : ""; - return createFrontendUrl(Const.WebPageURIs.ADMIN_ACCOUNTS_PAGE) - .withParam(Const.ParamsNames.INSTRUCTOR_ID, googleId) - .toAbsoluteString(); - } - - @AfterClass - public void classTeardown() { - for (AccountRequest request : testData.accountRequests.values()) { - BACKDOOR.deleteAccountRequest(request.getEmail(), request.getInstitute()); - } - } -} diff --git a/src/e2e/java/teammates/e2e/cases/sql/BaseE2ETestCase.java b/src/e2e/java/teammates/e2e/cases/sql/BaseE2ETestCase.java index fbfd60ea84b2..b61a4a8cf2ac 100644 --- a/src/e2e/java/teammates/e2e/cases/sql/BaseE2ETestCase.java +++ b/src/e2e/java/teammates/e2e/cases/sql/BaseE2ETestCase.java @@ -265,18 +265,4 @@ StudentData getStudent(String courseId, String studentEmailAddress) { protected StudentData getStudent(Student student) { return getStudent(student.getCourseId(), student.getEmail()); } - - /** - * Puts the documents in the database using BACKDOOR. - * @param dataBundle the data to be put in the database - * @return the result of the operation - */ - protected String putDocuments(SqlDataBundle dataBundle) { - try { - return BACKDOOR.putSqlDocuments(dataBundle); - } catch (HttpRequestFailedException e) { - e.printStackTrace(); - return null; - } - } } diff --git a/src/e2e/java/teammates/e2e/cases/sql/FeedbackNumScaleQuestionE2ETest.java b/src/e2e/java/teammates/e2e/cases/sql/FeedbackNumScaleQuestionE2ETest.java deleted file mode 100644 index 61e4fc619bed..000000000000 --- a/src/e2e/java/teammates/e2e/cases/sql/FeedbackNumScaleQuestionE2ETest.java +++ /dev/null @@ -1,124 +0,0 @@ -package teammates.e2e.cases.sql; - -import org.testng.annotations.Test; - -import teammates.common.datatransfer.questions.FeedbackNumericalScaleQuestionDetails; -import teammates.common.datatransfer.questions.FeedbackNumericalScaleResponseDetails; -import teammates.e2e.pageobjects.FeedbackSubmitPage; -import teammates.e2e.pageobjects.InstructorFeedbackEditPage; -import teammates.storage.sqlentity.FeedbackQuestion; -import teammates.storage.sqlentity.FeedbackResponse; -import teammates.storage.sqlentity.Student; - -/** - * SUT: {@link Const.WebPageURIs#INSTRUCTOR_SESSION_EDIT_PAGE}, {@link Const.WebPageURIs#SESSION_SUBMISSION_PAGE} - * specifically for NumScale questions. - */ -public class FeedbackNumScaleQuestionE2ETest extends BaseFeedbackQuestionE2ETest { - - @Override - protected void prepareTestData() { - testData = removeAndRestoreDataBundle(loadSqlDataBundle("/FeedbackNumScaleQuestionE2ESqlTest.json")); - - instructor = testData.instructors.get("instructor"); - course = testData.courses.get("course"); - feedbackSession = testData.feedbackSessions.get("openSession"); - student = testData.students.get("alice.tmms@FNumScaleQn.CS2104"); - } - - @Test - @Override - public void testAll() { - testEditPage(); - logout(); - testSubmitPage(); - } - - @Override - protected void testEditPage() { - InstructorFeedbackEditPage feedbackEditPage = loginToFeedbackEditPage(); - - ______TS("verify loaded question"); - FeedbackQuestion loadedQuestion = testData.feedbackQuestions.get("qn1ForFirstSession"); - FeedbackNumericalScaleQuestionDetails questionDetails = - (FeedbackNumericalScaleQuestionDetails) loadedQuestion.getQuestionDetailsCopy(); - feedbackEditPage.verifyNumScaleQuestionDetails(1, questionDetails); - - ______TS("add new question"); - // add new question exactly like loaded question - loadedQuestion.setQuestionNumber(2); - feedbackEditPage.addNumScaleQuestion(loadedQuestion); - feedbackEditPage.waitUntilAnimationFinish(); - - feedbackEditPage.verifyNumScaleQuestionDetails(2, questionDetails); - verifyPresentInDatabase(loadedQuestion); - - ______TS("copy question"); - FeedbackQuestion copiedQuestion = testData.feedbackQuestions.get("qn1ForSecondSession"); - questionDetails = (FeedbackNumericalScaleQuestionDetails) copiedQuestion.getQuestionDetailsCopy(); - feedbackEditPage.copyQuestion(copiedQuestion.getCourseId(), - copiedQuestion.getQuestionDetailsCopy().getQuestionText()); - copiedQuestion.setQuestionNumber(3); - copiedQuestion.setFeedbackSession(feedbackSession); - - feedbackEditPage.verifyNumScaleQuestionDetails(3, questionDetails); - verifyPresentInDatabase(copiedQuestion); - - ______TS("edit question"); - questionDetails = (FeedbackNumericalScaleQuestionDetails) loadedQuestion.getQuestionDetailsCopy(); - FeedbackNumericalScaleQuestionDetails newQuestionDetails = - (FeedbackNumericalScaleQuestionDetails) questionDetails.getDeepCopy(); - newQuestionDetails.setMinScale(0); - newQuestionDetails.setStep(1); - newQuestionDetails.setMaxScale(100); - loadedQuestion.setQuestionDetails(newQuestionDetails); - feedbackEditPage.editNumScaleQuestion(2, newQuestionDetails); - feedbackEditPage.waitForPageToLoad(); - - feedbackEditPage.verifyNumScaleQuestionDetails(2, newQuestionDetails); - verifyPresentInDatabase(loadedQuestion); - - // reset question details to original - loadedQuestion.setQuestionDetails(questionDetails); - } - - @Override - protected void testSubmitPage() { - FeedbackSubmitPage feedbackSubmitPage = loginToFeedbackSubmitPage(); - - ______TS("verify loaded question"); - FeedbackQuestion question = testData.feedbackQuestions.get("qn1ForFirstSession"); - Student receiver = testData.students.get("benny.tmms@FNumScaleQn.CS2104"); - feedbackSubmitPage.verifyNumScaleQuestion(1, receiver.getTeamName(), - (FeedbackNumericalScaleQuestionDetails) question.getQuestionDetailsCopy()); - - ______TS("submit response"); - FeedbackResponse response = getResponse(question, receiver, 5.4); - feedbackSubmitPage.fillNumScaleResponse(1, receiver.getTeamName(), response); - feedbackSubmitPage.clickSubmitQuestionButton(1); - - // TODO: uncomment when SubmitFeedbackResponse is working - // verifyPresentInDatabase(response); - - // ______TS("check previous response"); - // feedbackSubmitPage = getFeedbackSubmitPage(); - // feedbackSubmitPage.verifyNumScaleResponse(1, receiver.getTeamName(), response); - - // ______TS("edit response"); - // response = getResponse(question, receiver, 10.0); - // feedbackSubmitPage.fillNumScaleResponse(1, receiver.getTeamName(), response); - // feedbackSubmitPage.clickSubmitQuestionButton(1); - - // feedbackSubmitPage = getFeedbackSubmitPage(); - // feedbackSubmitPage.verifyNumScaleResponse(1, receiver.getTeamName(), response); - // verifyPresentInDatabase(response); - } - - private FeedbackResponse getResponse(FeedbackQuestion feedbackQuestion, Student receiver, Double answer) { - FeedbackNumericalScaleResponseDetails details = new FeedbackNumericalScaleResponseDetails(); - details.setAnswer(answer); - return FeedbackResponse.makeResponse( - feedbackQuestion, student.getEmail(), null, receiver.getTeamName(), null, details); - } - -} diff --git a/src/e2e/java/teammates/e2e/pageobjects/AdminSearchPage.java b/src/e2e/java/teammates/e2e/pageobjects/AdminSearchPage.java index 005b98a026b1..706e9ab5a207 100644 --- a/src/e2e/java/teammates/e2e/pageobjects/AdminSearchPage.java +++ b/src/e2e/java/teammates/e2e/pageobjects/AdminSearchPage.java @@ -18,9 +18,6 @@ import teammates.common.util.Const; import teammates.common.util.StringHelper; import teammates.storage.sqlentity.AccountRequest; -import teammates.storage.sqlentity.Course; -import teammates.storage.sqlentity.Instructor; -import teammates.storage.sqlentity.Student; /** * Represents the admin home page of the website. @@ -96,14 +93,6 @@ public void clickSearchButton() { waitForPageToLoad(); } - public void regenerateStudentKey(Student student) { - WebElement studentRow = getStudentRow(student); - studentRow.findElement(By.xpath("//button[text()='Regenerate key']")).click(); - - waitForConfirmationModalAndClickOk(); - waitForPageToLoad(true); - } - public void regenerateStudentKey(StudentAttributes student) { WebElement studentRow = getStudentRow(student); studentRow.findElement(By.xpath("//button[text()='Regenerate key']")).click(); @@ -112,30 +101,6 @@ public void regenerateStudentKey(StudentAttributes student) { waitForPageToLoad(true); } - public void verifyRegenerateStudentKey(Student student, String originalJoinLink) { - verifyStatusMessage("Student's key for this course has been successfully regenerated," - + " and the email has been sent."); - - String regeneratedJoinLink = getStudentJoinLink(student); - assertNotEquals(regeneratedJoinLink, originalJoinLink); - } - - public void verifyRegenerateStudentKey(StudentAttributes student, String originalJoinLink) { - verifyStatusMessage("Student's key for this course has been successfully regenerated," - + " and the email has been sent."); - - String regeneratedJoinLink = getStudentJoinLink(student); - assertNotEquals(regeneratedJoinLink, originalJoinLink); - } - - public void regenerateInstructorKey(Instructor instructor) { - WebElement instructorRow = getInstructorRow(instructor); - instructorRow.findElement(By.xpath("//button[text()='Regenerate key']")).click(); - - waitForConfirmationModalAndClickOk(); - waitForPageToLoad(true); - } - public void regenerateInstructorKey(InstructorAttributes instructor) { WebElement instructorRow = getInstructorRow(instructor); instructorRow.findElement(By.xpath("//button[text()='Regenerate key']")).click(); @@ -178,25 +143,6 @@ public String removeSpanFromText(String text) { return text.replace("", "").replace("", ""); } - public WebElement getStudentRow(Student student) { - String details = String.format("%s [%s] (%s)", student.getCourse().getId(), - student.getSection() == null - ? Const.DEFAULT_SECTION - : student.getSection().getName(), student.getTeam().getName()); - WebElement table = browser.driver.findElement(By.id("search-table-student")); - List rows = table.findElements(By.tagName("tr")); - for (WebElement row : rows) { - List columns = row.findElements(By.tagName("td")); - if (!columns.isEmpty() && removeSpanFromText(columns.get(STUDENT_COL_DETAILS - 1) - .getAttribute("innerHTML")).contains(details) - && removeSpanFromText(columns.get(STUDENT_COL_NAME - 1) - .getAttribute("innerHTML")).contains(student.getName())) { - return row; - } - } - return null; - } - public WebElement getStudentRow(StudentAttributes student) { String details = String.format("%s [%s] (%s)", student.getCourse(), student.getSection() == null ? Const.DEFAULT_SECTION : student.getSection(), student.getTeam()); @@ -249,25 +195,11 @@ public String getStudentJoinLink(WebElement studentRow) { return getExpandedRowInputValue(studentRow, EXPANDED_ROWS_HEADER_COURSE_JOIN_LINK); } - public String getStudentJoinLink(Student student) { - WebElement studentRow = getStudentRow(student); - return getStudentJoinLink(studentRow); - } - public String getStudentJoinLink(StudentAttributes student) { WebElement studentRow = getStudentRow(student); return getStudentJoinLink(studentRow); } - public void resetStudentGoogleId(Student student) { - WebElement studentRow = getStudentRow(student); - WebElement link = studentRow.findElement(By.linkText(LINK_TEXT_RESET_GOOGLE_ID)); - link.click(); - - waitForConfirmationModalAndClickOk(); - waitForElementStaleness(link); - } - public void resetStudentGoogleId(StudentAttributes student) { WebElement studentRow = getStudentRow(student); WebElement link = studentRow.findElement(By.linkText(LINK_TEXT_RESET_GOOGLE_ID)); @@ -277,21 +209,6 @@ public void resetStudentGoogleId(StudentAttributes student) { waitForElementStaleness(link); } - public WebElement getInstructorRow(Instructor instructor) { - WebElement table = browser.driver.findElement(By.id("search-table-instructor")); - List rows = table.findElements(By.tagName("tr")); - for (WebElement row : rows) { - List columns = row.findElements(By.tagName("td")); - if (columns.size() >= 3 && (removeSpanFromText(columns.get(2) - .getAttribute("innerHTML")).contains(instructor.getGoogleId()) - || removeSpanFromText(columns.get(1) - .getAttribute("innerHTML")).contains(instructor.getName()))) { - return row; - } - } - return null; - } - public WebElement getInstructorRow(InstructorAttributes instructor) { String courseId = instructor.getCourseId(); List rows = browser.driver.findElements(By.cssSelector("#search-table-instructor tbody tr")); @@ -339,25 +256,11 @@ public String getInstructorJoinLink(WebElement instructorRow) { return getExpandedRowInputValue(instructorRow, EXPANDED_ROWS_HEADER_COURSE_JOIN_LINK); } - public String getInstructorJoinLink(Instructor instructor) { - WebElement instructorRow = getInstructorRow(instructor); - return getInstructorJoinLink(instructorRow); - } - public String getInstructorJoinLink(InstructorAttributes instructor) { WebElement instructorRow = getInstructorRow(instructor); return getInstructorJoinLink(instructorRow); } - public void resetInstructorGoogleId(Instructor instructor) { - WebElement instructorRow = getInstructorRow(instructor); - WebElement link = instructorRow.findElement(By.linkText(LINK_TEXT_RESET_GOOGLE_ID)); - link.click(); - - waitForConfirmationModalAndClickOk(); - waitForElementStaleness(link); - } - public void resetInstructorGoogleId(InstructorAttributes instructor) { WebElement instructorRow = getInstructorRow(instructor); WebElement link = instructorRow.findElement(By.linkText(LINK_TEXT_RESET_GOOGLE_ID)); @@ -483,32 +386,6 @@ private String getExpandedRowInputValue(WebElement row, String rowHeader) { } } - public void verifyStudentRowContent(Student student, Course course, - String expectedDetails, String expectedManageAccountLink, - String expectedHomePageLink) { - WebElement studentRow = getStudentRow(student); - String actualDetails = getStudentDetails(studentRow); - String actualName = getStudentName(studentRow); - String actualGoogleId = getStudentGoogleId(studentRow); - String actualHomepageLink = getStudentHomeLink(studentRow); - String actualInstitute = getStudentInstitute(studentRow); - String actualComment = getStudentComments(studentRow); - String actualManageAccountLink = getStudentManageAccountLink(studentRow); - - String expectedName = student.getName(); - String expectedGoogleId = StringHelper.convertToEmptyStringIfNull(student.getGoogleId()); - String expectedInstitute = StringHelper.convertToEmptyStringIfNull(course.getInstitute()); - String expectedComment = StringHelper.convertToEmptyStringIfNull(student.getComments()); - - assertEquals(expectedDetails, actualDetails); - assertEquals(expectedName, actualName); - assertEquals(expectedGoogleId, actualGoogleId); - assertEquals(expectedInstitute, actualInstitute); - assertEquals(expectedComment, actualComment); - assertEquals(expectedManageAccountLink, actualManageAccountLink); - assertEquals(expectedHomePageLink, actualHomepageLink); - } - public void verifyStudentRowContent(StudentAttributes student, CourseAttributes course, String expectedDetails, String expectedManageAccountLink, String expectedHomePageLink) { @@ -535,35 +412,6 @@ public void verifyStudentRowContent(StudentAttributes student, CourseAttributes assertEquals(expectedHomePageLink, actualHomepageLink); } - public void verifyStudentRowContentAfterReset(Student student, Course course) { - WebElement studentRow = getStudentRow(student); - String actualName = getStudentName(studentRow); - String actualInstitute = getStudentInstitute(studentRow); - String actualComment = getStudentComments(studentRow); - - String expectedName = student.getName(); - String expectedInstitute = StringHelper.convertToEmptyStringIfNull(course.getInstitute()); - String expectedComment = StringHelper.convertToEmptyStringIfNull(student.getComments()); - - assertEquals(expectedName, actualName); - assertEquals(expectedInstitute, actualInstitute); - assertEquals(expectedComment, actualComment); - } - - public void verifyStudentExpandedLinks(Student student, int expectedNumExpandedRows) { - clickExpandStudentLinks(); - WebElement studentRow = getStudentRow(student); - String actualEmail = getStudentEmail(studentRow); - String actualJoinLink = getStudentJoinLink(studentRow); - int actualNumExpandedRows = getNumExpandedRows(studentRow); - - String expectedEmail = student.getEmail(); - - assertEquals(expectedEmail, actualEmail); - assertNotEquals("", actualJoinLink); - assertEquals(expectedNumExpandedRows, actualNumExpandedRows); - } - public void verifyStudentExpandedLinks(StudentAttributes student, int expectedNumExpandedRows) { clickExpandStudentLinks(); WebElement studentRow = getStudentRow(student); @@ -578,29 +426,6 @@ public void verifyStudentExpandedLinks(StudentAttributes student, int expectedNu assertEquals(expectedNumExpandedRows, actualNumExpandedRows); } - public void verifyInstructorRowContent(Instructor instructor, Course course, - String expectedManageAccountLink, String expectedHomePageLink) { - WebElement instructorRow = getInstructorRow(instructor); - String actualCourseId = getInstructorCourseId(instructorRow); - String actualName = getInstructorName(instructorRow); - String actualGoogleId = getInstructorGoogleId(instructorRow); - String actualHomePageLink = getInstructorHomePageLink(instructorRow); - String actualInstitute = getInstructorInstitute(instructorRow); - String actualManageAccountLink = getInstructorManageAccountLink(instructorRow); - - String expectedCourseId = instructor.getCourseId(); - String expectedName = instructor.getName(); - String expectedGoogleId = StringHelper.convertToEmptyStringIfNull(instructor.getGoogleId()); - String expectedInstitute = StringHelper.convertToEmptyStringIfNull(course.getInstitute()); - - assertEquals(expectedCourseId, actualCourseId); - assertEquals(expectedName, actualName); - assertEquals(expectedGoogleId, actualGoogleId); - assertEquals(expectedHomePageLink, actualHomePageLink); - assertEquals(expectedInstitute, actualInstitute); - assertEquals(expectedManageAccountLink, actualManageAccountLink); - } - public void verifyInstructorRowContent(InstructorAttributes instructor, CourseAttributes course, String expectedManageAccountLink, String expectedHomePageLink) { WebElement instructorRow = getInstructorRow(instructor); @@ -624,33 +449,6 @@ public void verifyInstructorRowContent(InstructorAttributes instructor, CourseAt assertEquals(expectedManageAccountLink, actualManageAccountLink); } - public void verifyInstructorRowContentAfterReset(Instructor instructor, Course course) { - WebElement instructorRow = getInstructorRow(instructor); - String actualCourseId = getInstructorCourseId(instructorRow); - String actualName = getInstructorName(instructorRow); - String actualInstitute = getInstructorInstitute(instructorRow); - - String expectedCourseId = instructor.getCourseId(); - String expectedName = instructor.getName(); - String expectedInstitute = StringHelper.convertToEmptyStringIfNull(course.getInstitute()); - - assertEquals(expectedCourseId, actualCourseId); - assertEquals(expectedName, actualName); - assertEquals(expectedInstitute, actualInstitute); - } - - public void verifyInstructorExpandedLinks(Instructor instructor) { - clickExpandInstructorLinks(); - WebElement instructorRow = getInstructorRow(instructor); - String actualEmail = getInstructorEmail(instructorRow); - String actualJoinLink = getInstructorJoinLink(instructorRow); - - String expectedEmail = instructor.getEmail(); - - assertEquals(expectedEmail, actualEmail); - assertNotEquals("", actualJoinLink); - } - public void verifyInstructorExpandedLinks(InstructorAttributes instructor) { clickExpandInstructorLinks(); WebElement instructorRow = getInstructorRow(instructor); @@ -717,43 +515,6 @@ public void verifyAccountRequestExpandedLinks(AccountRequest accountRequest) { assertFalse(actualRegistrationLink.isBlank()); } - public void verifyLinkExpansionButtons(Student student, - Instructor instructor, AccountRequest accountRequest) { - WebElement studentRow = getStudentRow(student); - WebElement instructorRow = getInstructorRow(instructor); - WebElement accountRequestRow = getAccountRequestRow(accountRequest); - - clickExpandStudentLinks(); - clickExpandInstructorLinks(); - clickExpandAccountRequestLinks(); - int numExpandedStudentRows = getNumExpandedRows(studentRow); - int numExpandedInstructorRows = getNumExpandedRows(instructorRow); - int numExpandedAccountRequestRows = getNumExpandedRows(accountRequestRow); - assertNotEquals(numExpandedStudentRows, 0); - assertNotEquals(numExpandedInstructorRows, 0); - assertNotEquals(numExpandedAccountRequestRows, 0); - - clickCollapseInstructorLinks(); - numExpandedStudentRows = getNumExpandedRows(studentRow); - numExpandedInstructorRows = getNumExpandedRows(instructorRow); - numExpandedAccountRequestRows = getNumExpandedRows(accountRequestRow); - assertNotEquals(numExpandedStudentRows, 0); - assertEquals(numExpandedInstructorRows, 0); - assertNotEquals(numExpandedAccountRequestRows, 0); - - clickExpandInstructorLinks(); - clickCollapseStudentLinks(); - clickCollapseAccountRequestLinks(); - waitUntilAnimationFinish(); - - numExpandedStudentRows = getNumExpandedRows(studentRow); - numExpandedInstructorRows = getNumExpandedRows(instructorRow); - numExpandedAccountRequestRows = getNumExpandedRows(accountRequestRow); - assertEquals(numExpandedStudentRows, 0); - assertNotEquals(numExpandedInstructorRows, 0); - assertEquals(numExpandedAccountRequestRows, 0); - } - public void verifyLinkExpansionButtons(StudentAttributes student, InstructorAttributes instructor, AccountRequestAttributes accountRequest) { WebElement studentRow = getStudentRow(student); @@ -828,11 +589,11 @@ public void verifyLinkExpansionButtons(StudentAttributes student, assertEquals(numExpandedAccountRequestRows, 0); } - public void verifyRegenerateInstructorKey(Instructor instructor, String originalJoinLink) { - verifyStatusMessage("Instructor's key for this course has been successfully regenerated," + public void verifyRegenerateStudentKey(StudentAttributes student, String originalJoinLink) { + verifyStatusMessage("Student's key for this course has been successfully regenerated," + " and the email has been sent."); - String regeneratedJoinLink = getInstructorJoinLink(instructor); + String regeneratedJoinLink = getStudentJoinLink(student); assertNotEquals(regeneratedJoinLink, originalJoinLink); } @@ -843,4 +604,5 @@ public void verifyRegenerateInstructorKey(InstructorAttributes instructor, Strin String regeneratedJoinLink = getInstructorJoinLink(instructor); assertNotEquals(regeneratedJoinLink, originalJoinLink); } + } diff --git a/src/e2e/java/teammates/e2e/pageobjects/FeedbackSubmitPage.java b/src/e2e/java/teammates/e2e/pageobjects/FeedbackSubmitPage.java index 11911490a041..e32bbcaaa96e 100644 --- a/src/e2e/java/teammates/e2e/pageobjects/FeedbackSubmitPage.java +++ b/src/e2e/java/teammates/e2e/pageobjects/FeedbackSubmitPage.java @@ -392,12 +392,6 @@ public void fillNumScaleResponse(int qnNumber, String recipient, FeedbackRespons fillTextBox(getNumScaleInput(qnNumber, recipient), Double.toString(responseDetails.getAnswer())); } - public void fillNumScaleResponse(int qnNumber, String recipient, FeedbackResponse response) { - FeedbackNumericalScaleResponseDetails responseDetails = - (FeedbackNumericalScaleResponseDetails) response.getFeedbackResponseDetailsCopy(); - fillTextBox(getNumScaleInput(qnNumber, recipient), Double.toString(responseDetails.getAnswer())); - } - public void verifyNumScaleResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) { FeedbackNumericalScaleResponseDetails responseDetails = (FeedbackNumericalScaleResponseDetails) response.getResponseDetailsCopy(); @@ -405,13 +399,6 @@ public void verifyNumScaleResponse(int qnNumber, String recipient, FeedbackRespo getDoubleString(responseDetails.getAnswer())); } - public void verifyNumScaleResponse(int qnNumber, String recipient, FeedbackResponse response) { - FeedbackNumericalScaleResponseDetails responseDetails = - (FeedbackNumericalScaleResponseDetails) response.getFeedbackResponseDetailsCopy(); - assertEquals(getNumScaleInput(qnNumber, recipient).getAttribute("value"), - getDoubleString(responseDetails.getAnswer())); - } - public void verifyConstSumQuestion(int qnNumber, String recipient, FeedbackConstantSumQuestionDetails questionDetails) { if (!questionDetails.isDistributeToRecipients()) { diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java index 8c824ccc1d7f..f104afcc7ba2 100644 --- a/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java +++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java @@ -674,16 +674,6 @@ public void addNumScaleQuestion(FeedbackQuestionAttributes feedbackQuestion) { clickSaveNewQuestionButton(); } - public void addNumScaleQuestion(FeedbackQuestion feedbackQuestion) { - addNewQuestion(5); - int questionNum = getNumQuestions(); - inputQuestionDetails(questionNum, feedbackQuestion); - FeedbackNumericalScaleQuestionDetails questionDetails = - (FeedbackNumericalScaleQuestionDetails) feedbackQuestion.getQuestionDetailsCopy(); - inputNumScaleDetails(questionNum, questionDetails); - clickSaveNewQuestionButton(); - } - public void editNumScaleQuestion(int questionNum, FeedbackNumericalScaleQuestionDetails questionDetails) { clickEditQuestionButton(questionNum); inputNumScaleDetails(questionNum, questionDetails); diff --git a/src/e2e/resources/data/AdminSearchPageE2ESqlTest.json b/src/e2e/resources/data/AdminSearchPageE2ESqlTest.json deleted file mode 100644 index 94b28ae6ad2a..000000000000 --- a/src/e2e/resources/data/AdminSearchPageE2ESqlTest.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "accounts": { - "instructor1OfCourse1": { - "id": "00000000-0000-4000-8000-000000000001", - "googleId": "tm.e2e.ASearch.instr1", - "name": "Instructor1 of Course1", - "email": "ASearch.instructor1@gmail.tmt" - }, - "instructor2OfCourse1": { - "id": "00000000-0000-4000-8000-000000000002", - "googleId": "tm.e2e.ASearch.instr2", - "name": "Instructor2 of Course1", - "email": "ASearch.instructor2@gmail.tmt" - }, - "student1InCourse1": { - "id": "00000000-0000-4000-8000-000000000003", - "googleId": "tm.e2e.ASearch.student1", - "name": "Student1 in course1", - "email": "ASearch.student@gmail.tmt" - } - }, - "accountRequests": { - "instructor1OfCourse1": { - "name": "Instructor1 of Course1", - "email": "ASearch.instructor1@gmail.tmt", - "institute": "TEAMMATES Test Institute 1", - "createdAt": "2011-01-01T00:00:00Z", - "registeredAt": "1970-02-14T00:00:00Z" - }, - "instructor2OfCourse1": { - "name": "Instructor2 of Course1", - "email": "ASearch.instructor2@gmail.tmt", - "institute": "TEAMMATES Test Institute 1", - "createdAt": "2011-01-01T00:00:00Z", - "registeredAt": "1970-02-14T00:00:00Z" - }, - "unregisteredInstructor1": { - "name": "Typical Instructor Name", - "email": "ASearch.unregisteredinstructor1@gmail.tmt", - "institute": "TEAMMATES Test Institute 1", - "createdAt": "2011-01-01T00:00:00Z" - } - }, - "courses": { - "typicalCourse1": { - "createdAt": "2012-04-01T23:59:00Z", - "id": "00000000-0000-4000-8000-000000000303", - "name": "ASearch Course 1", - "institute": "TEAMMATES Test Institute 0", - "timeZone": "Africa/Johannesburg" - } - }, - "sections": { - "section1InCourse1": { - "id": "00000000-0000-4000-8000-000000000201", - "course": { - "id": "00000000-0000-4000-8000-000000000303" - }, - "name": "Section 1" - } - }, - "teams": { - "team1InCourse1": { - "id": "00000000-0000-4000-8000-000000000301", - "section": { - "id": "00000000-0000-4000-8000-000000000201" - }, - "name": "Team 1" - } - }, - "instructors": { - "instructor1OfCourse1": { - "id": "00000000-0000-4000-8000-000000000501", - "account": { - "id": "00000000-0000-4000-8000-000000000001" - }, - "course": { - "id": "00000000-0000-4000-8000-000000000303" - }, - "name": "Instructor1 of ASearch Course1", - "email": "ASearch.instructor@gmail.tmt", - "role": "INSTRUCTOR_PERMISSION_ROLE_COOWNER", - "isDisplayedToStudents": true, - "displayName": "Instructor", - "privileges": { - "courseLevel": { - "canModifyCourse": true, - "canModifyInstructor": true, - "canModifySession": true, - "canModifyStudent": true, - "canViewStudentInSections": true, - "canViewSessionInSections": true, - "canSubmitSessionInSections": true, - "canModifySessionCommentsInSections": true - }, - "sectionLevel": {}, - "sessionLevel": {} - } - } - }, - "students": { - "student1InCourse1": { - "id": "00000000-0000-4000-8000-000000000601", - "account": { - "id": "00000000-0000-4000-8000-000000000003" - }, - "course": { - "id": "00000000-0000-4000-8000-000000000303" - }, - "team": { - "id": "00000000-0000-4000-8000-000000000301" - }, - "email": "ASearch.student@gmail.tmt", - "name": "Student1 In ASearch Course1", - "comments": "comment for student1Course1" - } - } -} diff --git a/src/e2e/resources/data/FeedbackNumScaleQuestionE2ESqlTest.json b/src/e2e/resources/data/FeedbackNumScaleQuestionE2ESqlTest.json deleted file mode 100644 index 908ecff638c6..000000000000 --- a/src/e2e/resources/data/FeedbackNumScaleQuestionE2ESqlTest.json +++ /dev/null @@ -1,268 +0,0 @@ -{ - "accounts": { - "instructorWithSessions": { - "googleId": "tm.e2e.FNumScaleQn.instructor", - "name": "Teammates Test", - "email": "tmms.test@gmail.tmt", - "id": "00000000-0000-4000-8000-000000000001" - }, - "tm.e2e.FNumScaleQn.alice.tmms": { - "googleId": "tm.e2e.FNumScaleQn.alice.tmms", - "name": "Alice Betsy", - "email": "alice.b.tmms@gmail.tmt", - "id": "00000000-0000-4000-8000-000000000002" - } - }, - "accountRequests": {}, - "courses": { - "course": { - "id": "tm.e2e.FNumScaleQn.CS2104", - "name": "Programming Language Concepts", - "institute": "TEAMMATES Test Institute 1", - "timeZone": "Africa/Johannesburg" - }, - "course2": { - "id": "tm.e2e.FNumScaleQn.CS1101", - "name": "Programming Methodology", - "institute": "TEAMMATES Test Institute 1", - "timeZone": "Africa/Johannesburg" - } - }, - "instructors": { - "instructor": { - "account": { - "id": "00000000-0000-4000-8000-000000000001" - }, - "name": "Teammates Test", - "email": "tmms.test@gmail.tmt", - "role": "INSTRUCTOR_PERMISSION_ROLE_COOWNER", - "isDisplayedToStudents": true, - "privileges": { - "courseLevel": { - "canViewStudentInSections": true, - "canSubmitSessionInSections": true, - "canModifySessionCommentsInSections": true, - "canModifyCourse": true, - "canViewSessionInSections": true, - "canModifySession": true, - "canModifyStudent": true, - "canModifyInstructor": true - }, - "sectionLevel": {}, - "sessionLevel": {} - }, - "id": "00000000-0000-4000-8000-000000000501", - "course": { - "id": "tm.e2e.FNumScaleQn.CS2104" - }, - "displayName": "Co-owner" - }, - "instructor2": { - "account": { - "id": "00000000-0000-4000-8000-000000000001" - }, - "name": "Teammates Test", - "email": "tmms.test@gmail.tmt", - "role": "INSTRUCTOR_PERMISSION_ROLE_COOWNER", - "isDisplayedToStudents": true, - "privileges": { - "courseLevel": { - "canViewStudentInSections": true, - "canSubmitSessionInSections": true, - "canModifySessionCommentsInSections": true, - "canModifyCourse": true, - "canViewSessionInSections": true, - "canModifySession": true, - "canModifyStudent": true, - "canModifyInstructor": true - }, - "sectionLevel": {}, - "sessionLevel": {} - }, - "id": "00000000-0000-4000-8000-000000000502", - "course": { - "id": "tm.e2e.FNumScaleQn.CS1101" - }, - "displayName": "Co-owner" - } - }, - "sections": { - "ProgrammingLanguageConceptsNone": { - "id": "00000000-0000-4000-8000-000000000101", - "course": { - "id": "tm.e2e.FNumScaleQn.CS2104" - }, - "name": "None" - }, - "ProgrammingMethodologyNone": { - "id": "00000000-0000-4000-8000-000000000102", - "course": { - "id": "tm.e2e.FNumScaleQn.CS1101" - }, - "name": "None" - } - }, - "teams": { - "ProgrammingLanguageConceptsTeam1": { - "id": "00000000-0000-4000-8000-000000000201", - "section": { - "id": "00000000-0000-4000-8000-000000000101" - }, - "name": "Team 1" - }, - "ProgrammingLanguageConceptsTeam2": { - "id": "00000000-0000-4000-8000-000000000202", - "section": { - "id": "00000000-0000-4000-8000-000000000101" - }, - "name": "Team 2" - }, - "ProgrammingMethodologyTeam1": { - "id": "00000000-0000-4000-8000-000000000203", - "section": { - "id": "00000000-0000-4000-8000-000000000102" - }, - "name": "Team 1" - } - }, - "feedbackSessions": { - "openSession": { - "creatorEmail": "tmms.test@gmail.tmt", - "instructions": "

Instructions for first session

", - "createdTime": "2012-04-01T23:59:00Z", - "startTime": "2012-04-01T22:00:00Z", - "endTime": "2026-04-30T22:00:00Z", - "sessionVisibleFromTime": "2012-04-01T22:00:00Z", - "resultsVisibleFromTime": "2026-05-01T22:00:00Z", - "timeZone": "Africa/Johannesburg", - "gracePeriod": 10, - "sentOpenEmail": false, - "sentClosingEmail": false, - "sentClosedEmail": false, - "sentPublishedEmail": false, - "isOpeningEmailEnabled": true, - "isClosingEmailEnabled": true, - "isPublishedEmailEnabled": true, - "studentDeadlines": {}, - "instructorDeadlines": {}, - "id": "00000000-0000-4000-8000-000000000701", - "course": { - "id": "tm.e2e.FNumScaleQn.CS2104" - }, - "name": "First Session" - }, - "openSession2": { - "creatorEmail": "tmms.test@gmail.tmt", - "instructions": "

Instructions for second session

", - "createdTime": "2012-04-01T23:59:00Z", - "startTime": "2012-04-01T22:00:00Z", - "endTime": "2026-04-30T22:00:00Z", - "sessionVisibleFromTime": "2012-04-01T22:00:00Z", - "resultsVisibleFromTime": "2026-05-01T22:00:00Z", - "timeZone": "Africa/Johannesburg", - "gracePeriod": 10, - "sentOpenEmail": false, - "sentClosingEmail": false, - "sentClosedEmail": false, - "sentPublishedEmail": false, - "isOpeningEmailEnabled": true, - "isClosingEmailEnabled": true, - "isPublishedEmailEnabled": true, - "studentDeadlines": {}, - "instructorDeadlines": {}, - "id": "00000000-0000-4000-8000-000000000702", - "course": { - "id": "tm.e2e.FNumScaleQn.CS1101" - }, - "name": "Second Session" - } - }, - "feedbackQuestions": { - "qn1ForFirstSession": { - "id": "00000000-0000-4000-8000-000000000801", - "feedbackSession": { - "id": "00000000-0000-4000-8000-000000000701" - }, - "questionDetails": { - "questionType": "NUMSCALE", - "questionText": "Rate this team's product", - "minScale": 0, - "maxScale": 10, - "step": 0.2 - }, - "description": "

Testing description for first session

", - "questionNumber": 1, - "giverType": "STUDENTS", - "recipientType": "TEAMS_EXCLUDING_SELF", - "numOfEntitiesToGiveFeedbackTo": 1, - "showResponsesTo": ["INSTRUCTORS", "RECEIVER"], - "showGiverNameTo": ["INSTRUCTORS"], - "showRecipientNameTo": ["INSTRUCTORS", "RECEIVER"] - }, - "qn1ForSecondSession": { - "id": "00000000-0000-4000-8000-000000000802", - "feedbackSession": { - "id": "00000000-0000-4000-8000-000000000702" - }, - "questionDetails": { - "questionType": "NUMSCALE", - "questionText": "Rate this team's teamwork", - "minScale": 1, - "maxScale": 10, - "step": 0.005 - }, - "description": "

Testing description for second session

", - "questionNumber": 1, - "giverType": "STUDENTS", - "recipientType": "TEAMS_EXCLUDING_SELF", - "numOfEntitiesToGiveFeedbackTo": 1, - "showResponsesTo": ["INSTRUCTORS", "RECEIVER"], - "showGiverNameTo": ["INSTRUCTORS"], - "showRecipientNameTo": ["INSTRUCTORS", "RECEIVER"] - } - }, - "notifications": {}, - "readNotifications": {}, - "feedbackResponseComments": {}, - "students": { - "alice.tmms@FNumScaleQn.CS2104": { - "id": "00000000-0000-4000-8000-000000000601", - "account": { - "id": "00000000-0000-4000-8000-000000000002" - }, - "course": { - "id": "tm.e2e.FNumScaleQn.CS2104" - }, - "team": { - "id": "00000000-0000-4000-8000-000000000201" - }, - "email": "alice.b.tmms@gmail.tmt", - "name": "Alice Betsy", - "comments": "This student's name is Alice Betsy" - }, - "benny.tmms@FNumScaleQn.CS2104": { - "id": "00000000-0000-4000-8000-000000000602", - "course": { - "id": "tm.e2e.FNumScaleQn.CS2104" - }, - "team": { - "id": "00000000-0000-4000-8000-000000000202" - }, - "email": "benny.tmms@gmail.tmt", - "name": "Benny Charles", - "comments": "This student's name is Benny Charles" - }, - "charlie.tmms@FNumScaleQn.CS1101": { - "id": "00000000-0000-4000-8000-000000000603", - "course": { - "id": "tm.e2e.FNumScaleQn.CS1101" - }, - "team": { - "id": "00000000-0000-4000-8000-000000000203" - }, - "email": "charlie.tmms@gmail.tmt", - "name": "Charlie Davis", - "comments": "This student's name is Charlie Davis" - } - } -} diff --git a/src/e2e/resources/testng-e2e-sql.xml b/src/e2e/resources/testng-e2e-sql.xml index 21c4b3146b75..78b6a952f732 100644 --- a/src/e2e/resources/testng-e2e-sql.xml +++ b/src/e2e/resources/testng-e2e-sql.xml @@ -10,10 +10,8 @@ - - diff --git a/src/it/java/teammates/it/ui/webapi/GetCoursesActionIT.java b/src/it/java/teammates/it/ui/webapi/GetCoursesActionIT.java index b10149b70b07..408821c38b72 100644 --- a/src/it/java/teammates/it/ui/webapi/GetCoursesActionIT.java +++ b/src/it/java/teammates/it/ui/webapi/GetCoursesActionIT.java @@ -122,7 +122,6 @@ public void testGetCoursesAction_withStudentEntityType_shouldReturnCorrectCourse loginAsStudent(student.getGoogleId()); CoursesData courses = getValidCourses(params); - courses.getCourses().sort((c1, c2) -> c1.getCourseId().compareTo(c2.getCourseId())); assertEquals(3, courses.getCourses().size()); Course expectedCourse1 = typicalBundle.courses.get("typicalCourse1"); Course expectedCourse2 = typicalBundle.courses.get("typicalCourse2"); diff --git a/src/lnp/java/teammates/lnp/sql/BaseLNPTestCase.java b/src/lnp/java/teammates/lnp/sql/BaseLNPTestCase.java deleted file mode 100644 index 94cb0eb698b8..000000000000 --- a/src/lnp/java/teammates/lnp/sql/BaseLNPTestCase.java +++ /dev/null @@ -1,361 +0,0 @@ -package teammates.lnp.sql; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Arrays; -import java.util.List; -import java.util.StringJoiner; - -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.jmeter.engine.StandardJMeterEngine; -import org.apache.jmeter.report.config.ConfigurationException; -import org.apache.jmeter.report.dashboard.GenerationException; -import org.apache.jmeter.report.dashboard.ReportGenerator; -import org.apache.jmeter.reporters.ResultCollector; -import org.apache.jmeter.reporters.Summariser; -import org.apache.jmeter.save.SaveService; -import org.apache.jmeter.util.JMeterUtils; -import org.apache.jorphan.collections.HashTree; -import org.apache.jorphan.collections.ListedHashTree; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.stream.JsonReader; - -import teammates.common.datatransfer.SqlDataBundle; -import teammates.common.exception.HttpRequestFailedException; -import teammates.common.util.JsonUtils; -import teammates.common.util.Logger; -import teammates.lnp.util.BackDoor; -import teammates.lnp.util.LNPResultsStatistics; -import teammates.lnp.util.LNPSpecification; -import teammates.lnp.util.LNPSqlTestData; -import teammates.lnp.util.TestProperties; -import teammates.test.BaseTestCase; -import teammates.test.FileHelper; - -/** - * Base class for all L&P test cases. - */ -public abstract class BaseLNPTestCase extends BaseTestCase { - - static final String GET = HttpGet.METHOD_NAME; - static final String POST = HttpPost.METHOD_NAME; - static final String PUT = HttpPut.METHOD_NAME; - static final String DELETE = HttpDelete.METHOD_NAME; - - private static final Logger log = Logger.getLogger(); - - private static final int RESULT_COUNT = 3; - - final BackDoor backdoor = BackDoor.getInstance(); - String timeStamp; - LNPSpecification specification; - - /** - * Returns the test data used for the current test. - */ - protected abstract LNPSqlTestData getTestData(); - - /** - * Returns the JMeter test plan for the L&P test case. - * @return A nested tree structure that consists of the various elements that are used in the JMeter test. - */ - protected abstract ListedHashTree getLnpTestPlan(); - - /** - * Sets up the specification for this L&P test case. - */ - protected abstract void setupSpecification(); - - /** - * Returns the path to the generated JSON data bundle file. - */ - protected String getJsonDataPath() { - return "/" + getClass().getSimpleName() + timeStamp + ".json"; - } - - /** - * Returns the path to the generated JMeter CSV config file. - */ - protected String getCsvConfigPath() { - return "/" + getClass().getSimpleName() + "Config" + timeStamp + ".csv"; - } - - /** - * Returns the path to the generated JTL test results file. - */ - protected String getJtlResultsPath() { - return "/" + getClass().getSimpleName() + timeStamp + ".jtl"; - } - - @Override - protected String getTestDataFolder() { - return TestProperties.LNP_TEST_DATA_FOLDER; - } - - /** - * Returns the path to the data file, relative to the project root directory. - */ - protected String getPathToTestDataFile(String fileName) { - return getTestDataFolder() + fileName; - } - - /** - * Returns the path to the JSON test results statistics file, relative to the project root directory. - */ - private String getPathToTestStatisticsResultsFile() { - return String.format("%s/%sStatistics%s.json", TestProperties.LNP_TEST_RESULTS_FOLDER, - this.getClass().getSimpleName(), this.timeStamp); - } - - String createFileAndDirectory(String directory, String fileName) throws IOException { - File dir = new File(directory); - if (!dir.exists()) { - dir.mkdir(); - } - - String pathToFile = directory + fileName; - File file = new File(pathToFile); - - // Write data to the file; overwrite if it already exists - if (file.exists()) { - file.delete(); - } - file.createNewFile(); - return pathToFile; - } - - /** - * Creates the JSON data and writes it to the file specified by {@link #getJsonDataPath()}. - */ - void createJsonDataFile(LNPSqlTestData testData) throws IOException { - SqlDataBundle jsonData = testData.generateJsonData(); - - String pathToResultFile = createFileAndDirectory(TestProperties.LNP_TEST_DATA_FOLDER, getJsonDataPath()); - try (BufferedWriter bw = Files.newBufferedWriter(Paths.get(pathToResultFile))) { - bw.write(JsonUtils.toJson(jsonData, SqlDataBundle.class)); - bw.flush(); - } - } - - /** - * Creates the CSV data and writes it to the file specified by {@link #getCsvConfigPath()}. - */ - private void createCsvConfigDataFile(LNPSqlTestData testData) throws IOException { - List headers = testData.generateCsvHeaders(); - List> valuesList = testData.generateCsvData(); - - String pathToCsvFile = createFileAndDirectory(TestProperties.LNP_TEST_DATA_FOLDER, getCsvConfigPath()); - try (BufferedWriter bw = Files.newBufferedWriter(Paths.get(pathToCsvFile))) { - // Write headers and data to the CSV file - bw.write(convertToCsv(headers)); - - for (List values : valuesList) { - bw.write(convertToCsv(values)); - } - - bw.flush(); - } - } - - /** - * Converts the list of {@code values} to a CSV row. - * @return A single string containing {@code values} separated by pipelines and ending with newline. - */ - String convertToCsv(List values) { - StringJoiner csvRow = new StringJoiner("|", "", "\n"); - for (String value : values) { - csvRow.add(value); - } - return csvRow.toString(); - } - - /** - * Returns the L&P test results statistics. - * @return The initialized result statistics from the L&P test results. - * @throws IOException if there is an error when loading the result file. - */ - private LNPResultsStatistics getResultsStatistics() throws IOException { - Gson gson = new Gson(); - JsonReader reader = new JsonReader(Files.newBufferedReader(Paths.get(getPathToTestStatisticsResultsFile()))); - JsonObject jsonObject = gson.fromJson(reader, JsonObject.class); - - JsonObject endpointStats = jsonObject.getAsJsonObject("HTTP Request Sampler"); - return gson.fromJson(endpointStats, LNPResultsStatistics.class); - } - - /** - * Renames the default results statistics file to the name of the test. - */ - private void renameStatisticsFile() { - File defaultFile = new File(TestProperties.LNP_TEST_RESULTS_FOLDER + "/statistics.json"); - File lnpStatisticsFile = new File(getPathToTestStatisticsResultsFile()); - - if (lnpStatisticsFile.exists()) { - lnpStatisticsFile.delete(); - } - if (!defaultFile.renameTo(lnpStatisticsFile)) { - log.warning("Failed to rename generated statistics.json file."); - } - } - - /** - * Setup and load the JMeter configuration and property files to run the Jmeter test. - * @throws IOException if the save service properties file cannot be loaded. - */ - private void setJmeterProperties() throws IOException { - JMeterUtils.loadJMeterProperties(TestProperties.JMETER_PROPERTIES_PATH); - JMeterUtils.setJMeterHome(TestProperties.JMETER_HOME); - JMeterUtils.initLocale(); - SaveService.loadProperties(); - } - - /** - * Creates the JSON test data and CSV config data files for the performance test from {@code testData}. - */ - protected void createTestData() throws IOException, HttpRequestFailedException { - LNPSqlTestData testData = getTestData(); - createJsonDataFile(testData); - persistTestData(); - createCsvConfigDataFile(testData); - } - - /** - * Creates the entities in the database from the JSON data file. - */ - protected void persistTestData() throws IOException, HttpRequestFailedException { - SqlDataBundle dataBundle = loadSqlDataBundle(getJsonDataPath()); - SqlDataBundle responseBody = backdoor.removeAndRestoreSqlDataBundle(dataBundle); - - String pathToResultFile = createFileAndDirectory(TestProperties.LNP_TEST_DATA_FOLDER, getJsonDataPath()); - String jsonValue = JsonUtils.toJson(responseBody, SqlDataBundle.class); - FileHelper.saveFile(pathToResultFile, jsonValue); - } - - /** - * Display the L&P results on the console. - */ - protected void displayLnpResults() throws IOException { - LNPResultsStatistics resultsStats = getResultsStatistics(); - - resultsStats.displayLnpResultsStatistics(); - specification.verifyLnpTestSuccess(resultsStats); - } - - /** - * Runs the JMeter test. - * @param shouldCreateJmxFile true if the generated test plan should be saved to a `.jmx` file which - * can be opened in the JMeter GUI, and false otherwise. - */ - protected void runJmeter(boolean shouldCreateJmxFile) throws IOException { - StandardJMeterEngine jmeter = new StandardJMeterEngine(); - setJmeterProperties(); - - HashTree testPlan = getLnpTestPlan(); - - if (shouldCreateJmxFile) { - String pathToConfigFile = createFileAndDirectory( - TestProperties.LNP_TEST_CONFIG_FOLDER, "/" + getClass().getSimpleName() + ".jmx"); - SaveService.saveTree(testPlan, Files.newOutputStream(Paths.get(pathToConfigFile))); - } - - // Add result collector to the test plan for generating results file - Summariser summariser = null; - String summariserName = JMeterUtils.getPropDefault("summariser.name", "summary"); - if (summariserName.length() > 0) { - summariser = new Summariser(summariserName); - } - - String resultsFile = createFileAndDirectory(TestProperties.LNP_TEST_RESULTS_FOLDER, getJtlResultsPath()); - ResultCollector resultCollector = new ResultCollector(summariser); - resultCollector.setFilename(resultsFile); - testPlan.add(testPlan.getArray()[0], resultCollector); - - // Run Jmeter Test - jmeter.configure(testPlan); - jmeter.run(); - - try { - ReportGenerator reportGenerator = new ReportGenerator(resultsFile, null); - reportGenerator.generate(); - } catch (ConfigurationException | GenerationException e) { - log.warning(e.getMessage()); - } - - renameStatisticsFile(); - } - - /** - * Deletes the data that was created in the database from the JSON data file. - */ - protected void deleteTestData() { - SqlDataBundle dataBundle = loadSqlDataBundle(getJsonDataPath()); - backdoor.removeSqlDataBundle(dataBundle); - } - - /** - * Deletes the JSON and CSV data files that were created. - */ - protected void deleteDataFiles() throws IOException { - String pathToJsonFile = getPathToTestDataFile(getJsonDataPath()); - String pathToCsvFile = getPathToTestDataFile(getCsvConfigPath()); - - Files.delete(Paths.get(pathToJsonFile)); - Files.delete(Paths.get(pathToCsvFile)); - } - - /** - * Deletes the oldest excess result .jtl file and the statistics file, if there are more than RESULT_COUNT. - */ - protected void cleanupResults() throws IOException { - File[] fileList = new File(TestProperties.LNP_TEST_RESULTS_FOLDER) - .listFiles((d, s) -> { - return s.contains(this.getClass().getSimpleName()); - }); - if (fileList == null) { - fileList = new File[] {}; - } - Arrays.sort(fileList, (a, b) -> { - return b.getName().compareTo(a.getName()); - }); - - int jtlCounter = 0; - int statisticsCounter = 0; - for (File file : fileList) { - if (file.getName().contains("Statistics")) { - statisticsCounter++; - if (statisticsCounter > RESULT_COUNT) { - Files.delete(file.toPath()); - } - } else { - jtlCounter++; - if (jtlCounter > RESULT_COUNT) { - Files.delete(file.toPath()); - } - } - } - } - - /** - * Sanitize the string to be CSV-safe string. - */ - protected String sanitizeForCsv(String originalString) { - return String.format("\"%s\"", originalString.replace(System.lineSeparator(), "").replace("\"", "\"\"")); - } - - /** - * Generates timestamp for generated statistics/CSV files in order to prevent concurrency issues. - */ - protected void generateTimeStamp() { - this.timeStamp = ZonedDateTime.now().format(DateTimeFormatter.ofPattern("_uuuuMMddHHmmss")); - } -} diff --git a/src/lnp/java/teammates/lnp/sql/InstructorCourseUpdateLNPTest.java b/src/lnp/java/teammates/lnp/sql/InstructorCourseUpdateLNPTest.java deleted file mode 100644 index 6196fd1f916f..000000000000 --- a/src/lnp/java/teammates/lnp/sql/InstructorCourseUpdateLNPTest.java +++ /dev/null @@ -1,216 +0,0 @@ -package teammates.lnp.sql; - -import java.io.IOException; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.apache.jmeter.protocol.http.control.HeaderManager; -import org.apache.jorphan.collections.HashTree; -import org.apache.jorphan.collections.ListedHashTree; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import teammates.common.datatransfer.InstructorPermissionRole; -import teammates.common.datatransfer.InstructorPrivileges; -import teammates.common.datatransfer.SqlDataBundle; -import teammates.common.exception.HttpRequestFailedException; -import teammates.common.util.Const; -import teammates.common.util.JsonUtils; -import teammates.lnp.util.JMeterElements; -import teammates.lnp.util.LNPSpecification; -import teammates.lnp.util.LNPSqlTestData; -import teammates.storage.sqlentity.Account; -import teammates.storage.sqlentity.Course; -import teammates.storage.sqlentity.FeedbackSession; -import teammates.storage.sqlentity.Instructor; -import teammates.ui.request.CourseUpdateRequest; - -/** - * L&P Test Case for course update cascade API. - */ -public class InstructorCourseUpdateLNPTest extends BaseLNPTestCase { - private static final int NUM_INSTRUCTORS = 1; - private static final int RAMP_UP_PERIOD = NUM_INSTRUCTORS * 2; - - private static final int NUM_FEEDBACK_SESSIONS = 500; - - private static final String COURSE_ID = "TestData.CS101"; - private static final String COURSE_NAME = "LnPCourse"; - private static final String COURSE_TIME_ZONE = "UTC"; - private static final String COURSE_INSTITUTE = "LnpInstitute"; - - private static final String ACCOUNT_NAME = "LnpAccount"; - - private static final String UPDATE_COURSE_NAME = "updatedCourse"; - private static final String UPDATE_COURSE_TIME_ZONE = "GMT"; - - private static final String INSTRUCTOR_ID = "LnPInstructor_id"; - private static final String INSTRUCTOR_NAME = "LnPInstructor"; - private static final String INSTRUCTOR_EMAIL = "tmms.test@gmail.tmt"; - - private static final String FEEDBACK_SESSION_NAME = "Test Feedback Session"; - - private static final double ERROR_RATE_LIMIT = 0.01; - private static final double MEAN_RESP_TIME_LIMIT = 10; - - @Override - protected LNPSqlTestData getTestData() { - Account instructorAccount = new Account(INSTRUCTOR_ID, ACCOUNT_NAME, INSTRUCTOR_EMAIL); - Course instructorCourse = new Course(COURSE_ID, COURSE_NAME, COURSE_TIME_ZONE, COURSE_INSTITUTE); - return new LNPSqlTestData() { - @Override - protected Map generateCourses() { - Map courses = new HashMap<>(); - - courses.put(COURSE_NAME, instructorCourse); - - return courses; - } - - @Override - protected Map generateAccounts() { - Map accounts = new HashMap<>(); - - accounts.put(ACCOUNT_NAME, instructorAccount); - - return accounts; - } - - @Override - protected Map generateInstructors() { - Map instructors = new HashMap<>(); - - Instructor instructor = new Instructor( - instructorCourse, INSTRUCTOR_NAME, INSTRUCTOR_EMAIL, - true, "Co-owner", InstructorPermissionRole.INSTRUCTOR_PERMISSION_ROLE_COOWNER, - new InstructorPrivileges(Const.InstructorPermissionRoleNames.INSTRUCTOR_PERMISSION_ROLE_COOWNER)); - - instructor.setAccount(instructorAccount); - instructors.put(INSTRUCTOR_NAME, instructor); - - return instructors; - } - - @Override - protected Map generateFeedbackSessions() { - Map feedbackSessions = new LinkedHashMap<>(); - - for (int i = 1; i <= NUM_FEEDBACK_SESSIONS; i++) { - Instant now = Instant.now(); - FeedbackSession session = new FeedbackSession(FEEDBACK_SESSION_NAME + " " + i, - instructorCourse, INSTRUCTOR_EMAIL, "", - now.plus(Duration.ofMinutes(1)), now.plus(Duration.ofDays(1)), - now, now.plus(Duration.ofDays(2)), null, false, false, false); - - feedbackSessions.put(FEEDBACK_SESSION_NAME + " " + i, session); - } - - return feedbackSessions; - } - - @Override - public List generateCsvHeaders() { - List headers = new ArrayList<>(); - - headers.add("loginId"); - headers.add("courseId"); - headers.add("updateData"); - - return headers; - } - - @Override - public List> generateCsvData() { - SqlDataBundle dataBundle = loadSqlDataBundle(getJsonDataPath()); - List> csvData = new ArrayList<>(); - - dataBundle.instructors.forEach((key, instructor) -> { - List csvRow = new ArrayList<>(); - - csvRow.add(INSTRUCTOR_ID); - csvRow.add(COURSE_ID); - - CourseUpdateRequest courseUpdateRequest = new CourseUpdateRequest(); - courseUpdateRequest.setCourseName(UPDATE_COURSE_NAME); - courseUpdateRequest.setTimeZone(UPDATE_COURSE_TIME_ZONE); - - String updateData = sanitizeForCsv(JsonUtils.toJson(courseUpdateRequest)); - csvRow.add(updateData); - - csvData.add(csvRow); - }); - - return csvData; - } - }; - } - - private Map getRequestHeaders() { - Map headers = new HashMap<>(); - - headers.put(Const.HeaderNames.CSRF_TOKEN, "${csrfToken}"); - headers.put("Content-Type", "application/json"); - - return headers; - } - - private String getTestEndpoint() { - return Const.ResourceURIs.COURSE + "?courseid=${courseId}"; - } - - @Override - protected ListedHashTree getLnpTestPlan() { - ListedHashTree testPlan = new ListedHashTree(JMeterElements.testPlan()); - HashTree threadGroup = testPlan.add( - JMeterElements.threadGroup(NUM_INSTRUCTORS, RAMP_UP_PERIOD, 1)); - - threadGroup.add(JMeterElements.csvDataSet(getPathToTestDataFile(getCsvConfigPath()))); - threadGroup.add(JMeterElements.cookieManager()); - threadGroup.add(JMeterElements.defaultSampler()); - - threadGroup.add(JMeterElements.onceOnlyController()) - .add(JMeterElements.loginSampler()) - .add(JMeterElements.csrfExtractor("csrfToken")); - - // Add HTTP sampler for test endpoint - HeaderManager headerManager = JMeterElements.headerManager(getRequestHeaders()); - threadGroup.add(JMeterElements.httpSampler(getTestEndpoint(), PUT, "${updateData}")) - .add(headerManager); - - return testPlan; - } - - @Override - protected void setupSpecification() { - this.specification = LNPSpecification.builder() - .withErrorRateLimit(ERROR_RATE_LIMIT) - .withMeanRespTimeLimit(MEAN_RESP_TIME_LIMIT) - .build(); - } - - @BeforeClass - public void classSetup() throws IOException, HttpRequestFailedException { - generateTimeStamp(); - createTestData(); - setupSpecification(); - } - - @Test - public void runLnpTest() throws IOException { - runJmeter(false); - displayLnpResults(); - } - - @AfterClass - public void classTearDown() throws IOException { - deleteTestData(); - deleteDataFiles(); - cleanupResults(); - } -} diff --git a/src/lnp/java/teammates/lnp/sql/package-info.java b/src/lnp/java/teammates/lnp/sql/package-info.java deleted file mode 100644 index bf87f3a9fe8f..000000000000 --- a/src/lnp/java/teammates/lnp/sql/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains L&P test cases. - */ -package teammates.lnp.sql; diff --git a/src/lnp/java/teammates/lnp/util/LNPSqlTestData.java b/src/lnp/java/teammates/lnp/util/LNPSqlTestData.java deleted file mode 100644 index 48aebf22f2df..000000000000 --- a/src/lnp/java/teammates/lnp/util/LNPSqlTestData.java +++ /dev/null @@ -1,92 +0,0 @@ -package teammates.lnp.util; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import teammates.common.datatransfer.SqlDataBundle; -import teammates.storage.sqlentity.Account; -import teammates.storage.sqlentity.Course; -import teammates.storage.sqlentity.FeedbackQuestion; -import teammates.storage.sqlentity.FeedbackResponse; -import teammates.storage.sqlentity.FeedbackResponseComment; -import teammates.storage.sqlentity.FeedbackSession; -import teammates.storage.sqlentity.Instructor; -import teammates.storage.sqlentity.Student; - -/** - * L&P test data generator. - */ -public abstract class LNPSqlTestData { - - // CHECKSTYLE.OFF:MissingJavadocMethod generator for different entities are self-explained by the method name - - protected Map generateAccounts() { - return new HashMap<>(); - } - - protected Map generateCourses() { - return new HashMap<>(); - } - - protected Map generateInstructors() { - return new HashMap<>(); - } - - protected Map generateStudents() { - return new HashMap<>(); - } - - protected Map generateFeedbackSessions() { - return new HashMap<>(); - } - - protected Map generateFeedbackQuestions() { - return new HashMap<>(); - } - - protected Map generateFeedbackResponses() { - return new HashMap<>(); - } - - protected Map generateFeedbackResponseComments() { - return new HashMap<>(); - } - - // CHECKSTYLE.ON:MissingJavadocMethod - - /** - * Returns a JSON data bundle containing the data relevant for the performance test. - */ - public SqlDataBundle generateJsonData() { - SqlDataBundle dataBundle = new SqlDataBundle(); - - dataBundle.accounts = generateAccounts(); - dataBundle.courses = generateCourses(); - dataBundle.instructors = generateInstructors(); - dataBundle.students = generateStudents(); - dataBundle.feedbackSessions = generateFeedbackSessions(); - dataBundle.feedbackQuestions = generateFeedbackQuestions(); - dataBundle.feedbackResponses = generateFeedbackResponses(); - dataBundle.feedbackResponseComments = generateFeedbackResponseComments(); - - return dataBundle; - } - - /** - * Returns list of header fields for the data in the CSV file to be generated. - * - *

Note that these header names should correspond to the variables used in the JMeter L&P test.

- */ - public abstract List generateCsvHeaders(); - - /** - * Returns the data for the entries in the CSV file to be generated. - * The order of the field values for each entry should correspond to the order of headers specified - * in {@link #generateCsvHeaders()}. - * - * @return List of entries, which are made up of a list of field values. - */ - public abstract List> generateCsvData(); - -} diff --git a/src/main/java/teammates/common/util/HibernateUtil.java b/src/main/java/teammates/common/util/HibernateUtil.java index 1bfec4e50b9e..5fb180758fc1 100644 --- a/src/main/java/teammates/common/util/HibernateUtil.java +++ b/src/main/java/teammates/common/util/HibernateUtil.java @@ -111,29 +111,22 @@ public static void buildSessionFactory(String dbUrl, String username, String pas Configuration config = new Configuration() .setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect") .setProperty("hibernate.connection.driver_class", "org.postgresql.Driver") - .setProperty("hibernate.connection.provider_class", - "org.hibernate.hikaricp.internal.HikariCPConnectionProvider") .setProperty("hibernate.connection.username", username) .setProperty("hibernate.connection.password", password) .setProperty("hibernate.connection.url", dbUrl) - .setProperty("hibernate.hbm2ddl.auto", "validate") + .setProperty("hibernate.hbm2ddl.auto", "update") .setProperty("show_sql", "true") .setProperty("hibernate.current_session_context_class", "thread") - .setProperty("hibernate.hikari.minimumIdle", "10") - .setProperty("hibernate.hikari.maximumPoolSize", "30") - .setProperty("hibernate.hikari.idleTimeout", "300000") - .setProperty("hibernate.hikari.connectionTimeout", "30000") + .setProperty("hibernate.agroal.minSize", "5") + .setProperty("hibernate.agroal.maxSize", "50") + .setProperty("hibernate.agroal.reapTimeout", "PT1M") // Uncomment only during migration for optimized batch-insertion, batch-update, and batch-fetch. - .setProperty("hibernate.jdbc.batch_size", "50") - .setProperty("hibernate.order_updates", "true") - .setProperty("hibernate.batch_versioned_data", "true") - .setProperty("hibernate.jdbc.fetch_size", "50") + // .setProperty("hibernate.jdbc.batch_size", "50") + // .setProperty("hibernate.order_updates", "true") + // .setProperty("hibernate.batch_versioned_data", "true") + // .setProperty("hibernate.jdbc.fetch_size", "50") .addPackage("teammates.storage.sqlentity"); - if (Config.IS_DEV_SERVER) { - config.setProperty("hibernate.hbm2ddl.auto", "update"); - } - for (Class cls : ANNOTATED_CLASSES) { config = config.addAnnotatedClass(cls); } diff --git a/src/main/java/teammates/sqllogic/api/SqlEmailGenerator.java b/src/main/java/teammates/sqllogic/api/SqlEmailGenerator.java index 50add8e08ff6..2bd8f2bfb08d 100644 --- a/src/main/java/teammates/sqllogic/api/SqlEmailGenerator.java +++ b/src/main/java/teammates/sqllogic/api/SqlEmailGenerator.java @@ -256,10 +256,12 @@ public EmailWrapper generateFeedbackSessionSummaryOfCourse( Course course = coursesLogic.getCourse(courseId); boolean isInstructor = emailType == EmailType.INSTRUCTOR_COURSE_LINKS_REGENERATED; - Student student = usersLogic.getStudentForEmail(courseId, userEmail); + Student student = null; Instructor instructor = null; if (isInstructor) { instructor = usersLogic.getInstructorForEmail(courseId, userEmail); + } else { + student = usersLogic.getStudentForEmail(courseId, userEmail); } List sessions = new ArrayList<>(); @@ -866,11 +868,11 @@ private EmailWrapper generateFeedbackSessionEmailBaseForNotifiedInstructors( } private boolean isYetToJoinCourse(Student student) { - return student.getAccount() == null || student.getAccount().getGoogleId().isEmpty(); + return student.getAccount().getGoogleId() == null || student.getAccount().getGoogleId().isEmpty(); } private boolean isYetToJoinCourse(Instructor instructor) { - return instructor.getAccount() == null || instructor.getAccount().getGoogleId().isEmpty(); + return instructor.getAccount().getGoogleId() == null || instructor.getAccount().getGoogleId().isEmpty(); } /** diff --git a/src/main/java/teammates/storage/sqlentity/FeedbackSession.java b/src/main/java/teammates/storage/sqlentity/FeedbackSession.java index 9f91c1b552ff..cf25a3897b5d 100644 --- a/src/main/java/teammates/storage/sqlentity/FeedbackSession.java +++ b/src/main/java/teammates/storage/sqlentity/FeedbackSession.java @@ -8,6 +8,8 @@ import java.util.UUID; import org.apache.commons.lang.StringUtils; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; import org.hibernate.annotations.UpdateTimestamp; @@ -90,10 +92,12 @@ public class FeedbackSession extends BaseEntity { private boolean isPublishedEmailSent; @OneToMany(mappedBy = "feedbackSession", cascade = CascadeType.REMOVE) + @Fetch(FetchMode.JOIN) @OnDelete(action = OnDeleteAction.CASCADE) private List deadlineExtensions = new ArrayList<>(); @OneToMany(mappedBy = "feedbackSession", cascade = CascadeType.REMOVE) + @Fetch(FetchMode.JOIN) @OnDelete(action = OnDeleteAction.CASCADE) private List feedbackQuestions = new ArrayList<>(); diff --git a/src/main/resources/db/changelog/db.changelog-root.xml b/src/main/resources/db/changelog/db.changelog-root.xml index af5c5e34d7e8..66d4b7d7c88e 100644 --- a/src/main/resources/db/changelog/db.changelog-root.xml +++ b/src/main/resources/db/changelog/db.changelog-root.xml @@ -4,5 +4,5 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"> - + diff --git a/src/main/resources/db/changelog/db.changelog-v9.0.0.xml b/src/main/resources/db/changelog/db.changelog-v9.0.0.xml deleted file mode 100644 index 94c02f08ed65..000000000000 --- a/src/main/resources/db/changelog/db.changelog-v9.0.0.xml +++ /dev/null @@ -1,539 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/db/changelog/db.changelog-v9.xml b/src/main/resources/db/changelog/db.changelog-v9.xml new file mode 100644 index 000000000000..57b6e7f9587c --- /dev/null +++ b/src/main/resources/db/changelog/db.changelog-v9.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/teammates/architecture/ArchitectureTest.java b/src/test/java/teammates/architecture/ArchitectureTest.java index 83d1221a6e23..1113db613728 100644 --- a/src/test/java/teammates/architecture/ArchitectureTest.java +++ b/src/test/java/teammates/architecture/ArchitectureTest.java @@ -49,7 +49,6 @@ public class ArchitectureTest { private static final String LNP_PACKAGE = "teammates.lnp"; private static final String LNP_CASES_PACKAGE = LNP_PACKAGE + ".cases"; - private static final String LNP_SQL_PACKAGE = LNP_PACKAGE + ".sql"; private static final String LNP_UTIL_PACKAGE = LNP_PACKAGE + ".util"; private static final String CLIENT_PACKAGE = "teammates.client"; @@ -414,13 +413,7 @@ public void testArchitecture_lnp_lnpShouldBeSelfContained() { @Test public void testArchitecture_lnp_lnpShouldNotTouchProductionCodeExceptCommonAndRequests() { noClasses().that().resideInAPackage(includeSubpackages(LNP_PACKAGE)) - .should().accessClassesThat(new DescribedPredicate<>("") { - @Override - public boolean apply(JavaClass input) { - return input.getPackageName().startsWith(STORAGE_PACKAGE) - && !input.getPackageName().startsWith(STORAGE_SQL_ENTITY_PACKAGE); - } - }) + .should().accessClassesThat().resideInAPackage(includeSubpackages(STORAGE_PACKAGE)) .orShould().accessClassesThat().resideInAPackage(includeSubpackages(LOGIC_PACKAGE)) .orShould().accessClassesThat(new DescribedPredicate<>("") { @Override @@ -449,23 +442,6 @@ public boolean apply(JavaClass input) { }).check(forClasses(LNP_CASES_PACKAGE)); } - @Test - public void testArchitecture_lnp_lnpSqlTestCasesShouldBeIndependentOfEachOther() { - noClasses().that(new DescribedPredicate<>("") { - @Override - public boolean apply(JavaClass input) { - return input.getPackageName().startsWith(LNP_SQL_PACKAGE) && !input.isInnerClass(); - } - }).should().accessClassesThat(new DescribedPredicate<>("") { - @Override - public boolean apply(JavaClass input) { - return input.getPackageName().startsWith(LNP_SQL_PACKAGE) - && !input.getSimpleName().startsWith("Base") - && !input.isInnerClass(); - } - }).check(forClasses(LNP_SQL_PACKAGE)); - } - @Test public void testArchitecture_lnp_lnpShouldNotHaveAnyDependency() { noClasses().that().resideInAPackage(includeSubpackages(LNP_UTIL_PACKAGE)) diff --git a/src/test/java/teammates/sqllogic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/sqllogic/core/AccountRequestsLogicTest.java deleted file mode 100644 index aca37f999636..000000000000 --- a/src/test/java/teammates/sqllogic/core/AccountRequestsLogicTest.java +++ /dev/null @@ -1,177 +0,0 @@ -package teammates.sqllogic.core; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import teammates.common.exception.EntityAlreadyExistsException; -import teammates.common.exception.EntityDoesNotExistException; -import teammates.common.exception.InvalidParametersException; -import teammates.common.util.Const; -import teammates.storage.sqlapi.AccountRequestsDb; -import teammates.storage.sqlentity.AccountRequest; -import teammates.test.BaseTestCase; - -/** - * SUT: {@link AccountRequestsLogic}. - */ -public class AccountRequestsLogicTest extends BaseTestCase { - - private final AccountRequestsLogic arLogic = AccountRequestsLogic.inst(); - private AccountRequestsDb arDb; - - @BeforeMethod - public void setUpMethod() { - arDb = mock(AccountRequestsDb.class); - arLogic.initLogicDependencies(arDb); - } - - @Test - public void testCreateAccountRequest_typicalRequest_success() throws Exception { - AccountRequest accountRequest = getTypicalAccountRequest(); - when(arDb.createAccountRequest(accountRequest)).thenReturn(accountRequest); - AccountRequest createdAccountRequest = arLogic.createAccountRequest(accountRequest); - - assertEquals(accountRequest, createdAccountRequest); - verify(arDb, times(1)).createAccountRequest(accountRequest); - } - - @Test - public void testCreateAccountRequest_requestAlreadyExists_failure() throws Exception { - AccountRequest duplicateAccountRequest = getTypicalAccountRequest(); - when(arDb.createAccountRequest(duplicateAccountRequest)) - .thenThrow(new EntityAlreadyExistsException("test exception")); - - assertThrows(EntityAlreadyExistsException.class, () -> { - arLogic.createAccountRequest(duplicateAccountRequest); - }); - verify(arDb, times(1)).createAccountRequest(duplicateAccountRequest); - } - - @Test - public void testCreateAccountRequest_invalidParams_failure() throws Exception { - AccountRequest invalidEmailAccountRequest = getTypicalAccountRequest(); - invalidEmailAccountRequest.setEmail("invalid email"); - when(arDb.createAccountRequest(invalidEmailAccountRequest)) - .thenThrow(new InvalidParametersException("test exception")); - - assertThrows(InvalidParametersException.class, () -> { - arLogic.createAccountRequest(invalidEmailAccountRequest); - }); - verify(arDb, times(1)).createAccountRequest(invalidEmailAccountRequest); - } - - @Test - public void testUpdateAccountRequest_typicalRequest_success() - throws InvalidParametersException, EntityDoesNotExistException { - AccountRequest ar = getTypicalAccountRequest(); - when(arDb.updateAccountRequest(ar)).thenReturn(ar); - AccountRequest updatedAr = arLogic.updateAccountRequest(ar); - - assertEquals(ar, updatedAr); - verify(arDb, times(1)).updateAccountRequest(ar); - } - - @Test - public void testUpdateAccountRequest_requestNotFound_failure() - throws InvalidParametersException, EntityDoesNotExistException { - AccountRequest arNotFound = getTypicalAccountRequest(); - when(arDb.updateAccountRequest(arNotFound)).thenThrow(new EntityDoesNotExistException("test message")); - - assertThrows(EntityDoesNotExistException.class, - () -> arLogic.updateAccountRequest(arNotFound)); - verify(arDb, times(1)).updateAccountRequest(any(AccountRequest.class)); - } - - @Test - public void testDeleteAccountRequest_typicalRequest_success() { - AccountRequest ar = getTypicalAccountRequest(); - when(arDb.getAccountRequest(ar.getEmail(), ar.getInstitute())).thenReturn(ar); - arLogic.deleteAccountRequest(ar.getEmail(), ar.getInstitute()); - - verify(arDb, times(1)).deleteAccountRequest(any(AccountRequest.class)); - } - - @Test - public void testDeleteAccountRequest_nonexistentRequest_shouldSilentlyDelete() { - arLogic.deleteAccountRequest("not_exist", "not_exist"); - - verify(arDb, times(1)).deleteAccountRequest(nullable(AccountRequest.class)); - } - - @Test - public void testGetAccountRequestByRegistrationKey_typicalRequest_success() { - AccountRequest ar = getTypicalAccountRequest(); - String regkey = "regkey"; - ar.setRegistrationKey(regkey); - when(arDb.getAccountRequestByRegistrationKey(regkey)).thenReturn(ar); - AccountRequest actualAr = - arLogic.getAccountRequestByRegistrationKey(ar.getRegistrationKey()); - - assertEquals(ar, actualAr); - verify(arDb, times(1)).getAccountRequestByRegistrationKey(regkey); - } - - @Test - public void testGetAccountRequestByRegistrationKey_nonexistentRequest_shouldReturnNull() throws Exception { - String nonexistentRegkey = "not_exist"; - when(arDb.getAccountRequestByRegistrationKey(nonexistentRegkey)).thenReturn(null); - - assertNull(arLogic.getAccountRequestByRegistrationKey(nonexistentRegkey)); - verify(arDb, times(1)).getAccountRequestByRegistrationKey(nonexistentRegkey); - } - - @Test - public void testGetAccountRequest_typicalRequest_success() { - AccountRequest expectedAr = getTypicalAccountRequest(); - when(arDb.getAccountRequest(expectedAr.getEmail(), expectedAr.getInstitute())).thenReturn(expectedAr); - AccountRequest actualAr = - arLogic.getAccountRequest(expectedAr.getEmail(), expectedAr.getInstitute()); - - assertEquals(expectedAr, actualAr); - verify(arDb, times(1)).getAccountRequest(expectedAr.getEmail(), expectedAr.getInstitute()); - } - - @Test - public void testGetAccountRequest_nonexistentRequest_shouldReturnNull() { - String nonexistentEmail = "not-found@test.com"; - String nonexistentInstitute = "not-found"; - when(arDb.getAccountRequest(nonexistentEmail, nonexistentInstitute)).thenReturn(null); - - assertNull(arLogic.getAccountRequest(nonexistentEmail, nonexistentInstitute)); - verify(arDb, times(1)).getAccountRequest(nonexistentEmail, nonexistentInstitute); - } - - @Test - public void testResetAccountRequest_typicalRequest_success() - throws InvalidParametersException, EntityDoesNotExistException { - AccountRequest accountRequest = getTypicalAccountRequest(); - accountRequest.setRegisteredAt(Const.TIME_REPRESENTS_NOW); - when(arDb.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())) - .thenReturn(accountRequest); - when(arDb.updateAccountRequest(accountRequest)).thenReturn(accountRequest); - accountRequest = arLogic.resetAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); - - assertNull(accountRequest.getRegisteredAt()); - verify(arDb, times(1)).getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); - } - - @Test - public void testResetAccountRequest_nonexistentRequest_failure() - throws InvalidParametersException, EntityDoesNotExistException { - AccountRequest accountRequest = getTypicalAccountRequest(); - accountRequest.setRegisteredAt(Const.TIME_REPRESENTS_NOW); - when(arDb.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())) - .thenReturn(null); - assertThrows(EntityDoesNotExistException.class, - () -> arLogic.resetAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())); - verify(arDb, times(1)).getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); - verify(arDb, times(0)).updateAccountRequest(nullable(AccountRequest.class)); - } -} diff --git a/src/test/java/teammates/test/BaseTestCase.java b/src/test/java/teammates/test/BaseTestCase.java index ffe08b218130..a7744061d552 100644 --- a/src/test/java/teammates/test/BaseTestCase.java +++ b/src/test/java/teammates/test/BaseTestCase.java @@ -29,7 +29,6 @@ import teammates.common.util.TimeHelperExtension; import teammates.sqllogic.core.DataBundleLogic; import teammates.storage.sqlentity.Account; -import teammates.storage.sqlentity.AccountRequest; import teammates.storage.sqlentity.Course; import teammates.storage.sqlentity.FeedbackQuestion; import teammates.storage.sqlentity.FeedbackResponse; @@ -207,10 +206,6 @@ protected FeedbackResponseComment getTypicalResponseComment(Long id) { return comment; } - protected AccountRequest getTypicalAccountRequest() { - return new AccountRequest("valid@test.com", "Test account Name", "TEAMMATES Test Institute 1"); - } - /** * Populates the feedback question and response IDs within the data bundle. *