Skip to content
Snippets Groups Projects
Verified Commit 765a6bcd authored by Luc Everse's avatar Luc Everse :passport_control:
Browse files

Test the export endpoint

parent 9f1ea8dd
No related branches found
No related tags found
2 merge requests!122AuTA 2.0 master merge,!80Feature - Assignment exports
Pipeline #178795 passed
package nl.tudelft.ewi.auta.core.controller;
import com.google.gson.Gson;
import nl.tudelft.ewi.auta.common.model.entity.ProjectEntity;
import nl.tudelft.ewi.auta.common.model.metric.Verdict;
import nl.tudelft.ewi.auta.core.database.EntityContainer;
import nl.tudelft.ewi.auta.core.database.IdentityContainer;
import nl.tudelft.ewi.auta.core.database.Repositories;
import nl.tudelft.ewi.auta.core.database.RepositoriesTestHelper;
import nl.tudelft.ewi.auta.core.model.Assignment;
import nl.tudelft.ewi.auta.core.model.Submission;
import nl.tudelft.ewi.auta.core.model.SubmissionPipelineLog;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class AssignmentExportControllerTest {
private static final String AID = "test assignment ID";
private static final String ANAME = "test assignment - look ma, no metrics";
private static final String SID_1 = "test assignment uno";
private static final String SID_2 = "test assignment twee";
private static final String SID_3 = "test assignment III";
private static final String SNAME_1 = "test assignment! part one";
private static final String SNAME_2 = "test assignment! part two: electric boogaloo";
private static final String SNAME_3 = "test assignment! part three: the search for more code";
private static final String IDENT_1 = "Arnold Schwarzenegger";
private SubmissionPipelineLog log1;
private SubmissionPipelineLog log2;
private SubmissionPipelineLog log3;
private EntityContainer econ2;
private EntityContainer econ3;
private IdentityContainer ident1;
private Repositories repositories;
private List<Submission> submissions;
private Gson gson;
private MongoTemplate mongoTemplate;
private Assignment assignment;
private MockMvc mvc;
@BeforeEach
public void before() {
// Dependencies
this.repositories = RepositoriesTestHelper.getMockRepositories();
this.gson = new Gson();
this.mongoTemplate = mock(MongoTemplate.class);
// Pipeline logs
this.log1 = new SubmissionPipelineLog();
this.log2 = new SubmissionPipelineLog();
this.log2.setSubmitted(Instant.ofEpochSecond(1234));
this.log2.setDispatched(Instant.ofEpochSecond(11223344));
this.log3 = new SubmissionPipelineLog();
this.log3.setSubmitted(Instant.ofEpochSecond(909090909));
this.log3.setDispatched(Instant.ofEpochSecond(9090909090L));
this.log3.setAnalysisDone(Instant.ofEpochSecond(90909090909L));
this.log3.setReportDone(Instant.ofEpochSecond(909090909090L));
// Submissions
final var sub1 = new Submission();
sub1.setId(SID_1);
sub1.setName(SNAME_1);
sub1.setPipelineLog(this.log1);
final var sub2 = new Submission();
sub2.setId(SID_2);
sub2.setName(SNAME_2);
sub2.setPipelineLog(this.log2);
final var sub3 = new Submission();
sub3.setId(SID_3);
sub3.setName(SNAME_3);
sub3.setPipelineLog(this.log3);
this.submissions = List.of(sub1, sub2, sub3);
when(this.mongoTemplate.stream(any(Query.class), eq(Submission.class)))
.thenReturn(new CloseableIteratorAdaptor<>(this.submissions.iterator()));
// Identities
this.ident1 = new IdentityContainer(SID_1, IDENT_1);
doReturn(Optional.of(this.ident1))
.when(this.repositories.getIdentityRepository()).findById(eq(SID_1));
// Entities
this.econ2 = new EntityContainer(new ProjectEntity(), SID_2, false, null, AID);
this.econ3 = new EntityContainer(new ProjectEntity(), SID_3, false, null, AID);
this.econ3.setVerdict(Verdict.PASS);
doReturn(Optional.of(this.econ2))
.when(this.repositories.getEntityRepository()).findByParentIds(eq(SID_2), eq(AID));
doReturn(Optional.of(this.econ3))
.when(this.repositories.getEntityRepository()).findByParentIds(eq(SID_3), eq(AID));
// Assignment
this.assignment = new Assignment();
this.assignment.setId(AID);
this.assignment.setName(ANAME);
doReturn(this.assignment)
.when(this.repositories.getAssignmentRepository()).findExisting(eq(AID));
// Spring
this.mvc = MockMvcBuilders.standaloneSetup(new AssignmentExportController(
this.repositories, this.gson, this.mongoTemplate
))
.setControllerAdvice(new RestExceptionHandler())
.build();
}
@Test
public void testDefaultExportIsJson() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export"))
.andExpect(status().isOk())
.andExpect(header().string("Content-Type", "application/json"));
}
@Test
public void testDefaultExportEverythingIsPresent() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(3)))
.andExpect(jsonPath("$[0].id", equalTo(SID_1)))
.andExpect(jsonPath("$[0].name", equalTo(SNAME_1)))
.andExpect(jsonPath("$[1].id", equalTo(SID_2)))
.andExpect(jsonPath("$[1].name", equalTo(SNAME_2)))
.andExpect(jsonPath("$[2].id", equalTo(SID_3)))
.andExpect(jsonPath("$[2].name", equalTo(SNAME_3)));
}
@Test
public void testDefaultExportNoEntityMeansPending() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].verdict", equalTo("PENDING")));
}
@Test
public void testDefaultExportUnfinishedEntityMeansPending() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[1].verdict", equalTo("PENDING")));
}
@Test
public void testDefaultExportVerdictIsTakenFromEntityContainer() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[2].verdict", equalTo("PENDING")));
}
@Test
public void testDefaultExportMissingInstantIsNotPresent() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].submitted").doesNotExist());
}
@Test
public void testDefaultExportPresentInstantIsSet() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[2].processed", equalTo("+30777-12-11T12:31:30Z")));
}
@Test
public void testDefaultExportIdentityIsIncluded() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].identity", equalTo(IDENT_1)));
}
@Test
public void testDefaultExportMissingIdentityIsNotPresent() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[1].identity").doesNotExist());
}
@Test
public void testExplicitJsonIsJson() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export?format=json"))
.andExpect(status().isOk())
.andExpect(header().string("Content-Type", "application/json"));
}
@Test
public void testBadFormat() throws Exception {
this.mvc.perform(get("/api/v1/assignment/" + AID + "/export?format=fortune-cookie"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors", hasSize(1)))
.andExpect(jsonPath("$.errors[0].code", equalTo("INVALID_FORMAT")));
}
@Test
public void testExplicitCsvIsCsv() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export?format=csv"))
.andExpect(status().isOk())
.andExpect(header().string("Content-Type", "text/csv"));
}
@Test
public void testExplicitCsvWithFormatOverrideIsCsv() throws Exception {
this.performAsync(get("/api/v1/assignment/" + AID + "/export?format=csv&csvformat=mysql"))
.andExpect(status().isOk())
.andExpect(header().string("Content-Type", "text/csv"));
}
@Test
public void testExplicitCsvWithBadFormat() throws Exception {
this.mvc.perform(get("/api/v1/assignment/" + AID + "/export?format=csv&csvformat=colons"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors", hasSize(1)))
.andExpect(jsonPath("$.errors[0].code", equalTo("INVALID_FORMAT")));
}
@Test
public void testCsvContents() throws Exception {
final var csv = this.performAsync(get("/api/v1/assignment/" + AID + "/export?format=csv"))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
assertThat(csv).hasLineCount(4);
final var lines = csv.lines().collect(Collectors.toList());
assertThat(lines).containsExactly(
"identity,submitted,dispatched,analyzed,processed,id,name,verdict,grade",
"Arnold Schwarzenegger,,,,,test assignment uno,test assignment! part one,"
+ "PENDING,0.0",
",1970-01-01T00:20:34Z,1970-05-10T21:35:44Z,,,test assignment twee,test assignment!"
+ " part two: electric boogaloo,PENDING,0.0",
",1998-10-22T21:15:09Z,2258-01-29T20:31:30Z,4850-10-17T13:15:09Z,"
+ "+30777-12-11T12:31:30Z,test assignment III,test assignment! part three:"
+ " the search for more code,PENDING,0.0"
);
}
@Test
public void testCsvHasHeader() throws Exception {
final var csv = this.performAsync(get("/api/v1/assignment/" + AID + "/export?format=csv"))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
assertThat(csv).hasLineCount(4);
final var lines = csv.lines().collect(Collectors.toList());
assertThat(lines).startsWith(
"identity,submitted,dispatched,analyzed,processed,id,name,verdict,grade"
);
}
private ResultActions performAsync(final RequestBuilder builder) throws Exception {
return this.mvc.perform(asyncDispatch(this.mvc.perform(builder).andReturn()));
}
}
package nl.tudelft.ewi.auta.core.controller;
import org.springframework.data.util.CloseableIterator;
import java.util.Iterator;
import java.util.function.Consumer;
/**
* An adaptor for normal iterators to the Spring ClosableIterator interface.
*
* @param <T> the type of data the iterator iterates over
*/
public final class CloseableIteratorAdaptor<T> implements CloseableIterator<T> {
/**
* The target iterator.
*/
private final Iterator<T> target;
public CloseableIteratorAdaptor(final Iterator<T> target) {
this.target = target;
}
@Override
public void close() {
// Does nothing
}
@Override
public boolean hasNext() {
return this.target.hasNext();
}
@Override
public T next() {
return this.target.next();
}
@Override
public void remove() {
this.target.remove();
}
@Override
public void forEachRemaining(final Consumer<? super T> action) {
this.target.forEachRemaining(action);
}
}
package nl.tudelft.ewi.auta.core.database;
import com.google.gson.Gson;
import nl.tudelft.ewi.auta.core.model.Assignment;
import nl.tudelft.ewi.auta.core.model.Submission;
import nl.tudelft.ewi.auta.core.response.exception.NoResultsException;
import nl.tudelft.ewi.auta.core.response.exception.NoSuchAssignmentException;
import nl.tudelft.ewi.auta.core.response.exception.NoSuchSubmissionException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.withSettings;
/**
* A helper class for creating testing repositories.
*/
public final class RepositoriesTestHelper {
/**
* The default assignment ID set when assignments are saved in the mock.
*/
public static final String DEFAULT_ASSIGNMENT_ID =
"repositories test helper default assignment id";
/**
* The default submission ID set when submissions are saved in the mock.
*/
public static final String DEFAULT_SUBMISSION_ID =
"repositories test helper default submission id";
/**
* Gson to be used where copy constructor
*/
private static final Gson GSON = new Gson();
private RepositoriesTestHelper() {
assert false : "Do not instantiate";
}
/**
* Creates a new set of mocked repositories.
*
* @return the repositories
*/
public static Repositories getMockRepositories() {
return new Repositories(
mock(ReportRepository.class),
getMockEntityRepository(),
getMockSubmissionRepository(),
getMockAssignmentRepository(),
getMockIdentityRepository()
);
}
/**
* Creates a new mock entity repository.
*
* All queries result in a {@link NoResultsException}.
*
* @return the entity repository
*/
public static EntityRepository getMockEntityRepository() {
final var m = mock(
EntityRepository.class,
withSettings().defaultAnswer(invocation -> {
throw new NoResultsException("no results");
})
);
doAnswer(inv -> {
final EntityContainer ec = inv.getArgument(0);
ec.preProcess();
// Copy constructor would be nice, is too much work for now though.
try (var sw = new StringWriter()) {
GSON.toJson(ec, sw);
try (var sl = new StringReader(sw.toString())) {
return GSON.fromJson(sl, EntityContainer.class);
}
}
}).when(m).saveWithPreprocess(any());
doReturn(Optional.empty()).when(m).findById(any());
doReturn(Optional.empty()).when(m).findByParentIds(any(), any());
return m;
}
/**
* Creates a new mock submission repository.
*
* All queries result in a {@link NoSuchSubmissionException}.
*
* @return the submission repository
*/
public static SubmissionRepository getMockSubmissionRepository() {
final var m = mock(
SubmissionRepository.class,
withSettings().defaultAnswer(invocation -> {
throw new NoSuchSubmissionException("no such submission");
})
);
doAnswer(inv -> {
final Submission submission = inv.getArgument(0);
final var res = new Submission(submission);
res.setId(DEFAULT_SUBMISSION_ID);
return res;
}).when(m).save(any());
doReturn(Optional.empty()).when(m).findById(any());
return m;
}
/**
* Creates a new mock assignment repository.
*
* All queries result in a {@link NoSuchAssignmentException}.
*
* @return the assignment repository
*/
public static AssignmentRepository getMockAssignmentRepository() {
final var m = mock(
AssignmentRepository.class,
withSettings().defaultAnswer(invocation -> {
throw new NoSuchAssignmentException("no such assignment");
})
);
doAnswer(inv -> {
final Assignment assignment = inv.getArgument(0);
final var res = new Assignment(assignment);
res.setId(DEFAULT_ASSIGNMENT_ID);
return res;
}).when(m).save(any());
doReturn(Optional.empty()).when(m).findById(any());
return m;
}
/**
* Creates a new mock identity repository.
*
* All queries result in a {@link NoSuchSubmissionException}.
*
* @return the identity repository
*/
public static IdentityRepository getMockIdentityRepository() {
final var m = mock(
IdentityRepository.class,
withSettings().defaultAnswer(invocation -> {
throw new NoSuchSubmissionException("no such submission (for identity)");
})
);
doAnswer(inv -> {
final IdentityContainer id = inv.getArgument(0);
return new IdentityContainer(id.getSubmissionId(), id.getIdentifier());
}).when(m).save(any());
doReturn(Optional.empty()).when(m).findById(any());
return m;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment