Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development: Fix exercise deletion with existing Iris messages #9875

Merged
merged 8 commits into from
Dec 3, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>

<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="20241125000900" author="kaancayli">
<preConditions onFail="MARK_RAN">
<!-- Check if the foreign keys exists before modifying -->
<foreignKeyConstraintExists foreignKeyName="fk_iris_message_session_id"/>
<foreignKeyConstraintExists foreignKeyName="fk_iris_message_content_message_id"/>
<foreignKeyConstraintExists foreignKeyName="fk_text_id_to_content_id"/>
<foreignKeyConstraintExists foreignKeyName="fk_json_id_to_content_id"/>
</preConditions>

<!-- Drop the existing foreign keys -->
<dropForeignKeyConstraint
baseTableName="iris_message"
constraintName="fk_iris_message_session_id"/>

<dropForeignKeyConstraint
baseTableName="iris_message_content"
constraintName="fk_iris_message_content_message_id"/>

<dropForeignKeyConstraint
baseTableName="iris_json_message_content"
constraintName="fk_json_id_to_content_id"/>

<dropForeignKeyConstraint
baseTableName="iris_text_message_content"
constraintName="fk_text_id_to_content_id"/>

<!-- Add the foreign keys back with ON DELETE CASCADE -->
<addForeignKeyConstraint
baseTableName="iris_message"
baseColumnNames="session_id"
referencedTableName="iris_session"
referencedColumnNames="id"
constraintName="fk_iris_message_session_id"
onDelete="CASCADE"/>

<addForeignKeyConstraint
baseTableName="iris_message_content"
baseColumnNames="message_id"
constraintName="fk_iris_message_content_message_id"
referencedTableName="iris_message"
referencedColumnNames="id"
onDelete="CASCADE"/>

<addForeignKeyConstraint
baseColumnNames="id"
baseTableName="iris_text_message_content"
constraintName="fk_text_id_to_content_id"
referencedColumnNames="id"
referencedTableName="iris_message_content"
onDelete="CASCADE"/>

<addForeignKeyConstraint
baseColumnNames="id"
baseTableName="iris_json_message_content"
constraintName="fk_json_id_to_content_id"
referencedColumnNames="id"
referencedTableName="iris_message_content"
onDelete="CASCADE"/>

</changeSet>
</databaseChangeLog>
1 change: 1 addition & 0 deletions src/main/resources/config/liquibase/master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<include file="classpath:config/liquibase/changelog/20241023456789_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20241101121000_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20241112123600_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20241125000900_changelog.xml" relativeToChangelogFile="false"/>

<!-- NOTE: please use the format "YYYYMMDDhhmmss_changelog.xml", i.e. year month day hour minutes seconds and not something else! -->
<!-- we should also stay in a chronological order! -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,30 @@
import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.test.context.support.WithMockUser;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import de.tum.cit.aet.artemis.core.domain.Course;
import de.tum.cit.aet.artemis.iris.domain.message.IrisJsonMessageContent;
import de.tum.cit.aet.artemis.iris.domain.message.IrisMessage;
import de.tum.cit.aet.artemis.iris.domain.message.IrisMessageContent;
import de.tum.cit.aet.artemis.iris.domain.message.IrisMessageSender;
import de.tum.cit.aet.artemis.iris.domain.message.IrisTextMessageContent;
import de.tum.cit.aet.artemis.iris.domain.session.IrisExerciseChatSession;
import de.tum.cit.aet.artemis.iris.domain.session.IrisSession;
import de.tum.cit.aet.artemis.iris.dto.IrisStatusDTO;
import de.tum.cit.aet.artemis.iris.repository.IrisExerciseChatSessionRepository;
import de.tum.cit.aet.artemis.iris.repository.IrisMessageRepository;
import de.tum.cit.aet.artemis.iris.service.IrisMessageService;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise;

class IrisExerciseChatSessionIntegrationTest extends AbstractIrisIntegrationTest {
Expand All @@ -24,6 +36,12 @@ class IrisExerciseChatSessionIntegrationTest extends AbstractIrisIntegrationTest
@Autowired
private IrisExerciseChatSessionRepository irisExerciseChatSessionRepository;

@Autowired
private IrisMessageService irisMessageService;

@Autowired
private IrisMessageRepository irisMessageRepository;

private ProgrammingExercise exercise;

@BeforeEach
Expand All @@ -37,6 +55,41 @@ void initTestCase() {
activateIrisFor(exercise);
}

private IrisExerciseChatSession createSessionForUser(String userLogin) {
var user = userUtilService.getUserByLogin(TEST_PREFIX + userLogin);
return irisExerciseChatSessionRepository.save(new IrisExerciseChatSession(exercise, user));
}

private IrisMessage createDefaultMockTextMessage(IrisSession irisSession) {
var messageToSend = irisSession.newMessage();
messageToSend.addContent(createMockTextContent(), createMockTextContent(), createMockTextContent());
return messageToSend;
}

private IrisMessageContent createMockTextContent() {
String[] adjectives = { "happy", "sad", "angry", "funny", "silly", "crazy", "beautiful", "smart" };
String[] nouns = { "dog", "cat", "house", "car", "book", "computer", "phone", "shoe" };

var rdm = ThreadLocalRandom.current();
String randomAdjective = adjectives[rdm.nextInt(adjectives.length)];
String randomNoun = nouns[rdm.nextInt(nouns.length)];

var text = "The " + randomAdjective + " " + randomNoun + " jumped over the lazy dog.";
return new IrisTextMessageContent(text);
}

private IrisMessage createDefaultMockJsonMessage(IrisSession irisSession) {
var messageToSend = irisSession.newMessage();
messageToSend.addContent(createMockJsonContent(), createMockJsonContent(), createMockJsonContent());
return messageToSend;
}

private IrisJsonMessageContent createMockJsonContent() {
var jsonMap = Map.of("key1", "value1", "key2", "value2", "key3", "value3");
JsonNode jsonNode = new ObjectMapper().valueToTree(jsonMap);
return new IrisJsonMessageContent(jsonNode);
}

@Test
@WithMockUser(username = TEST_PREFIX + "student1", roles = "USER")
void createSession() throws Exception {
Expand Down Expand Up @@ -101,6 +154,44 @@ void testDeleteExerciseWithIrisSession() throws Exception {
assertThat(irisExerciseChatSessionRepository.findById(irisSession.getId())).isEmpty();
}

@Test
@WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR")
void testDeleteExerciseWithIrisMessagesWithTextMessageContent() throws Exception {
var irisSession = createSessionForUser("instructor1");

// Create and some messages to the session
irisMessageService.saveMessage(createDefaultMockTextMessage(irisSession), irisSession, IrisMessageSender.USER);
irisMessageService.saveMessage(createDefaultMockTextMessage(irisSession), irisSession, IrisMessageSender.LLM);
irisMessageService.saveMessage(createDefaultMockTextMessage(irisSession), irisSession, IrisMessageSender.USER);
irisMessageService.saveMessage(createDefaultMockTextMessage(irisSession), irisSession, IrisMessageSender.LLM);

assertThat(irisExerciseChatSessionRepository.findByIdElseThrow(irisSession.getId())).isNotNull();
// Set the URL request parameters to prevent an internal server error which is irrelevant for this test
var url = "/api/programming-exercises/" + exercise.getId() + "?deleteStudentReposBuildPlans=false&deleteBaseReposBuildPlans=false";
request.delete(url, HttpStatus.OK);
assertThat(irisExerciseChatSessionRepository.findById(irisSession.getId())).isEmpty();
assertThat(irisMessageRepository.findAllBySessionId(irisSession.getId())).isEmpty();
}

@Test
@WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR")
void testDeleteExerciseWithIrisMessagesWithJsonMessageContent() throws Exception {
var irisSession = createSessionForUser("instructor1");

// Create and some messages to the session
irisMessageService.saveMessage(createDefaultMockJsonMessage(irisSession), irisSession, IrisMessageSender.USER);
irisMessageService.saveMessage(createDefaultMockJsonMessage(irisSession), irisSession, IrisMessageSender.LLM);
irisMessageService.saveMessage(createDefaultMockJsonMessage(irisSession), irisSession, IrisMessageSender.USER);
irisMessageService.saveMessage(createDefaultMockJsonMessage(irisSession), irisSession, IrisMessageSender.LLM);

assertThat(irisExerciseChatSessionRepository.findByIdElseThrow(irisSession.getId())).isNotNull();
// Set the URL request parameters to prevent an internal server error which is irrelevant for this test
var url = "/api/programming-exercises/" + exercise.getId() + "?deleteStudentReposBuildPlans=false&deleteBaseReposBuildPlans=false";
request.delete(url, HttpStatus.OK);
assertThat(irisExerciseChatSessionRepository.findById(irisSession.getId())).isEmpty();
assertThat(irisMessageRepository.findAllBySessionId(irisSession.getId())).isEmpty();
}

private static String exerciseChatUrl(long sessionId) {
return "/api/iris/exercise-chat/" + sessionId + "/sessions";
}
Expand Down
Loading